1
\documentclass[10pt]{article}
6
\lstset{language=Delphi}%
7
\lstset{basicstyle=\sffamily\small}%
8
\lstset{commentstyle=\itshape}%
9
\lstset{keywordstyle=\bfseries}%
10
%\lstset{blankstring=true}%
12
\ifx\pdfoutput\undefined
19
\title{Programming GTK in Free Pascal:\\ Menus
22
\author{Florian Kl\"ampfl\\and\\Micha\"el Van Canneyt}
25
\section{Introduction}
26
In the third article on programming the GTK toolkit, the us of menus in GTK
29
%explored: The programming of menus and the use of marshallers.
31
Menus can be built in essentially 2 ways; the easier way through the
32
use of a itemfactory, and the more complex way, doing all necessary calls
33
manually. The advantages of both ways are discussed.
35
%Marshallers can be used to replace the default signal handling mechanisms
36
%of GTK. The use of marshallers will be demonstrated by building a small
37
%object which will have custom handlers for certain events.
39
\section{Menus the easy way: The item factory}
40
The easy way to construct a menu is to use an item factory. An Item factory
41
gets as input an array of records, which describe a menu structure, and
42
returns a completely built menu, ready to be added to a window.
44
The great advantage of an item factory is that it is easy to use; a
47
\item There is less control over the produced menu items; e.g.
48
displaying a menu item with a small icon is not possible.
49
\item The callbacks of the constructed menu is different from the usual
50
signal model, making it difficult to combine a menu entry with a
51
speedbutton. There are also 2 types of callback, so type checking is not
53
\item In Pascal, constant records must be specified using the names of the
54
members; this makes the array with the menu items to be rendered quite
58
To create a menu, first the item factory must be created. The function to do
59
this is defined as follows:
61
gtk_item_factory_new(container_type:TGtkType;
63
accel_group:PGtkAccelGroup):PGtkItemFactory;
65
The three arguments to this function have the following meaning:
67
\item[container\_type] This identifies the kind of menu that will be
68
rendered. It can have one of the following values:
70
\item[GTK\_MENU\_BAR\_TYPE] A menu bar will be created to hold all items.
71
\item[GTK\_MENU\_TYPE] A menu that can be used as a popup menu, or that can be
72
attached as a sub-menu to another menu, will be created.
73
\item[GTK\_OPTION\_MENU\_TYPE] Makes everything in a drop-down style menu which
74
can be used to select one value.
76
\item[path] is the name of the menu to be generated.
77
\item[accel\_group] Is a pointer to a group of accelerators. All
78
accellerators for the generated menu will be attached to this group.
81
The accelerator group needed for the item factory can be constructed
82
using a simple call to \lstinline|gtk_accel_group_new|; this function
83
takes no arguments, and returns a pointer to a new accelerator group.
85
To actually create the menu, a call to
86
\lstinline|gtk_item_factory_create_items| is needed; This procedure is
89
gtk_item_factory_create_items(ifactory:PGtkItemFactory;
91
entries:PGtkItemFactoryEntry;
92
callback_data:gpointer);
94
The first argument to this call, \lstinline|ifactory|, is the itemfactory;
95
the second argument, \lstinline|n_entries|, is the number of items in the
96
array of records describing the menu. The third argument, \lstinline|entries|,
97
is the actual array describing the menu. The last argument
98
\lstinline|callback_data| is a pointer that will be passed to the menu
101
The menu structure that should be created by the item factory is an
102
array of records of the type \lstinline|TGtkItemFactoryEntry|.
103
This record is defined as follows:
105
TGtkItemFactoryEntry = record
107
accelerator : Pgchar;
108
callback : TGtkItemFactoryCallback;
109
callback_action : guint;
113
The fields have the following meaning:
116
The first entry is the path of the menu item. This indicates the place of
117
the menu entry in the whole menu. For instance, the menu item \textbf{New}
118
in the menu \textbf{File} would be designated by \lstinline|'/File/New'|.
119
So, the slash is used to separate the menu levels.
121
To make one of the letters of the menu item name active, so the item can be
122
selected by pressing the letter (on the keyboard) when the menu is opened,
123
the key to be used should be preceded by an underscore.
124
In e.g. \lstinline|'/File/_New'|, the letter \textbf{N} could be used to
125
select the \textbf{New} item if the \textbf{File} menu is active.
127
\item[accelerator] To make a shortcut to the menu item so it can be
128
activated at all times, the shortcut name can be specified in the
129
\lstinline|accelerator| field. This can be any key, together with some
130
modifiers. e.g. \lstinline|'<control>N'| will make the key combination
133
The accelerator should be speciefied as normal text. A list of possible
134
modifiers can be found in table \ref{tab:modifiers}.
137
\caption{List of modifier strings for shortcut keys}\label{tab:modifiers}
139
Modifier & alias \\ \hline
140
\lstinline|<control>| & \lstinline|<ctl>|, \lstinline|<ctrl>| \\
141
\lstinline|<shift>| & \lstinline|<shft>| \\
142
\lstinline|<alt>| & \lstinline|<mod1>| \\ \hline
147
\item[callback] Contains a pointer to the function that should be called
148
when the menu item is activated. The type of the menu handler is not the
149
same as a normal signal handler; The actual callback should be of the type
150
\lstinline|TGtkItemFactoryCallback1|:
152
procedure (callback_data:gpointer;
153
callback_action:guint;
154
widget:PGtkWidget);cdecl;
156
Which is not the same as the type of the \lstinline|callback| field, so
157
a typecast will always be necessary.
159
\item[callback\_action] This is passed on to the callback in the
160
\lstinline|callback_action| parameter.
162
\item[item\_type] is the type of menu item. Several types can be used; the
163
complete list can be found in \ref{tab:menutypes}, but the must important
164
ones are \lstinline|'<Item>'|, which specifies a normal menu item,
165
and \lstinline|'<Branch>'|, which indicates a sub-menu.
168
\caption{Possible menu item types}\label{tab:menutypes}
169
\begin{tabularx}{\textwidth}{lX}%
170
Item type & Menu kind \\ \hline
171
\lstinline|'<Item>'| & indicates a normal item. An empty string or \lstinline|Nil|
172
have the same meaning. \\
173
\lstinline|'<CheckItem>'| & a check menu item. \\
174
\lstinline|'<ToggleItem>'| & a toggle menu item (same as check menu). \\
175
\lstinline|'<RadioItem>'| & a radio item. \\
176
\lstinline|'<Separator>'| & a separator bar. \\
177
\lstinline|'<Branch>'| & an item to hold a submenu.\\
178
\lstinline|'<LastBranch>'| & an item to hold a submenu, but right aligned.\\ \hline
183
Now all elements to create a menu are introduced, and the menu can be
184
created. The following definitions should now be clear:
188
MenuBar : PGtkWidget;
191
FC = TGtkItemFactoryCallback;
195
TheMenu : Array[1..NrMenuItems] of TGtkItemFactoryEntry = (
196
(path:'/_File';Accelerator:Nil;
197
Callback:Nil;Callback_action:1;item_type:'<Branch>'),
198
(path:'/File/_New';Accelerator:'<ctrl>N';
199
Callback:FC(@Menu);Callback_action:1;item_type:Nil),
202
Here the \lstinline|FC| type is introduced to make the typecast of the
203
\lstinline|Menu| handler easier; the
204
\lstinline|TheMenu| constant is not given completely, since it is too long
205
and not instructive. The complete structure can be found in the sources
206
accompanying this article.
208
Using the above definitions, the menu can now be constructed:
213
Factory : PGtkItemFactory;
214
Accel : PGtkAccelGroup;
217
Accel:=gtk_accel_group_new;
218
Factory :=gtk_item_factory_new(GTK_MENU_BAR_TYPE,'<main>',accel);
219
gtk_item_factory_create_items(Factory,NrMenuItems,@TheMenu,Nil);
220
gtk_window_add_accel_group(GTK_Window(Window),accel);
221
MenuBar:=gtk_item_factory_get_widget (Factory, '<main>');
224
The \lstinline|gtk_window_add_accel_group| call attaches the accelerator
225
group that was filled up by the item factory to the window.
227
The \lstinline|gtk_item_factory_get_widget| call finally fetches the
228
object created by the item factory and stores it in a widget variable.
230
The \lstinline|Menu| callback used in the menus is defined as follows:
232
procedure menu(Data : GPointer;
234
Widget : pGtkWidget); cdecl;
237
TheLabel : PgtkWidget;
242
TheLabel:=g_list_nth_data(
243
gtk_container_children(
244
GTK_CONTAINER(Widget)),0);
245
gtk_label_get(gtk_Label(theLabel),@LabelText);
246
S := 'Chosen menu : ' + Strpas(Labeltext);
247
gtk_label_set_text(GTK_LABEL(DisplayLabel),pchar(S));
250
The \lstinline|DisplayLabel| is a label located on the window, it is used to
251
give some feedback on the used menu. The code to extract the menu name from
252
the menu widget passed to the handler will be explained later on.
254
The result of all this is shown in figure \ref{fig:ex1}.
256
\caption{The menu made by the item factory.}\label{fig:ex1}
257
\epsfig{file=gtk3ex/ex1.png}
260
As can be seen from the code above, the creation of a menu using an item
261
factory in GTK is not so hard. The drawback of the above method lies mainly
262
in the fact that Pascal handles constant records differently than C, which
263
makes the array that describes the menu structure rather difficult to read.
265
The second drawback is that there is little control over the created items.
267
\section{Menus the hard way: manually}
268
When creating menus manually, mainly 4 objects are involved:
270
\item The menu items themselves. To a menu item, a menu can be assiciated,
272
\item Menus, which contain a collection of menu items,
273
\item A accelerator group. This will be used to keep a collection of
274
shortcut keys for the menu items.
275
\item A menu bar, which can hold several menu items and their associated
278
The last object is optional, if e.g. a pop-up menu is wanted.
280
To create a menu in a window, the following steps are involved:
282
\item Create an accellerator group. The accelerator group should be
283
connected to the window. \label{stepone}
284
\item Create a menu bar, and attach it to the window.
285
\item For each menu that should appear in a menu bar, do the following:
287
\item Create a menu item, which will be shown in the menu bar.
288
\item Create a menu to hold the items that should pop up when the menu is
291
\item To each menu created in the previous step, add as many menu items as
292
needed. Add an accelarator to the group created in step \ref{stepone}.
295
To make these steps easier (each of them involves quite some calls to GTK
296
functions) some functions will be introduced that make this easier.
298
The first function is the most simple one; it attaches a separator to a
301
Function AddSeparatorToMenu(Menu:PgtkMenu):PgtkMenuItem;
304
Result:=pgtkmenuitem(gtk_menu_item_new);
305
gtk_menu_append(Menu,pgtkWidget(result));
306
gtk_widget_show(PgtkWidget(result));
309
The function takes one parameter, \lstinline|Menu|, the menu to which the
310
separator will be attached. A separator is created by simply creating an
311
empty menu item. Creating a new (empty) menu item is done with the
312
\lstinline|gtk_menu_item_new| call.
314
With the \lstinline|gtk_menu_append| call, the newly created item is then
315
added to the menu. Lastly, the item is shown; it will not become actually
316
visible till the menu is activated. If this is omitted, it will also not
317
be visible when the menu is activated.
319
Adding a menu with a shortcut key to a menu is a little more involved. Some
320
more elements are needed:
322
\item The menu to which to attach the menu item.
323
\item The accelarator group to which the accelerator key should be added.
324
\item The caption of the menu. An underscore character will indicate the
325
letter of themenu that will be used as a shortcut to activate the item.
326
\item The shortcut for the menu item. An empty string means no shortcut.
327
\item A callback function which will be called when the menu item is
328
activated, and callback data which will sent to the callback.
330
All these elements are found in the declaration of the following function:
332
Function AddItemToMenu (Menu : PGtkMenu;
333
ShortCuts : PGtkAccelGroup;
334
Caption : AnsiString;
335
ShortCut : AnsiString;
336
CallBack : TgtkSignalFunc;
337
CallBackdata : Pointer
341
Key,Modifiers : guint;
342
LocalAccelGroup : PGtkAccelGroup;
343
TheLabel : PGtkLabel;
347
The variables declared in this function will be explained as the code is
350
First of all, a menu item must be created. Since a caption for the menu is
351
provided, the \lstinline|gtk_menu_item_new_with_label| will be used to
352
create a menu that has a label as a child:
354
Result:=pgtkmenuitem(gtk_menu_item_new_with_label(''));
355
TheLabel:=GTK_LABEL(GTK_BIN(Result)^.child);
356
Key:=gtk_label_parse_uline(TheLabel,Pchar(Caption));
358
After the menu item is created, the child label is fetched. The label caption is
359
then set using the \lstinline|gtk_label_parse_uline| function. This function
360
will search a text for underscore characters, remove them from the text, and
361
then set the label's caption with the result. All letters which had an
362
underscore character prepended will be underlined in the label.
364
The function returns the first letter that had an underscore prepended. It
365
is stored, so it can be used to make an accelerator:
369
LocalAccelGroup:=gtk_menu_ensure_uline_accel_group(Menu);
370
gtk_widget_add_accelerator(PGtkWidget(result),'activate_item',
372
0,TGtkAccelFlags(0));
375
The call to \lstinline|gtk_menu_ensure_uline_accel_group| returns the
376
accelarator group associated with the menu. If no group existed yet, one
377
will be created. The \lstinline|gtk_widget_add_accelerator| call takes the
378
following parameters:
380
\item A pointer to a widget to which the accelerator should be attached.
381
\item The name of the signal which will be triggered when the shortcut
383
\item The accelerator group to which the shortcut should be installed,
384
usually this will be the accelerator group for the window to which the
385
widget is attached, but in this case this is the accelerator group of the
386
menu (which will only be active when the menu is actually shown)
387
\item The key from the shortcut.
388
\item The modifiers that should be pressed together with the key. For the
389
menu, this should be 0, since just the key should be hit.
390
\item The accelerator flags.
393
After the menu item was created and it's underlined key was made into an
394
accelerator, the menu can be attached to the menu:
396
gtk_menu_append(Menu,pgtkWidget(result));
399
If a shortcut key was passed along to the procedure, can be added to the
400
window's accelerator group with the following code:
402
If (ShortCut<>'') and (ShortCuts<>Nil) then
404
gtk_accelerator_parse (pchar(ShortCut), @key, @modifiers);
405
gtk_widget_add_accelerator(PGtkWidget(result),'activate_item',
407
modifiers, GTK_ACCEL_VISIBLE);
410
The call to \lstinline|gtk_accelerator_parse| will parse a string which
411
describes a shortcut key, and returns the corresponding key and modifiers,
412
which can then be passed on to the \lstinline|gtk_widget_add_accelerator|
415
After the accellerator has been installed, the only thing that remains to be
416
done is to connect the callback to the activation of the menu:
418
If CallBack<>Nil then
419
gtk_signal_connect(PGtkObject(result),'activate',
420
CallBack,CallBackdata);
421
gtk_widget_show(PgtkWidget(result));
424
As the last line in the procedure, the newly created menu item is shown.
425
If the menu isn't visible yet, this will do nothing, but will ensure that
426
the item is also visible when the menu is visible.
428
Now a menu-item and a separator can be added to a menu. What remains to be
429
done is to add a menu to a menu bar. This is done in the following
430
procedure, which is given in its entirety:
432
Function AddMenuToMenuBar(MenuBar : PGtkMenuBar;
433
ShortCuts : PGtkAccelGroup;
434
Caption : AnsiString;
435
CallBack : TgtkSignalFunc;
436
CallBackdata : Pointer;
437
AlignRight : Boolean;
438
Var MenuItem : PgtkMenuItem
443
TheLabel : PGtkLabel;
446
MenuItem:=pgtkmenuitem(gtk_menu_item_new_with_label(''));
448
gtk_menu_item_right_justify(MenuItem);
449
TheLabel:=GTK_LABEL(GTK_BIN(MenuItem)^.child);
450
Key:=gtk_label_parse_uline(TheLabel,Pchar(Caption));
452
gtk_widget_add_accelerator(PGtkWidget(MenuItem),'activate_item',
454
GDK_MOD1_MASK,GTK_ACCEL_LOCKED);
455
Result:=PGtkMenu(gtk_menu_new);
456
If CallBack<>Nil then
457
gtk_signal_connect(PGtkObject(result),'activate',
458
CallBack,CallBackdata);
459
gtk_widget_show(PgtkWidget(MenuItem));
460
gtk_menu_item_set_submenu(MenuItem, PgtkWidget(Result));
461
gtk_menu_bar_append(MenuBar,PgtkWidget(MenuItem));
463
The code for this procedure quite similar as the previous one. The main
466
\item The result is not a menuitem, but a whole menu. The menuitem that is
467
displayed in the menu bar itself is returned in the \lstinline|MenuItem|
469
\item The shortcut key for the underlined item is added to the window's
470
accelerator group, and has the \textsc{Alt} key (or \textsf{Mod1}) as
472
\item the created menu is attached to the menu item as a sub menu, and it is
473
the menu-item which is attached to the menu bar.
476
With the above calls, a menu can be constructed with a simple set of calls:
478
FileMenu:=AddMenuToMenuBar(MenuBar,accel,'_File',Nil,
479
Nil,False,TempMenuItem);
480
AddItemToMenu(FileMenu,accel,'_New','<control>N',
481
TgtkSignalFunc(@menu),DisplayLabel);
482
AddItemToMenu(FileMenu,accel,'_Open','<control>O',
483
TgtkSignalFunc(@menu),DisplayLabel);
484
AddItemToMenu(FileMenu,accel,'_Save','<control>S',
485
TgtkSignalFunc(@menu),DisplayLabel);
486
AddSeparatorToMenu(PGtkMenu(FileMenu));
487
AddItemToMenu(FileMenu,accel,'_Quit','<control>Q',
488
TgtkSignalFunc(@destroy),Nil);
491
The complete list of calls to create the menu can be found in the sources
492
accompagnying this article.
494
The second program is of course bigger than the first, due to all the code
495
to create the menus. Nevertheless, the manual way of creating has it's
496
advantages: it's quite easy to extend the AddItemToMenu to add a bitmap to
497
the menu entry as well. Using a itemfactory, there is (currently) no way to
498
add images to a menu.
500
Adding a bitmap to a menu is quite easy, and requires only a few extra
501
lines of code. The key point is that the gtkmenuitem object is just an empty
502
container (it descends from gtkbin), which does not display anything by itself. The
503
\lstinline|gtk_menu_item_new_with_label| call creates a menu item and puts a
504
gtklabel in it to display the menu item caption. Instead of a label object,
505
almost any other object can be put in the item. This fact is used in the
506
following code to add a bitmap in front of the menu caption, in a new
507
procedure to be called \lstinline|AddImageItemToMenu|:
509
Result:=pgtkmenuitem(gtk_menu_item_new);
510
hbox:=PGtkHBox(gtk_hbox_new(false,0));
511
gtk_container_add(pgtkcontainer(result),pgtkWidget(hbox));
512
pixmap:=gdk_pixmap_create_from_xpm(Nil,@BitmapData,Nil,pchar(BitMap));
513
Image := PgtkPixMap(gtk_pixmap_new(Pixmap,BitmapData));
514
gtk_box_pack_start(PGtkBox(hbox),pgtkWidget(image),false,false,0);
515
TheLabel:=PgtkLabel(gtk_label_new(''));
516
gtk_box_pack_start(PGtkBox(hbox),pgtkWidget(TheLabel),True,True,0);
517
Key:=gtk_label_parse_uline(TheLabel,Pchar(Caption));
519
In the first line, a plain menu item is created with
520
\lstinline|gtk_menu_item_new|. In the following two lines,
521
a \lstinline|GTKHBox| is added to the menu item, and a reference to the box
522
is stored in the \lstinline|hbox| variable.
524
Then, a pixmap is created from a filename. The filename is passed in the
525
\lstinline|BitMap| parameter to our routine. Using the newly created pixmap,
526
an Image is created, which can then be added to the box.
528
Finally, a regular GTK label is created to hold the caption of the menu
529
item, and added to the box. After that the procedure continues as for a
532
The complete code for the above \lstinline|AddImageItemToMenu| routine can
533
be found in the sources of the third example, accompagnying this article.
534
The result can be seen in figure \ref{fig:pixmenu}
536
\caption{The menu with bitmaps}\label{fig:pixmenu}
537
\epsfig{file=gtk3ex/ex3.png}
540
Some notes regarding this algorithm are in order:
542
\item It would be possible to have not a filename passed to the routine, but
543
directly pass a pixmap object as well; for instance when using a toolbar,
544
toolbuttons corresponding to the menu entries could share the same pixmaps
546
\item Some alignment issues may arise when the menu contains items with and
547
without bitmaps. The above code does not address these issues. To solve
548
them, the regular menu items should also be constructed e.g. using a hbox or a
549
table with an empty cell. Also, an algorithm to determine whether any item of
550
the menu has an image would be needed.
551
\item The shortcut key is no longer shown in the menu widget; The reason for
552
this is unknown to the authors of this article; unfortunately the lack of
553
documentation on GTK prevents the implementation of a remedy.
554
\item The menu callback can no longer retrieve the menu text using a
555
straightforward approach, since the label displaying the caption is
556
no longer the only child widget of the menu item. The callback has been
557
adapted for this in the example.
559
Taking into account the above arguments should make it possible to write
560
better menu-creating routines which would replace the item factory
561
completely, and which would enable the use of bitmaps in menu items.