17.18 WINDOWS WITH MENUS IN GNOMEGTK


17.18 WINDOWS WITH MENUS IN GNOME/GTK+

We will use the same example as in Section 17.16 for Java to illustrate how menus and menu items are specified in GNOME/GTK+. Basically, we want a rudimentary GUI that would help us edit a small text file. So we want a GNOME/GTK+ program that will create a window with a menu bar at the top containing one menu item named "File". As for the case of Java, we want to associate with "File" a pull-down menu consisting of the following items:

      New      Open      Save      Exit 

In GNOME, a menu is simply an array of GnomeUIInfo objects, one for each menu item. For example, the pull-down menu associated with "File" consists of the following array of GnomeUIInfo objects. As the reader can tell, the first element of this array is the GnomeUIInfo object for the menu item "New," the second for the menu item "Open," and so on.

      GnomeUIInfo fileMenu[] = {                                 //(A)           { GNOME_APP_UI_ITEM,                                 //(A1)           "New",                                               //(A2)           "Allow text entry into window",                      //(A3)           allowTextEntry,                                      //(A4)           NULL,                                                //(A5)           NULL,                                                //(A6)           GNOME_APP_PIXMAP_NONE,                               //(A7)           NULL,                                                //(A8)           0,                                                   //(A9)           0,                                                  //(A10)           NULL },                                             //(A11)         { GNOME_APP_UI_ITEM, "Open",           "Open an existing file",           selectFileForLoad, NULL, NULL,           GNOME_APP_PIXMAP_NONE,           NULL, 0, 0, NULL },         { GNOME_APP_UI_ITEM, "Save",           "save contents to the current file",           selectFileForSave, NULL, NULL,           GNOME_APP_PIXMAP_NONE,           NULL, 0, 0, NULL },         GNOMEUIINFO_SEPARATOR,                                  //(B)       { GNOME_APP_UI_ITEM, "Exit",           "Close the window and cease",           eventDestroy, NULL, NULL,           GNOME_APP_PIXMAP_NONE,           NULL, 0, 0, NULL },         GNOMEUIINFO_END      }; 

In the above declaration, we have written the first GnomeUIInfo object with one field value per line, and the rest more compactly.[27] As should be evident from the GnomeUIInfo object created for the menu item "New", the GnomeUIInfo struct has 11 fields:

  1. Th first field requires a type label for the menu item. The different possible type labels are

          GNOME_APP_UI_ITEM                     //(C)      GNOME_APP_UI_SEPARATOR                //(D)      GNOME_APP_UI_SUBTREE                  //(E)      GNOME_APP_UI_ENDOFINFO                //(F)      GNOME_APP_UI_HELP                     //(G)      GNOME_APP_UI_RADIOITEMS      GNOME_APP_UI_TOGGLEITEM      etc. 

    For the GnomeUIInfo object corresponding to "New," line (A1) declares the menu item to be of type GNOME_APP_UI_ITEM. This is also the case for the GnomeUIInfo objects for the menu items "Open", "Save" and "Exit".

  2. The second field requires a character string, of type gchar*,[28] that is actually displayed in the menu for the menu item. Line (A2) shows this character string for the "New" menu item. When this string is preceded by an underscore, the menu item has an accelerator key associated with it. For illustration, if for the first menu item shown above we changed the second field from New to_New in line (A2), we'd be able to select this menu item by pressing on the keyboard either Alt-N or Meta-N, depending on the operating system.

  3. A character string, of type gchar*, that appears in the status bar of a window, if one is provided, when the mouse pointer is on the menu item. This string, shown in line (A3) for the "New" menu item, serves as an elaboration of the label that actually appears in the menu.

  4. This is a pointer whose meaning is predicated on the type label used for the first field. When the type label in the first field is GNOME_APP_UI_ITEM, this field is a pointer to the callback function to invoke when the menu item in question is selected. When the type label in the first field is GNOME_APP_UI_SUBTREE, this field points to another array of GnomeUIInfo objects. (In this manner, one can create hierarchical menus.) When the type label in the first field points to GNOME_APP_UI_HELP, this field specifies the name of the help node to be loaded, and so on. In our example above, this entry in line (A4) is a pointer to a callback function.

  5. If the previous field specifies a callback function and the function expects arguments, this field can be used to specify a pointer to the arguments. As line (A5) shows, this value is NULL for the "New" menu item since the callback does not expect any arguments.

  6. Reserved for future use.

  7. The type of pixmap to be used for the menu button. A pixmap for a menu button may be incorporated directly within the program as data, or supplied through a separate file, or retrieved from an icon source, and so on. This field tells the program which of these is true. The type can be one of

          GNOME_APP_PIXMAP_DATA      GNOME_APP_PIXMAP_FILENAME      GNOME_APP_PIXMAP_STOCK      GNOME_APP_PIXMAP_NONE 

    In our menu example, line (A7) declares this field to be GNOME_APP_PIXMAP_NONE to signify that the menu button for "New" will not display a pixmap icon.

  8. If the previous field causes a program to expect a pixmap for a menu button, this field supplies a pointer to the pixmap. If the previous field mentions GNOME_APP_PIXMAP_FILENAME, this field is a pointer to the character string that is the name of the file containing the XPM data.

  9. This field specifies the accelerator key, which must be of type gint. The symbol 0 means no accelerator key for the menu item. Line (A9) in our example declares 0 for this field.

  10. This is a mask of the modifier keys for the accelerator. Must be of type GtkModifierType.

  11. If access to the widget corresponding to the menu button is desired, this field can serve as a pointer to the widget.

Once an array of GnomeUIInfo objects is declared, as we did for fileMenu above in line (A), it can be directly incorporated into a top level window by

    gnome_app_create_menus(GNOME_APP(app), fileMenu); 

where app is a pointer to the GNOME application window, which must be of type GNOME_APP. The above invocation will display all the menu buttons horizontally at the top of the application window. If the menu created through the array fileMenu in line (A) is to be a pop-down menu, you'd need to incorporate it within a "higher level" menu which could be a menu bar consisting of buttons corresponding to the names of the individual menus. For the simple case here, the menu corresponding to the menu bar can be declared by

      GnomeUIInfo mainMenu[] = {           GNOMEUIINFO_SUBTREE("File", fileMenu),                    //(H)           GNOMEUIINFO_END                                           //(I)      }; 

where the second argument, fileMenu, to the macro GNOMEUIINFO_SUBTREE in line (H) is as defined in line (A) previously. This would cause a button labeled "File" to show up in the menubar at the top of the window. Clicking on this button would then reveal the menu declared through the array fileMenu in line (A). To include another menu in the menubar, say a menu declared through a GnomeUIInfo array named helpMenu, we could do that by expanding the above declaration as

      GnomeUIInfo mainMenu[] = {           GNOMEUIINFO_SUBTREE("File", fileMenu),           GNOMEUIINFO_SUBTREE("Help", helpMenu),           GNOMEUIINFO_END                                           //(J)      }; 

The important thing to note here is that all menus are arrays of GnomeUIInfo objects. This applies as much to the menu consisting of the buttons that are usually displayed horizontally in a menubar as it does to a pop-down menu that becomes visible when you select a menu item from a "higher level" menu.

Note that the invocation GNOMEUIINFO_SUBTREE in line (H) above is a macro that creates a GnomeUIInfo object of type GNOME_APP_UI_SUBTREE, a menu item type listed in line (E). There are many other macros available that mitigate the task of having to specify values for all eleven fields of a GnomeUIInfo object. For another illustration, the macro GNOMEUIINFO_HELP(app_name) directly constructs a GnomeUIInfo object of type GNOME_APP_UI_HELP of line (G) without having to specify the other 10 fields. The identifiers GNOMEUIINFO_END in lines (I) and (J) and GNOMEUIINFO_SEPERATOR used in line (B) are also macros. These macros can also be used for constructing toolbars; a toolbar is the same as a menu except that it uses graphics for buttons instead of labels.

To provide an explanation of the program shown below, the initial part of the code in main is similar to what we have shown before for other GNOME/GTK+ programs. Lines (N1) through (N3) of main first construct a text area (using a call that was explained earlier in the CrazyWindow.c program of Section 17.15.1), set the text area to be uneditable to begin with, and insert the text area into the top-level window. Line (N4) endows the top-level window with the menu defined previously in global scope in line (M). Apart from main, the program shown below consists of the following callback functions:

allowTextEntry, declared in line (K2) and defined in line (O). This callback is associated with the menu button "New" of the "File" menu by virtue of its appearing in line (L1) at the position shown. The definition of this callback makes the text area editable and thus allows a user to enter text from the keyboard.

selectFileForLoad, declared in line (K3) and defined in line (P). This callback is associated with the menu button "Open" of the "File" menu by virtue of its appearing in line (L2) at the position shown. The definition of this callback first brings up a file dialog window through the invocation of the function gtk_file_selection_new in line (P1). The rest of the code for selectFileForLoad is for managing the interaction of the user with the file dialog window. As implied by the "connect" statement that begins in line (P1), if the user clicks on the "OK" button of the file dialog window, the signal produced invokes getTextFromFile, a callback declared in line (K5) and defined in line (Q). The other two "connect" statements, in lines (P2) and (P3), are for the responses needed from the file dialog window if the user either hits the "Cancel" button or simply closes the window.

selectFileForSave, declared in line (K4) and defined in line (R). This callback is associated with the menu button "Save" of the "File" menu by virtue of its appearance in line (L3) at the position shown. The invocation of gtk_file_selection_new in line (R1) again brings up a file dialog window to make it convenient for the user to choose or name a file in which to save the content of the text area. As the statement that begins in line (R2) shows, if the user clicks on the "OK" button of the file dialog window, the callback saveTextToFile, declared in line (K6) and defined in line (S), is invoked.

getTextFromFile is declared in line (K5) and defined in line (Q). As mentioned previously, this callback is invoked when the user clicks on the "OK" button in a file dialog window that pops up as a result of the user clicking on the "Open" menu button. Regarding its implementation, the statement in line (Q1) first retrieves the file name from the file dialog window. This file is opened in read mode in line (Q2). The function reads the file one character at a time and inserts it in the text area of the GUI in the loop in line (Q3).

saveTextToFile is declared in line (K6) and defined in line (S). Its implementation is straightforward, being similar to that of getTextFromFile, except for the fact it writes out the content of the text area into a file chosen through the file dialog window.

eventDestroy The reader has already seen this callback in all the GNOME/GTK+ programs shown so far. This function is declared in line (K1) and defined at the end of the program. This callback is associated with the menu button "Exit" because it appears in line (L4) as shown.

The source code follows:

 
//WindowWithMenu.c #include <gnome.h> GtkWidget* textarea; gchar* selected_filename; GtkWidget* file_selector; gint eventDestroy(GtkWidget* widget, GdkEvent* event, gpointer data); //(K1) void allowTextEntry(GtkObject* object, gpointer data); //(K2) void selectFileForLoad(GtkObject* object, gpointer data); //(K3) void selectFileForSave(GtkObject* object, gpointer data); //(K4) int getTextFromFile(GtkFileSelection* selector, gpointer data); //(K5) int saveTextToFile(GtkFileSelection* selector, gpointer data); //(K6) GnomeUIInfo fileMenu[] = { //(L) { GNOME_APP_UI_ITEM, "New", "Allow text entry into window", allowTextEntry, NULL, NULL, //(L1) GNOME_APP_PIXMAP_NONE, NULL, 0, 0, NULL }, { GNOME_APP_UI_ITEM, "Open", "Open an existing file", selectFileForLoad, NULL, NULL, //(L2) GNOME_APP_PIXMAP_NONE, NULL, 0, 0, NULL }, { GNOME_APP_UI_ITEM, "save", "save contents to the current file", selectFileForSave, NULL, NULL, //(L3) GNOME_APP_PIXMAP_NONE, NULL, 0, 0, NULL }, GNOMEUIINFO_SEPARATOR, { GNOME_APP_UI_ITEM, "Exit", "Close the window and cease", eventDestroy, NULL, NULL, //(L4) GNOME_APP_PIXMAP_NONE, NULL, 0, 0, NULL }, GNOMEUIINFO_END }; GnomeUIInfo mainMenu[] = { //(M) GNOMEUIINFO_SUBTREE("File", fileMenu), GNOMEUIINFO_END }; int main(int argc, char* argv[]) { GtkWidget* app; gnome_init("aspect", "1.0", argc, argv); app = gnome_app_new("menudemo", "Menu Demo Window"); gtk_window_set_default_size(GTK_WINDOW(app), 300, 200); gtk_signal_connect(GTK_OBJECT(app), "destroy", GTK_SIGNAL_FUNC(eventDestroy), NULL); textarea = gtk_text_new(NULL, NULL); //(N1) gtk_text_set_editable(GtkText*) textarea, FALSE); //(N2) gnome_app_set_contents(GNOME_APP(app), textarea); //(N3) gnome_app_create_menus(GNOME_APP(app), mainMenu); //(N4) gtk_widget_show_all(app); gtk_main(); exit(0); } void allowTextEntry(GtkObject* object, gpointer data) { //(O) gtk_text_set_editable((GtkText*) textarea, TRUE); } void selectFileForLoad(GtkObject* object, gpointer data) { //(P) file_selector = gtk_file_selection_new("Load Dialog"); gtk_signal_connect( //(P1) GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button), "clicked", GTK_SIGNAL_FUNC(getTextFromFile), NULL); gtk_signal_connect_object( //(P2) GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer) file_selector); gtk_signal_connect_object( //(P3) GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer) file_selector); gtk_widget_show(file_selector); } int getTextFromFile(GtkFileSelection* selector, gpointer data){ //(Q) FILE* fp; gint ch; gchar* str = g_malloc(1 + 1); selected_filename = gtk_file_selection_get_filename( //(Q1) GTK_FILE_SELECTION(file_selector)); g_print("Filename retrieved: %s\n", selected_filename); if ((fp = fopen(selected_filename, "r")) == NULL) { //(Q2) g_print("Unable to open the file"); return 1; } str[1] = '\0'; while ((ch = getc(fp)) != EOF) { //(Q3) str[ 0 ] = (gchar) ch; gtk_text_insert(GTK_TEXT(textarea), NULL, NULL, NULL, str, -1); } gtk_text_set_editable((GtkText*) textarea, TRUE); fclose(fp); g_free(str); return 0; } void selectFileForSave(GtkObject* object, gpointer data) { //(R) file_selector = gtk_file_selection_new("Save Dialog"); //(R1) gtk_signal_connect( //(R2) GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button), "clicked", GTK_SIGNAL_FUNC(saveTextToFile), NULL); gtk_signal_connect_object( GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->ok_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer) file_selector); gtk_signal_connect_object( GTK_OBJECT(GTK_FILE_SELECTION(file_selector)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer) file_selector); gtk_widget_show(file_selector); } int saveTextToFile (GtkFileSelection* selector, gpointer data) { //(S) FILE* fp; int len; int i = 0; gchar* str = gtk_editable_get_chars((GtkEditable*) textarea, 0, -1); len = strlen(str); selected_filename = gtk_file_selection_get_filename( GTK_FILE_SELECTION(file_selector)); if ((fp = fopen(selected_filename, "w")) == NULL) { g_print("Unable to open the file for save"); return 1; } while (i < len) fputc(str[ i++ ], fp); fclose (fp); g_free(str); return 0; } gint eventDestroy(GtkWidget* widget, GdkEvent* event, gpointer data) { gtk_main_quit(); return 0; }

This program can be compiled using the following makefile that is named Makefile_GTK_WindowWithMenu. The makefile is invoked by

      make -f Makefile_gtk_WindowWithMenu 
 
CC=gcc LDLIBS='gnome-config --libs gnomeui' CFLAGS=-Wall -g 'gnome-config --cflags gnomeui' WindowWithMenu: WindowWithMenu.o Makefile_gtk_WindowWithMenu $(CC) $(LDLIBS) WindowWithMenu.o -o WindowWithMenu WindowWithMenu.o: WindowWithMenu.c $(CC) $(CFLAGS) -c WindowWithMenu.c clean: rm -f WindowWithMenu rm -f WindowWithMenu.o

The window produced by this program is shown in Figure 17.33. The screen shot was taken with the "File" pull-down menu visible.

click to expand
Figure 17.33

[27]In the array of five GnomeUIInfo objects shown above starting at line (A), there is an odd-looking object in line (B)—odd since it does not correspond to the structure of the GnomeUIInfo struct. The object GNOMEUIINFO_SEPARATOR is actually a macro that we will talk about later.

[28]Section 17.4 briefly mentions the different data types of GNOME/GTK+.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net