/* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * GtkItemFactory: Flexible item factory with automatic rc handling * Copyright (C) 1998 Tim Janik * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "gtkitemfactory.h" #include "gtk/gtksignal.h" #include "gtk/gtkoptionmenu.h" #include "gtk/gtkmenubar.h" #include "gtk/gtkmenu.h" #include "gtk/gtkmenuitem.h" #include "gtk/gtkradiomenuitem.h" #include "gtk/gtkcheckmenuitem.h" #include "gtk/gtktearoffmenuitem.h" #include "gtk/gtkaccellabel.h" #include "gdk/gdkkeysyms.h" #include #include #include #include #include /* --- defines --- */ #define ITEM_FACTORY_STRING ((gchar*) item_factory_string) #define ITEM_BLOCK_SIZE (128) /* --- structures --- */ typedef struct _GtkIFCBData GtkIFCBData; typedef struct _GtkIFActionLink GtkIFActionLink; typedef struct _GtkIFDumpData GtkIFDumpData; struct _GtkIFCBData { GtkItemFactoryCallback func; guint callback_type; gpointer func_data; guint callback_action; }; struct _GtkIFActionLink { GtkWidget *widget; guint callback_action; }; struct _GtkIFDumpData { GtkPrintFunc print_func; gpointer func_data; guint modified_only : 1; GtkPatternSpec *pspec; }; /* --- prototypes --- */ static void gtk_item_factory_class_init (GtkItemFactoryClass *klass); static void gtk_item_factory_init (GtkItemFactory *ifactory); static void gtk_item_factory_destroy (GtkObject *object); static void gtk_item_factory_finalize (GtkObject *object); /* --- static variables --- */ static GtkItemFactoryClass *gtk_item_factory_class = NULL; static GtkObjectClass *parent_class = NULL; static const gchar *item_factory_string = "Gtk-"; static GMemChunk *ifactory_item_chunks = NULL; static GMemChunk *ifactory_cb_data_chunks = NULL; static const gchar *key_popup_data = "GtkItemFactory-popup-data"; static GQuark quark_popup_data = 0; static const gchar *key_if_menu_pos = "GtkItemFactory-menu-position"; static GQuark quark_if_menu_pos = 0; static const gchar *key_item_factory = "GtkItemFactory"; static GQuark quark_item_factory = 0; static const gchar *key_item_factory_path = "GtkItemFactory-path"; static GQuark quark_item_factory_path = 0; static const gchar *key_type_item = ""; static GQuark quark_type_item = 0; static const gchar *key_type_title = ""; static GQuark quark_type_title = 0; static const gchar *key_type_radio_item = "<RadioItem>"; static GQuark quark_type_radio_item = 0; static const gchar *key_type_check_item = "<CheckItem>"; static GQuark quark_type_check_item = 0; static const gchar *key_type_toggle_item = "<ToggleItem>"; static GQuark quark_type_toggle_item = 0; static const gchar *key_type_tearoff_item = "<Tearoff>"; static GQuark quark_type_tearoff_item = 0; static const gchar *key_type_separator_item = "<Separator>"; static GQuark quark_type_separator_item = 0; static const gchar *key_type_branch = "<Branch>"; static GQuark quark_type_branch = 0; static const gchar *key_type_last_branch = "<LastBranch>"; static GQuark quark_type_last_branch = 0; static GScannerConfig ifactory_scanner_config = { ( " \t\n" ) /* cset_skip_characters */, ( G_CSET_a_2_z "_" G_CSET_A_2_Z ) /* cset_identifier_first */, ( G_CSET_a_2_z "-+_0123456789" G_CSET_A_2_Z G_CSET_LATINS G_CSET_LATINC ) /* cset_identifier_nth */, ( ";\n" ) /* cpair_comment_single */, FALSE /* case_sensitive */, TRUE /* skip_comment_multi */, TRUE /* skip_comment_single */, FALSE /* scan_comment_multi */, TRUE /* scan_identifier */, FALSE /* scan_identifier_1char */, FALSE /* scan_identifier_NULL */, TRUE /* scan_symbols */, TRUE /* scan_binary */, TRUE /* scan_octal */, TRUE /* scan_float */, TRUE /* scan_hex */, FALSE /* scan_hex_dollar */, TRUE /* scan_string_sq */, TRUE /* scan_string_dq */, TRUE /* numbers_2_int */, FALSE /* int_2_float */, FALSE /* identifier_2_string */, TRUE /* char_2_token */, FALSE /* symbol_2_token */, }; /* --- functions --- */ GtkType gtk_item_factory_get_type (void) { static GtkType item_factory_type = 0; if (!item_factory_type) { GtkTypeInfo item_factory_info = { "GtkItemFactory", sizeof (GtkItemFactory), sizeof (GtkItemFactoryClass), (GtkClassInitFunc) gtk_item_factory_class_init, (GtkObjectInitFunc) gtk_item_factory_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; item_factory_type = gtk_type_unique (GTK_TYPE_OBJECT, &item_factory_info); } return item_factory_type; } static void gtk_item_factory_class_init (GtkItemFactoryClass *class) { GtkObjectClass *object_class; gtk_item_factory_class = class; parent_class = gtk_type_class (GTK_TYPE_OBJECT); object_class = (GtkObjectClass*) class; object_class->destroy = gtk_item_factory_destroy; object_class->finalize = gtk_item_factory_finalize; class->cpair_comment_single = g_strdup (";\n"); class->item_ht = g_hash_table_new (g_str_hash, g_str_equal); ifactory_item_chunks = g_mem_chunk_new ("GtkItemFactoryItem", sizeof (GtkItemFactoryItem), sizeof (GtkItemFactoryItem) * ITEM_BLOCK_SIZE, G_ALLOC_ONLY); ifactory_cb_data_chunks = g_mem_chunk_new ("GtkIFCBData", sizeof (GtkIFCBData), sizeof (GtkIFCBData) * ITEM_BLOCK_SIZE, G_ALLOC_AND_FREE); quark_popup_data = g_quark_from_static_string (key_popup_data); quark_if_menu_pos = g_quark_from_static_string (key_if_menu_pos); quark_item_factory = g_quark_from_static_string (key_item_factory); quark_item_factory_path = g_quark_from_static_string (key_item_factory_path); quark_type_item = g_quark_from_static_string (key_type_item); quark_type_title = g_quark_from_static_string (key_type_title); quark_type_radio_item = g_quark_from_static_string (key_type_radio_item); quark_type_check_item = g_quark_from_static_string (key_type_check_item); quark_type_toggle_item = g_quark_from_static_string (key_type_toggle_item); quark_type_tearoff_item = g_quark_from_static_string (key_type_tearoff_item); quark_type_separator_item = g_quark_from_static_string (key_type_separator_item); quark_type_branch = g_quark_from_static_string (key_type_branch); quark_type_last_branch = g_quark_from_static_string (key_type_last_branch); } static void gtk_item_factory_init (GtkItemFactory *ifactory) { GtkObject *object; object = GTK_OBJECT (ifactory); ifactory->path = NULL; ifactory->accel_group = NULL; ifactory->widget = NULL; ifactory->widgets_by_action = NULL; } GtkItemFactory* gtk_item_factory_new (GtkType container_type, const gchar *path, GtkAccelGroup *accel_group) { GtkItemFactory *ifactory; g_return_val_if_fail (path != NULL, NULL); ifactory = gtk_type_new (GTK_TYPE_ITEM_FACTORY); gtk_item_factory_construct (ifactory, container_type, path, accel_group); return ifactory; } static void gtk_item_factory_callback_marshal (GtkWidget *widget, gpointer func_data) { GtkIFCBData *data; data = func_data; if (data->callback_type == 1) { GtkItemFactoryCallback1 func1 = data->func; func1 (data->func_data, data->callback_action, widget); } else if (data->callback_type == 2) { GtkItemFactoryCallback2 func2 = data->func; func2 (widget, data->func_data, data->callback_action); } } static void gtk_item_factory_propagate_accelerator (GtkItemFactoryItem *item, GtkWidget *exclude) { GSList *widget_list; GSList *slist; if (item->in_propagation) return; item->in_propagation = TRUE; widget_list = NULL; for (slist = item->widgets; slist; slist = slist->next) { GtkWidget *widget; widget = slist->data; if (widget != exclude) { gtk_widget_ref (widget); widget_list = g_slist_prepend (widget_list, widget); } } for (slist = widget_list; slist; slist = slist->next) { GtkWidget *widget; GtkItemFactory *ifactory; widget = slist->data; ifactory = gtk_item_factory_from_widget (widget); if (ifactory) { guint signal_id; signal_id = gtk_signal_lookup ("activate", GTK_OBJECT_TYPE (widget)); if (signal_id) { if (item->accelerator_key) gtk_widget_add_accelerator (widget, "activate", ifactory->accel_group, item->accelerator_key, item->accelerator_mods, GTK_ACCEL_VISIBLE); else { GSList *work; work = gtk_accel_group_entries_from_object (GTK_OBJECT (widget)); while (work) { GtkAccelEntry *ac_entry; ac_entry = work->data; work = work->next; if (ac_entry->accel_flags & GTK_ACCEL_VISIBLE && ac_entry->accel_group == ifactory->accel_group && ac_entry->signal_id == signal_id) gtk_widget_remove_accelerator (GTK_WIDGET (widget), ac_entry->accel_group, ac_entry->accelerator_key, ac_entry->accelerator_mods); } } } } gtk_widget_unref (widget); } g_slist_free (widget_list); item->in_propagation = FALSE; } static gint gtk_item_factory_item_add_accelerator (GtkWidget *widget, guint accel_signal_id, GtkAccelGroup *accel_group, guint accel_key, guint accel_mods, GtkAccelFlags accel_flags, GtkItemFactoryItem *item) { if (!item->in_propagation && g_slist_find (item->widgets, widget) && accel_signal_id == gtk_signal_lookup ("activate", GTK_OBJECT_TYPE (widget))) { item->accelerator_key = accel_key; item->accelerator_mods = accel_mods; item->modified = TRUE; gtk_item_factory_propagate_accelerator (item, widget); } return TRUE; } static void gtk_item_factory_item_remove_accelerator (GtkWidget *widget, GtkAccelGroup *accel_group, guint accel_key, guint accel_mods, GtkItemFactoryItem *item) { if (!item->in_propagation && g_slist_find (item->widgets, widget) && item->accelerator_key == accel_key && item->accelerator_mods == accel_mods) { item->accelerator_key = 0; item->accelerator_mods = 0; item->modified = TRUE; gtk_item_factory_propagate_accelerator (item, widget); } } static void gtk_item_factory_item_remove_widget (GtkWidget *widget, GtkItemFactoryItem *item) { item->widgets = g_slist_remove (item->widgets, widget); gtk_object_remove_data_by_id (GTK_OBJECT (widget), quark_item_factory); gtk_object_remove_data_by_id (GTK_OBJECT (widget), quark_item_factory_path); } static void ifactory_cb_data_free (gpointer mem) { g_mem_chunk_free (ifactory_cb_data_chunks, mem); } static void gtk_item_factory_add_item (GtkItemFactory *ifactory, const gchar *path, const gchar *accelerator, GtkItemFactoryCallback callback, guint callback_action, gpointer callback_data, guint callback_type, gchar *item_type, GtkWidget *widget) { GtkItemFactoryClass *class; GtkItemFactoryItem *item; gchar *fpath; g_return_if_fail (widget != NULL); g_return_if_fail (item_type != NULL); class = GTK_ITEM_FACTORY_CLASS (GTK_OBJECT (ifactory)->klass); fpath = g_strconcat (ifactory->path, path, NULL); item = g_hash_table_lookup (class->item_ht, fpath); /* link the widget into its item-entry */ if (!item) { guint keyval; guint mods; if (accelerator) gtk_accelerator_parse (accelerator, &keyval, &mods); else { keyval = 0; mods = 0; } item = g_chunk_new (GtkItemFactoryItem, ifactory_item_chunks); item->path = fpath; fpath = NULL; item->accelerator_key = keyval; item->accelerator_mods = mods; item->modified = FALSE; item->in_propagation = FALSE; item->item_type = NULL; item->widgets = NULL; g_hash_table_insert (class->item_ht, item->path, item); } g_free (fpath); if (item->item_type == NULL) { g_assert (item->widgets == NULL); if (item_type != ITEM_FACTORY_STRING) item->item_type = g_strdup (item_type); else item->item_type = ITEM_FACTORY_STRING; } item->widgets = g_slist_prepend (item->widgets, widget); gtk_signal_connect (GTK_OBJECT (widget), "destroy", GTK_SIGNAL_FUNC (gtk_item_factory_item_remove_widget), item); /* set back pointers for the widget */ gtk_object_set_data_by_id (GTK_OBJECT (widget), quark_item_factory, ifactory); gtk_object_set_data_by_id (GTK_OBJECT (widget), quark_item_factory_path, item->path); gtk_widget_set_name (widget, item->path); /* set accelerator group on menu widgets */ if (GTK_IS_MENU (widget)) gtk_menu_set_accel_group ((GtkMenu*) widget, ifactory->accel_group); /* install defined accelerators */ if (gtk_signal_lookup ("activate", GTK_OBJECT_TYPE (widget))) { if (item->accelerator_key) gtk_widget_add_accelerator (widget, "activate", ifactory->accel_group, item->accelerator_key, item->accelerator_mods, GTK_ACCEL_VISIBLE); else gtk_widget_remove_accelerators (widget, "activate", TRUE); } /* keep track of accelerator changes */ gtk_signal_connect_after (GTK_OBJECT (widget), "add-accelerator", GTK_SIGNAL_FUNC (gtk_item_factory_item_add_accelerator), item); gtk_signal_connect_after (GTK_OBJECT (widget), "remove-accelerator", GTK_SIGNAL_FUNC (gtk_item_factory_item_remove_accelerator), item); /* keep a per-action list of the widgets on the factory */ if (callback_action) { GtkIFActionLink *link; link = g_new (GtkIFActionLink, 1); link->widget = widget; link->callback_action = callback_action; ifactory->widgets_by_action = g_slist_prepend (ifactory->widgets_by_action, link); } /* connect callback if neccessary */ if (callback) { GtkIFCBData *data; data = g_chunk_new (GtkIFCBData, ifactory_cb_data_chunks); data->func = callback; data->callback_type = callback_type; data->func_data = callback_data; data->callback_action = callback_action; gtk_object_weakref (GTK_OBJECT (widget), ifactory_cb_data_free, data); gtk_signal_connect (GTK_OBJECT (widget), "activate", GTK_SIGNAL_FUNC (gtk_item_factory_callback_marshal), data); } } void gtk_item_factory_construct (GtkItemFactory *ifactory, GtkType container_type, const gchar *path, GtkAccelGroup *accel_group) { GtkAccelGroup *menu_group; guint len; g_return_if_fail (ifactory != NULL); g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory)); g_return_if_fail (ifactory->accel_group == NULL); g_return_if_fail (path != NULL); if (!gtk_type_is_a (container_type, GTK_TYPE_OPTION_MENU)) g_return_if_fail (gtk_type_is_a (container_type, GTK_TYPE_MENU_SHELL)); len = strlen (path); if (path[0] != '<' && path[len - 1] != '>') { g_warning ("GtkItemFactory: invalid factory path `%s'", path); return; } if (accel_group) { ifactory->accel_group = accel_group; gtk_accel_group_ref (ifactory->accel_group); } else ifactory->accel_group = gtk_accel_group_new (); ifactory->path = g_strdup (path); ifactory->widget = gtk_widget_new (container_type, "GtkObject::signal::destroy", gtk_widget_destroyed, &ifactory->widget, NULL); gtk_object_ref (GTK_OBJECT (ifactory)); gtk_object_sink (GTK_OBJECT (ifactory)); menu_group = gtk_accel_group_new (); gtk_accel_group_attach (menu_group, GTK_OBJECT (ifactory->widget)); /* gtk_signal_connect_object_while_alive (GTK_OBJECT (ifactory->widget), "destroy", GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (ifactory)); */ gtk_item_factory_add_item (ifactory, "", NULL, NULL, 0, NULL, 0, ITEM_FACTORY_STRING, ifactory->widget); } GtkItemFactory* gtk_item_factory_from_path (const gchar *path) { GtkItemFactoryClass *class; GtkItemFactoryItem *item; gchar *fname; guint i; g_return_val_if_fail (path != NULL, NULL); g_return_val_if_fail (path[0] == '<', NULL); class = gtk_type_class (GTK_TYPE_ITEM_FACTORY); i = 0; while (path[i] && path[i] != '>') i++; if (path[i] != '>') { g_warning ("gtk_item_factory_from_path(): invalid factory path \"%s\"", path); return NULL; } fname = g_new (gchar, i + 2); g_memmove (fname, path, i + 1); fname[i + 1] = 0; item = g_hash_table_lookup (class->item_ht, fname); if (item && item->widgets) return gtk_item_factory_from_widget (item->widgets->data); return NULL; } static void gtk_item_factory_destroy (GtkObject *object) { GtkItemFactory *ifactory; GSList *slist; g_return_if_fail (object != NULL); g_return_if_fail (GTK_IS_ITEM_FACTORY (object)); ifactory = (GtkItemFactory*) object; if (ifactory->widget) { GtkObject *dobj; dobj = GTK_OBJECT (ifactory->widget); gtk_object_ref (dobj); gtk_object_sink (dobj); gtk_object_destroy (dobj); gtk_object_unref (dobj); ifactory->widget = NULL; } for (slist = ifactory->widgets_by_action; slist; slist = slist->next) g_free (slist->data); g_slist_free (ifactory->widgets_by_action); ifactory->widgets_by_action = NULL; parent_class->destroy (object); } static void gtk_item_factory_finalize (GtkObject *object) { GtkItemFactory *ifactory; g_return_if_fail (object != NULL); g_return_if_fail (GTK_IS_ITEM_FACTORY (object)); ifactory = GTK_ITEM_FACTORY (object); gtk_accel_group_unref (ifactory->accel_group); g_free (ifactory->path); g_assert (ifactory->widget == NULL); parent_class->finalize (object); } GtkItemFactory* gtk_item_factory_from_widget (GtkWidget *widget) { g_return_val_if_fail (widget != NULL, NULL); g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); return gtk_object_get_data_by_id (GTK_OBJECT (widget), quark_item_factory); } gchar* gtk_item_factory_path_from_widget (GtkWidget *widget) { g_return_val_if_fail (widget != NULL, NULL); g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); return gtk_object_get_data_by_id (GTK_OBJECT (widget), quark_item_factory_path); } static void gtk_item_factory_foreach (gpointer hash_key, gpointer value, gpointer user_data) { GtkItemFactoryItem *item; GtkIFDumpData *data; gchar *string; gchar *name; gchar comment_prefix[2] = "\000\000"; item = value; data = user_data; if (data->pspec && !gtk_pattern_match_string (data->pspec, item->path)) return; comment_prefix[0] = gtk_item_factory_class->cpair_comment_single[0]; name = gtk_accelerator_name (item->accelerator_key, item->accelerator_mods); string = g_strconcat (item->modified ? "" : comment_prefix, "(menu-path \"", hash_key, "\" \"", name, "\")", NULL); g_free (name); data->print_func (data->func_data, string); g_free (string); } void gtk_item_factory_dump_items (GtkPatternSpec *path_pspec, gboolean modified_only, GtkPrintFunc print_func, gpointer func_data) { GtkIFDumpData data; g_return_if_fail (print_func != NULL); if (!gtk_item_factory_class) gtk_type_class (GTK_TYPE_ITEM_FACTORY); data.print_func = print_func; data.func_data = func_data; data.modified_only = (modified_only != FALSE); data.pspec = path_pspec; g_hash_table_foreach (gtk_item_factory_class->item_ht, gtk_item_factory_foreach, &data); } void gtk_item_factory_print_func (gpointer FILE_pointer, gchar *string) { FILE *f_out = FILE_pointer; g_return_if_fail (FILE_pointer != NULL); g_return_if_fail (string != NULL); fputs (string, f_out); fputc ('\n', f_out); } void gtk_item_factory_dump_rc (const gchar *file_name, GtkPatternSpec *path_pspec, gboolean modified_only) { FILE *f_out; g_return_if_fail (file_name != NULL); f_out = fopen (file_name, "w"); if (!f_out) return; fputs ("; ", f_out); if (g_get_prgname ()) fputs (g_get_prgname (), f_out); fputs (" GtkItemFactory rc-file -*- scheme -*-\n", f_out); fputs ("; this file is an automated menu-path dump\n", f_out); fputs (";\n", f_out); gtk_item_factory_dump_items (path_pspec, modified_only, gtk_item_factory_print_func, f_out); fclose (f_out); } void gtk_item_factory_create_items (GtkItemFactory *ifactory, guint n_entries, GtkItemFactoryEntry *entries, gpointer callback_data) { gtk_item_factory_create_items_ac (ifactory, n_entries, entries, callback_data, 1); } void gtk_item_factory_create_items_ac (GtkItemFactory *ifactory, guint n_entries, GtkItemFactoryEntry *entries, gpointer callback_data, guint callback_type) { guint i; g_return_if_fail (ifactory != NULL); g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory)); g_return_if_fail (callback_type >= 1 && callback_type <= 2); if (n_entries == 0) return; g_return_if_fail (entries != NULL); for (i = 0; i < n_entries; i++) gtk_item_factory_create_item (ifactory, entries + i, callback_data, callback_type); } GtkWidget* gtk_item_factory_get_widget (GtkItemFactory *ifactory, const gchar *path) { GtkItemFactoryClass *class; GtkItemFactoryItem *item; g_return_val_if_fail (ifactory != NULL, NULL); g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL); g_return_val_if_fail (path != NULL, NULL); class = GTK_ITEM_FACTORY_CLASS (GTK_OBJECT (ifactory)->klass); if (path[0] == '<') item = g_hash_table_lookup (class->item_ht, (gpointer) path); else { gchar *fpath; fpath = g_strconcat (ifactory->path, path, NULL); item = g_hash_table_lookup (class->item_ht, fpath); g_free (fpath); } if (item) { GSList *slist; for (slist = item->widgets; slist; slist = slist->next) { if (gtk_item_factory_from_widget (slist->data) == ifactory) return slist->data; } } return NULL; } GtkWidget* gtk_item_factory_get_widget_by_action (GtkItemFactory *ifactory, guint action) { GSList *slist; g_return_val_if_fail (ifactory != NULL, NULL); g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL); for (slist = ifactory->widgets_by_action; slist; slist = slist->next) { GtkIFActionLink *link; link = slist->data; if (link->callback_action == action) return link->widget; } return NULL; } static gboolean gtk_item_factory_parse_path (gchar *str, gchar **path, gchar **parent_path, gchar **item) { gchar *p, *q; *path = g_strdup (str); p = q = *path; while (*p) { if (*p != '_') { *q++ = *p; } p++; } *q = 0; *parent_path = g_strdup (*path); p = strrchr (*parent_path, '/'); if (!p) { g_warning ("GtkItemFactory: invalid entry path `%s'", str); return FALSE; } *p = 0; p = strrchr (str, '/'); p++; *item = g_strdup (p); return TRUE; } void gtk_item_factory_create_item (GtkItemFactory *ifactory, GtkItemFactoryEntry *entry, gpointer callback_data, guint callback_type) { GtkWidget *parent; GtkWidget *widget; GSList *radio_group; gchar *name; gchar *parent_path; gchar *path; guint accel_key; guint type_id; GtkType type; gchar *item_type_path; GtkAccelGroup *parent_accel_group = NULL; GSList *tmp_list; g_return_if_fail (ifactory != NULL); g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory)); g_return_if_fail (entry != NULL); g_return_if_fail (entry->path != NULL); g_return_if_fail (entry->path[0] == '/'); g_return_if_fail (callback_type >= 1 && callback_type <= 2); if (!entry->item_type || entry->item_type[0] == 0) { item_type_path = (gpointer) key_type_item; type_id = quark_type_item; } else { item_type_path = entry->item_type; type_id = gtk_object_data_try_key (item_type_path); } radio_group = NULL; if (type_id == quark_type_item) type = GTK_TYPE_MENU_ITEM; else if (type_id == quark_type_title) type = GTK_TYPE_MENU_ITEM; else if (type_id == quark_type_radio_item) type = GTK_TYPE_RADIO_MENU_ITEM; else if (type_id == quark_type_check_item) type = GTK_TYPE_CHECK_MENU_ITEM; else if (type_id == quark_type_tearoff_item) type = GTK_TYPE_TEAROFF_MENU_ITEM; else if (type_id == quark_type_toggle_item) type = GTK_TYPE_CHECK_MENU_ITEM; else if (type_id == quark_type_separator_item) type = GTK_TYPE_MENU_ITEM; else if (type_id == quark_type_branch) type = GTK_TYPE_MENU_ITEM; else if (type_id == quark_type_last_branch) type = GTK_TYPE_MENU_ITEM; else { GtkWidget *radio_link; radio_link = gtk_item_factory_get_widget (ifactory, item_type_path); if (radio_link && GTK_IS_RADIO_MENU_ITEM (radio_link)) { type = GTK_TYPE_RADIO_MENU_ITEM; radio_group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (radio_link)); } else { g_warning ("GtkItemFactory: entry path `%s' has invalid type `%s'", entry->path, item_type_path); return; } } if (!gtk_item_factory_parse_path (entry->path, &path, &parent_path, &name)) return; parent = gtk_item_factory_get_widget (ifactory, parent_path); if (!parent) { GtkItemFactoryEntry pentry; pentry.path = parent_path; pentry.accelerator = NULL; pentry.callback = NULL; pentry.callback_action = 0; pentry.item_type = "<Branch>"; gtk_item_factory_create_item (ifactory, &pentry, NULL, 1); parent = gtk_item_factory_get_widget (ifactory, parent_path); } g_free (parent_path); g_return_if_fail (parent != NULL); tmp_list = gtk_accel_groups_from_object (GTK_OBJECT (parent)); if (tmp_list) parent_accel_group = tmp_list->data; widget = gtk_widget_new (type, "GtkWidget::visible", TRUE, "GtkWidget::sensitive", (type_id != quark_type_separator_item && type_id != quark_type_title), "GtkWidget::parent", parent, NULL); if (type == GTK_TYPE_RADIO_MENU_ITEM) gtk_radio_menu_item_set_group (GTK_RADIO_MENU_ITEM (widget), radio_group); if (GTK_IS_CHECK_MENU_ITEM (widget)) gtk_check_menu_item_set_show_toggle (GTK_CHECK_MENU_ITEM (widget), TRUE); if ((type_id != quark_type_separator_item) && (type_id != quark_type_tearoff_item) && *name) { GtkWidget *label; label = gtk_widget_new (GTK_TYPE_ACCEL_LABEL, "GtkWidget::visible", TRUE, "GtkWidget::parent", widget, "GtkAccelLabel::accel_widget", widget, "GtkMisc::xalign", 0.0, NULL); accel_key = gtk_label_parse_uline (GTK_LABEL (label), name); if ((accel_key != GDK_VoidSymbol) && GTK_IS_MENU_BAR (parent)) { gtk_widget_add_accelerator (widget, "activate_item", ifactory->accel_group, accel_key, GDK_MOD1_MASK, GTK_ACCEL_LOCKED); } if ((accel_key != GDK_VoidSymbol) && parent_accel_group) { gtk_widget_add_accelerator (widget, "activate_item", parent_accel_group, accel_key, 0, GTK_ACCEL_LOCKED); } } g_free (name); if (type_id == quark_type_branch || type_id == quark_type_last_branch) { GtkAccelGroup *menu_group; menu_group = gtk_accel_group_new (); if (type_id == quark_type_last_branch) gtk_menu_item_right_justify (GTK_MENU_ITEM (widget)); parent = widget; widget = gtk_widget_new (GTK_TYPE_MENU, NULL); gtk_accel_group_attach (menu_group, GTK_OBJECT (widget)); gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), widget); } gtk_item_factory_add_item (ifactory, path, entry->accelerator, entry->callback, entry->callback_action, callback_data, callback_type, item_type_path, widget); } void gtk_item_factory_create_menu_entries (guint n_entries, GtkMenuEntry *entries) { static GtkPatternSpec pspec_separator = { 42, 0 }; static GtkPatternSpec pspec_check = { 42, 0 }; guint i; if (!n_entries) return; g_return_if_fail (entries != NULL); if (pspec_separator.pattern_length == 0) { gtk_pattern_spec_init (&pspec_separator, "*<separator>*"); gtk_pattern_spec_init (&pspec_check, "*<check>*"); } for (i = 0; i < n_entries; i++) { GtkItemFactory *ifactory; GtkItemFactoryEntry entry; gchar *path; gchar *cpath; path = entries[i].path; ifactory = gtk_item_factory_from_path (path); if (!ifactory) { g_warning ("gtk_item_factory_create_menu_entries(): " "entry[%u] refers to unknown item factory: \"%s\"", i, entries[i].path); continue; } while (*path != '>') path++; path++; cpath = NULL; entry.path = path; entry.accelerator = entries[i].accelerator; entry.callback = entries[i].callback; entry.callback_action = 0; if (gtk_pattern_match_string (&pspec_separator, path)) entry.item_type = (gpointer) key_type_separator_item; else if (!gtk_pattern_match_string (&pspec_check, path)) entry.item_type = NULL; else { gboolean in_brace = FALSE; gchar *c; cpath = g_new (gchar, strlen (path)); c = cpath; while (*path != 0) { if (*path == '<') in_brace = TRUE; else if (*path == '>') in_brace = FALSE; else if (!in_brace) *(c++) = *path; path++; } *c = 0; entry.item_type = (gpointer) key_type_toggle_item; entry.path = cpath; } gtk_item_factory_create_item (ifactory, &entry, entries[i].callback_data, 2); entries[i].widget = gtk_item_factory_get_widget (ifactory, entries[i].path); g_free (cpath); } } void gtk_item_factories_path_delete (const gchar *ifactory_path, const gchar *path) { GtkItemFactoryClass *class; GtkItemFactoryItem *item; g_return_if_fail (path != NULL); class = gtk_type_class (GTK_TYPE_ITEM_FACTORY); if (path[0] == '<') item = g_hash_table_lookup (class->item_ht, (gpointer) path); else { gchar *fpath; g_return_if_fail (ifactory_path != NULL); fpath = g_strconcat (ifactory_path, path, NULL); item = g_hash_table_lookup (class->item_ht, fpath); g_free (fpath); } if (item) { GSList *widget_list; GSList *slist; widget_list = NULL; for (slist = item->widgets; slist; slist = slist->next) { GtkWidget *widget; widget = slist->data; widget_list = g_slist_prepend (widget_list, widget); gtk_widget_ref (widget); } for (slist = widget_list; slist; slist = slist->next) { GtkWidget *widget; widget = slist->data; gtk_widget_destroy (widget); gtk_widget_unref (widget); } g_slist_free (widget_list); } } void gtk_item_factory_delete_item (GtkItemFactory *ifactory, const gchar *path) { GtkItemFactoryClass *class; GtkItemFactoryItem *item; gchar *fpath; g_return_if_fail (ifactory != NULL); g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory)); g_return_if_fail (path != NULL); class = GTK_ITEM_FACTORY_CLASS (GTK_OBJECT (ifactory)->klass); fpath = g_strconcat (ifactory->path, path, NULL); item = g_hash_table_lookup (class->item_ht, fpath); g_free (fpath); if (item) { GtkWidget *widget = NULL; GSList *slist; for (slist = item->widgets; slist; slist = slist->next) { widget = slist->data; if (gtk_item_factory_from_widget (widget) == ifactory) break; } if (slist) gtk_widget_destroy (widget); } } void gtk_item_factory_delete_entry (GtkItemFactory *ifactory, GtkItemFactoryEntry *entry) { g_return_if_fail (ifactory != NULL); g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory)); g_return_if_fail (entry != NULL); gtk_item_factory_delete_item (ifactory, entry->path); } void gtk_item_factory_delete_entries (GtkItemFactory *ifactory, guint n_entries, GtkItemFactoryEntry *entries) { guint i; g_return_if_fail (ifactory != NULL); g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory)); if (n_entries > 0) g_return_if_fail (entries != NULL); for (i = 0; i < n_entries; i++) gtk_item_factory_delete_item (ifactory, (entries + i)->path); } typedef struct { guint x; guint y; } MenuPos; static void gtk_item_factory_menu_pos (GtkMenu *menu, gint *x, gint *y, gpointer func_data) { MenuPos *mpos = func_data; *x = mpos->x; *y = mpos->y; } gpointer gtk_item_factory_popup_data_from_widget (GtkWidget *widget) { GtkItemFactory *ifactory; g_return_val_if_fail (widget != NULL, NULL); g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); ifactory = gtk_item_factory_from_widget (widget); if (ifactory) return gtk_object_get_data_by_id (GTK_OBJECT (ifactory), quark_popup_data); return NULL; } gpointer gtk_item_factory_popup_data (GtkItemFactory *ifactory) { g_return_val_if_fail (ifactory != NULL, NULL); g_return_val_if_fail (GTK_IS_ITEM_FACTORY (ifactory), NULL); return gtk_object_get_data_by_id (GTK_OBJECT (ifactory), quark_popup_data); } static void ifactory_delete_popup_data (GtkObject *object, GtkItemFactory *ifactory) { gtk_signal_disconnect_by_func (object, GTK_SIGNAL_FUNC (ifactory_delete_popup_data), ifactory); gtk_object_remove_data_by_id (GTK_OBJECT (ifactory), quark_popup_data); } void gtk_item_factory_popup (GtkItemFactory *ifactory, guint x, guint y, guint mouse_button, guint32 time) { gtk_item_factory_popup_with_data (ifactory, NULL, NULL, x, y, mouse_button, time); } void gtk_item_factory_popup_with_data (GtkItemFactory *ifactory, gpointer popup_data, GtkDestroyNotify destroy, guint x, guint y, guint mouse_button, guint32 time) { g_return_if_fail (ifactory != NULL); g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory)); g_return_if_fail (GTK_IS_MENU (ifactory->widget)); if (!GTK_WIDGET_VISIBLE (ifactory->widget)) { MenuPos *mpos; mpos = gtk_object_get_data_by_id (GTK_OBJECT (ifactory->widget), quark_if_menu_pos); if (!mpos) { mpos = g_new0 (MenuPos, 1); gtk_object_set_data_by_id_full (GTK_OBJECT (ifactory->widget), quark_if_menu_pos, mpos, g_free); } mpos->x = x; mpos->y = y; if (popup_data != NULL) { gtk_object_set_data_by_id_full (GTK_OBJECT (ifactory), quark_popup_data, popup_data, destroy); gtk_signal_connect (GTK_OBJECT (ifactory->widget), "selection-done", GTK_SIGNAL_FUNC (ifactory_delete_popup_data), ifactory); } gtk_menu_popup (GTK_MENU (ifactory->widget), NULL, NULL, gtk_item_factory_menu_pos, mpos, mouse_button, time); } } static guint gtk_item_factory_parse_menu_path (GScanner *scanner, GtkItemFactoryClass *class) { GtkItemFactoryItem *item; g_scanner_get_next_token (scanner); if (scanner->token != G_TOKEN_STRING) return G_TOKEN_STRING; g_scanner_peek_next_token (scanner); if (scanner->next_token != G_TOKEN_STRING) { g_scanner_get_next_token (scanner); return G_TOKEN_STRING; } item = g_hash_table_lookup (class->item_ht, scanner->value.v_string); if (!item) { item = g_chunk_new (GtkItemFactoryItem, ifactory_item_chunks); item->path = g_strdup (scanner->value.v_string); item->accelerator_key = 0; item->accelerator_mods = 0; item->modified = TRUE; item->in_propagation = FALSE; item->item_type = NULL; item->widgets = NULL; g_hash_table_insert (class->item_ht, item->path, item); } g_scanner_get_next_token (scanner); if (!item->in_propagation) { guint old_keyval; guint old_mods; old_keyval = item->accelerator_key; old_mods = item->accelerator_mods; gtk_accelerator_parse (scanner->value.v_string, &item->accelerator_key, &item->accelerator_mods); if (old_keyval != item->accelerator_key || old_mods != item->accelerator_mods) { item->modified = TRUE; gtk_item_factory_propagate_accelerator (item, NULL); } } g_scanner_get_next_token (scanner); if (scanner->token != ')') return ')'; else return G_TOKEN_NONE; } static void gtk_item_factory_parse_statement (GScanner *scanner, GtkItemFactoryClass *class) { guint expected_token; g_scanner_get_next_token (scanner); if (scanner->token == G_TOKEN_SYMBOL) { guint (*parser_func) (GScanner*, GtkItemFactoryClass*); parser_func = scanner->value.v_symbol; /* check whether this is a GtkItemFactory symbol... */ if (parser_func == gtk_item_factory_parse_menu_path) expected_token = parser_func (scanner, class); else expected_token = G_TOKEN_SYMBOL; } else expected_token = G_TOKEN_SYMBOL; /* skip rest of statement on errrors */ if (expected_token != G_TOKEN_NONE) { register guint level; level = 1; if (scanner->token == ')') level--; if (scanner->token == '(') level++; while (!g_scanner_eof (scanner) && level > 0) { g_scanner_get_next_token (scanner); if (scanner->token == '(') level++; else if (scanner->token == ')') level--; } } } void gtk_item_factory_parse_rc_string (const gchar *rc_string) { GScanner *scanner; g_return_if_fail (rc_string != NULL); ifactory_scanner_config.cpair_comment_single = gtk_item_factory_class->cpair_comment_single; scanner = g_scanner_new (&ifactory_scanner_config); g_scanner_input_text (scanner, rc_string, strlen (rc_string)); gtk_item_factory_parse_rc_scanner (scanner); g_scanner_destroy (scanner); } void gtk_item_factory_parse_rc_scanner (GScanner *scanner) { gpointer saved_symbol; g_return_if_fail (scanner != NULL); if (!gtk_item_factory_class) gtk_type_class (GTK_TYPE_ITEM_FACTORY); saved_symbol = g_scanner_lookup_symbol (scanner, "menu-path"); g_scanner_remove_symbol (scanner, "menu-path"); g_scanner_add_symbol (scanner, "menu-path", gtk_item_factory_parse_menu_path); g_scanner_peek_next_token (scanner); while (scanner->next_token == '(') { g_scanner_get_next_token (scanner); gtk_item_factory_parse_statement (scanner, gtk_item_factory_class); g_scanner_peek_next_token (scanner); } g_scanner_remove_symbol (scanner, "menu-path"); g_scanner_add_symbol (scanner, "menu-path", saved_symbol); } void gtk_item_factory_parse_rc (const gchar *file_name) { gint fd; GScanner *scanner; g_return_if_fail (file_name != NULL); if (!S_ISREG (g_scanner_stat_mode (file_name))) return; fd = open (file_name, O_RDONLY); if (fd < 0) return; ifactory_scanner_config.cpair_comment_single = gtk_item_factory_class->cpair_comment_single; scanner = g_scanner_new (&ifactory_scanner_config); g_scanner_input_file (scanner, fd); gtk_item_factory_parse_rc_scanner (scanner); g_scanner_destroy (scanner); close (fd); }