Index: llvm/test/tools/llvm-rc/Inputs/tag-menu-bad-menuitem-id.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-menu-bad-menuitem-id.rc @@ -0,0 +1,3 @@ +1 MENU { + MENUITEM "Wrong", 100000, CHECKED +} Index: llvm/test/tools/llvm-rc/Inputs/tag-menu.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-menu.rc @@ -0,0 +1,60 @@ +CheckRecursion MENU { + POPUP "A" { + POPUP "B" { + MENUITEM "a", 1 + MENUITEM "b", 2 + MENUITEM "c", 3 + POPUP "C" { + POPUP "D" { POPUP "E" { POPUP "F" { POPUP "G" { POPUP "H" { POPUP "I" { + MENUITEM "d", 57134 + }}}}}} + } + } + MENUITEM "efg", 23333 + } +} + + +CheckFlags MENU { + MENUITEM "a", 1, CHECKED + MENUITEM "b", 2, GRAYED + MENUITEM "c", 3, HELP + MENUITEM "d", 4, INACTIVE + MENUITEM "e", 5, MENUBARBREAK + MENUITEM "f", 6, MENUBREAK + MENUITEM "ad", 7, CHECKED, INACTIVE + MENUITEM SEPARATOR + POPUP "A", CHECKED { MENUITEM "x", 100 } + POPUP "B", GRAYED { MENUITEM "x", 101 } + POPUP "C", HELP { MENUITEM "x", 102 } + POPUP "D", INACTIVE { MENUITEM "x", 103 } + POPUP "E", MENUBARBREAK { MENUITEM "x", 104 } + POPUP "F", MENUBREAK { MENUITEM "x", 105 } + POPUP "G", HELP, MENUBARBREAK, GRAYED { + POPUP "H", CHECKED, MENUBREAK, HELP, INACTIVE { + MENUITEM SEPARATOR + MENUITEM "x", 106, INACTIVE, MENUBARBREAK + MENUITEM SEPARATOR + } + } + MENUITEM "abcdef", 8, help, inactive, menubarbreak, checked, grayed, menubreak +} + + +CheckOpts MENU +CHARACTERISTICS 500 +LANGUAGE 1, 1 +VERSION 128 +BEGIN + POPUP "&Only separator" { + MENUITEM SEPARATOR + } + POPUP "O&ther things" { + MENUITEM "&abcde", 1 + MENUITEM "a&bcde", 2 + MENUITEM "ab&cde", 3 + MENUITEM "abc&de", 4 + MENUITEM "abcd&e", 5 + } +END + Index: llvm/test/tools/llvm-rc/tag-menu.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/tag-menu.test @@ -0,0 +1,7 @@ +; RUN: llvm-rc /FO %t %p/Inputs/tag-menu.rc +; RUN: diff %t %p/Inputs/tag-menu.res + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-menu-bad-menuitem-id.rc 2>&1 | FileCheck %s --check-prefix BADID + +; BADID: llvm-rc: Error in MENU statement (ID 1): +; BADID-NEXT: MENUITEM action ID (100000) does not fit in 16 bits. Index: llvm/tools/llvm-rc/ResourceScriptStmt.h =================================================================== --- llvm/tools/llvm-rc/ResourceScriptStmt.h +++ llvm/tools/llvm-rc/ResourceScriptStmt.h @@ -283,6 +283,8 @@ MENUBREAK = (1 << 5) }; + enum MenuDefKind { MkBase, MkSeparator, MkMenuItem, MkPopup }; + static constexpr size_t NumFlags = 6; static StringRef OptionsStr[NumFlags]; static raw_ostream &logFlags(raw_ostream &, uint8_t Flags); @@ -290,13 +292,17 @@ return OS << "Base menu definition\n"; } virtual ~MenuDefinition() {} + + static uint16_t processFlags(uint8_t Flags); + virtual uint16_t getResFlags() const { return 0; } + virtual MenuDefKind getKind() const { return MkBase; } }; // Recursive description of a whole submenu. class MenuDefinitionList : public MenuDefinition { +public: std::vector> Definitions; -public: void addDefinition(std::unique_ptr Def) { Definitions.push_back(std::move(Def)); } @@ -309,47 +315,74 @@ class MenuSeparator : public MenuDefinition { public: raw_ostream &log(raw_ostream &) const override; + + MenuDefKind getKind() const override { return MkSeparator; } + static bool classof(const MenuDefinition *D) { + return D->getKind() == MkSeparator; + } }; // MENUITEM statement definition. // // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx class MenuItem : public MenuDefinition { +public: StringRef Name; uint32_t Id; uint8_t Flags; -public: MenuItem(StringRef Caption, uint32_t ItemId, uint8_t ItemFlags) : Name(Caption), Id(ItemId), Flags(ItemFlags) {} raw_ostream &log(raw_ostream &) const override; + + uint16_t getResFlags() const override { return processFlags(Flags); } + MenuDefKind getKind() const override { return MkMenuItem; } + static bool classof(const MenuDefinition *D) { + return D->getKind() == MkMenuItem; + } }; // POPUP statement definition. // // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381030(v=vs.85).aspx class PopupItem : public MenuDefinition { +public: StringRef Name; uint8_t Flags; MenuDefinitionList SubItems; -public: PopupItem(StringRef Caption, uint8_t ItemFlags, MenuDefinitionList &&SubItemsList) : Name(Caption), Flags(ItemFlags), SubItems(std::move(SubItemsList)) {} raw_ostream &log(raw_ostream &) const override; + + // This has an additional (0x10) flag. It doesn't match with documented + // 0x01 flag, though. + uint16_t getResFlags() const override { return processFlags(Flags) | 0x10; } + MenuDefKind getKind() const override { return MkPopup; } + static bool classof(const MenuDefinition *D) { + return D->getKind() == MkPopup; + } }; // Menu resource definition. class MenuResource : public RCResource { +public: MenuDefinitionList Elements; -public: MenuResource(OptionalStmtList &&OptStmts, MenuDefinitionList &&Items) : Elements(std::move(Items)) { OptStatements = make_unique(std::move(OptStmts)); } raw_ostream &log(raw_ostream &) const override; + + IntOrString getResourceType() const override { return 4; } + Twine getResourceTypeName() const override { return "MENU"; } + Error visit(Visitor *V) const override { return V->visitMenuResource(this); } + ResourceKind getKind() const override { return RkMenu; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkMenu; + } }; // STRINGTABLE resource. Contains a list of strings, each having its unique ID. Index: llvm/tools/llvm-rc/ResourceScriptStmt.cpp =================================================================== --- llvm/tools/llvm-rc/ResourceScriptStmt.cpp +++ llvm/tools/llvm-rc/ResourceScriptStmt.cpp @@ -75,6 +75,23 @@ return OS; } +uint16_t MenuDefinition::processFlags(uint8_t Flags) { + uint16_t Result = 0; + static uint16_t RCFlags[NumFlags] = { + 0x0008, // CHECKED + 0x0001, // GRAYED + 0x4000, // HELP + 0x0002, // INACTIVE + 0x0020, // MENUBARBREAK + 0x0040 // MENUBREAK + }; + + for (size_t i = 0; i < NumFlags; ++i) + if (Flags & (1U << i)) + Result |= RCFlags[i]; + return Result; +} + raw_ostream &MenuDefinitionList::log(raw_ostream &OS) const { OS << " Menu list starts\n"; for (auto &Item : Definitions) Index: llvm/tools/llvm-rc/ResourceSerializator.h =================================================================== --- llvm/tools/llvm-rc/ResourceSerializator.h +++ llvm/tools/llvm-rc/ResourceSerializator.h @@ -30,6 +30,7 @@ Error visitNullResource(const RCResource *) override; Error visitAcceleratorsResource(const RCResource *) override; Error visitHTMLResource(const RCResource *) override; + Error visitMenuResource(const RCResource *) override; Error visitCharacteristicsStmt(const CharacteristicsStmt *) override; Error visitLanguageStmt(const LanguageResource *) override; @@ -61,6 +62,12 @@ // HTMLResource Error writeHTMLBody(const RCResource *); + // MenuResource + Error writeMenuDefinition(const std::unique_ptr &, + uint16_t Flags); + Error writeMenuDefinitionList(const MenuDefinitionList &List); + Error writeMenuBody(const RCResource *); + // Output stream handling. std::unique_ptr FS; Index: llvm/tools/llvm-rc/ResourceSerializator.cpp =================================================================== --- llvm/tools/llvm-rc/ResourceSerializator.cpp +++ llvm/tools/llvm-rc/ResourceSerializator.cpp @@ -205,6 +205,10 @@ return writeResource(Res, &ResourceSerializator::writeHTMLBody); } +Error ResourceSerializator::visitMenuResource(const RCResource *Res) { + return writeResource(Res, &ResourceSerializator::writeMenuBody); +} + Error ResourceSerializator::visitCharacteristicsStmt( const CharacteristicsStmt *Stmt) { ObjectData.Characteristics = Stmt->Value; @@ -389,5 +393,54 @@ return appendFile(cast(Base)->HTMLLoc); } +// --- MenuResource helpers. --- // + +Error ResourceSerializator::writeMenuDefinition( + const std::unique_ptr &Def, uint16_t Flags) { + assert(Def); + const MenuDefinition *DefPtr = Def.get(); + + if (auto *MenuItemPtr = dyn_cast(DefPtr)) { + writeObject(ulittle16_t(Flags)); + RETURN_IF_ERROR( + checkNumberFits(MenuItemPtr->Id, "MENUITEM action ID")); + writeObject(ulittle16_t(MenuItemPtr->Id)); + RETURN_IF_ERROR(writeCString(MenuItemPtr->Name)); + return Error::success(); + } + + if (isa(DefPtr)) { + writeObject(ulittle16_t(Flags)); + writeObject(uint32_t(0)); + return Error::success(); + } + + auto *PopupPtr = cast(DefPtr); + writeObject(ulittle16_t(Flags)); + RETURN_IF_ERROR(writeCString(PopupPtr->Name)); + return writeMenuDefinitionList(PopupPtr->SubItems); +} + +Error ResourceSerializator::writeMenuDefinitionList( + const MenuDefinitionList &List) { + for (auto &Def : List.Definitions) { + uint16_t Flags = Def->getResFlags(); + // Last element receives an additional 0x80 flag. + if (&Def == &List.Definitions.back()) + Flags |= 0x80; + + RETURN_IF_ERROR(writeMenuDefinition(Def, Flags)); + } + return Error::success(); +} + +Error ResourceSerializator::writeMenuBody(const RCResource *Base) { + // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0. + // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx + writeObject(0); + + return writeMenuDefinitionList(cast(Base)->Elements); +} + } // namespace rc } // namespace llvm Index: llvm/tools/llvm-rc/ResourceVisitor.h =================================================================== --- llvm/tools/llvm-rc/ResourceVisitor.h +++ llvm/tools/llvm-rc/ResourceVisitor.h @@ -30,6 +30,7 @@ virtual Error visitNullResource(const RCResource *) = 0; virtual Error visitAcceleratorsResource(const RCResource *) = 0; virtual Error visitHTMLResource(const RCResource *) = 0; + virtual Error visitMenuResource(const RCResource *) = 0; virtual Error visitCharacteristicsStmt(const CharacteristicsStmt *) = 0; virtual Error visitLanguageStmt(const LanguageResource *) = 0;