Nuke GtkMenuFactory in favour of GtkItemFactory



hi all.

since the first time i tried to use GtkMenuFactory, i've found
it's concept fundamentally broken. here is a short (but by no means
comprehensive) list of what i personally dislike about it:

-	GtkMenuFactory is a structure allocated and destructed on demand,
	which is used in many places (windows) in multi-widnow applications,
	but does not honour the reference counting scheme.
-	therefore automatic destruction for menu fectories is *not* possible.
-	there is no way to track whether a GtkMenuFactory adds/removes new
	entries (i.e. signal connections are *not* possible).
-	accelerator saving/parsing is not supported, though that is really
	easy to implement (along the lines gtk_rc_* work).
-	accelerator tables are only supported on a per menu basis, not a
	per window basis (this creates problems when using different menus
	e.g. a popup and a menubar for the same window).
-	a certain menu entry can only have one instance, so one cannot use
	the same menu factory entry table for two different windows that need
	the same menubar.
-	right justification for menuitems on a menubar is not supported (though
	that could eventually be implemented).
-	there is no easy way to have a menuitem callback operate on a certain
	widget (without iterating through the entries array and setting
	callback_data to the widget prior to adding them to GtkMenuFactory).
-	there is no clean way to use the same callback function for multiple
	menuitems and switch() to a specific intended behaviour (one could
	use ((gpointer)1) ((gpointer)2) etc. for the callback_data, but then
	this interferes with the above mentioned point).

no as a result of the problems i encountered with GtkMenuFactory, i decided to
reimplement it as GtkItemFactory.
GtkItemFactory basically is a GtkObject, which nukes the idea of subfactories
and suports automatic accelerator handling.
that means accelerators can automatically be parsed from a menurc file and
dumped via a print function. also, multiple menubars on different windows
can share the same item factory entries, and therefore adjust their
accelerators automatically.
since i've only began to write on the code a few hours ago, nothing is really
set in stone already, and i'm curious to hear about people's comments.

i've appended the current gtkitemfactory.h file and will answer questions
(extend on this issue) after i got a couple of hours sleep. anyways, the
current test implementation i have so far works like a charme (except for the
unimplemented parsing ;) and will be used in BEAST/BSE and GLE (once both get
into a state where they can be consumed by a variety of people). it is also
a whole lot faster since i'm doing only hash lookups on the path lookup and
on the item_types.

now for the API side of GtkItemFactory, i'm quoting the GtkItemFactoryEntry
definition and give a short example:

[from gtkitemfactory.h]
struct _GtkItemFactoryEntry
{
  gchar *path;
  gchar *accelerator;

  GtkItemFactoryCallback callback;
  guint                  callback_action;

  /* possible values:
   * NULL               -> "<Item>"
   * ""                 -> "<Item>"
   * "<Item>"           -> create a simple item
   * "<CheckItem>"      -> create a check item
   * "<ToggleItem>"     -> create a toggle item
   * "<RadioItem>"      -> create a radio item
   * <path>             -> path to a radio item to link against
   * "<Separator>"      -> create a separator
   * "<Branch>"         -> create an item to hold sub items
   * "<LastBranch>"     -> create a right justified item to hold sub items
   */
  gchar          *item_type;
};

[application side]
static void
my_file_callback (gpointer              callback_data,
                  guint                 callback_action,
                  GtkWidget            *menu_item)
{
  GImage *image;
  
  image = callback_data;
  
  gtk_widget_set_sensitive (menu_item, FALSE);
  
  switch (callback_action)
  {
    case  CB_OPEN:
      spawn_open_dialog (image);
      break;
    case  CB_SAVE:
      spawn_save_dialog (image, image->filename);
      break;
    case  CB_SAVE_AS:
      spawn_save_dialog (image, NULL);
      break;
  }
}

static GtkItemFactoryEntry      menubar_entries[] =
{
  { "/File",		NULL,	   NULL,             0,       NULL /* this line could be omitted */}, 
  { "/File/Open...",	"<ctrl>O", my_file_callback, CB_OPEN, NULL },
  { "/File/Save...",	"<ctrl>S", my_file_callback, CB_SAVE, NULL },
  { "/File/Save As...",	"<ctrl>A", my_file_callback, CB_SAVE_AS, NULL },
  { "/File/",		NULL,	   NULL,             0,       "<Separator>" },
  { "/File/About...",   NULL,      spawn_about_box,  0,       NULL },
  { "/File/",		NULL,	   NULL,             0,       "<Separator>" },
  { "/File/Show Tips",  NULL,      NULL,             0,       "<CheckItem>" },
  { "/File/Dialogs/Gradians", "<ctrl>G", NULL,       0,       NULL },
  { "/File/this is ignored", NULL, NULL,             0,       "<Separator>" },
  { "/File/Destroy Image", NULL,   gtk_widget_destroy, 0,     NULL },
  { "/Help", 	        NULL,      NULL,             0,       "<LastBranch>" /* this one cannot */},
  { "/Help/Authors",    NULL,      spawn_authors_dialog, 0,   NULL },
};
static guint n_menubar_entries = sizeof (menubar_entries) /
                                 sizeof (menubar_entries[0]);
static GtkItemFactoryEntry      popup_entries[] =
{
  { "/Choose 1", NULL,   NULL, 0,     "<RadioItem>" },
  { "/Choose 2", NULL,   NULL, 0,     "/Choose 1" },
  { "/Choose 3", NULL,   NULL, 0,     "/Choose 1" },
  { "/Choose 4", NULL,   NULL, 0,     "/Choose 1" },
  { "/Stale Radio", NULL,NULL, 0,     "<RadioItem>" },
  { "/Quit Popup", NULL,   NULL, 0,     NULL },
};
static guint n_popup_entries = sizeof (popup_entries) /
                               sizeof (popup_entries[0]);
  
static void
create_factories (GImage *image)
{
  GtkObject *ifactory_mbar;
  GtkObject *ifactory_popup;
  
  ifactory_mbar = gtk_item_factory_new (gtk_menu_bar_get_type (),
                                        "<Image-MenuBar>",
                                         NULL);
  gtk_window_add_accelerator_table (image->window,
                                    GTK_ITEM_FACTORY (ifactory_mbar)->table);
  gtk_item_factory_create_items (GTK_ITEM_FACTORY (ifactory_mbar),
                                 n_menubar_entries,
                                 menubar_entries,
                                 image);

  ifactory_popup = gtk_item_factory_new (gtk_menu_bar_get_type (),
                                         "<Image-Popup>",
                                         GTK_ITEM_FACTORY (ifactory_mbar)->table);
  gtk_item_factory_create_items (GTK_ITEM_FACTORY (ifactory_popup),
                                 n_popup_entries,
                                 popup_entries,
                                 image);
  image->popup = GTK_ITEM_FACTORY (ifactory_popup);
}

[...]

image_spawn_popup (GImage *image, GdkEventButton *event)
{
  gtk_item_factory_popup (image->ifactory_popup,
                          event->x_root, event->y_root,
                          event->button, event->time);
}


boy, that became longer than i wanted it to ;)
anyways, still need to get some sleep. i'll pay attention to your flames
later this day ;)


PS.: i don't really want to nuke GtkMenuFactory ;) GtkItemFactory should
     go into the 1.1.x tree, and GtkMenuFactory should be kept around
     as long as there are applications using it (though its use should be
     discouraged).

---
ciaoTJ


/* 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.
 */
#ifndef __GTK_ITEM_FACTORY_H__
#define	__GTK_ITEM_FACTORY_H__


#include <gtk/gtkwidget.h>

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


typedef void	(*GtkPrintFunc)		  (gpointer		 func_data,
					   gchar		*str);
typedef	void	(*GtkItemFactoryCallback) (gpointer		 callback_data,
					   guint		 callback_action,
					   GtkWidget		*widget);

#define	GTK_ITEM_FACTORY(object)	(GTK_CHECK_CAST (object, gtk_item_factory_get_type (), GtkItemFactory))
#define	GTK_ITEM_FACTORY_CLASS(klass)	(GTK_CHECK_CLASS_CAST (klass, gtk_item_factory_get_type (), GtkItemFactoryClass))
#define	GTK_IS_ITEM_FACTORY(object)	(GTK_CHECK_TYPE (object, gtk_item_factory_get_type ()))

typedef	struct	_GtkItemFactory			GtkItemFactory;
typedef	struct	_GtkItemFactoryClass		GtkItemFactoryClass;
typedef	struct	_GtkItemFactoryEntry		GtkItemFactoryEntry;
typedef	struct	_GtkItemFactoryItem		GtkItemFactoryItem;

struct _GtkItemFactory
{
  GtkObject		 object;

  gchar			*path;
  GtkAcceleratorTable	*table;
  GtkWidget		*widget;
};

struct _GtkItemFactoryClass
{
  GtkObjectClass		object_class;

  GHashTable			*item_ht;

  void	(*create_item)		(GtkItemFactory		*ifactory,
				 GtkItemFactoryEntry	*entry,
				 gpointer		 callback_data);
  void	(*delete_item)		(GtkItemFactory		*ifactory,
				 gchar			*path);
};

struct _GtkItemFactoryEntry
{
  gchar *path;
  gchar *accelerator;

  GtkItemFactoryCallback callback;
  guint			 callback_action;

  /* possible values:
   * NULL		-> "<Item>"
   * ""			-> "<Item>"
   * "<Item>"		-> create a simple item
   * "<CheckItem>"	-> create a check item
   * "<ToggleItem>"	-> create a toggle item
   * "<RadioItem>"	-> create a radio item
   * <path>		-> path to a radio item to link against
   * "<Separator>"	-> create a separator
   * "<Branch>"		-> create an item to hold sub items
   * "<LastBranch>"	-> create a right justified item to hold sub items
   */
  gchar		 *item_type;
};

struct _GtkItemFactoryItem
{
  gchar *path;
  gchar *accelerator;
  gchar *item_type;

  GSList *widgets;
};


GtkType		gtk_item_factory_get_type	    (void);

/* `container_type' currently has to be of gtk_menu_bar_get_type (),
 * gtk_menu_get_type () or gtk_option_menu_get_type ().
 */
GtkObject*	gtk_item_factory_new	   (GtkType		 container_type,
					    const gchar		*path,
					    GtkAcceleratorTable *table);
void		gtk_item_factory_construct (GtkItemFactory	*ifactory,
					    GtkType		 container_type,
					    const gchar		*path,
					    GtkAcceleratorTable *table);
     
/* These functions operate on GtkItemFactoryClass basis.
 */
void		gtk_item_factory_parse_rc	    (const gchar    *filename);
void		gtk_item_factory_parse_rc_string    (const gchar    *rc_string);

/* If `ifactory' is passed as `NULL', this function will iterate over all
 * hash entries.
 */
void		gtk_item_factory_dump_rc	    (GtkItemFactory *ifactory,
						     GtkPrintFunc    print_func,
						     gpointer	     func_data);
GtkItemFactory*	gtk_item_factory_from_widget	    (GtkWidget	      *widget);
gchar*		gtk_item_factory_path_from_widget   (GtkWidget	      *widget);

GtkWidget*	gtk_item_factory_get_widget	    (GtkItemFactory   *ifactory,
						     const gchar      *path);

void	gtk_item_factory_create_item	(GtkItemFactory		*ifactory,
					 GtkItemFactoryEntry	*entry,
					 gpointer		 callback_data);
void	gtk_item_factory_create_items	(GtkItemFactory		*ifactory,
					 guint			 n_entries,
					 GtkItemFactoryEntry	*entries,
					 gpointer		 callback_data);
void	gtk_item_factory_delete_path	(GtkItemFactory		*ifactory,
					 const gchar		*path);
void	gtk_item_factory_delete_entry	(GtkItemFactory		*ifactory,
					 GtkItemFactoryEntry	*entry);
void	gtk_item_factory_delete_entries	(GtkItemFactory		*ifactory,
					 guint			 n_entries,
					 GtkItemFactoryEntry	*entries);
void	gtk_item_factory_popup		(GtkItemFactory		*ifactory,
					 guint			 x,
					 guint			 y,
					 guint			 mouse_button,
					 guint32		 time);
     



#ifdef __cplusplus
#pragma {
}
#endif /* __cplusplus */


#endif	/* __GTK_ITEM_FACTORY_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]