Previous Next Contents

14. Menu物件

有两种方式来产生选单物件, 一种简单的, 一种难的. 两种各有其用途, 但您可以用menu_factory(简单的). 难的方法是一个一个产生. 简单的是用gtk_menu_factory 这个简单多了, 但各有其优劣之处.

menufactory很好用, 虽然另外写一些函数, 以手动函数来产生这些选单会比较有用. 不过, 以menufactory, 也是可以加影像到选单中.

14.1 Manual Menu Creation

在教学的目的上, 我们先来看看难的方法.:)

先看看产生选单的函数. 第一个当然是产生一个新的选单.

GtkWidget *gtk_menu_bar_new()
GtkWidget *gtk_menu_new();

这个函数返回一个新的选单, 它还不会显示.

以下两个函数是用来产生选单项目.

GtkWidget *gtk_menu_item_new()

and

GtkWidget *gtk_menu_item_new_with_label(const char *label)

动态新增

gtk_menu_item_append()

gtk_menu_item_set_submenu()

gtk_menu_new_with_label及gtk_menu_new函数 一个产生一个新的选单项目并带标签, 另一个则是个空的选单项目.

产生选单的步骤大致如下:

14.2 Manual Menu范例

我们来做做看, 看看一个范例会比较有帮助.

#include <gtk/gtk.h>

int main (int argc, char *argv[])
{

    GtkWidget *window;
    GtkWidget *menu;
    GtkWidget *menu_bar;
    GtkWidget *root_menu;
    GtkWidget *menu_items;
    char buf[128];
    int i;

    gtk_init (&argc, &argv);

    /* create a new window */
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW (window), "GTK Menu Test");
    gtk_signal_connect(GTK_OBJECT (window), "destroy",
                       (GtkSignalFunc) gtk_exit, NULL);

    /* Init the menu-widget, and remember -- never
     * gtk_show_widget() the menu widget!! */
    menu = gtk_menu_new();

    /* This is the root menu, and will be the label will be the menu name displayed on
     * the menu bar.  There won't be
     * a signal handler attached, as it only pops up the rest of the menu when pressed. */
    root_menu = gtk_menu_item_new_with_label("Root Menu");

    gtk_widget_show(root_menu);

    /* Next we make a little loop that makes three menu-entries for "test-menu".
     * Notice the call to gtk_menu_append.  Here we are adding a list of menu items
     * to our menu.  Normally, we'd also catch the "clicked" signal on each of the
     * menu items and setup a callback for it, but it's omitted here to save space. */

    for(i = 0; i < 3; i++)
        {
            /* Copy the names to the buf. */
            sprintf(buf, "Test-undermenu - %d", i);

            /* Create a new menu-item with a name... */
            menu_items = gtk_menu_item_new_with_label(buf);

            /* ...and add it to the menu. */
            gtk_menu_append(GTK_MENU (menu), menu_items);

            /* Show the widget */
            gtk_widget_show(menu_items);
        }

    /* Now we specify that we want our newly created "menu" to be the menu for the "root menu" */
    gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);

    /* Create a menu-bar to hold the menus and add it to our main window*/
    menu_bar = gtk_menu_bar_new();
    gtk_container_add(GTK_CONTAINER(window), menu_bar);
    gtk_widget_show(menu_bar);

    /* And finally we append the menu-item to the menu-bar -- this is the "root"
     * menu-item I have been raving about =) */
    gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), root_menu);

    /* always display the window as the last step so it all splashes on the screen at once. */
    gtk_widget_show(window);

    gtk_main ();

    return 0;
}

您也可以设定一个选单项目无效, 并使用accelerator table结合 按键到选单功能.

14.3 使用GtkMenuFactory

我们已经示范了难的方法, 这里是用gtk_menu_factory的方法.

14.4 Menu Factory范例

这里是menu factory的范例. 这是第一个档案, menus.h. 另有menus.c及main.c

#ifndef __MENUS_H__
#define __MENUS_H__

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

void get_main_menu (GtkWidget **menubar, GtkAcceleratorTable **table);
void menus_create(GtkMenuEntry *entries, int nmenu_entries);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* __MENUS_H__ */

And here is the menus.c file.


#include <gtk/gtk.h>
#include <strings.h>

#include "main.h"


static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path);
static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path);
void menus_init(void);
void menus_create(GtkMenuEntry * entries, int nmenu_entries);


/* this is the GtkMenuEntry structure used to create new menus.  The
 * first member is the menu definition string.  The second, the
 * default accelerator key used to access this menu function with
 * the keyboard.  The third is the callback function to call when
 * this menu item is selected (by the accelerator key, or with the
 * mouse.) The last member is the data to pass to your callback function.
 */

static GtkMenuEntry menu_items[] =
{
        {"<Main>/File/New", "<control>N", NULL, NULL},
        {"<Main>/File/Open", "<control>O", NULL, NULL},
        {"<Main>/File/Save", "<control>S", NULL, NULL},
        {"<Main>/File/Save as", NULL, NULL, NULL},
        {"<Main>/File/<separator>", NULL, NULL, NULL},
        {"<Main>/File/Quit", "<control>Q", file_quit_cmd_callback, "OK, I'll quit"},
        {"<Main>/Options/Test", NULL, NULL, NULL}
};

/* calculate the number of menu_item's */
static int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);

static int initialize = TRUE;
static GtkMenuFactory *factory = NULL;
static GtkMenuFactory *subfactory[1];
static GHashTable *entry_ht = NULL;

void get_main_menu(GtkWidget ** menubar, GtkAcceleratorTable ** table)
{
    if (initialize)
            menus_init();
    
    if (menubar)
            *menubar = subfactory[0]->widget;
    if (table)
            *table = subfactory[0]->table;
}

void menus_init(void)
{
    if (initialize) {
        initialize = FALSE;
        
        factory = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR);
        subfactory[0] = gtk_menu_factory_new(GTK_MENU_FACTORY_MENU_BAR);
        
        gtk_menu_factory_add_subfactory(factory, subfactory[0], "<Main>");
        menus_create(menu_items, nmenu_items);
    }
}

void menus_create(GtkMenuEntry * entries, int nmenu_entries)
{
    char *accelerator;
    int i;
    
    if (initialize)
            menus_init();
    
    if (entry_ht)
            for (i = 0; i < nmenu_entries; i++) {
                accelerator = g_hash_table_lookup(entry_ht, entries[i].path);
                if (accelerator) {
                    if (accelerator[0] == '\0')
                            entries[i].accelerator = NULL;
                    else
                            entries[i].accelerator = accelerator;
                }
            }
    gtk_menu_factory_add_entries(factory, entries, nmenu_entries);
    
    for (i = 0; i < nmenu_entries; i++)
            if (entries[i].widget) {
                gtk_signal_connect(GTK_OBJECT(entries[i].widget), "install_accelerator",
                                   (GtkSignalFunc) menus_install_accel,
                                   entries[i].path);
                gtk_signal_connect(GTK_OBJECT(entries[i].widget), "remove_accelerator",
                                   (GtkSignalFunc) menus_remove_accel,
                                   entries[i].path);
            }
}

static gint menus_install_accel(GtkWidget * widget, gchar * signal_name, gchar key, gchar modifiers, gchar * path)
{
    char accel[64];
    char *t1, t2[2];
    
    accel[0] = '\0';
    if (modifiers & GDK_CONTROL_MASK)
            strcat(accel, "<control>");
    if (modifiers & GDK_SHIFT_MASK)
            strcat(accel, "<shift>");
    if (modifiers & GDK_MOD1_MASK)
            strcat(accel, "<alt>");
    
    t2[0] = key;
    t2[1] = '\0';
    strcat(accel, t2);
    
    if (entry_ht) {
        t1 = g_hash_table_lookup(entry_ht, path);
        g_free(t1);
    } else
            entry_ht = g_hash_table_new(g_string_hash, g_string_equal);
    
    g_hash_table_insert(entry_ht, path, g_strdup(accel));
    
    return TRUE;
}

static void menus_remove_accel(GtkWidget * widget, gchar * signal_name, gchar * path)
{
    char *t;
    
    if (entry_ht) {
        t = g_hash_table_lookup(entry_ht, path);
        g_free(t);
        
        g_hash_table_insert(entry_ht, path, g_strdup(""));
    }
}

void menus_set_sensitive(char *path, int sensitive)
{
    GtkMenuPath *menu_path;
    
    if (initialize)
            menus_init();
    
    menu_path = gtk_menu_factory_find(factory, path);
    if (menu_path)
            gtk_widget_set_sensitive(menu_path->widget, sensitive);
    else
            g_warning("Unable to set sensitivity for menu which doesn't exist: %s", path);
}

And here's the main.h

#ifndef __MAIN_H__
#define __MAIN_H__


#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

void file_quit_cmd_callback(GtkWidget *widget, gpointer data);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* __MAIN_H__ */

And main.c

#include <gtk/gtk.h>

#include "main.h"
#include "menus.h"


int main(int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *main_vbox;
    GtkWidget *menubar;
    
    GtkAcceleratorTable *accel;
    
    gtk_init(&argc, &argv);
    
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_signal_connect(GTK_OBJECT(window), "destroy", 
                       GTK_SIGNAL_FUNC(file_quit_cmd_callback), 
                       "WM destroy");
    gtk_window_set_title(GTK_WINDOW(window), "Menu Factory");
    gtk_widget_set_usize(GTK_WIDGET(window), 300, 200);
    
    main_vbox = gtk_vbox_new(FALSE, 1);
    gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
    gtk_container_add(GTK_CONTAINER(window), main_vbox);
    gtk_widget_show(main_vbox);
    
    get_main_menu(&menubar, &accel);
    gtk_window_add_accelerator_table(GTK_WINDOW(window), accel);
    gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
    gtk_widget_show(menubar);
    
    gtk_widget_show(window);
    gtk_main();
    
    return(0);
}

/* This is just to demonstrate how callbacks work when using the
 * menufactory.  Often, people put all the callbacks from the menus
 * in a separate file, and then have them call the appropriate functions
 * from there.  Keeps it more organized. */
void file_quit_cmd_callback (GtkWidget *widget, gpointer data)
{
    g_print ("%s\n", (char *) data);
    gtk_exit(0);
}

这里是makefile.

CC      = gcc
PROF    = -g
C_FLAGS =  -Wall $(PROF) -L/usr/local/include -DDEBUG
L_FLAGS =  $(PROF) -L/usr/X11R6/lib -L/usr/local/lib 
L_POSTFLAGS = -lgtk -lgdk -lglib -lXext -lX11 -lm
PROGNAME = at

O_FILES = menus.o main.o

$(PROGNAME): $(O_FILES)
        rm -f $(PROGNAME)
        $(CC) $(L_FLAGS) -o $(PROGNAME) $(O_FILES) $(L_POSTFLAGS)

.c.o: 
        $(CC) -c $(C_FLAGS) $<

clean: 
        rm -f core *.o $(PROGNAME) nohup.out
distclean: clean 
        rm -f *~


Previous Next Contents