]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkuimanager.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkuimanager.c
index 7162f77e924146de8ff1275c696dc7c3337f4b26..75189aac3ecc695ec664994f695a554f1f5c795c 100644 (file)
@@ -14,9 +14,7 @@
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public
- * License along with the Gnome Library; see the file COPYING.LIB.  If not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 /*
 #include "gtkmenutoolbutton.h"
 #include "gtkseparatormenuitem.h"
 #include "gtkseparatortoolitem.h"
-#include "gtktearoffmenuitem.h"
 #include "gtktoolbar.h"
-#include "gtkuimanager.h"
 #include "gtkwindow.h"
 #include "gtkprivate.h"
 
+#undef GDK_DEPRECATED
+#undef GDK_DEPRECATED_FOR
+#define GDK_DEPRECATED
+#define GDK_DEPRECATED_FOR(f)
+
+#include "gtkuimanager.h"
+#include "deprecated/gtktearoffmenuitem.h"
+
+/**
+ * SECTION:gtkuimanager
+ * @Short_description: Constructing menus and toolbars from an XML description
+ * @Title: GtkUIManager
+ * @See_also:#GtkBuilder
+ *
+ * A #GtkUIManager constructs a user interface (menus and toolbars) from
+ * one or more UI definitions, which reference actions from one or more
+ * action groups.
+ *
+ * <refsect2 id="XML-UI">
+ * <title>UI Definitions</title>
+ * <para>
+ * The UI definitions are specified in an XML format which can be
+ * roughly described by the following DTD.
+ *
+ * <note><para>
+ * Do not confuse the GtkUIManager UI Definitions described here with
+ * the similarly named <link linkend="BUILDER-UI">GtkBuilder UI
+ * Definitions</link>.
+ * </para></note>
+ *
+ * <programlisting>
+ * <![CDATA[
+ * <!ELEMENT ui          (menubar|toolbar|popup|accelerator)* >
+ * <!ELEMENT menubar     (menuitem|separator|placeholder|menu)* >
+ * <!ELEMENT menu        (menuitem|separator|placeholder|menu)* >
+ * <!ELEMENT popup       (menuitem|separator|placeholder|menu)* >
+ * <!ELEMENT toolbar     (toolitem|separator|placeholder)* >
+ * <!ELEMENT placeholder (menuitem|toolitem|separator|placeholder|menu)* >
+ * <!ELEMENT menuitem     EMPTY >
+ * <!ELEMENT toolitem     (menu?) >
+ * <!ELEMENT separator    EMPTY >
+ * <!ELEMENT accelerator  EMPTY >
+ * <!ATTLIST menubar      name                      #IMPLIED
+ *                        action                    #IMPLIED >
+ * <!ATTLIST toolbar      name                      #IMPLIED
+ *                        action                    #IMPLIED >
+ * <!ATTLIST popup        name                      #IMPLIED
+ *                        action                    #IMPLIED
+ *                        accelerators (true|false) #IMPLIED >
+ * <!ATTLIST placeholder  name                      #IMPLIED
+ *                        action                    #IMPLIED >
+ * <!ATTLIST separator    name                      #IMPLIED
+ *                        action                    #IMPLIED
+ *                        expand       (true|false) #IMPLIED >
+ * <!ATTLIST menu         name                      #IMPLIED
+ *                        action                    #REQUIRED
+ *                        position     (top|bot)    #IMPLIED >
+ * <!ATTLIST menuitem     name                      #IMPLIED
+ *                        action                    #REQUIRED
+ *                        position     (top|bot)    #IMPLIED
+ *                        always-show-image (true|false) #IMPLIED >
+ * <!ATTLIST toolitem     name                      #IMPLIED
+ *                        action                    #REQUIRED
+ *                        position     (top|bot)    #IMPLIED >
+ * <!ATTLIST accelerator  name                      #IMPLIED
+ *                        action                    #REQUIRED >
+ * ]]>
+ * </programlisting>
+ * There are some additional restrictions beyond those specified in the
+ * DTD, e.g. every toolitem must have a toolbar in its anchestry and
+ * every menuitem must have a menubar or popup in its anchestry. Since
+ * a #GMarkup parser is used to parse the UI description, it must not only
+ * be valid XML, but valid #GMarkup.
+ *
+ * If a name is not specified, it defaults to the action. If an action is
+ * not specified either, the element name is used. The name and action
+ * attributes must not contain '/' characters after parsing (since that
+ * would mess up path lookup) and must be usable as XML attributes when
+ * enclosed in doublequotes, thus they must not '"' characters or references
+ * to the &quot; entity.
+ *
+ * <example>
+ * <title>A UI definition</title>
+ * <programlisting><![CDATA[
+ * <ui>
+ *   <menubar>
+ *     <menu name="FileMenu" action="FileMenuAction">
+ *       <menuitem name="New" action="New2Action" />
+ *       <placeholder name="FileMenuAdditions" />
+ *     </menu>
+ *     <menu name="JustifyMenu" action="JustifyMenuAction">
+ *       <menuitem name="Left" action="justify-left"/>
+ *       <menuitem name="Centre" action="justify-center"/>
+ *       <menuitem name="Right" action="justify-right"/>
+ *       <menuitem name="Fill" action="justify-fill"/>
+ *     </menu>
+ *   </menubar>
+ *   <toolbar action="toolbar1">
+ *     <placeholder name="JustifyToolItems">
+ *       <separator/>
+ *       <toolitem name="Left" action="justify-left"/>
+ *       <toolitem name="Centre" action="justify-center"/>
+ *       <toolitem name="Right" action="justify-right"/>
+ *       <toolitem name="Fill" action="justify-fill"/>
+ *       <separator/>
+ *     </placeholder>
+ *   </toolbar>
+ * </ui>
+ * ]]></programlisting>
+ * </example>
+ *
+ * The constructed widget hierarchy is very similar to the element tree
+ * of the XML, with the exception that placeholders are merged into their
+ * parents. The correspondence of XML elements to widgets should be
+ * almost obvious:
+ * <variablelist>
+ * <varlistentry>
+ * <term>menubar</term>
+ * <listitem><para>a #GtkMenuBar</para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>toolbar</term>
+ * <listitem><para>a #GtkToolbar</para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>popup</term>
+ * <listitem><para>a toplevel #GtkMenu</para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>menu</term>
+ * <listitem><para>a #GtkMenu attached to a menuitem</para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>menuitem</term>
+ * <listitem><para>a #GtkMenuItem subclass, the exact type depends on the
+ * action</para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>toolitem</term>
+ * <listitem><para>a #GtkToolItem subclass, the exact type depends on the
+ * action. Note that toolitem elements may contain a menu element, but only
+ * if their associated action specifies a #GtkMenuToolButton as proxy.</para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>separator</term>
+ * <listitem><para>a #GtkSeparatorMenuItem or
+ * #GtkSeparatorToolItem</para></listitem>
+ * </varlistentry>
+ * <varlistentry>
+ * <term>accelerator</term>
+ * <listitem><para>a keyboard accelerator</para></listitem>
+ * </varlistentry>
+ * </variablelist>
+ *
+ * The "position" attribute determines where a constructed widget is positioned
+ * wrt. to its siblings in the partially constructed tree. If it is
+ * "top", the widget is prepended, otherwise it is appended.
+ * </para>
+ * </refsect2>
+ * <refsect2 id="UI-Merging">
+ * <title>UI Merging</title>
+ * <para>
+ * The most remarkable feature of #GtkUIManager is that it can overlay a set
+ * of menuitems and toolitems over another one, and demerge them later.
+ *
+ * Merging is done based on the names of the XML elements. Each element is
+ * identified by a path which consists of the names of its anchestors, separated
+ * by slashes. For example, the menuitem named "Left" in the example above
+ * has the path <literal>/ui/menubar/JustifyMenu/Left</literal> and the
+ * toolitem with the same name has path
+ * <literal>/ui/toolbar1/JustifyToolItems/Left</literal>.
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Accelerators</title>
+ * <para>
+ * Every action has an accelerator path. Accelerators are installed together with
+ * menuitem proxies, but they can also be explicitly added with &lt;accelerator&gt;
+ * elements in the UI definition. This makes it possible to have accelerators for
+ * actions even if they have no visible proxies.
+ * </para>
+ * </refsect2>
+ * <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.
+ *
+ * For separators in toolbars, you can set <literal>expand="true"</literal> to
+ * turn them from a small, visible separator to an expanding, invisible one.
+ * Toolitems following an expanding separator are effectively right-aligned.
+ * </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 "hide_if_empty" property of the action
+ * to which the submenu is associated.
+ * </para>
+ * </refsect2>
+ * <refsect2 id="GtkUIManager-BUILDER-UI">
+ * <title>GtkUIManager as GtkBuildable</title>
+ * <para>
+ * The GtkUIManager implementation of the GtkBuildable interface accepts
+ * GtkActionGroup objects as &lt;child&gt; elements in UI definitions.
+ *
+ * A GtkUIManager UI definition as described above can be embedded in
+ * an GtkUIManager &lt;object&gt; element in a GtkBuilder UI definition.
+ *
+ * The widgets that are constructed by a GtkUIManager can be embedded in
+ * other parts of the constructed user interface with the help of the
+ * "constructor" attribute. See the example below.
+ *
+ * <example>
+ * <title>An embedded GtkUIManager UI definition</title>
+ * <programlisting><![CDATA[
+ * <object class="GtkUIManager" id="uiman">
+ *   <child>
+ *     <object class="GtkActionGroup" id="actiongroup">
+ *       <child>
+ *         <object class="GtkAction" id="file">
+ *           <property name="label">_File</property>
+ *         </object>
+ *       </child>
+ *     </object>
+ *   </child>
+ *   <ui>
+ *     <menubar name="menubar1">
+ *       <menu action="file">
+ *       </menu>
+ *     </menubar>
+ *   </ui>
+ * </object>
+ * <object class="GtkWindow" id="main-window">
+ *   <child>
+ *     <object class="GtkMenuBar" id="menubar1" constructor="uiman"/>
+ *   </child>
+ * </object>
+ * ]]></programlisting>
+ * </example>
+ * </para>
+ * </refsect2>
+ */
+
+
 #undef DEBUG_UI_MANAGER
 
 typedef enum
@@ -168,6 +425,8 @@ static void     gtk_ui_manager_buildable_custom_tag_end (GtkBuildable        *buildab
                                                         GObject         *child,
                                                         const gchar     *tagname,
                                                         gpointer        *data);
+static void gtk_ui_manager_do_set_add_tearoffs          (GtkUIManager *manager,
+                                                         gboolean      add_tearoffs);
 
 
 
@@ -218,6 +477,9 @@ gtk_ui_manager_class_init (GtkUIManagerClass *klass)
    * menus never have tearoff menu items.   
    *
    * Since: 2.4
+   *
+   * Deprecated: 3.4: Tearoff menus are deprecated and should not
+   *     be used in newly written code.
    */
   g_object_class_install_property (gobject_class,
                                    PROP_ADD_TEAROFFS,
@@ -225,7 +487,7 @@ gtk_ui_manager_class_init (GtkUIManagerClass *klass)
                                                         P_("Add tearoffs to menus"),
                                                         P_("Whether tearoff menu items should be added to menus"),
                                                          FALSE,
-                                                        GTK_PARAM_READWRITE));
+                                                        GTK_PARAM_READWRITE | G_PARAM_DEPRECATED));
 
   g_object_class_install_property (gobject_class,
                                   PROP_UI,
@@ -421,9 +683,7 @@ gtk_ui_manager_finalize (GObject *object)
   g_node_destroy (manager->private_data->root_node);
   manager->private_data->root_node = NULL;
   
-  g_list_foreach (manager->private_data->action_groups,
-                  (GFunc) g_object_unref, NULL);
-  g_list_free (manager->private_data->action_groups);
+  g_list_free_full (manager->private_data->action_groups, g_object_unref);
   manager->private_data->action_groups = NULL;
 
   g_object_unref (manager->private_data->accel_group);
@@ -454,7 +714,6 @@ gtk_ui_manager_buildable_add_child (GtkBuildable  *buildable,
 
   pos = g_list_length (manager->private_data->action_groups);
 
-  g_object_ref (child);
   gtk_ui_manager_insert_action_group (manager,
                                      GTK_ACTION_GROUP (child),
                                      pos);
@@ -518,7 +777,7 @@ gtk_ui_manager_set_property (GObject         *object,
   switch (prop_id)
     {
     case PROP_ADD_TEAROFFS:
-      gtk_ui_manager_set_add_tearoffs (manager, g_value_get_boolean (value));
+      gtk_ui_manager_do_set_add_tearoffs (manager, g_value_get_boolean (value));
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -611,6 +870,9 @@ gtk_ui_manager_new (void)
  * Return value: whether tearoff menu items are added
  *
  * Since: 2.4
+ *
+ * Deprecated: 3.4: Tearoff menus are deprecated and should not
+ *     be used in newly written code.
  **/
 gboolean 
 gtk_ui_manager_get_add_tearoffs (GtkUIManager *manager)
@@ -633,19 +895,29 @@ gtk_ui_manager_get_add_tearoffs (GtkUIManager *manager)
  * menus never have tearoff menu items.
  *
  * Since: 2.4
+ *
+ * Deprecated: 3.4: Tearoff menus are deprecated and should not
+ *     be used in newly written code.
  **/
-void 
+void
 gtk_ui_manager_set_add_tearoffs (GtkUIManager *manager,
-                                gboolean      add_tearoffs)
+                                 gboolean      add_tearoffs)
 {
   g_return_if_fail (GTK_IS_UI_MANAGER (manager));
 
+  gtk_ui_manager_do_set_add_tearoffs (manager, add_tearoffs);
+}
+
+static void
+gtk_ui_manager_do_set_add_tearoffs (GtkUIManager *manager,
+                                    gboolean      add_tearoffs)
+{
   add_tearoffs = add_tearoffs != FALSE;
 
   if (add_tearoffs != manager->private_data->add_tearoffs)
     {
       manager->private_data->add_tearoffs = add_tearoffs;
-      
+
       dirty_all_nodes (manager);
 
       g_object_notify (G_OBJECT (manager), "add-tearoffs");
@@ -696,6 +968,10 @@ cb_proxy_post_activate (GtkActionGroup *group,
  * with @manager. Actions in earlier groups hide actions with the same 
  * name in later groups. 
  *
+ * If @pos is larger than the number of action groups in @manager, or
+ * negative, @action_group will be inserted at the end of the internal
+ * list.
+ *
  * Since: 2.4
  **/
 void
@@ -1119,18 +1395,16 @@ static gboolean
 free_node (GNode *node)
 {
   Node *info = NODE_INFO (node);
-  
-  g_list_foreach (info->uifiles, (GFunc) node_ui_reference_free, NULL);
-  g_list_free (info->uifiles);
 
-  if (info->action)
-    g_object_unref (info->action);
-  if (info->proxy)
-    g_object_unref (info->proxy);
-  if (info->extra)
-    g_object_unref (info->extra);
-  g_free (info->name);
+  g_list_free_full (info->uifiles, node_ui_reference_free);
+  info->uifiles = NULL;
+
+  g_clear_object (&info->action);
+  g_clear_object (&info->proxy);
+  g_clear_object (&info->extra);
+  g_clear_pointer (&info->name, g_free);
   g_slice_free (Node, info);
+  node->data = NULL;
 
   return FALSE;
 }
@@ -1700,7 +1974,7 @@ gtk_ui_manager_add_ui_from_string (GtkUIManager *manager,
 /**
  * gtk_ui_manager_add_ui_from_file:
  * @manager: a #GtkUIManager object
- * @filename: the name of the file to parse 
+ * @filename: (type filename): the name of the file to parse 
  * @error: return location for an error
  * 
  * Parses a file containing a <link linkend="XML-UI">UI definition</link> and 
@@ -1732,6 +2006,41 @@ gtk_ui_manager_add_ui_from_file (GtkUIManager *manager,
   return res;
 }
 
+/**
+ * gtk_ui_manager_add_ui_from_resource:
+ * @manager: a #GtkUIManager object
+ * @resource_path: the resource path of the file to parse
+ * @error: return location for an error
+ *
+ * Parses a resource file containing a <link linkend="XML-UI">UI definition</link> and
+ * merges it with the current contents of @manager.
+ *
+ * Return value: The merge id for the merged UI. The merge id can be used
+ *   to unmerge the UI with gtk_ui_manager_remove_ui(). If an error occurred,
+ *   the return value is 0.
+ *
+ * Since: 3.4
+ **/
+guint
+gtk_ui_manager_add_ui_from_resource (GtkUIManager *manager,
+                                    const gchar  *resource_path,
+                                    GError      **error)
+{
+  GBytes *data;
+  guint res;
+
+  g_return_val_if_fail (GTK_IS_UI_MANAGER (manager), 0);
+
+  data = g_resources_lookup_data (resource_path, 0, error);
+  if (data == NULL)
+    return 0;
+
+  res = add_ui_from_string (manager, g_bytes_get_data (data, NULL), g_bytes_get_size (data), FALSE, error);
+  g_bytes_unref (data);
+
+  return res;
+}
+
 /**
  * gtk_ui_manager_add_ui:
  * @manager: a #GtkUIManager
@@ -2106,50 +2415,6 @@ find_toolbar_position (GNode      *node,
   return TRUE;
 }
 
-/**
- * _gtk_menu_is_empty:
- * @menu: (allow-none): 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;
-  gboolean result = TRUE;
-
-  g_return_val_if_fail (menu == NULL || GTK_IS_MENU (menu), TRUE);
-
-  if (!menu)
-    return FALSE;
-
-  children = gtk_container_get_children (GTK_CONTAINER (menu));
-
-  cur = children;
-  while (cur) 
-    {
-      if (gtk_widget_get_visible (cur->data))
-       {
-         if (!GTK_IS_TEAROFF_MENU_ITEM (cur->data) &&
-             !g_object_get_data (cur->data, "gtk-empty-menu-item"))
-            {
-             result = FALSE;
-              break;
-            }
-       }
-      cur = cur->next;
-    }
-  g_list_free (children);
-
-  return result;
-}
-
 enum {
   SEPARATOR_MODE_SMART,
   SEPARATOR_MODE_VISIBLE,