mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-09 17:28:50 +02:00
564 lines
23 KiB
TeX
564 lines
23 KiB
TeX
\documentclass[10pt]{article}
|
|
\usepackage{a4}
|
|
\usepackage{epsfig}
|
|
\usepackage{listings}
|
|
\usepackage{tabularx}
|
|
\lstset{language=Delphi}%
|
|
\lstset{basicstyle=\sffamily\small}%
|
|
\lstset{commentstyle=\itshape}%
|
|
\lstset{keywordstyle=\bfseries}%
|
|
%\lstset{blankstring=true}%
|
|
\newif\ifpdf
|
|
\ifx\pdfoutput\undefined
|
|
\pdffalse
|
|
\else
|
|
\pdfoutput=1
|
|
\pdftrue
|
|
\fi
|
|
\begin{document}
|
|
\title{Programming GTK in Free Pascal:\\ Menus
|
|
%and Marshallers
|
|
}
|
|
\author{Florian Kl\"ampfl\\and\\Micha\"el Van Canneyt}
|
|
\date{September 2000}
|
|
\maketitle
|
|
\section{Introduction}
|
|
In the third article on programming the GTK toolkit, the us of menus in GTK
|
|
is explored.
|
|
%two topics are
|
|
%explored: The programming of menus and the use of marshallers.
|
|
|
|
Menus can be built in essentially 2 ways; the easier way through the
|
|
use of a itemfactory, and the more complex way, doing all necessary calls
|
|
manually. The advantages of both ways are discussed.
|
|
|
|
%Marshallers can be used to replace the default signal handling mechanisms
|
|
%of GTK. The use of marshallers will be demonstrated by building a small
|
|
%object which will have custom handlers for certain events.
|
|
|
|
\section{Menus the easy way: The item factory}
|
|
The easy way to construct a menu is to use an item factory. An Item factory
|
|
gets as input an array of records, which describe a menu structure, and
|
|
returns a completely built menu, ready to be added to a window.
|
|
|
|
The great advantage of an item factory is that it is easy to use; a
|
|
disadvantage is that
|
|
\begin{enumerate}
|
|
\item There is less control over the produced menu items; e.g.
|
|
displaying a menu item with a small icon is not possible.
|
|
\item The callbacks of the constructed menu is different from the usual
|
|
signal model, making it difficult to combine a menu entry with a
|
|
speedbutton. There are also 2 types of callback, so type checking is not
|
|
possible.
|
|
\item In Pascal, constant records must be specified using the names of the
|
|
members; this makes the array with the menu items to be rendered quite
|
|
complicated.
|
|
\end{enumerate}
|
|
|
|
To create a menu, first the item factory must be created. The function to do
|
|
this is defined as follows:
|
|
\begin{lstlisting}{}
|
|
gtk_item_factory_new(container_type:TGtkType;
|
|
path:Pgchar;
|
|
accel_group:PGtkAccelGroup):PGtkItemFactory;
|
|
\end{lstlisting}
|
|
The three arguments to this function have the following meaning:
|
|
\begin{description}
|
|
\item[container\_type] This identifies the kind of menu that will be
|
|
rendered. It can have one of the following values:
|
|
\begin{description}
|
|
\item[GTK\_MENU\_BAR\_TYPE] A menu bar will be created to hold all items.
|
|
\item[GTK\_MENU\_TYPE] A menu that can be used as a popup menu, or that can be
|
|
attached as a sub-menu to another menu, will be created.
|
|
\item[GTK\_OPTION\_MENU\_TYPE] Makes everything in a drop-down style menu which
|
|
can be used to select one value.
|
|
\end{description}
|
|
\item[path] is the name of the menu to be generated.
|
|
\item[accel\_group] Is a pointer to a group of accelerators. All
|
|
accellerators for the generated menu will be attached to this group.
|
|
\end{description}
|
|
|
|
The accelerator group needed for the item factory can be constructed
|
|
using a simple call to \lstinline|gtk_accel_group_new|; this function
|
|
takes no arguments, and returns a pointer to a new accelerator group.
|
|
|
|
To actually create the menu, a call to
|
|
\lstinline|gtk_item_factory_create_items| is needed; This procedure is
|
|
defined as follows:
|
|
\begin{lstlisting}{}
|
|
gtk_item_factory_create_items(ifactory:PGtkItemFactory;
|
|
n_entries:guint;
|
|
entries:PGtkItemFactoryEntry;
|
|
callback_data:gpointer);
|
|
\end{lstlisting}
|
|
The first argument to this call, \lstinline|ifactory|, is the itemfactory;
|
|
the second argument, \lstinline|n_entries|, is the number of items in the
|
|
array of records describing the menu. The third argument, \lstinline|entries|,
|
|
is the actual array describing the menu. The last argument
|
|
\lstinline|callback_data| is a pointer that will be passed to the menu
|
|
callbacks.
|
|
|
|
The menu structure that should be created by the item factory is an
|
|
array of records of the type \lstinline|TGtkItemFactoryEntry|.
|
|
This record is defined as follows:
|
|
\begin{lstlisting}{}
|
|
TGtkItemFactoryEntry = record
|
|
path : Pgchar;
|
|
accelerator : Pgchar;
|
|
callback : TGtkItemFactoryCallback;
|
|
callback_action : guint;
|
|
item_type : Pgchar;
|
|
end;
|
|
\end{lstlisting}
|
|
The fields have the following meaning:
|
|
\begin{description}
|
|
\item[path]
|
|
The first entry is the path of the menu item. This indicates the place of
|
|
the menu entry in the whole menu. For instance, the menu item \textbf{New}
|
|
in the menu \textbf{File} would be designated by \lstinline|'/File/New'|.
|
|
So, the slash is used to separate the menu levels.
|
|
|
|
To make one of the letters of the menu item name active, so the item can be
|
|
selected by pressing the letter (on the keyboard) when the menu is opened,
|
|
the key to be used should be preceded by an underscore.
|
|
In e.g. \lstinline|'/File/_New'|, the letter \textbf{N} could be used to
|
|
select the \textbf{New} item if the \textbf{File} menu is active.
|
|
|
|
\item[accelerator] To make a shortcut to the menu item so it can be
|
|
activated at all times, the shortcut name can be specified in the
|
|
\lstinline|accelerator| field. This can be any key, together with some
|
|
modifiers. e.g. \lstinline|'<control>N'| will make the key combination
|
|
'CTRL-N' a shortcut.
|
|
|
|
The accelerator should be speciefied as normal text. A list of possible
|
|
modifiers can be found in table \ref{tab:modifiers}.
|
|
\begin{table}[ht]
|
|
\begin{center}
|
|
\caption{List of modifier strings for shortcut keys}\label{tab:modifiers}
|
|
\begin{tabular}{cc}
|
|
Modifier & alias \\ \hline
|
|
\lstinline|<control>| & \lstinline|<ctl>|, \lstinline|<ctrl>| \\
|
|
\lstinline|<shift>| & \lstinline|<shft>| \\
|
|
\lstinline|<alt>| & \lstinline|<mod1>| \\ \hline
|
|
\end{tabular}
|
|
\end{center}
|
|
\end{table}
|
|
|
|
\item[callback] Contains a pointer to the function that should be called
|
|
when the menu item is activated. The type of the menu handler is not the
|
|
same as a normal signal handler; The actual callback should be of the type
|
|
\lstinline|TGtkItemFactoryCallback1|:
|
|
\begin{lstlisting}{}
|
|
procedure (callback_data:gpointer;
|
|
callback_action:guint;
|
|
widget:PGtkWidget);cdecl;
|
|
\end{lstlisting}
|
|
Which is not the same as the type of the \lstinline|callback| field, so
|
|
a typecast will always be necessary.
|
|
|
|
\item[callback\_action] This is passed on to the callback in the
|
|
\lstinline|callback_action| parameter.
|
|
|
|
\item[item\_type] is the type of menu item. Several types can be used; the
|
|
complete list can be found in \ref{tab:menutypes}, but the must important
|
|
ones are \lstinline|'<Item>'|, which specifies a normal menu item,
|
|
and \lstinline|'<Branch>'|, which indicates a sub-menu.
|
|
\begin{table}[ht]
|
|
\begin{center}
|
|
\caption{Possible menu item types}\label{tab:menutypes}
|
|
\begin{tabularx}{\textwidth}{lX}%
|
|
Item type & Menu kind \\ \hline
|
|
\lstinline|'<Item>'| & indicates a normal item. An empty string or \lstinline|Nil|
|
|
have the same meaning. \\
|
|
\lstinline|'<CheckItem>'| & a check menu item. \\
|
|
\lstinline|'<ToggleItem>'| & a toggle menu item (same as check menu). \\
|
|
\lstinline|'<RadioItem>'| & a radio item. \\
|
|
\lstinline|'<Separator>'| & a separator bar. \\
|
|
\lstinline|'<Branch>'| & an item to hold a submenu.\\
|
|
\lstinline|'<LastBranch>'| & an item to hold a submenu, but right aligned.\\ \hline
|
|
\end{tabularx}
|
|
\end{center}
|
|
\end{table}
|
|
\end{description}
|
|
Now all elements to create a menu are introduced, and the menu can be
|
|
created. The following definitions should now be clear:
|
|
\begin{lstlisting}{}
|
|
Var
|
|
Window : PGtkWidget;
|
|
MenuBar : PGtkWidget;
|
|
|
|
Type
|
|
FC = TGtkItemFactoryCallback;
|
|
|
|
Const
|
|
NrMenuItems = 21;
|
|
TheMenu : Array[1..NrMenuItems] of TGtkItemFactoryEntry = (
|
|
(path:'/_File';Accelerator:Nil;
|
|
Callback:Nil;Callback_action:1;item_type:'<Branch>'),
|
|
(path:'/File/_New';Accelerator:'<ctrl>N';
|
|
Callback:FC(@Menu);Callback_action:1;item_type:Nil),
|
|
{ ... }
|
|
\end{lstlisting}
|
|
Here the \lstinline|FC| type is introduced to make the typecast of the
|
|
\lstinline|Menu| handler easier; the
|
|
\lstinline|TheMenu| constant is not given completely, since it is too long
|
|
and not instructive. The complete structure can be found in the sources
|
|
accompanying this article.
|
|
|
|
Using the above definitions, the menu can now be constructed:
|
|
\begin{lstlisting}{}
|
|
Procedure MakeMenu;
|
|
|
|
Var
|
|
Factory : PGtkItemFactory;
|
|
Accel : PGtkAccelGroup;
|
|
|
|
begin
|
|
Accel:=gtk_accel_group_new;
|
|
Factory :=gtk_item_factory_new(GTK_MENU_BAR_TYPE,'<main>',accel);
|
|
gtk_item_factory_create_items(Factory,NrMenuItems,@TheMenu,Nil);
|
|
gtk_window_add_accel_group(GTK_Window(Window),accel);
|
|
MenuBar:=gtk_item_factory_get_widget (Factory, '<main>');
|
|
end;
|
|
\end{lstlisting}
|
|
The \lstinline|gtk_window_add_accel_group| call attaches the accelerator
|
|
group that was filled up by the item factory to the window.
|
|
|
|
The \lstinline|gtk_item_factory_get_widget| call finally fetches the
|
|
object created by the item factory and stores it in a widget variable.
|
|
|
|
The \lstinline|Menu| callback used in the menus is defined as follows:
|
|
\begin{lstlisting}{}
|
|
procedure menu(Data : GPointer;
|
|
Action : Guint;
|
|
Widget : pGtkWidget); cdecl;
|
|
|
|
Var
|
|
TheLabel : PgtkWidget;
|
|
LabelText : Pchar;
|
|
S : AnsiString;
|
|
|
|
begin
|
|
TheLabel:=g_list_nth_data(
|
|
gtk_container_children(
|
|
GTK_CONTAINER(Widget)),0);
|
|
gtk_label_get(gtk_Label(theLabel),@LabelText);
|
|
S := 'Chosen menu : ' + Strpas(Labeltext);
|
|
gtk_label_set_text(GTK_LABEL(DisplayLabel),pchar(S));
|
|
end;
|
|
\end{lstlisting}
|
|
The \lstinline|DisplayLabel| is a label located on the window, it is used to
|
|
give some feedback on the used menu. The code to extract the menu name from
|
|
the menu widget passed to the handler will be explained later on.
|
|
|
|
The result of all this is shown in figure \ref{fig:ex1}.
|
|
\begin{figure}
|
|
\caption{The menu made by the item factory.}\label{fig:ex1}
|
|
\epsfig{file=gtk3ex/ex1.png}
|
|
\end{figure}
|
|
|
|
As can be seen from the code above, the creation of a menu using an item
|
|
factory in GTK is not so hard. The drawback of the above method lies mainly
|
|
in the fact that Pascal handles constant records differently than C, which
|
|
makes the array that describes the menu structure rather difficult to read.
|
|
|
|
The second drawback is that there is little control over the created items.
|
|
|
|
\section{Menus the hard way: manually}
|
|
When creating menus manually, mainly 4 objects are involved:
|
|
\begin{itemize}
|
|
\item The menu items themselves. To a menu item, a menu can be assiciated,
|
|
creating a sub-menu.
|
|
\item Menus, which contain a collection of menu items,
|
|
\item A accelerator group. This will be used to keep a collection of
|
|
shortcut keys for the menu items.
|
|
\item A menu bar, which can hold several menu items and their associated
|
|
menus.
|
|
\end{itemize}
|
|
The last object is optional, if e.g. a pop-up menu is wanted.
|
|
|
|
To create a menu in a window, the following steps are involved:
|
|
\begin{enumerate}
|
|
\item Create an accellerator group. The accelerator group should be
|
|
connected to the window. \label{stepone}
|
|
\item Create a menu bar, and attach it to the window.
|
|
\item For each menu that should appear in a menu bar, do the following:
|
|
\begin{itemize}
|
|
\item Create a menu item, which will be shown in the menu bar.
|
|
\item Create a menu to hold the items that should pop up when the menu is
|
|
activated.
|
|
\end{itemize}
|
|
\item To each menu created in the previous step, add as many menu items as
|
|
needed. Add an accelarator to the group created in step \ref{stepone}.
|
|
\end{enumerate}
|
|
|
|
To make these steps easier (each of them involves quite some calls to GTK
|
|
functions) some functions will be introduced that make this easier.
|
|
|
|
The first function is the most simple one; it attaches a separator to a
|
|
menu:
|
|
\begin{lstlisting}{}
|
|
Function AddSeparatorToMenu(Menu:PgtkMenu):PgtkMenuItem;
|
|
|
|
begin
|
|
Result:=pgtkmenuitem(gtk_menu_item_new);
|
|
gtk_menu_append(Menu,pgtkWidget(result));
|
|
gtk_widget_show(PgtkWidget(result));
|
|
end;
|
|
\end{lstlisting}
|
|
The function takes one parameter, \lstinline|Menu|, the menu to which the
|
|
separator will be attached. A separator is created by simply creating an
|
|
empty menu item. Creating a new (empty) menu item is done with the
|
|
\lstinline|gtk_menu_item_new| call.
|
|
|
|
With the \lstinline|gtk_menu_append| call, the newly created item is then
|
|
added to the menu. Lastly, the item is shown; it will not become actually
|
|
visible till the menu is activated. If this is omitted, it will also not
|
|
be visible when the menu is activated.
|
|
|
|
Adding a menu with a shortcut key to a menu is a little more involved. Some
|
|
more elements are needed:
|
|
\begin{enumerate}
|
|
\item The menu to which to attach the menu item.
|
|
\item The accelarator group to which the accelerator key should be added.
|
|
\item The caption of the menu. An underscore character will indicate the
|
|
letter of themenu that will be used as a shortcut to activate the item.
|
|
\item The shortcut for the menu item. An empty string means no shortcut.
|
|
\item A callback function which will be called when the menu item is
|
|
activated, and callback data which will sent to the callback.
|
|
\end{enumerate}
|
|
All these elements are found in the declaration of the following function:
|
|
\begin{lstlisting}{}
|
|
Function AddItemToMenu (Menu : PGtkMenu;
|
|
ShortCuts : PGtkAccelGroup;
|
|
Caption : AnsiString;
|
|
ShortCut : AnsiString;
|
|
CallBack : TgtkSignalFunc;
|
|
CallBackdata : Pointer
|
|
) : PGtkMenuItem;
|
|
|
|
Var
|
|
Key,Modifiers : guint;
|
|
LocalAccelGroup : PGtkAccelGroup;
|
|
TheLabel : PGtkLabel;
|
|
|
|
begin
|
|
\end{lstlisting}
|
|
The variables declared in this function will be explained as the code is
|
|
presented.
|
|
|
|
First of all, a menu item must be created. Since a caption for the menu is
|
|
provided, the \lstinline|gtk_menu_item_new_with_label| will be used to
|
|
create a menu that has a label as a child:
|
|
\begin{lstlisting}{}
|
|
Result:=pgtkmenuitem(gtk_menu_item_new_with_label(''));
|
|
TheLabel:=GTK_LABEL(GTK_BIN(Result)^.child);
|
|
Key:=gtk_label_parse_uline(TheLabel,Pchar(Caption));
|
|
\end{lstlisting}
|
|
After the menu item is created, the child label is fetched. The label caption is
|
|
then set using the \lstinline|gtk_label_parse_uline| function. This function
|
|
will search a text for underscore characters, remove them from the text, and
|
|
then set the label's caption with the result. All letters which had an
|
|
underscore character prepended will be underlined in the label.
|
|
|
|
The function returns the first letter that had an underscore prepended. It
|
|
is stored, so it can be used to make an accelerator:
|
|
\begin{lstlisting}{}
|
|
If Key<>0 then
|
|
begin
|
|
LocalAccelGroup:=gtk_menu_ensure_uline_accel_group(Menu);
|
|
gtk_widget_add_accelerator(PGtkWidget(result),'activate_item',
|
|
LocalAccelGroup,Key,
|
|
0,TGtkAccelFlags(0));
|
|
end;
|
|
\end{lstlisting}
|
|
The call to \lstinline|gtk_menu_ensure_uline_accel_group| returns the
|
|
accelarator group associated with the menu. If no group existed yet, one
|
|
will be created. The \lstinline|gtk_widget_add_accelerator| call takes the
|
|
following parameters:
|
|
\begin{itemize}
|
|
\item A pointer to a widget to which the accelerator should be attached.
|
|
\item The name of the signal which will be triggered when the shortcut
|
|
is activated.
|
|
\item The accelerator group to which the shortcut should be installed,
|
|
usually this will be the accelerator group for the window to which the
|
|
widget is attached, but in this case this is the accelerator group of the
|
|
menu (which will only be active when the menu is actually shown)
|
|
\item The key from the shortcut.
|
|
\item The modifiers that should be pressed together with the key. For the
|
|
menu, this should be 0, since just the key should be hit.
|
|
\item The accelerator flags.
|
|
\end{itemize}
|
|
|
|
After the menu item was created and it's underlined key was made into an
|
|
accelerator, the menu can be attached to the menu:
|
|
\begin{lstlisting}{}
|
|
gtk_menu_append(Menu,pgtkWidget(result));
|
|
\end{lstlisting}
|
|
|
|
If a shortcut key was passed along to the procedure, can be added to the
|
|
window's accelerator group with the following code:
|
|
\begin{lstlisting}{}
|
|
If (ShortCut<>'') and (ShortCuts<>Nil) then
|
|
begin
|
|
gtk_accelerator_parse (pchar(ShortCut), @key, @modifiers);
|
|
gtk_widget_add_accelerator(PGtkWidget(result),'activate_item',
|
|
ShortCuts,Key,
|
|
modifiers, GTK_ACCEL_VISIBLE);
|
|
end;
|
|
\end{lstlisting}
|
|
The call to \lstinline|gtk_accelerator_parse| will parse a string which
|
|
describes a shortcut key, and returns the corresponding key and modifiers,
|
|
which can then be passed on to the \lstinline|gtk_widget_add_accelerator|
|
|
call.
|
|
|
|
After the accellerator has been installed, the only thing that remains to be
|
|
done is to connect the callback to the activation of the menu:
|
|
\begin{lstlisting}{}
|
|
If CallBack<>Nil then
|
|
gtk_signal_connect(PGtkObject(result),'activate',
|
|
CallBack,CallBackdata);
|
|
gtk_widget_show(PgtkWidget(result));
|
|
end;
|
|
\end{lstlisting}
|
|
As the last line in the procedure, the newly created menu item is shown.
|
|
If the menu isn't visible yet, this will do nothing, but will ensure that
|
|
the item is also visible when the menu is visible.
|
|
|
|
Now a menu-item and a separator can be added to a menu. What remains to be
|
|
done is to add a menu to a menu bar. This is done in the following
|
|
procedure, which is given in its entirety:
|
|
\begin{lstlisting}{}
|
|
Function AddMenuToMenuBar(MenuBar : PGtkMenuBar;
|
|
ShortCuts : PGtkAccelGroup;
|
|
Caption : AnsiString;
|
|
CallBack : TgtkSignalFunc;
|
|
CallBackdata : Pointer;
|
|
AlignRight : Boolean;
|
|
Var MenuItem : PgtkMenuItem
|
|
) : PGtkMenu;
|
|
|
|
Var
|
|
Key : guint;
|
|
TheLabel : PGtkLabel;
|
|
|
|
begin
|
|
MenuItem:=pgtkmenuitem(gtk_menu_item_new_with_label(''));
|
|
If AlignRight Then
|
|
gtk_menu_item_right_justify(MenuItem);
|
|
TheLabel:=GTK_LABEL(GTK_BIN(MenuItem)^.child);
|
|
Key:=gtk_label_parse_uline(TheLabel,Pchar(Caption));
|
|
If Key<>0 then
|
|
gtk_widget_add_accelerator(PGtkWidget(MenuItem),'activate_item',
|
|
Shortcuts,Key,
|
|
GDK_MOD1_MASK,GTK_ACCEL_LOCKED);
|
|
Result:=PGtkMenu(gtk_menu_new);
|
|
If CallBack<>Nil then
|
|
gtk_signal_connect(PGtkObject(result),'activate',
|
|
CallBack,CallBackdata);
|
|
gtk_widget_show(PgtkWidget(MenuItem));
|
|
gtk_menu_item_set_submenu(MenuItem, PgtkWidget(Result));
|
|
gtk_menu_bar_append(MenuBar,PgtkWidget(MenuItem));
|
|
\end{lstlisting}
|
|
The code for this procedure quite similar as the previous one. The main
|
|
differences are:
|
|
\begin{itemize}
|
|
\item The result is not a menuitem, but a whole menu. The menuitem that is
|
|
displayed in the menu bar itself is returned in the \lstinline|MenuItem|
|
|
parameter.
|
|
\item The shortcut key for the underlined item is added to the window's
|
|
accelerator group, and has the \textsc{Alt} key (or \textsf{Mod1}) as
|
|
the modifier key.
|
|
\item the created menu is attached to the menu item as a sub menu, and it is
|
|
the menu-item which is attached to the menu bar.
|
|
\end{itemize}
|
|
|
|
With the above calls, a menu can be constructed with a simple set of calls:
|
|
\begin{lstlisting}{}
|
|
FileMenu:=AddMenuToMenuBar(MenuBar,accel,'_File',Nil,
|
|
Nil,False,TempMenuItem);
|
|
AddItemToMenu(FileMenu,accel,'_New','<control>N',
|
|
TgtkSignalFunc(@menu),DisplayLabel);
|
|
AddItemToMenu(FileMenu,accel,'_Open','<control>O',
|
|
TgtkSignalFunc(@menu),DisplayLabel);
|
|
AddItemToMenu(FileMenu,accel,'_Save','<control>S',
|
|
TgtkSignalFunc(@menu),DisplayLabel);
|
|
AddSeparatorToMenu(PGtkMenu(FileMenu));
|
|
AddItemToMenu(FileMenu,accel,'_Quit','<control>Q',
|
|
TgtkSignalFunc(@destroy),Nil);
|
|
{ ... }
|
|
\end{lstlisting}
|
|
The complete list of calls to create the menu can be found in the sources
|
|
accompagnying this article.
|
|
|
|
The second program is of course bigger than the first, due to all the code
|
|
to create the menus. Nevertheless, the manual way of creating has it's
|
|
advantages: it's quite easy to extend the AddItemToMenu to add a bitmap to
|
|
the menu entry as well. Using a itemfactory, there is (currently) no way to
|
|
add images to a menu.
|
|
|
|
Adding a bitmap to a menu is quite easy, and requires only a few extra
|
|
lines of code. The key point is that the gtkmenuitem object is just an empty
|
|
container (it descends from gtkbin), which does not display anything by itself. The
|
|
\lstinline|gtk_menu_item_new_with_label| call creates a menu item and puts a
|
|
gtklabel in it to display the menu item caption. Instead of a label object,
|
|
almost any other object can be put in the item. This fact is used in the
|
|
following code to add a bitmap in front of the menu caption, in a new
|
|
procedure to be called \lstinline|AddImageItemToMenu|:
|
|
\begin{lstlisting}{}
|
|
Result:=pgtkmenuitem(gtk_menu_item_new);
|
|
hbox:=PGtkHBox(gtk_hbox_new(false,0));
|
|
gtk_container_add(pgtkcontainer(result),pgtkWidget(hbox));
|
|
pixmap:=gdk_pixmap_create_from_xpm(Nil,@BitmapData,Nil,pchar(BitMap));
|
|
Image := PgtkPixMap(gtk_pixmap_new(Pixmap,BitmapData));
|
|
gtk_box_pack_start(PGtkBox(hbox),pgtkWidget(image),false,false,0);
|
|
TheLabel:=PgtkLabel(gtk_label_new(''));
|
|
gtk_box_pack_start(PGtkBox(hbox),pgtkWidget(TheLabel),True,True,0);
|
|
Key:=gtk_label_parse_uline(TheLabel,Pchar(Caption));
|
|
\end{lstlisting}
|
|
In the first line, a plain menu item is created with
|
|
\lstinline|gtk_menu_item_new|. In the following two lines,
|
|
a \lstinline|GTKHBox| is added to the menu item, and a reference to the box
|
|
is stored in the \lstinline|hbox| variable.
|
|
|
|
Then, a pixmap is created from a filename. The filename is passed in the
|
|
\lstinline|BitMap| parameter to our routine. Using the newly created pixmap,
|
|
an Image is created, which can then be added to the box.
|
|
|
|
Finally, a regular GTK label is created to hold the caption of the menu
|
|
item, and added to the box. After that the procedure continues as for a
|
|
normal menu.
|
|
|
|
The complete code for the above \lstinline|AddImageItemToMenu| routine can
|
|
be found in the sources of the third example, accompagnying this article.
|
|
The result can be seen in figure \ref{fig:pixmenu}
|
|
\begin{figure}[ht]
|
|
\caption{The menu with bitmaps}\label{fig:pixmenu}
|
|
\epsfig{file=gtk3ex/ex3.png}
|
|
\end{figure}
|
|
|
|
Some notes regarding this algorithm are in order:
|
|
\begin{enumerate}
|
|
\item It would be possible to have not a filename passed to the routine, but
|
|
directly pass a pixmap object as well; for instance when using a toolbar,
|
|
toolbuttons corresponding to the menu entries could share the same pixmaps
|
|
as the menu entries.
|
|
\item Some alignment issues may arise when the menu contains items with and
|
|
without bitmaps. The above code does not address these issues. To solve
|
|
them, the regular menu items should also be constructed e.g. using a hbox or a
|
|
table with an empty cell. Also, an algorithm to determine whether any item of
|
|
the menu has an image would be needed.
|
|
\item The shortcut key is no longer shown in the menu widget; The reason for
|
|
this is unknown to the authors of this article; unfortunately the lack of
|
|
documentation on GTK prevents the implementation of a remedy.
|
|
\item The menu callback can no longer retrieve the menu text using a
|
|
straightforward approach, since the label displaying the caption is
|
|
no longer the only child widget of the menu item. The callback has been
|
|
adapted for this in the example.
|
|
\end{enumerate}
|
|
Taking into account the above arguments should make it possible to write
|
|
better menu-creating routines which would replace the item factory
|
|
completely, and which would enable the use of bitmaps in menu items.
|
|
|
|
\end{document}
|