Search code examples
c++menubargtkmmglibmm

Menu items always disabled in gtkmm3


I'm trying to create a program using gtkmm3 and the Application::set_menubar method. I can create the menu, but when I run the program, all of the items in the menus are greyed out. I have scoured both the glibmm and glib libraries looking for ways to enable (and later disable/hide) menu items, and I cannot find any function or method to do it. How do I fix this?

I'm compiling with:

g++ -std=c++0x -o program program.cpp `pkg-config --cflags --libs gtkmm-3.0`

Code code is as follows:

// vim:set ai et shiftwidth=4 softtabstop=4 :

#include <gtkmm.h>
#include <glibmm.h>
#include <giomm/menulinkiter.h>

class ApplicationMain{
    private:
        Glib::RefPtr<Gtk::Application> app;
        Glib::RefPtr<Gio::Menu> mnuMenu;

        /// Builds menu and puts menu actions into the application's action map
        void createMenu();

        /// Recursive method that iterates over the supplied MenuModel and
        /// extracts actions into the application's action map
        void extractMenuActions(const Glib::RefPtr<Gio::MenuModel> &model);

    public:
        ApplicationMain(int argc, char **argv);
        ~ApplicationMain();

        int run();
};

using namespace std;

/// Container for menu xml data
struct MenuStringContainer{
    static const char *string;
};

/// Builds menu and puts menu actions into the application's action map
void ApplicationMain::createMenu(){
    // Build menu from xml data
    Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create_from_string(MenuStringContainer::string);
    mnuMenu = Glib::RefPtr<Gio::Menu>::cast_dynamic(builder->get_object("menubar"));

    // Extract actions from menu model and add them to our action map
    extractMenuActions(Glib::RefPtr<Gio::MenuModel>::cast_static(mnuMenu));
}

void ApplicationMain::extractMenuActions(const Glib::RefPtr<Gio::MenuModel> &model){
    // Get the number of items in this menu model
    gint count = g_menu_model_get_n_items(model->gobj());

    // Iterate over the items in this model
    for(gint i = 0; i < count; i++){
        // Iterate over and recurse into the links in this menu model item
        auto iter = model->iterate_item_links(i);
        while(iter->next()){
            extractMenuActions(iter->get_value());
        }

        try{
            // Get the action for this item. Throws std::bad_cast if the cast can't be made
            Glib::Variant<Glib::ustring> act = Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::ustring>>(
                model->get_item_attribute(i, Gio::MENU_ATTRIBUTE_ACTION, Glib::Variant<Glib::ustring>::variant_type())
            );

            // If this action is valid, get the action name and add it to the action map
            if(act.gobj() != nullptr){
                Glib::ustring actName = act.get();
                app->add_action(Gio::SimpleAction::create(actName));
            }
        } catch(std::bad_cast &){
        }
    }
}

ApplicationMain::ApplicationMain(int argc, char **argv){
    app = Gtk::Application::create(argc, argv, "com.angellistaliuu.jaguar.Chummer5");

    chdir(Gio::File::create_for_commandline_arg(argv[0])->get_basename().c_str());
}

ApplicationMain::~ApplicationMain(){
}

int ApplicationMain::run(){
    if(app->register_application()){
        createMenu();

        app->set_menubar(mnuMenu);
        {
            Gtk::ApplicationWindow *frm = new Gtk::ApplicationWindow();

            // Allow pointer to be unreferenced when this scope ends
            Glib::RefPtr<Gtk::ApplicationWindow> frmPtr(frm);

            app->run(*frm);
        }
    } else {
        app->activate();
    }
}

int main(int argc, char **argv){
    ApplicationMain appMain(argc, argv);

    return appMain.run();
}

// XML data for application menu
const char *MenuStringContainer::string = R"rawstring(
<?xml version="1.0" encoding="UTF-8"?>
<interface>
    <menu id="menubar">
        <submenu>
            <attribute name="label">_File</attribute>
            <section>
                <item>
                    <attribute name="label">_New Character</attribute>
                    <attribute name="action">file.newchar</attribute>
                </item>
                <item>
                    <attribute name="label">New _Critter</attribute>
                    <attribute name="action">file.newcrit</attribute>
                </item>
                <item>
                    <attribute name="label">_Open</attribute>
                    <attribute name="action">file.open</attribute>
                </item>
            </section>

            <section>
                <item>
                    <attribute name="label">_Print</attribute>
                    <attribute name="action">file.print</attribute>
                </item>
                <item>
                    <attribute name="label">Print _Multiple</attribute>
                    <attribute name="action">file.printmultiple</attribute>
                </item>
                <item>
                    <attribute name="label">Print Setup</attribute>
                    <attribute name="action">file.printsetup</attribute>
                </item>
            </section>

            <section id="mru">
                <item>
                    <attribute name="label">[StickyMRU0]</attribute>
                    <attribute name="action">file.smru0</attribute>
                </item>
                <item>
                    <attribute name="label">[StickyMRU1]</attribute>
                    <attribute name="action">file.smru1</attribute>
                </item>
                <item>
                    <attribute name="label">[StickyMRU2]</attribute>
                    <attribute name="action">file.smru2</attribute>
                </item>
                <item>
                    <attribute name="label">[StickyMRU3]</attribute>
                    <attribute name="action">file.smru3</attribute>
                </item>
                <item>
                    <attribute name="label">[StickyMRU4]</attribute>
                    <attribute name="action">file.smru4</attribute>
                </item>
                <item>
                    <attribute name="label">[StickyMRU5]</attribute>
                    <attribute name="action">file.smru5</attribute>
                </item>
                <item>
                    <attribute name="label">[StickyMRU6]</attribute>
                    <attribute name="action">file.smru6</attribute>
                </item>
                <item>
                    <attribute name="label">[StickyMRU7]</attribute>
                    <attribute name="action">file.smru7</attribute>
                </item>
                <item>
                    <attribute name="label">[StickyMRU8]</attribute>
                    <attribute name="action">file.smru8</attribute>
                </item>
                <item>
                    <attribute name="label">[StickyMRU9]</attribute>
                    <attribute name="action">file.smru9</attribute>
                </item>

                <item>
                    <attribute name="label">[MRU0]</attribute>
                    <attribute name="action">file.mru0</attribute>
                </item>
                <item>
                    <attribute name="label">[MRU1]</attribute>
                    <attribute name="action">file.mru1</attribute>
                </item>
                <item>
                    <attribute name="label">[MRU2]</attribute>
                    <attribute name="action">file.mru2</attribute>
                </item>
                <item>
                    <attribute name="label">[MRU3]</attribute>
                    <attribute name="action">file.mru3</attribute>
                </item>
                <item>
                    <attribute name="label">[MRU4]</attribute>
                    <attribute name="action">file.mru4</attribute>
                </item>
                <item>
                    <attribute name="label">[MRU5]</attribute>
                    <attribute name="action">file.mru5</attribute>
                </item>
                <item>
                    <attribute name="label">[MRU6]</attribute>
                    <attribute name="action">file.mru6</attribute>
                </item>
                <item>
                    <attribute name="label">[MRU7]</attribute>
                    <attribute name="action">file.mru7</attribute>
                </item>
                <item>
                    <attribute name="label">[MRU8]</attribute>
                    <attribute name="action">file.mru8</attribute>
                </item>
                <item>
                    <attribute name="label">[MRU9]</attribute>
                    <attribute name="action">file.mru9</attribute>
                </item>
            </section>

            <section>
                <item>
                    <attribute name="label">E_xit</attribute>
                    <attribute name="action">file.exit</attribute>
                </item>
            </section>
        </submenu>
        <submenu>
            <attribute name="label">_Tools</attribute>
            <section>
                <item>
                    <attribute name="label">_Dice Roller</attribute>
                    <attribute name="action">tools.roller</attribute>
                </item>
            </section>

            <section>
                <item>
                    <attribute name="label">_Options</attribute>
                    <attribute name="action">tools.options</attribute>
                </item>
                <item>
                    <attribute name="label">Check for Updates</attribute>
                    <attribute name="action">tools.updates</attribute>
                </item>
                <item>
                    <attribute name="label">Omae</attribute>
                    <attribute name="action">tools.omae</attribute>
                </item>
            </section>
        </submenu>
        <submenu>
            <attribute name="label">_Windows</attribute>
            <section>
                <item>
                    <attribute name="label">_New Window</attribute>
                    <attribute name="action">windows.new</attribute>
                </item>
                <item>
                    <attribute name="label">C_lose All</attribute>
                    <attribute name="action">windows.closeall</attribute>
                </item>
            </section>
            <section id="windowlist">
            </section>
        </submenu>
        <submenu>
            <attribute name="label">_Help</attribute>
            <section>
            <section>
                <item>
                    <attribute name="label">Chummer Wiki</attribute>
                    <attribute name="action">help.chummerwiki</attribute>
                </item>
            </section>
                <item>
                    <attribute name="label">_Revision History</attribute>
                    <attribute name="action">help.revisionhistory</attribute>
                </item>
                <item>
                    <attribute name="label">_Dumpshock Thread</attribute>
                    <attribute name="action">help.dumpshockthread</attribute>
                </item>
                <item>
                    <attribute name="label">_About...</attribute>
                    <attribute name="action">help.about</attribute>
                </item>
            </section>
        </submenu>
    </menu>
</interface>
)rawstring"; //"(

Solution

  • You are calling register_application() manually, rather than letting GtkApplication/GApplication do that for you. You probably did that to avoid this warning, which I think is the main clue, because the gtkmm examples never need to do that:

    (a.out:24959): Gtk-CRITICAL **: gtk_application_set_menubar: assertion 'g_application_get_is_registered (G_APPLICATION (application))' failed
    

    I have reduced your test case down to a simpler test case with no class definition and no use of GtkBuilder, and now I guess the problem is probably that you are adding these actions, or the menu, too early, though I'm not sure exactly what is not yet set up properly. I can post that test case here, if you like.

    For instance, this example works, but notice that it adds the menu and its actions in the derived Gtk::Application's on_startup(): https://git.gnome.org/browse/gtkmm-documentation/tree/examples/book/application/app_and_win_menus/exampleapplication.cc#n32

    Deriving a Gtk::Application is the more correct structure anyway, allowing the app to do the right thing at the right time. GtkApplication is a rather awkward and unforgiving API, in my opinion, so I wouldn't stray from the path.

    Incidentally, it's also rather confusing to define an ApplicationMain class that doesn't derive from Gtk::Application. And you should avoid using RefPtr<> with a widget (Gtk::ApplicationWindow) class. You should never need to do any referencing or unreferencing of widgets in your application code.