2003-12-08 22:28:27 +00:00
|
|
|
<chapter id="gtk-migrating-GtkAction">
|
|
|
|
<chapterinfo>
|
|
|
|
<author>
|
|
|
|
<firstname>Federico</firstname>
|
|
|
|
<surname>Mena-Quintero</surname>
|
|
|
|
<affiliation>
|
|
|
|
<address>
|
|
|
|
<email>federico@ximian.com</email>
|
|
|
|
</address>
|
|
|
|
</affiliation>
|
|
|
|
</author>
|
|
|
|
</chapterinfo>
|
|
|
|
|
|
|
|
<title>Migrating from old menu and toolbar systems to GtkAction</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Prior to GTK+ 2.4, there were several APIs in use to create menus
|
2004-02-27 22:06:40 +00:00
|
|
|
and toolbars. GTK+ itself included <link
|
|
|
|
linkend="GtkItemFactory">GtkItemFactory</link>, which was
|
2003-12-08 22:28:27 +00:00
|
|
|
historically used in the GIMP; libgnomeui provided the gnome-ui
|
|
|
|
set of macros; libbonoboui provided a complex mechanism to do menu
|
|
|
|
merging across embedded components. GTK+ 2.4 includes a system
|
|
|
|
for creating menus and toolbars, with merging of items, based
|
2004-02-27 22:06:40 +00:00
|
|
|
around the <link linkend="GtkAction">GtkAction</link> mechanism.
|
2003-12-08 22:28:27 +00:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<section id="actions-and-action-groups">
|
|
|
|
<title>Actions and Action Groups</title>
|
|
|
|
|
|
|
|
<para>
|
2004-02-27 22:06:40 +00:00
|
|
|
A <link linkend="GtkAction">GtkAction</link> represents an
|
|
|
|
operation that the user can perform from the menus and toolbars
|
|
|
|
of an application. It is similar to "verbs" in other menu
|
|
|
|
systems. A <link linkend="GtkAction">GtkAction</link> has a
|
|
|
|
name, which is its identifier, and it can have several widgets
|
|
|
|
that represent it in the user interface. For example, an action
|
|
|
|
for <symbol>EditCopy</symbol> can have a menu item as well as a
|
2003-12-08 22:28:27 +00:00
|
|
|
toolbar button associated to it. If there is nothing selected
|
|
|
|
in the document, the application can simply de-sensitize the
|
|
|
|
<symbol>EditCopy</symbol> action; this will cause both the menu
|
|
|
|
item and the toolbar button to be de-sensitized automatically.
|
|
|
|
Similarly, whenever the user selects the menu item or the
|
|
|
|
toolbar button associated to the <symbol>EditCopy</symbol>
|
2004-02-27 22:06:40 +00:00
|
|
|
action, the corresponding <link
|
|
|
|
linkend="GtkAction">GtkAction</link> object will emit an
|
2003-12-08 22:28:27 +00:00
|
|
|
"activate" signal.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2004-02-27 22:06:40 +00:00
|
|
|
<link linkend="GtkActionGroup">GtkActionGroup</link> is simply a
|
|
|
|
group of <link linkend="GtkAction">GtkAction</link> objects. An
|
2003-12-08 22:28:27 +00:00
|
|
|
application may want to have several groups: one for global
|
|
|
|
actions such as "new document", "about", and "exit"; then one
|
|
|
|
group for each open document with actions specific to the
|
|
|
|
document, such as "cut", "copy", "paste", and "print".
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Normal actions are simply commands, such as
|
|
|
|
<symbol>FileSave</symbol> or <symbol>EditCopy</symbol>. Toggle
|
|
|
|
actions can be active or inactive, such as
|
|
|
|
<symbol>FormatBold</symbol> or <symbol>ViewShowRulers</symbol>.
|
|
|
|
Radio actions define a set of items for which one and only one
|
|
|
|
can be active at a time, for example, {
|
|
|
|
<symbol>ViewHighQuality</symbol>,
|
|
|
|
<symbol>ViewNormalQuality</symbol>,
|
|
|
|
<symbol>ViewLowQuality</symbol> }.
|
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="ui-manager">
|
|
|
|
<title>User Interface Manager Object</title>
|
|
|
|
|
|
|
|
<para>
|
2004-02-27 22:06:40 +00:00
|
|
|
<link linkend="GtkUIManager">GtkUIManager</link> is an object
|
|
|
|
that can construct menu and toolbar widgets from an XML
|
|
|
|
description. These widgets are in turn associated to
|
|
|
|
corresponding actions and action groups.
|
2003-12-08 22:28:27 +00:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
2004-02-27 22:06:40 +00:00
|
|
|
<link linkend="GtkUIManager">GtkUIManager</link> supports
|
|
|
|
merging of menus and toolbars for applications that have
|
|
|
|
multiple components, each with separate sets of commands. For
|
|
|
|
example, a word processor that can embed images may want to have
|
|
|
|
toolbar buttons for Bold and Italic when the cursor is on a text
|
|
|
|
block, but Crop and Brightness/Contrast buttons when the cursor
|
|
|
|
is on an image. These actions, which change depending on the
|
|
|
|
state of the application, can be merged and de-merged from a
|
|
|
|
<link linkend="GtkUIManager">GtkUIManager</link> as appropriate.
|
2003-12-08 22:28:27 +00:00
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section id="migrating-gnomeuiinfo">
|
|
|
|
<title>Migrating from GnomeUIInfo</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Prior to GTK+ 2.4, some applications used the GnomeUIInfo
|
|
|
|
mechanism from
|
|
|
|
<filename><libgnomeui/gnome-app-helper.h></filename> to
|
|
|
|
define their menus and toolbars. With it, a program decleres an
|
|
|
|
array of <structname>GnomeUIInfo</structname> structures, which
|
|
|
|
contain information for menu or toolbar items such as their
|
|
|
|
label, icon, and accelerator key. Then, one calls
|
|
|
|
<function>gnome_app_fill_menu()</function> or
|
|
|
|
<function>gnome_app_fill_toolbar()</function>, or one of the
|
|
|
|
related functions, to create the appropriate widgets based on
|
|
|
|
these structures.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
A downside of this API is that the same structures are used to
|
|
|
|
pass back pointers to the widgets that got created. This means
|
|
|
|
that the structures cannot simply be kept around if the program
|
|
|
|
requires multiple instances of the user interface (e.g. several
|
|
|
|
windows); each new invocation of
|
|
|
|
<function>gnome_app_fill_menu()</function> would overwrite the
|
|
|
|
widget fields of the structures.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Another disadvantage is that there is no automatic way to
|
|
|
|
synchronize the state of related controls. If there are toolbar
|
|
|
|
toogle buttons for "Bold", "Italic", "Underline", and also
|
|
|
|
corresponding menu items under "Format/Bold", etc., one has to
|
|
|
|
synchronize their toggled states by hand whenever the user
|
|
|
|
selects any one of them.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Finally, there is no way to do menu and toolbar merging for
|
|
|
|
applications that require embedded components.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
To convert an application that uses GnomeUIInfo into the new
|
|
|
|
GtkAction mechanism, you need to do several things:
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<orderedlist>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Separate your existing GnomeUIInfo entries into normal
|
2004-02-27 22:06:40 +00:00
|
|
|
actions, toggle actions, and radio actions, and then create
|
|
|
|
a separate array of <link
|
|
|
|
linkend="GtkActionEntry">GtkActionEntry</link> structures
|
|
|
|
for each group. This will allow you to create the necessary
|
|
|
|
<link linkend="GtkActionGroup">GtkActionGroup</link>
|
|
|
|
objects. Note that this does not describe the actual
|
|
|
|
"shape" that your menus and toolbars will have; it simply
|
|
|
|
defines the set of commands that will appear in them.
|
2003-12-08 22:28:27 +00:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Create an XML description of your menus and toolbars for use
|
2004-02-27 22:06:40 +00:00
|
|
|
with <link linkend="GtkUIManager">GtkUIManager</link>. This
|
|
|
|
defines the actual shape of the menus and toolbars.
|
2003-12-08 22:28:27 +00:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
<listitem>
|
|
|
|
<para>
|
|
|
|
Port the code that uses gnome-app and gnome-app-helper to
|
2004-02-27 22:06:40 +00:00
|
|
|
<link linkend="GtkAction">GtkAction</link> and <link
|
|
|
|
linkend="GtkUIManager">GtkUIManager</link>.
|
2003-12-08 22:28:27 +00:00
|
|
|
</para>
|
|
|
|
</listitem>
|
|
|
|
</orderedlist>
|
|
|
|
|
|
|
|
<example id="gnomeuiinfo-example">
|
|
|
|
<title>GnomeUIInfo Example</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
The following code shows a declaration of a simple menu bar to
|
|
|
|
be used with <function>gnome_app_fill_menu()</function> or
|
|
|
|
similar. The menu hierarchy looks like this:
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
<listitem>
|
|
|
|
<para><guimenu>File</guimenu></para>
|
|
|
|
<simplelist>
|
|
|
|
<member><guimenuitem>Open</guimenuitem></member>
|
|
|
|
<member><guimenuitem>—</guimenuitem></member>
|
|
|
|
<member><guimenuitem>Exit</guimenuitem></member>
|
|
|
|
</simplelist>
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
<para><guimenu>View</guimenu></para>
|
|
|
|
<simplelist>
|
|
|
|
<member><guimenuitem>Zoom In</guimenuitem></member>
|
|
|
|
<member><guimenuitem>Zoom Out</guimenuitem></member>
|
|
|
|
<member><guimenuitem>—</guimenuitem></member>
|
|
|
|
<member><guimenuitem>[ ] Full Screen</guimenuitem></member>
|
|
|
|
<member><guimenuitem>—</guimenuitem></member>
|
|
|
|
<member><guimenuitem>( ) High Quality</guimenuitem></member>
|
|
|
|
<member><guimenuitem>( ) Normal Quality</guimenuitem></member>
|
|
|
|
<member><guimenuitem>( ) Low Quality</guimenuitem></member>
|
|
|
|
</simplelist>
|
|
|
|
</listitem>
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
static GnomeUIInfo file_menu_items[] = {
|
|
|
|
{ GNOME_APP_UI_ITEM, "_Open", "Open a file",
|
|
|
|
open_callback, NULL, NULL, GNOME_APP_PIXMAP_STOCK, GTK_STOCK_OPEN,
|
|
|
|
'o', GDK_CONTROL_MASK, NULL },
|
|
|
|
{ GNOME_APP_UI_SEPARATOR },
|
|
|
|
{ GNOME_APP_UI_ITEM, "E_xit", "Exit the program",
|
|
|
|
exit_callback, NULL, NULL, GNOME_APP_PIXMAP_STOCK, GTK_STOCK_QUIT,
|
|
|
|
'q', GDK_CONTROL_MASK, NULL},
|
|
|
|
{ GNOME_APP_UI_ENDOFINFO }
|
|
|
|
};
|
|
|
|
|
|
|
|
static GnomeUIInfo view_radio_items[] = {
|
|
|
|
{ GNOME_APP_UI_ITEM, "_High Quality", "Display images in high quality, slow mode",
|
|
|
|
high_quality_callback, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
|
|
|
|
0, 0, NULL },
|
|
|
|
{ GNOME_APP_UI_ITEM, "_Normal Quality", "Display images in normal quality",
|
|
|
|
normal_quality_callback, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
|
|
|
|
0, 0, NULL },
|
|
|
|
{ GNOME_APP_UI_ITEM, "_Low Quality", "Display images in low quality, fast mode",
|
|
|
|
low_quality_callback, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
|
|
|
|
0, 0, NULL },
|
|
|
|
{ GNOME_APP_UI_ENDOFINFO }
|
|
|
|
};
|
|
|
|
|
|
|
|
static GnomeUIInfo view_menu_items[] = {
|
|
|
|
{ GNOME_APP_UI_ITEM, "Zoom _In", "Zoom into the image",
|
|
|
|
zoom_in_callback, NULL, NULL, GNOME_APP_PIXMAP_STOCK, GTK_STOCK_ZOOM_IN,
|
|
|
|
GDK_PLUS, 0, NULL },
|
|
|
|
{ GNOME_APP_UI_ITEM, "Zoom _Out", "Zoom away from the image",
|
|
|
|
zoom_out_callback, NULL, NULL, GNOME_APP_PIXMAP_STOCK, GTK_STOCK_ZOOM_OUT,
|
|
|
|
GDK_MINUS, 0, NULL },
|
|
|
|
{ GNOME_APP_UI_SEPARATOR },
|
|
|
|
{ GNOME_APP_UI_TOGGLEITEM, "_Full Screen", "Switch between full screen and windowed mode",
|
|
|
|
full_screen_callback, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
|
|
|
|
GDK_F11, 0, NULL },
|
|
|
|
{ GNOME_APP_UI_SEPARATOR },
|
|
|
|
{ GNOME_APP_UI_RADIOITEMS, NULL, NULL, view_radio_items },
|
|
|
|
{ GNOME_APP_UI_ENDOFINFO }
|
|
|
|
};
|
|
|
|
|
|
|
|
static GnomeUIInfo menubar[] = {
|
|
|
|
{ GNOME_APP_UI_SUBTREE, "_File", NULL, file_menu_items },
|
|
|
|
{ GNOME_APP_UI_SUBTREE, "_View", NULL, view_menu_items },
|
|
|
|
{ GNOME_APP_UI_ENDOFINFO }
|
|
|
|
}
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
<example id="gnomeuiinfo-action-entries">
|
|
|
|
<title><structname>GtkActionEntry</structname> Structures</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
The following code is the set of actions that are present in
|
|
|
|
the <link linkend="gnomeuiinfo-example">previous
|
|
|
|
example</link>. Note that the toggle and radio entries are
|
2004-02-27 22:06:40 +00:00
|
|
|
separate from normal actions. Also, note that <link
|
|
|
|
linkend="GtkActionEntry">GtkActionEntry</link> structures take
|
|
|
|
key names in the format of gdk_accelerator_parse() rather than
|
|
|
|
key values plus modifiers; you will have to convert these
|
|
|
|
values by hand. For example, <constant>GDK_F11</constant>
|
|
|
|
with no modifiers is equivalent to a key name of
|
|
|
|
<literal>"F11"</literal>. Likewise, <literal>"o"</literal>
|
|
|
|
with <constant>GDK_CONTROL_MASK</constant> is equivalent to
|
2003-12-08 22:28:27 +00:00
|
|
|
<literal>"<ontrol>O"</literal>.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
/* Normal items */
|
|
|
|
static GtkActionEntry entries[] = {
|
|
|
|
{ "FileMenu", NULL, "_File" },
|
|
|
|
{ "ViewMenu", NULL, "_View" },
|
|
|
|
{ "Open", GTK_STOCK_OPEN, "_Open", "<control>O", "Open a file", open_action_callback },
|
|
|
|
{ "Exit", GTK_STOCK_OPEN, "E_xit", "<control>Q", "Exit the program", exit_action_callback },
|
|
|
|
{ "ZoomIn", GTK_STOCK_ZOOM_IN, "Zoom _In", "plus", "Zoom into the image", zoom_in_action_callback },
|
|
|
|
{ "ZoomOut", GTK_STOCK_ZOOM_OUT, "Zoom _Out", "minus", "Zoom away from the image", zoom_out_action_callback },
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Toggle items */
|
|
|
|
static GtkToggleActionEntry toggle_entries[] = {
|
|
|
|
{ "FullScreen", NULL, "_Full Screen", "F11", "Switch between full screen and windowed mode", full_screen_action_callback, FALSE }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Radio items */
|
|
|
|
static GtkRadioActionEntry radio_entries[] = {
|
|
|
|
{ "HighQuality", NULL, "_High Quality", NULL, "Display images in high quality, slow mode", 0 },
|
|
|
|
{ "NormalQuality", NULL, "_Normal Quality", NULL, "Display images in normal quality", 1 },
|
|
|
|
{ "LowQuality", NULL, "_Low Quality", NULL, "Display images in low quality, fast mode", 2 }
|
|
|
|
};
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
<example id="gnomeuiinfo-xml">
|
|
|
|
<title>XML Description</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
After extracting the actions, you will need to create an XML
|
|
|
|
description of the actual layout of your menus and toolbars
|
2004-02-27 22:06:40 +00:00
|
|
|
for use with <link linkend="GtkUIManager">GtkUIManager</link>.
|
|
|
|
The following code shows a simple menu bar that corresponds to
|
|
|
|
the <link linkend="gnomeuiinfo-example">previous
|
|
|
|
example</link>. Note that the <guimenu>File</guimenu> and
|
|
|
|
<guimenu>View</guimenu> menus have their names specified in
|
|
|
|
the <link linkend="gnomeuiinfo-action-entries">action
|
|
|
|
entries</link>, not in the XML itself. This is because the
|
|
|
|
XML description only contains <emphasis>identifiers</emphasis>
|
|
|
|
for the items in the GUI, rather than human-readable names.
|
2003-12-08 22:28:27 +00:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
static const char *ui_description =
|
|
|
|
"<ui>"
|
|
|
|
" <menubar name="MainMenu">"
|
|
|
|
" <menu action="FileMenu">"
|
|
|
|
" <menuitem action="Open"/>"
|
|
|
|
" <menuitem action="Exit"/>"
|
|
|
|
" </menu>"
|
|
|
|
" <menu action="ViewMenu">"
|
|
|
|
" <menuitem action="ZoomIn"/>"
|
|
|
|
" <menuitem action="ZoomOut"/>"
|
|
|
|
" <separator/>"
|
|
|
|
" <menuitem action="FullScreen"/>"
|
|
|
|
" <separator/>"
|
|
|
|
" <menuitem action="HighQuality"/>"
|
|
|
|
" <menuitem action="NormalQuality"/>"
|
|
|
|
" <menuitem action="LowQuality"/>"
|
|
|
|
" </menu>"
|
|
|
|
" </menubar>"
|
|
|
|
"</ui>";
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
|
|
|
|
<example id="gnomeuiinfo-code">
|
|
|
|
<title>Creating the Menu Bar</title>
|
|
|
|
|
|
|
|
<para>
|
2004-02-27 22:06:40 +00:00
|
|
|
In this last example, we will create a <link
|
|
|
|
linkend="GtkActionGroup">GtkActionGroup</link> based on the
|
|
|
|
<link linkend="gnomeuiinfo-action-entries">action
|
|
|
|
entries</link> we created above. We will then create a <link
|
|
|
|
linkend="GtkUIManager">GtkUIManager</link> with the <link
|
|
|
|
linkend="gnomeuiinfo-xml">XML description</link> of the menu
|
|
|
|
layout. We will also extract the accelerator group and the
|
|
|
|
widgets from the <link
|
|
|
|
linkend="GtkUIManager">GtkUIManager</link> put them into a
|
|
|
|
window.
|
2003-12-08 22:28:27 +00:00
|
|
|
</para>
|
|
|
|
|
|
|
|
<programlisting>
|
|
|
|
GtkWidget *window;
|
|
|
|
GtkWidget *vbox;
|
|
|
|
GtkWidget *menubar;
|
|
|
|
GtkActionGroup *action_group;
|
|
|
|
GtkUIManager *ui_manager;
|
|
|
|
GtkAccelGroup *accel_group;
|
|
|
|
GError *error;
|
|
|
|
|
|
|
|
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
|
|
|
|
|
|
|
vbox = gtk_vbox_new (FALSE, 0);
|
|
|
|
gtk_container_add (GTK_CONTAINER (window), vbox);
|
|
|
|
|
|
|
|
action_group = gtk_action_group_new ("MenuActions");
|
|
|
|
gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
|
|
|
|
gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
|
|
|
|
gtk_action_group_add_radio_actions (action_group, radio_entries, G_N_ELEMENTS (radio_entries), 0, radio_action_callback, window);
|
|
|
|
|
|
|
|
ui_manager = gtk_ui_manager_new ();
|
|
|
|
gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
|
|
|
|
|
|
|
|
accel_group = gtk_ui_manager_get_accel_group (ui_manager);
|
|
|
|
gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
|
|
|
|
|
|
|
|
error = NULL;
|
|
|
|
if (!gtk_ui_manager_add_ui_from_string (ui_manager, ui_description, -1, &error))
|
|
|
|
{
|
|
|
|
g_message ("building menus failed: %s", error->message);
|
|
|
|
g_error_free (error);
|
|
|
|
exit (EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
menubar = gtk_ui_manager_get_widget ("/MainMenu");
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
|
|
|
|
|
|
|
|
gtk_widget_show_all (window);
|
|
|
|
</programlisting>
|
|
|
|
</example>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
</chapter>
|
|
|
|
|
|
|
|
<!--
|
|
|
|
Local variables:
|
|
|
|
mode: sgml
|
|
|
|
sgml-parent-document: ("gtk-docs.sgml" "book" "part" "chapter")
|
|
|
|
End:
|
|
|
|
-->
|