Smart empty submenus



It occurred to me that submenus pose similar problems to separators in
connection with merging: you don't know in advance whether they will end
up with visible children after merging. For those submenus which end up
empty, there are two reasonable behaviours:

a) make them disappear by hiding the menu item they're attached to
b) add an insensitive "Empty" item.

Here is a patch which implements this by reusing the "is_important"
property of the associated action. If the action is not marked as
important, the menu will be hidden when empty. The patch currently
involves some inter-module communication via object data, I'll rework
that it there is interest in this feature. The text used for the "Empty"
item is currently hardwired to "Empty". Should this be settable
per-menu?

Matthias


Index: gtk/gtkaction.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkaction.c,v
retrieving revision 1.13
diff -u -p -r1.13 gtkaction.c
--- gtk/gtkaction.c	26 Sep 2003 23:01:31 -0000	1.13
+++ gtk/gtkaction.c	28 Sep 2003 23:35:49 -0000
@@ -40,6 +40,7 @@
 #include "gtkmarshalers.h"
 #include "gtkmenuitem.h"
 #include "gtkstock.h"
+#include "gtktearoffmenuitem.h"
 #include "gtktoolbutton.h"
 #include "gtktoolbar.h"
 
@@ -530,6 +531,56 @@ gtk_action_sync_property (GtkAction  *ac
   g_value_unset (&value);
 }
 
+static gboolean 
+menu_empty (GtkWidget *menu)
+{
+  GList *children, *cur;
+
+  if (!menu)
+    return TRUE;
+
+  children = gtk_container_get_children (GTK_CONTAINER (menu));
+
+  cur = children;
+  while (cur) 
+    {
+      if (GTK_WIDGET_VISIBLE (cur->data))
+	{
+	  if (!GTK_IS_TEAROFF_MENU_ITEM (cur->data) &&
+	      !g_object_get_data (cur->data, "gtk-empty-menu-filler"))
+	    return FALSE;
+	}
+      cur = cur->next;
+    }
+
+  return TRUE;
+}
+
+static void
+gtk_action_sync_visible (GtkAction  *action, 
+			 GParamSpec *pspec,
+			 GtkWidget  *proxy)
+{
+  if (GTK_IS_MENU_ITEM (proxy))
+    {
+      GtkWidget *menu;
+      gboolean visible, important;
+
+      g_object_get (G_OBJECT (action), 
+		    "visible", &visible, 
+		    "is_important", &important,
+		    NULL);
+
+      menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy));
+      g_object_set (G_OBJECT (proxy),
+		    "visible", visible && (important || 
+					   !menu_empty (menu)),
+		    NULL);
+    }
+  else
+    gtk_action_sync_property (action, pspec, proxy);
+}
+
 static void
 gtk_action_sync_label (GtkAction  *action, 
 		       GParamSpec *pspec, 
@@ -627,7 +678,7 @@ connect_proxy (GtkAction     *action, 
   gtk_widget_set_sensitive (proxy, action->private_data->sensitive);
 
   g_signal_connect_object (action, "notify::visible",
-			   G_CALLBACK (gtk_action_sync_property), proxy, 0);
+			   G_CALLBACK (gtk_action_sync_visible), proxy, 0);
   if (action->private_data->visible)
     gtk_widget_show (proxy);
   else
Index: gtk/gtkuimanager.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkuimanager.c,v
retrieving revision 1.26
diff -u -p -r1.26 gtkuimanager.c
--- gtk/gtkuimanager.c	26 Sep 2003 23:01:31 -0000	1.26
+++ gtk/gtkuimanager.c	28 Sep 2003 23:35:50 -0000
@@ -60,8 +60,7 @@ typedef enum 
   NODE_TYPE_ACCELERATOR
 } NodeType;
 
-
-typedef struct _Node  Node;
+typedef struct _Node Node;
 
 struct _Node {
   NodeType type;
@@ -71,7 +70,7 @@ struct _Node {
   GQuark action_name;
   GtkAction *action;
   GtkWidget *proxy;
-  GtkWidget *extra; /*GtkMenu for submenus, second separator for placeholders*/
+  GtkWidget *extra; /* second separator for placeholders */
 
   GList *uifiles;
 
@@ -831,13 +830,14 @@ start_element_handler (GMarkupParseConte
   const gchar *action;
   GQuark action_quark;
   gboolean top;
-
+  
   gboolean raise_error = TRUE;
 
   node_name = NULL;
   action = NULL;
   action_quark = 0;
   top = FALSE;
+
   for (i = 0; attribute_names[i] != NULL; i++)
     {
       if (!strcmp (attribute_names[i], "name"))
@@ -1661,15 +1661,23 @@ update_smart_separators (GtkWidget *prox
   if (parent) 
     {
       gboolean visible;
+      gboolean empty;
       GList *children, *cur, *last;
+      GtkWidget *filler;
 
       children = gtk_container_get_children (GTK_CONTAINER (parent));
       
       visible = FALSE;
       last = NULL;
+      empty = TRUE;
+      filler = NULL;
+
       cur = children;
       while (cur) 
 	{
+	  if (g_object_get_data (cur->data, "gtk-empty-menu-filler"))
+	    filler = cur->data;
+
 	  if (GTK_IS_SEPARATOR_MENU_ITEM (cur->data) ||
 	      GTK_IS_SEPARATOR_TOOL_ITEM (cur->data))
 	    {
@@ -1701,10 +1709,13 @@ update_smart_separators (GtkWidget *prox
 	  else if (GTK_WIDGET_VISIBLE (cur->data))
 	    {
 	      last = NULL;
-	      if (GTK_IS_TEAROFF_MENU_ITEM (cur->data))
+	      if (GTK_IS_TEAROFF_MENU_ITEM (cur->data) || cur->data == filler)
 		visible = FALSE;
 	      else 
-		visible = TRUE;
+		{
+		  visible = TRUE;
+		  empty = FALSE;
+		}
 	    }
 	  
 	  cur = cur->next;
@@ -1712,6 +1723,25 @@ update_smart_separators (GtkWidget *prox
 
       if (last)
 	gtk_widget_hide (GTK_WIDGET (last->data));
+
+      if (GTK_IS_MENU (parent)) 
+	{
+	  gboolean important;
+	  GtkWidget *item;
+	  GtkAction *action;
+
+	  item = gtk_menu_get_attach_widget (GTK_MENU (parent));
+	  action = g_object_get_data (G_OBJECT (item), "gtk-action");
+	  g_object_get (G_OBJECT (action),
+			"visible", &visible,
+			"is_important", &important,
+			NULL);
+	  
+	  g_object_set (G_OBJECT (item),
+			"visible", visible && (important || !empty),
+			NULL);
+	  g_object_set (G_OBJECT (filler), "visible", empty, NULL);
+	}
     }
 }
 
@@ -1843,12 +1873,20 @@ update_node (GtkUIManager *self, 
 		if (find_menu_position (node, &menushell, &pos))
 		  {
 		    GtkWidget *tearoff;
+		    GtkWidget *filler;
 
 		    info->proxy = gtk_action_create_menu_item (action);
 		    menu = gtk_menu_new ();
 		    tearoff = gtk_tearoff_menu_item_new ();
 		    gtk_widget_set_no_show_all (tearoff, TRUE);
 		    gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
+		    filler = gtk_menu_item_new_with_label (_("Empty"));
+		    g_object_set_data (G_OBJECT (filler),
+				       "gtk-empty-menu-filler",
+				       GINT_TO_POINTER (TRUE));
+		    gtk_widget_set_sensitive (filler, FALSE);
+		    gtk_widget_set_no_show_all (filler, TRUE);
+		    gtk_menu_shell_append (GTK_MENU_SHELL (menu), filler);
 		    gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), menu);
 		    gtk_menu_shell_insert (GTK_MENU_SHELL (menushell), info->proxy, pos);
 		  }


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