gtk2/gtk/gtkitemfactory.c
Elliot Lee 060978e069 I submitted this patch twice to gtk-devel-list, and received no comments,
I submitted this patch twice to gtk-devel-list, and received no comments, so
am committing it.  Although not exhaustively tested, I have been using this
gtk+ for a week w/o problems, and I did read the code to ensure that nothing
ever writes to these data structures. If by chance people encounter SEGV's in
gtk+ code that is setting values in global data structures, this patch could
be a possible culprit.

1998-11-30  Elliot Lee  <sopwith@cuc.ml.org>

	* {gdk,gtk}/*.c: Make read-only data structures "static const" to
	allow them to be shared, mainly including (but not limited to) the
	GtkTypeInfo structures for each class.

	* gtk/gtkfilesel.c: Add /net to the "leave me alone" directory listing.
1998-11-30 19:07:15 +00:00

1602 lines
40 KiB
C

/* 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 <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
/* --- 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-<ItemFactory>";
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 = "<Item>";
static GQuark quark_type_item = 0;
static const gchar *key_type_title = "<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)
{
static const 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)
{
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);
g_free (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;
if (entry->callback)
g_warning ("gtk_item_factory_create_item(): Can't specify a callback on a branch: \"%s\"",
entry->path);
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,
(type_id == quark_type_branch ||
type_id == quark_type_last_branch) ?
(GtkItemFactoryCallback) NULL : entry->callback,
entry->callback_action, callback_data,
callback_type,
item_type_path,
widget);
g_free (path);
}
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);
}