Tutorial

Inti Logo

« Menus
Using an Item Factory »

Manual Menu Creation

In the true tradition of teaching, we'll show you the hard way first. There are three widgets that go into making a menubar and submenus:
This is slightly complicated by the fact that menu item widgets are used for two different things. They are both the widgets that are packed into the menu, and the widget that is packed into the menubar, which, when selected, activates the menu.

Let's look at the constructors that are used to create menus and menubars. This first constructor is used to create a new menubar.

MenuBar();

You can either use Gtk::Container::add() to add the new menubar to a window, or the Gtk::Box packing methods to pack it into a box - the same as buttons.

There are two constructors that create a new menu:

Menu();

Menu(const Gtk::AccelGroup& accel_group);

The accel_group argument is the accelerator group which holds the global accelerators for this menu. The first constructor creates the accelerator group for you and can be retrieved by calling:

Gtk::AccelGroup* get_accel_group() const;

The next three constructors are used to create menu items that are packed into the menu (and menubar).

MenuItem();

explicit MenuItem(const String& label, bool use_underline = false);

MenuItem(const String& label, Gtk::Menu& submenu, bool use_underline = false);

The label argument is the menu item text, submenu is a menu to associate with this menu item and use_underline, if true specifies that label should be parsed for the mnemonic character. The first constructor creates an empty menu item, the second constructor creates a menu item with a label already packed into it and the last constructor creates a menu item that displays another menu when activated.

Remember to differentiate between a "menu" and a "menu item". A menu item is like a button with an associated action, whereas a menu is a container holding menu items.

Once you've created a menu item you have to put it into a menu. This is done using one of the Gtk::MenuShell methods - append, prepend or insert. In order to capture when the item is selected by the user, we need to connect a signal handler to the item's activate signal. The append, prepend and insert methods will do this connection for you if you pass the method a valid slot. So, if we wanted to create a standard File menu, with the options Open, Save, and Quit, the code would look something like this:

Gtk::Menu *file_menu = new Gtk::Menu;

// Create menu items
Gtk::MenuItem *item = new Gtk::Menu("Open");
file_menu->append(item, slot(this, &MyClass::on_file_open));

item = new Gtk::Menu("Save");
file_menu->append(item, slot(this, &MyClass::on_file_save));

item = new Gtk::Menu("Quit");
file_menu->append(item, slot(this, &MyClass::on_file_quit));

// Show all the menu items
file_menu->show_all();

At this point we have our menu. Now we need to create a menubar and a menu item for the File entry, to which we add our menu. The code looks like this:

Gtk::MenuBar *menubar = new Gtk::MenuBar
window->add(*menubar);

Gtk::MenuItem *file_item = new Gtk::MenuItem("File", *file_menu);
menubar->append(*file_item);

file_item->show();
menubar->show();

If we wanted the menu right justified on the menubar, such as help menus often are, we can use the following Gtk::MenuItem method on our menu item before attaching it to the menubar.

void set_right_justified(bool right_justified);

Here is a summary of the steps needed to create a menu bar with menus attached:
Creating a popup menu is nearly the same. The difference is that the menu is not posted "automatically" by a menubar, but explicitly by calling the function Gtk::Menu::popup() from a button-press event. The button-press event handler has the prototype:

bool MyClass::event_handler(GdkEventButton *event);

To connect a button_press event handler and use the event argument to find out where to pop up the menu, Take these steps:

Connect to an event handler with the following extended prototype.

bool MyClass::event_handler(GdkEventButton *event, Gtk::Menu *menu);

Use bind to bind a pointer to the menu to your event handler.

bind(slot(this, &MyClass:event_handler), menu);

where menu is the bound menu pointer. Remember, bound data is always passed as the last argument to the bound method, in this case event_handler.

In the event handler, if the event is a mouse button press, treat the event as a button event (which it is) and use it as shown in the sample code to pass information to Gtk::Menu::popup().

bool
MyClass::event_handler(GdkEventButton *event, Gtk::Menu *menu)
{
    menu->popup(event->button, event->time);
   
    // return true to indicate event has been handled
    return true;
}

That should just about do it. Let's take a look at an example to help clarify.

Menu Example

The header file for MenuWindow is menu.h. Menu Window displays a simple window with a menubar across the top. When you click a mouse button down anywhere inside its client area the menubar's File menu pops up at the current pointer position.

#include<inti/main.h>
#include <inti/core.h>
#include <inti/gtk/menu.h>


using namespace Inti;

class MenuWindow : public Gtk::Window
{
    MenuWindow(const MenuWindow&);
    MenuWindow& operator=(const MenuWindow&);

protected:
    bool on_button_press(GdkEventButton *event, Gtk::Menu *menu);

public:
    MenuWindow();
    virtual ~MenuWindow();

    void menu_item_selected(const char *parent, const char *item);

    void on_file_new();
    void on_file_open();
    void on_file_save();
    void on_file_save_as();
    void on_file_quit();
    void on_options_preferences();
    void on_help_about();
};

and the source file is menu.cc:

#include"menu.h"
#include <inti/gtk/accelgroup.h>
#include <inti/gtk/eventbox.h>
#include <inti/gtk/label.h>
#include <inti/gtk/menubar.h>
#include <inti/gtk/menuitem.h>
#include <inti/gtk/separatormenuitem.h>
#include <inti/bind.h>


MenuWindow::MenuWindow()
{
    set_title("Menu Window");
    set_size_request(300, 200);

    // Boxes don't receive button events so use an eventbox. The eventbox is added first
    // and then all the other widgets added to it.
    Gtk::EventBox *eventbox = new Gtk::EventBox;
    add(*eventbox);

    // Set the events the eventbox is to receive. These can be any number of or'd (|) values
    // from the Gdk::EventMask enumeration.
    eventbox->set_events(Gdk::BUTTON_PRESS_MASK);

    // Add the packing box to eventbox
    Gtk::VBox *vbox = new Gtk::VBox(false, 1);
    vbox->set_border_width(1);
    eventbox->add(*vbox);

    // Create the menubar. Menus can be created by using the append, prepend or insert methods
    // in menushell.h to add menu items.
    Gtk::MenuBar *menubar = new Gtk::MenuBar;
    Gtk::AccelGroup *accel_group = add_accel_group();

    // Create the File menu
    Gtk::Menu *menu = new Gtk::Menu(*accel_group);
    menu->append(*(new Gtk::MenuItem("_New", true)), "<control>N", slot(this, &MenuWindow::on_file_new));
    menu->append(*(new Gtk::MenuItem("_Open", true)), "<control>O", slot(this, &MenuWindow::on_file_open));
    menu->append(*(new Gtk::MenuItem("_Save", true)), "<control>S", slot(this, &MenuWindow::on_file_save));
    menu->append(*(new Gtk::MenuItem("Save _As", true)), slot(this, &MenuWindow::on_file_save_as));
    menu->append(*(new Gtk::SeparatorMenuItem));
    menu->append(*(new Gtk::MenuItem("_Quit", true)), "<control>Q", slot(this, &MenuWindow::on_file_quit));
    menubar->append(*(new Gtk::MenuItem("_File", *menu, true)));

    // Bind the file menu to the button_press event and use it as the popup menu.
    eventbox->sig_button_press_event().connect(bind(slot(this, &MenuWindow::on_button_press), menu));

    // Create Options menu
    menu = new Gtk::Menu;
    menu->append(*(new Gtk::MenuItem("_Preferences", true)), slot(this, &MenuWindow::on_options_preferences));
    menubar->append(*(new Gtk::MenuItem("_Options", *menu, true)));

    // Create Help menu
    menu = new Gtk::Menu;
    menu->append(*(new Gtk::MenuItem("About")), slot(this, &MenuWindow::on_help_about));
    Gtk::MenuItem *menu_item = new Gtk::MenuItem("_Help", *menu, true);
    menu_item->set_right_justified(true);
    menubar->append(*menu_item);
   
    // Pack the menubar into the vbox
    vbox->pack_start(*menubar, false);

    // Add a label that tells the user to click the mouse button inside the client area.
    Gtk::Label *label = new Gtk::Label("Click mouse button here...");
    vbox->pack_start(*label);

    // Being lazy, just show everything with one call.
    show_all();
}

MenuWindow::~MenuWindow()
{
}

bool
MenuWindow::on_button_press(GdkEventButton *event, Gtk::Menu *menu)
{
    menu->popup(event->button, event->time);
    return true;
}

void
MenuWindow::menu_item_selected(const char *parent, const char *item)
{
    g_message("Menu: activated the \"%s\" menu item: \"%s\"", parent, item);
}

void
MenuWindow::on_file_new()
{
    menu_item_selected("File", "New");
}

void
MenuWindow::on_file_open()
{
    menu_item_selected("File", "Open");
}

void
MenuWindow::on_file_save()
{
    menu_item_selected("File", "Save");
}

void
MenuWindow::on_file_save_as()
{
    menu_item_selected("File", "Save As");
}

void
MenuWindow::on_file_quit()
{
    dispose();
}

void
MenuWindow::on_options_preferences()
{
    menu_item_selected("Options", "Preferences");
}

void
MenuWindow::on_help_about()
{
    menu_item_selected("Help", "About");
}

INTI_MAIN(MenuWindow)


The INTI_MAIN macro is a convenience macro that writes a simple main function, its only argument is the name of the main window class. The macro is defined in <inti/main.h> as

#define INTI_MAIN(MainWidget)\
    int main (int argc, char *argv[])\
    {\
        Inti::Main::init(&argc, &argv);\
        MainWidget main_widget;\
        main_widget.sig_destroy().connect(slot(&Inti::Main::quit));\
        main_widget.show();\
        Inti::Main::run();\
        return 0;\
    }

There is a second manual menu creation example in "examples/menu/stock.cc" that creates the same Menu Window as above, except that it uses stock menu items and their icons. The source code is otherwise identical.

Stock Menu Example



« Menus Index
Top
Using an Item Factory »