menu merging: smart separators



I never got a reply to my proposal for "smart" separators. Here is an
attempt at implementing this functionality. It works by running the 
update_smart_separators() function whenever a menu or toolbar is
modified and whenever one of its children changes visibility.
update_smart_separators() iterates once over the children, setting the
visibility of separators as it goes. A separator is hidden if one of the
following holds true:

a) it is the first child
b) it is the last child
c) it's preceding sibling is a tearoff menu item
d) it's preceding sibling is another separator 

Additionally, the visibility of separators can by forced either way.
This is currently used only internally to hide the separators used to
fence placeholder children, but it could be exposed as an attribute of 
<separator> elements if there is a need for it.

Matthias


Index: gtk/gtkuimanager.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkuimanager.c,v
retrieving revision 1.17
diff -u -p -r1.17 gtkuimanager.c
@@ -1538,6 +1560,79 @@ find_toolbar_position (GNode      *node,
   return TRUE;
 }
 
+enum {
+  SEPARATOR_MODE_SMART,
+  SEPARATOR_MODE_VISIBLE,
+  SEPARATOR_MODE_HIDDEN
+};
+
+static void
+update_smart_separators (GtkWidget *proxy)
+{
+  GtkWidget *parent = NULL;
+
+  if (GTK_IS_MENU (proxy) || GTK_IS_TOOLBAR (proxy))
+    parent = proxy;
+  else if (GTK_IS_MENU_ITEM (proxy) || GTK_IS_TOOL_ITEM (proxy))
+    parent = gtk_widget_get_parent (proxy);
+
+  if (parent) 
+    {
+      gboolean visible;
+      GList *children, *cur, *last;
+
+      children = gtk_container_get_children (GTK_CONTAINER (parent));
+      
+      visible = FALSE;
+      last = NULL;
+      cur = children;
+      while (cur) 
+	{
+	  if (GTK_IS_SEPARATOR_MENU_ITEM (cur->data) ||
+	      GTK_IS_SEPARATOR_TOOL_ITEM (cur->data))
+	    {
+	      gint mode = 
+		GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cur->data), 
+						    "gtk-separator-mode"));
+	      switch (mode) 
+		{
+		case SEPARATOR_MODE_VISIBLE:
+		  gtk_widget_show (GTK_WIDGET (cur->data));
+		  last = NULL;
+		  visible = FALSE;
+		  break;
+		case SEPARATOR_MODE_HIDDEN:
+		  gtk_widget_hide (GTK_WIDGET (cur->data));
+		  break;
+		case SEPARATOR_MODE_SMART:
+		  if (visible)
+		    {
+		      gtk_widget_show (GTK_WIDGET (cur->data));
+		      last = cur;
+		      visible = FALSE;
+		    }
+		  else
+		    gtk_widget_hide (GTK_WIDGET (cur->data));
+		  break;
+		}
+	    }
+	  else if (GTK_WIDGET_VISIBLE (cur->data))
+	    {
+	      last = NULL;
+	      if (GTK_IS_TEAROFF_MENU_ITEM (cur->data))
+		visible = FALSE;
+	      else 
+		visible = TRUE;
+	    }
+	  
+	  cur = cur->next;
+	}
+
+      if (last)
+	gtk_widget_hide (GTK_WIDGET (last->data));
+    }
+}
+
 static void
 update_node (GtkUIManager *self, 
 	     GNode        *node,
@@ -1725,11 +1818,17 @@ update_node (GtkUIManager *self, 
 
 	      if (find_menu_position (node, &menushell, &pos))
 		{
-		  NODE_INFO (node)->proxy = gtk_separator_menu_item_new ();
+		  info->proxy = gtk_separator_menu_item_new ();
+		  g_object_set_data (G_OBJECT (info->proxy),
+				     "gtk-separator-mode",
+				     GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
 		  gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
 					NODE_INFO (node)->proxy, pos);
 
-		  NODE_INFO (node)->extra = gtk_separator_menu_item_new ();
+		  info->extra = gtk_separator_menu_item_new ();
+		  g_object_set_data (G_OBJECT (info->extra),
+				     "gtk-separator-mode",
+				     GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
 		  gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
 					 NODE_INFO (node)->extra, pos+1);
 		}
@@ -1764,11 +1863,17 @@ update_node (GtkUIManager *self, 
 
 		  item = gtk_separator_tool_item_new ();
 		  gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos);
-		  NODE_INFO(node)->proxy = GTK_WIDGET (item);
+		  info->proxy = GTK_WIDGET (item);
+		  g_object_set_data (G_OBJECT (info->proxy),
+				     "gtk-separator-mode",
+				     GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
 
 		  item = gtk_separator_tool_item_new ();
 		  gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos+1);
-		  NODE_INFO (node)->extra = GTK_WIDGET (item);
+		  info->extra = GTK_WIDGET (item);
+		  g_object_set_data (G_OBJECT (info->extra),
+				     "gtk-separator-mode",
+				     GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
 		}
 	    }
 	  break;
@@ -1777,6 +1882,9 @@ update_node (GtkUIManager *self, 
 	  if (info->proxy &&  G_OBJECT_TYPE (info->proxy) !=
 	      GTK_ACTION_GET_CLASS (action)->menu_item_type)
 	    {
+	      g_signal_handlers_disconnect_by_func (info->proxy,
+						    G_CALLBACK (update_smart_separators),
+						    0);  
 	      gtk_action_disconnect_proxy (info->action, info->proxy);
 	      gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
 				    info->proxy);
@@ -1798,15 +1906,23 @@ update_node (GtkUIManager *self, 
 	    }
 	  else
 	    {
+	      g_signal_handlers_disconnect_by_func (info->proxy,
+						    G_CALLBACK (update_smart_separators),
+						    0);
 	      gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), NULL);
 	      gtk_action_connect_proxy (action, info->proxy);
 	    }
+	  g_signal_connect (info->proxy, "notify::visible",
+			    G_CALLBACK (update_smart_separators), 0);
 	  break;
 	case NODE_TYPE_TOOLITEM:
 	  /* remove the proxy if it is of the wrong type ... */
 	  if (info->proxy &&  G_OBJECT_TYPE (info->proxy) !=
 	      GTK_ACTION_GET_CLASS (action)->toolbar_item_type)
 	    {
+	      g_signal_handlers_disconnect_by_func (info->proxy,
+						    G_CALLBACK (update_smart_separators),
+						    0);
 	      gtk_action_disconnect_proxy (info->action, info->proxy);
 	      gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
 				    info->proxy);
@@ -1828,8 +1944,13 @@ update_node (GtkUIManager *self, 
 	    }
 	  else
 	    {
+	      g_signal_handlers_disconnect_by_func (info->proxy,
+						    G_CALLBACK (update_smart_separators),
+						    0);
 	      gtk_action_connect_proxy (action, info->proxy);
 	    }
+	  g_signal_connect (info->proxy, "notify::visible",
+			    G_CALLBACK (update_smart_separators), 0);
 	  break;
 	case NODE_TYPE_SEPARATOR:
 	  if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLBAR ||
@@ -1850,6 +1971,9 @@ update_node (GtkUIManager *self, 
 		  GtkToolItem *item = gtk_separator_tool_item_new ();
 		  gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos);
 		  info->proxy = GTK_WIDGET (item);
+		  g_object_set_data (G_OBJECT (info->proxy),
+				     "gtk-separator-mode",
+				     GINT_TO_POINTER (SEPARATOR_MODE_SMART));
 		  gtk_widget_show (info->proxy);
 		}
 	    }
@@ -1868,6 +1992,9 @@ update_node (GtkUIManager *self, 
 	      if (find_menu_position (node, &menushell, &pos))
 		{
 		  info->proxy = gtk_separator_menu_item_new ();
+		  g_object_set_data (G_OBJECT (info->proxy),
+				     "gtk-separator-mode",
+				     GINT_TO_POINTER (SEPARATOR_MODE_SMART));
 		  gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
 					 info->proxy, pos);
 		  gtk_widget_show (info->proxy);
@@ -1895,8 +2025,14 @@ update_node (GtkUIManager *self, 
       update_node (self, current, add_tearoffs && (info->type != NODE_TYPE_POPUP));
     }
 
+  if (info->type == NODE_TYPE_MENU) 
+    update_smart_separators (gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy)));
+  else if (info->type == NODE_TYPE_TOOLBAR)
+    update_smart_separators (info->proxy);
+
+
   /* handle cleanup of dead nodes */
   if (node->children == NULL && info->uifiles == NULL)
     {


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