mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-10-31 16:51:35 +01: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}
 | 
