Add support for both prepending and appending custom menu items.

2007-02-09  Emmanuele Bassi  <ebassi@gnome.org>

	* gtk/gtkrecentchoosermenu.c: Add support for both prepending
	and appending custom menu items.

	(gtk_recent_chooser_menu_constructor): Add a placeholder menu
	item for the empty menu case, and for giving us a starting
	point for the recent items populating process.

	(gtk_recent_chooser_menu_insert_item),
	(gtk_recent_chooser_menu_dispose_items): Insert an item at
	the position following the placeholder (and find that position
	if needed).

	(idle_populate_func), (idle_populate_clean_up): Show the
	placeholder menu item, instead of creating one each time.

	(gtk_recent_chooser_menu_populate): Kill some indirections
	and hide the placeholder before populating the menu.

	(set_recent_manager): Remember to remove the idle population
	source if the manager changes.

	* tests/testrecentchoosermenu.c: Test the appending and
	prepending of the menu items to the recent chooser menu
	widget.

svn path=/trunk/; revision=17281
This commit is contained in:
Emmanuele Bassi 2007-02-09 14:24:19 +00:00 committed by Emmanuele Bassi
parent 661a6bc021
commit 4a559d6ec8
3 changed files with 158 additions and 57 deletions

View File

@ -1,3 +1,30 @@
2007-02-09 Emmanuele Bassi <ebassi@gnome.org>
* gtk/gtkrecentchoosermenu.c: Add support for both prepending
and appending custom menu items.
(gtk_recent_chooser_menu_constructor): Add a placeholder menu
item for the empty menu case, and for giving us a starting
point for the recent items populating process.
(gtk_recent_chooser_menu_insert_item),
(gtk_recent_chooser_menu_dispose_items): Insert an item at
the position following the placeholder (and find that position
if needed).
(idle_populate_func), (idle_populate_clean_up): Show the
placeholder menu item, instead of creating one each time.
(gtk_recent_chooser_menu_populate): Kill some indirections
and hide the placeholder before populating the menu.
(set_recent_manager): Remember to remove the idle population
source if the manager changes.
* tests/testrecentchoosermenu.c: Test the appending and
prepending of the menu items to the recent chooser menu
widget.
2007-02-08 Emmanuele Bassi <ebassi@gnome.org> 2007-02-08 Emmanuele Bassi <ebassi@gnome.org>
* gtk/gtkrecentchoosermenu.c (idle_populate_func): Keep count * gtk/gtkrecentchoosermenu.c (idle_populate_func): Keep count

View File

@ -59,6 +59,9 @@ struct _GtkRecentChooserMenuPrivate
/* max size of the menu item label */ /* max size of the menu item label */
gint label_width; gint label_width;
gint first_recent_item_pos;
GtkWidget *placeholder;
/* RecentChooser properties */ /* RecentChooser properties */
gint limit; gint limit;
guint show_private : 1; guint show_private : 1;
@ -237,6 +240,9 @@ gtk_recent_chooser_menu_init (GtkRecentChooserMenu *menu)
priv->label_width = DEFAULT_LABEL_WIDTH; priv->label_width = DEFAULT_LABEL_WIDTH;
priv->first_recent_item_pos = -1;
priv->placeholder = NULL;
priv->current_filter = NULL; priv->current_filter = NULL;
priv->tooltips = gtk_tooltips_new (); priv->tooltips = gtk_tooltips_new ();
@ -300,19 +306,43 @@ gtk_recent_chooser_menu_dispose (GObject *object)
static GObject * static GObject *
gtk_recent_chooser_menu_constructor (GType type, gtk_recent_chooser_menu_constructor (GType type,
guint n_construct_properties, guint n_params,
GObjectConstructParam *construct_params) GObjectConstructParam *params)
{ {
GtkRecentChooserMenu *menu; GtkRecentChooserMenu *menu;
GtkRecentChooserMenuPrivate *priv;
GObjectClass *parent_class;
GObject *object; GObject *object;
object = G_OBJECT_CLASS (gtk_recent_chooser_menu_parent_class)->constructor (type, parent_class = G_OBJECT_CLASS (gtk_recent_chooser_menu_parent_class);
n_construct_properties, object = parent_class->constructor (type, n_params, params);
construct_params);
menu = GTK_RECENT_CHOOSER_MENU (object); menu = GTK_RECENT_CHOOSER_MENU (object);
priv = menu->priv;
g_assert (menu->priv->manager); g_assert (priv->manager);
/* we create a placeholder menuitem, to be used in the case
* were the menu is empty. this placeholder will stay around
* for the entire lifetime of the menu, and we just hide it
* when it's not used. we have to do this, and do it here,
* because we need a marker for the beginning of the recent
* items list, so that we can insert the new items at the
* right place when populating the menu, in case the user
* appended or prepended custom menuitems to the recent
* chooser menu widget.
*/
priv->placeholder = gtk_menu_item_new_with_label (_("No items found"));
gtk_widget_set_sensitive (priv->placeholder, FALSE);
g_object_set_data (G_OBJECT (priv->placeholder),
"gtk-recent-menu-placeholder",
GINT_TO_POINTER (TRUE));
gtk_menu_shell_insert (GTK_MENU_SHELL (menu), priv->placeholder, 0);
gtk_widget_show (priv->placeholder);
/* (re)populate the menu */
gtk_recent_chooser_menu_populate (menu);
return object; return object;
} }
@ -962,6 +992,41 @@ gtk_recent_chooser_menu_create_item (GtkRecentChooserMenu *menu,
return item; return item;
} }
static void
gtk_recent_chooser_menu_insert_item (GtkRecentChooserMenu *menu,
GtkWidget *menuitem,
gint position)
{
GtkRecentChooserMenuPrivate *priv = menu->priv;
gint real_position;
GList *children, *l;
if (priv->first_recent_item_pos == -1)
{
children = gtk_container_get_children (GTK_CONTAINER (menu));
for (real_position = 0, l = children;
l != NULL;
real_position += 1, l = l->next)
{
GtkWidget *child = l->data;
gint mark = 0;
mark = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (child),
"gtk-recent-menu-placeholder"));
if (mark == 1)
break;
}
g_list_free (children);
priv->first_recent_item_pos = real_position;
}
else
real_position = priv->first_recent_item_pos;
gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem,
real_position + position);
gtk_widget_show (menuitem);
}
/* removes the items we own from the menu */ /* removes the items we own from the menu */
static void static void
gtk_recent_chooser_menu_dispose_items (GtkRecentChooserMenu *menu) gtk_recent_chooser_menu_dispose_items (GtkRecentChooserMenu *menu)
@ -992,6 +1057,9 @@ gtk_recent_chooser_menu_dispose_items (GtkRecentChooserMenu *menu)
} }
} }
/* recalculate the position of the first recent item */
menu->priv->first_recent_item_pos = -1;
g_list_free (children); g_list_free (children);
} }
@ -1023,29 +1091,15 @@ idle_populate_func (gpointer data)
pdata->items = gtk_recent_chooser_get_items (GTK_RECENT_CHOOSER (pdata->menu)); pdata->items = gtk_recent_chooser_get_items (GTK_RECENT_CHOOSER (pdata->menu));
if (!pdata->items) if (!pdata->items)
{ {
item = gtk_menu_item_new_with_label (_("No items found")); /* show the placeholder here */
gtk_widget_set_sensitive (item, FALSE); gtk_widget_show (priv->placeholder);
/* we also mark this item, so that it gets removed when rebuilding
* the menu on the next map event
*/
g_object_set_data (G_OBJECT (item), "gtk-recent-menu-mark",
GINT_TO_POINTER (1));
gtk_menu_shell_prepend (GTK_MENU_SHELL (pdata->menu), item);
gtk_widget_show (item);
pdata->displayed_items = 1; pdata->displayed_items = 1;
/* no items: add a placeholder menu */
GDK_THREADS_LEAVE (); GDK_THREADS_LEAVE ();
return FALSE; return FALSE;
} }
/* reverse the list */
pdata->items = g_list_reverse (pdata->items);
pdata->n_items = g_list_length (pdata->items); pdata->n_items = g_list_length (pdata->items);
pdata->loaded_items = 0; pdata->loaded_items = 0;
} }
@ -1085,16 +1139,9 @@ idle_populate_func (gpointer data)
goto check_and_return; goto check_and_return;
gtk_recent_chooser_menu_add_tip (pdata->menu, info, item); gtk_recent_chooser_menu_add_tip (pdata->menu, info, item);
gtk_recent_chooser_menu_insert_item (pdata->menu, item,
/* FIXME pdata->displayed_items);
*
* We should really place our items taking into account user
* defined menu items; this would also remove the need of
* reverting the scan order.
*/
gtk_menu_shell_prepend (GTK_MENU_SHELL (pdata->menu), item);
gtk_widget_show (item);
pdata->displayed_items += 1; pdata->displayed_items += 1;
/* mark the menu item as one of our own */ /* mark the menu item as one of our own */
@ -1129,23 +1176,13 @@ static void
idle_populate_clean_up (gpointer data) idle_populate_clean_up (gpointer data)
{ {
MenuPopulateData *pdata = data; MenuPopulateData *pdata = data;
GtkRecentChooserMenuPrivate *priv = pdata->menu->priv;
/* show the placeholder in case no item survived
* the filtering process in the idle loop
*/
if (!pdata->displayed_items) if (!pdata->displayed_items)
{ gtk_widget_show (priv->placeholder);
GtkWidget *item;
item = gtk_menu_item_new_with_label (_("No items found"));
gtk_widget_set_sensitive (item, FALSE);
/* we also mark this item, so that it gets removed when rebuilding
* the menu on the next map event
*/
g_object_set_data (G_OBJECT (item), "gtk-recent-menu-mark",
GINT_TO_POINTER (1));
gtk_menu_shell_prepend (GTK_MENU_SHELL (pdata->menu), item);
gtk_widget_show (item);
}
g_slice_free (MenuPopulateData, data); g_slice_free (MenuPopulateData, data);
} }
@ -1154,6 +1191,7 @@ static void
gtk_recent_chooser_menu_populate (GtkRecentChooserMenu *menu) gtk_recent_chooser_menu_populate (GtkRecentChooserMenu *menu)
{ {
MenuPopulateData *pdata; MenuPopulateData *pdata;
GtkRecentChooserMenuPrivate *priv = menu->priv;
if (menu->priv->populate_id) if (menu->priv->populate_id)
return; return;
@ -1165,15 +1203,16 @@ gtk_recent_chooser_menu_populate (GtkRecentChooserMenu *menu)
pdata->displayed_items = 0; pdata->displayed_items = 0;
pdata->menu = menu; pdata->menu = menu;
menu->priv->icon_size = get_icon_size_for_widget (GTK_WIDGET (menu)); priv->icon_size = get_icon_size_for_widget (GTK_WIDGET (menu));
/* dispose our menu items first */ /* remove our menu items first and hide the placeholder */
gtk_recent_chooser_menu_dispose_items (menu); gtk_recent_chooser_menu_dispose_items (menu);
gtk_widget_hide (priv->placeholder);
menu->priv->populate_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30, priv->populate_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30,
idle_populate_func, idle_populate_func,
pdata, pdata,
idle_populate_clean_up); idle_populate_clean_up);
} }
/* bounce activate signal from the recent menu item widget /* bounce activate signal from the recent menu item widget
@ -1207,7 +1246,16 @@ set_recent_manager (GtkRecentChooserMenu *menu,
if (priv->manager) if (priv->manager)
{ {
if (priv->manager_changed_id) if (priv->manager_changed_id)
g_signal_handler_disconnect (priv->manager, priv->manager_changed_id); {
g_signal_handler_disconnect (priv->manager, priv->manager_changed_id);
priv->manager_changed_id = 0;
}
if (priv->populate_id)
{
g_source_remove (priv->populate_id);
priv->populate_id = 0;
}
priv->manager = NULL; priv->manager = NULL;
} }
@ -1221,8 +1269,6 @@ set_recent_manager (GtkRecentChooserMenu *menu,
priv->manager_changed_id = g_signal_connect (priv->manager, "changed", priv->manager_changed_id = g_signal_connect (priv->manager, "changed",
G_CALLBACK (manager_changed_cb), G_CALLBACK (manager_changed_cb),
menu); menu);
/* (re)populate the menu */
gtk_recent_chooser_menu_populate (menu);
} }
static gint static gint

View File

@ -56,6 +56,8 @@ static GtkWidget *
create_recent_chooser_menu (void) create_recent_chooser_menu (void)
{ {
GtkWidget *menu; GtkWidget *menu;
GtkRecentFilter *filter;
GtkWidget *menuitem;
menu = gtk_recent_chooser_menu_new_for_manager (manager); menu = gtk_recent_chooser_menu_new_for_manager (manager);
@ -66,12 +68,38 @@ create_recent_chooser_menu (void)
gtk_recent_chooser_menu_set_show_numbers (GTK_RECENT_CHOOSER_MENU (menu), gtk_recent_chooser_menu_set_show_numbers (GTK_RECENT_CHOOSER_MENU (menu),
TRUE); TRUE);
filter = gtk_recent_filter_new ();
gtk_recent_filter_set_name (filter, "Gedit files");
gtk_recent_filter_add_application (filter, "gedit");
gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (menu), filter);
g_signal_connect (menu, "item-activated", g_signal_connect (menu, "item-activated",
G_CALLBACK (item_activated_cb), G_CALLBACK (item_activated_cb),
NULL); NULL);
gtk_widget_show (menu); gtk_widget_show (menu);
menuitem = gtk_separator_menu_item_new ();
gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show (menuitem);
menuitem = gtk_menu_item_new_with_label ("Test prepend");
gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show (menuitem);
menuitem = gtk_separator_menu_item_new ();
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show (menuitem);
menuitem = gtk_menu_item_new_with_label ("Test append");
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show (menuitem);
menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLEAR, NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_widget_show (menuitem);
return menu; return menu;
} }