summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <maclas@gmx.de>2003-09-30 20:55:24 +0000
committerMatthias Clasen <matthiasc@src.gnome.org>2003-09-30 20:55:24 +0000
commit57f13d815ddbdb0d001fa92c823d44744b3577e4 (patch)
tree132e7f9da4eaa9e59fb3443c548db4b714192871
parent039c6b3bb16343428708ef827a937a7ac64a44a0 (diff)
downloadgdk-pixbuf-57f13d815ddbdb0d001fa92c823d44744b3577e4.tar.gz
Test handling of empty menus.
2003-09-30 Matthias Clasen <maclas@gmx.de> * tests/merge-*.ui: * tests/testmerge.c: Test handling of empty menus. * gtk/gtkuimanager.c (_gtk_menu_is_empty): New function to determine whether a menu is empty. Used in gtkaction.c. (update_smart_separators): Also update the visibility of empty menus. (update_node): When creating a new menu proxy, insert an "Empty" menu item which only gets shown if the menu is empty. * gtk/gtkaction.c (gtk_action_class_init): Document the meaning of "is_important" for menu proxies. (_gtk_action_sync_menu_visible): New function to sync the visibility of menu proxies. Used in gtkuimanager.c. (gtk_action_sync_visible): New function to sync the visibility of proxies.
-rw-r--r--ChangeLog18
-rw-r--r--ChangeLog.pre-2-1018
-rw-r--r--ChangeLog.pre-2-418
-rw-r--r--ChangeLog.pre-2-618
-rw-r--r--ChangeLog.pre-2-818
-rw-r--r--docs/reference/ChangeLog2
-rw-r--r--docs/reference/gtk/tmpl/gtkuimanager.sgml30
-rw-r--r--gtk/gtkaction.c65
-rw-r--r--gtk/gtkuimanager.c83
-rw-r--r--tests/merge-1.ui7
-rw-r--r--tests/merge-2.ui8
-rw-r--r--tests/testmerge.c6
12 files changed, 273 insertions, 18 deletions
diff --git a/ChangeLog b/ChangeLog
index 9c226228c..55cb342d1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2003-09-30 Matthias Clasen <maclas@gmx.de>
+
+ * tests/merge-*.ui:
+ * tests/testmerge.c: Test handling of empty menus.
+
+ * gtk/gtkuimanager.c (_gtk_menu_is_empty): New function to determine
+ whether a menu is empty. Used in gtkaction.c.
+ (update_smart_separators): Also update the visibility of empty menus.
+ (update_node): When creating a new menu proxy, insert an "Empty" menu
+ item which only gets shown if the menu is empty.
+
+ * gtk/gtkaction.c (gtk_action_class_init): Document the meaning of
+ "is_important" for menu proxies.
+ (_gtk_action_sync_menu_visible): New function to sync the visibility
+ of menu proxies. Used in gtkuimanager.c.
+ (gtk_action_sync_visible): New function to sync the visibility of
+ proxies.
+
Tue Sep 30 21:43:34 2003 Kristian Rietveld <kris@gtk.org>
* gtk/gtkcombobox.c (gtk_combo_box_menu_button_press): set
diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10
index 9c226228c..55cb342d1 100644
--- a/ChangeLog.pre-2-10
+++ b/ChangeLog.pre-2-10
@@ -1,3 +1,21 @@
+2003-09-30 Matthias Clasen <maclas@gmx.de>
+
+ * tests/merge-*.ui:
+ * tests/testmerge.c: Test handling of empty menus.
+
+ * gtk/gtkuimanager.c (_gtk_menu_is_empty): New function to determine
+ whether a menu is empty. Used in gtkaction.c.
+ (update_smart_separators): Also update the visibility of empty menus.
+ (update_node): When creating a new menu proxy, insert an "Empty" menu
+ item which only gets shown if the menu is empty.
+
+ * gtk/gtkaction.c (gtk_action_class_init): Document the meaning of
+ "is_important" for menu proxies.
+ (_gtk_action_sync_menu_visible): New function to sync the visibility
+ of menu proxies. Used in gtkuimanager.c.
+ (gtk_action_sync_visible): New function to sync the visibility of
+ proxies.
+
Tue Sep 30 21:43:34 2003 Kristian Rietveld <kris@gtk.org>
* gtk/gtkcombobox.c (gtk_combo_box_menu_button_press): set
diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4
index 9c226228c..55cb342d1 100644
--- a/ChangeLog.pre-2-4
+++ b/ChangeLog.pre-2-4
@@ -1,3 +1,21 @@
+2003-09-30 Matthias Clasen <maclas@gmx.de>
+
+ * tests/merge-*.ui:
+ * tests/testmerge.c: Test handling of empty menus.
+
+ * gtk/gtkuimanager.c (_gtk_menu_is_empty): New function to determine
+ whether a menu is empty. Used in gtkaction.c.
+ (update_smart_separators): Also update the visibility of empty menus.
+ (update_node): When creating a new menu proxy, insert an "Empty" menu
+ item which only gets shown if the menu is empty.
+
+ * gtk/gtkaction.c (gtk_action_class_init): Document the meaning of
+ "is_important" for menu proxies.
+ (_gtk_action_sync_menu_visible): New function to sync the visibility
+ of menu proxies. Used in gtkuimanager.c.
+ (gtk_action_sync_visible): New function to sync the visibility of
+ proxies.
+
Tue Sep 30 21:43:34 2003 Kristian Rietveld <kris@gtk.org>
* gtk/gtkcombobox.c (gtk_combo_box_menu_button_press): set
diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6
index 9c226228c..55cb342d1 100644
--- a/ChangeLog.pre-2-6
+++ b/ChangeLog.pre-2-6
@@ -1,3 +1,21 @@
+2003-09-30 Matthias Clasen <maclas@gmx.de>
+
+ * tests/merge-*.ui:
+ * tests/testmerge.c: Test handling of empty menus.
+
+ * gtk/gtkuimanager.c (_gtk_menu_is_empty): New function to determine
+ whether a menu is empty. Used in gtkaction.c.
+ (update_smart_separators): Also update the visibility of empty menus.
+ (update_node): When creating a new menu proxy, insert an "Empty" menu
+ item which only gets shown if the menu is empty.
+
+ * gtk/gtkaction.c (gtk_action_class_init): Document the meaning of
+ "is_important" for menu proxies.
+ (_gtk_action_sync_menu_visible): New function to sync the visibility
+ of menu proxies. Used in gtkuimanager.c.
+ (gtk_action_sync_visible): New function to sync the visibility of
+ proxies.
+
Tue Sep 30 21:43:34 2003 Kristian Rietveld <kris@gtk.org>
* gtk/gtkcombobox.c (gtk_combo_box_menu_button_press): set
diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8
index 9c226228c..55cb342d1 100644
--- a/ChangeLog.pre-2-8
+++ b/ChangeLog.pre-2-8
@@ -1,3 +1,21 @@
+2003-09-30 Matthias Clasen <maclas@gmx.de>
+
+ * tests/merge-*.ui:
+ * tests/testmerge.c: Test handling of empty menus.
+
+ * gtk/gtkuimanager.c (_gtk_menu_is_empty): New function to determine
+ whether a menu is empty. Used in gtkaction.c.
+ (update_smart_separators): Also update the visibility of empty menus.
+ (update_node): When creating a new menu proxy, insert an "Empty" menu
+ item which only gets shown if the menu is empty.
+
+ * gtk/gtkaction.c (gtk_action_class_init): Document the meaning of
+ "is_important" for menu proxies.
+ (_gtk_action_sync_menu_visible): New function to sync the visibility
+ of menu proxies. Used in gtkuimanager.c.
+ (gtk_action_sync_visible): New function to sync the visibility of
+ proxies.
+
Tue Sep 30 21:43:34 2003 Kristian Rietveld <kris@gtk.org>
* gtk/gtkcombobox.c (gtk_combo_box_menu_button_press): set
diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog
index dbfc1ac38..68c2d53ba 100644
--- a/docs/reference/ChangeLog
+++ b/docs/reference/ChangeLog
@@ -1,5 +1,7 @@
2003-09-30 Matthias Clasen <maclas@gmx.de>
+ * gtk/tmpl/gtkuimanager.sgml: Add a section about empty menus.
+
* gdk/tmpl/keys.sgml: Small addition.
* gdk/gdk-sections.txt: Add GdkDisplayClass and GdkScreenClass.
diff --git a/docs/reference/gtk/tmpl/gtkuimanager.sgml b/docs/reference/gtk/tmpl/gtkuimanager.sgml
index 619500435..c2aa34014 100644
--- a/docs/reference/gtk/tmpl/gtkuimanager.sgml
+++ b/docs/reference/gtk/tmpl/gtkuimanager.sgml
@@ -146,15 +146,31 @@ actions even if they have no visible proxies.
<refsect2 id="Smart-Separators">
<title>Smart Separators</title>
<para>
-The separators created by #GtkUIManager are "smart", i.e. they do not show up in the
-UI unless they end up between two visible menu or tool items. Separators which are located
-at the very beginning or end of the menu or toolbar containing them, or multiple separators
-next to each other, are hidden. This is a useful feature, since the merging of UI elements
-from multiple sources can make it hard or impossible to determine in advance whether a
-separator will end up in such an unfortunate position.
+The separators created by #GtkUIManager are "smart", i.e. they do not show up
+in the UI unless they end up between two visible menu or tool items. Separators
+which are located at the very beginning or end of the menu or toolbar
+containing them, or multiple separators next to each other, are hidden. This
+is a useful feature, since the merging of UI elements from multiple sources
+can make it hard or impossible to determine in advance whether a separator
+will end up in such an unfortunate position.
+</para>
+</refsect2>
+<refsect2>
+<title>Empty Menus</title>
+<para>
+Submenus pose similar problems to separators inconnection with merging. It is
+impossible to know in advance whether they will end up empty after merging.
+#GtkUIManager offers two ways to treat empty submenus:
+<itemizedlist>
+<listitem><para>make them disappear by hiding the menu item they're attached to
+</para></listitem>
+<listitem><para>add an insensitive "Empty" item
+</para></listitem>
+</itemizedlist>
+The behaviour is chosen based on the "is_important" property of the action
+to which the submenu is associated.
</para>
</refsect2>
-
<!-- ##### SECTION See_Also ##### -->
<para>
diff --git a/gtk/gtkaction.c b/gtk/gtkaction.c
index 50bc3c4f0..33bacf738 100644
--- a/gtk/gtkaction.c
+++ b/gtk/gtkaction.c
@@ -40,6 +40,7 @@
#include "gtkmarshalers.h"
#include "gtkmenuitem.h"
#include "gtkstock.h"
+#include "gtktearoffmenuitem.h"
#include "gtktoolbutton.h"
#include "gtktoolbar.h"
@@ -88,7 +89,7 @@ enum
PROP_STOCK_ID,
PROP_IS_IMPORTANT,
PROP_SENSITIVE,
- PROP_VISIBLE,
+ PROP_VISIBLE
};
static void gtk_action_init (GtkAction *action);
@@ -216,7 +217,7 @@ gtk_action_class_init (GtkActionClass *klass)
PROP_IS_IMPORTANT,
g_param_spec_boolean ("is_important",
_("Is important"),
- _("Whether the action is considered important. When TRUE, toolitem proxies for this action show text in GTK_TOOLBAR_BOTH_HORIZ mode"),
+ _("Whether the action is considered important. When TRUE, toolitem proxies for this action show text in GTK_TOOLBAR_BOTH_HORIZ mode, and empty menu proxies for this action are not hidden."),
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
@@ -234,7 +235,6 @@ gtk_action_class_init (GtkActionClass *klass)
TRUE,
G_PARAM_READWRITE));
-
/**
* GtkAction::activate:
* @action: the #GtkAction
@@ -530,6 +530,63 @@ gtk_action_sync_property (GtkAction *action,
g_value_unset (&value);
}
+/**
+ * _gtk_action_sync_menu_visible:
+ * @action: a #GtkAction, or %NULL to determine the action from @proxy
+ * @proxy: a proxy menu item
+ * @empty: whether the submenu attached to @proxy is empty
+ *
+ * Updates the visibility of @proxy from the visibility of @action
+ * according to the following rules:
+ * <itemizedlist>
+ * <listitem><para>if @action is invisible, @proxy is too
+ * </para></listitem>
+ * <listitem><para>if @empty is %TRUE, hide @proxy unless @action is important
+ * </para></listitem>
+ * </itemizedlist>
+ *
+ * This function is used in the implementation of #GtkUIManager.
+ **/
+void
+_gtk_action_sync_menu_visible (GtkAction *action,
+ GtkWidget *proxy,
+ gboolean empty)
+{
+ gboolean visible, important;
+
+ g_return_if_fail (GTK_IS_MENU_ITEM (proxy));
+ g_return_if_fail (action == NULL || GTK_IS_ACTION (action));
+
+ if (action == NULL)
+ action = g_object_get_data (G_OBJECT (proxy), "gtk-action");
+
+ g_object_get (G_OBJECT (action),
+ "visible", &visible,
+ "is_important", &important,
+ NULL);
+
+ g_object_set (G_OBJECT (proxy),
+ "visible", visible && (important || !empty),
+ NULL);
+}
+
+gboolean _gtk_menu_is_empty (GtkWidget *menu);
+
+static void
+gtk_action_sync_visible (GtkAction *action,
+ GParamSpec *pspec,
+ GtkWidget *proxy)
+{
+ if (GTK_IS_MENU_ITEM (proxy))
+ {
+ GtkWidget *menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy));
+
+ _gtk_action_sync_menu_visible (action, proxy, _gtk_menu_is_empty (menu));
+ }
+ else
+ gtk_action_sync_property (action, pspec, proxy);
+}
+
static void
gtk_action_sync_label (GtkAction *action,
GParamSpec *pspec,
@@ -627,7 +684,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
diff --git a/gtk/gtkuimanager.c b/gtk/gtkuimanager.c
index 16796c3ca..c3040c295 100644
--- a/gtk/gtkuimanager.c
+++ b/gtk/gtkuimanager.c
@@ -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 (GMarkupParseContext *context,
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"))
@@ -1642,12 +1642,55 @@ find_toolbar_position (GNode *node,
return TRUE;
}
+/**
+ * _gtk_menu_is_empty:
+ * @menu: a #GtkMenu or %NULL
+ *
+ * Determines whether @menu is empty. A menu is considered empty if it
+ * the only visible children are tearoff menu items or "filler" menu
+ * items which were inserted to mark the menu as empty.
+ *
+ * This function is used by #GtkAction.
+ *
+ * Return value: whether @menu is empty.
+ **/
+gboolean
+_gtk_menu_is_empty (GtkWidget *menu)
+{
+ GList *children, *cur;
+
+ g_return_val_if_fail (menu == NULL || GTK_IS_MENU (menu), TRUE);
+
+ 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-item"))
+ return FALSE;
+ }
+ cur = cur->next;
+ }
+
+ return TRUE;
+}
+
enum {
SEPARATOR_MODE_SMART,
SEPARATOR_MODE_VISIBLE,
SEPARATOR_MODE_HIDDEN
};
+void _gtk_action_sync_menu_visible (GtkAction *action,
+ GtkWidget *proxy,
+ gboolean empty);
+
static void
update_smart_separators (GtkWidget *proxy)
{
@@ -1661,15 +1704,23 @@ update_smart_separators (GtkWidget *proxy)
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-item"))
+ filler = cur->data;
+
if (GTK_IS_SEPARATOR_MENU_ITEM (cur->data) ||
GTK_IS_SEPARATOR_TOOL_ITEM (cur->data))
{
@@ -1701,10 +1752,13 @@ update_smart_separators (GtkWidget *proxy)
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 +1766,15 @@ update_smart_separators (GtkWidget *proxy)
if (last)
gtk_widget_hide (GTK_WIDGET (last->data));
+
+ if (GTK_IS_MENU (parent))
+ {
+ GtkWidget *item;
+
+ item = gtk_menu_get_attach_widget (GTK_MENU (parent));
+ _gtk_action_sync_menu_visible (NULL, item, empty);
+ g_object_set (G_OBJECT (filler), "visible", empty, NULL);
+ }
}
}
@@ -1843,12 +1906,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-item",
+ 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);
}
diff --git a/tests/merge-1.ui b/tests/merge-1.ui
index a7fb9779d..b2cb72218 100644
--- a/tests/merge-1.ui
+++ b/tests/merge-1.ui
@@ -3,15 +3,20 @@
<menubar>
<menu name="FileMenu" action="FileMenuAction">
<menuitem name="Open" action="OpenAction" />
+ <menuitem name="Bold" action="BoldAction" />
</menu>
<menu name="EditMenu" action="EditMenuAction">
<menuitem name="Cut" action="CutAction" />
+ <menu name="EmptyMenu1" action="EmptyMenu1Action">
+ </menu>
+ <menu name="EmptyMenu2" action="EmptyMenu2Action">
+ </menu>
</menu>
<placeholder name="TestPlaceholder" />
</menubar>
<toolbar name="toolbar1">
<placeholder name="ToolbarPlaceholder">
- <toolitem name="nb2" action="NewAction" />
+ <toolitem name="nb2" action="New2Action" />
<separator name="Sep1" />
</placeholder>
<toolitem name="NewButton" action="NewAction" />
diff --git a/tests/merge-2.ui b/tests/merge-2.ui
index a073f917d..05a641fe9 100644
--- a/tests/merge-2.ui
+++ b/tests/merge-2.ui
@@ -8,6 +8,14 @@
<menuitem name="Quit" action="QuitAction" />
<separator name="Sep3" />
</menu>
+ <menu name="EditMenu" action="EditMenuAction">
+ <menu name="EmptyMenu1" action="EmptyMenu1Action">
+ <menuitem name="Cut" action="CutAction" />
+ </menu>
+ <menu name="EmptyMenu2" action="EmptyMenu2Action">
+ <menuitem name="Cut" action="CutAction" />
+ </menu>
+ </menu>
<menu name="HelpMenu" action="HelpMenuAction">
<menuitem name="About" action="AboutAction" />
</menu>
diff --git a/tests/testmerge.c b/tests/testmerge.c
index d74ed6fd7..5c9a59b73 100644
--- a/tests/testmerge.c
+++ b/tests/testmerge.c
@@ -123,6 +123,8 @@ static GtkActionEntry entries[] = {
{ "EditMenuAction", NULL, "_Edit" },
{ "HelpMenuAction", NULL, "_Help" },
{ "JustifyMenuAction", NULL, "_Justify" },
+ { "EmptyMenu1Action", NULL, "Empty 1" },
+ { "EmptyMenu2Action", NULL, "Empty 2" },
{ "Test", NULL, "Test" },
{ "QuitAction", GTK_STOCK_QUIT, NULL, "<control>q", "Quit", G_CALLBACK (gtk_main_quit) },
@@ -541,6 +543,10 @@ main (int argc, char **argv)
gtk_action_group_add_actions (action_group,
entries, n_entries,
NULL);
+ action = gtk_action_group_get_action (action_group, "EmptyMenu1Action");
+ g_object_set (G_OBJECT (action), "is_important", TRUE, NULL);
+ action = gtk_action_group_get_action (action_group, "EmptyMenu2Action");
+ g_object_set (G_OBJECT (action), "is_important", FALSE, NULL);
gtk_action_group_add_toggle_actions (action_group,
toggle_entries, n_toggle_entries,
NULL);