]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkmenu.c
Practically everything changed.
[~andy/gtk] / gtk / gtkmenu.c
index 354ab21875f2259bcdf2df8d86656ee4800cab78..d9abe030c0a47480b60fe11307cea29471157cbf 100644 (file)
@@ -1,4 +1,4 @@
-/* GTK - The GIMP Toolkit
+/* GTK - The GTK+ Toolkit
  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
  *
  * This library is free software; you can redistribute it and/or
@@ -25,7 +25,7 @@
  */
 
 #define GTK_MENU_INTERNALS
-#include <config.h>
+#include "config.h"
 #include <string.h> /* memset */
 #include "gdk/gdkkeysyms.h"
 #include "gtkaccellabel.h"
@@ -121,8 +121,13 @@ enum {
 
 enum {
   PROP_0,
+  PROP_ACTIVE,
+  PROP_ACCEL_GROUP,
+  PROP_ACCEL_PATH,
+  PROP_ATTACH_WIDGET,
   PROP_TEAROFF_STATE,
-  PROP_TEAROFF_TITLE
+  PROP_TEAROFF_TITLE,
+  PROP_MONITOR
 };
 
 enum {
@@ -152,7 +157,6 @@ static void     gtk_menu_get_child_property(GtkContainer     *container,
                                             GValue           *value,
                                             GParamSpec       *pspec);
 static void     gtk_menu_destroy           (GtkObject        *object);
-static void     gtk_menu_finalize          (GObject          *object);
 static void     gtk_menu_realize           (GtkWidget        *widget);
 static void     gtk_menu_unrealize         (GtkWidget        *widget);
 static void     gtk_menu_size_request      (GtkWidget        *widget,
@@ -265,6 +269,12 @@ menu_queue_resize (GtkMenu *menu)
   gtk_widget_queue_resize (GTK_WIDGET (menu));
 }
 
+static void
+attach_info_free (AttachInfo *info)
+{
+  g_slice_free (AttachInfo, info);
+}
+
 static AttachInfo *
 get_attach_info (GtkWidget *child)
 {
@@ -273,8 +283,9 @@ get_attach_info (GtkWidget *child)
 
   if (!ai)
     {
-      ai = g_new0 (AttachInfo, 1);
-      g_object_set_data_full (object, I_(ATTACH_INFO_KEY), ai, g_free);
+      ai = g_slice_new0 (AttachInfo);
+      g_object_set_data_full (object, I_(ATTACH_INFO_KEY), ai,
+                              (GDestroyNotify) attach_info_free);
     }
 
   return ai;
@@ -431,7 +442,6 @@ gtk_menu_class_init (GtkMenuClass *class)
   GtkMenuShellClass *menu_shell_class = GTK_MENU_SHELL_CLASS (class);
   GtkBindingSet *binding_set;
   
-  gobject_class->finalize = gtk_menu_finalize;
   gobject_class->set_property = gtk_menu_set_property;
   gobject_class->get_property = gtk_menu_get_property;
 
@@ -477,13 +487,73 @@ gtk_menu_class_init (GtkMenuClass *class)
                             _gtk_marshal_VOID__ENUM,
                             G_TYPE_NONE, 1,
                             GTK_TYPE_SCROLL_TYPE);
-  
+
+  /**
+   * GtkMenu:active:
+   *
+   * The currently selected menu item.
+   *
+   * Since: 2.14
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_ACTIVE,
+                                   g_param_spec_uint ("active",
+                                                     P_("Active"),
+                                                     P_("The currently selected menu item"),
+                                                     0, G_MAXUINT, 0,
+                                                     GTK_PARAM_READWRITE));
+
+  /**
+   * GtkMenu:accel-group:
+   *
+   * The accel group holding accelerators for the menu.
+   *
+   * Since: 2.14
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_ACCEL_GROUP,
+                                   g_param_spec_object ("accel-group",
+                                                       P_("Accel Group"),
+                                                       P_("The accel group holding accelerators for the menu"),
+                                                       GTK_TYPE_ACCEL_GROUP,
+                                                       GTK_PARAM_READWRITE));
+
+  /**
+   * GtkMenu:accel-path:
+   *
+   * An accel path used to conveniently construct accel paths of child items.
+   *
+   * Since: 2.14
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_ACCEL_PATH,
+                                   g_param_spec_string ("accel-path",
+                                                       P_("Accel Path"),
+                                                       P_("An accel path used to conveniently construct accel paths of child items"),
+                                                       NULL,
+                                                       GTK_PARAM_READWRITE));
+
+  /**
+   * GtkMenu:attach-widget:
+   *
+   * The widget the menu is attached to.
+   *
+   * Since: 2.14
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_ACCEL_PATH,
+                                   g_param_spec_string ("attach-widget",
+                                                       P_("Attach Widget"),
+                                                       P_("The widget the menu is attached to"),
+                                                       NULL,
+                                                       GTK_PARAM_READWRITE));
+
   g_object_class_install_property (gobject_class,
                                    PROP_TEAROFF_TITLE,
                                    g_param_spec_string ("tearoff-title",
                                                         P_("Tearoff Title"),
                                                         P_("A title that may be displayed by the window manager when this menu is torn-off"),
-                                                        "",
+                                                        NULL,
                                                         GTK_PARAM_READWRITE));
 
   /**
@@ -501,6 +571,21 @@ gtk_menu_class_init (GtkMenuClass *class)
                                                         FALSE,
                                                         GTK_PARAM_READWRITE));
 
+  /**
+   * GtkMenu:monitor:
+   *
+   * The monitor the menu will be popped up on.
+   *
+   * Since: 2.14
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_MONITOR,
+                                   g_param_spec_int ("monitor",
+                                                    P_("Monitor"),
+                                                    P_("The monitor the menu will be popped up on"),
+                                                    -1, G_MAXINT, -1,
+                                                    GTK_PARAM_READWRITE));
+
   gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_int ("vertical-padding",
                                                             P_("Vertical Padding"),
@@ -697,12 +782,27 @@ gtk_menu_set_property (GObject      *object,
   
   switch (prop_id)
     {
+    case PROP_ACTIVE:
+      gtk_menu_set_active (menu, g_value_get_uint (value));
+      break;
+    case PROP_ACCEL_GROUP:
+      gtk_menu_set_accel_group (menu, g_value_get_object (value));
+      break;
+    case PROP_ACCEL_PATH:
+      gtk_menu_set_accel_path (menu, g_value_get_string (value));
+      break;
+    case PROP_ATTACH_WIDGET:
+      gtk_menu_attach (menu, g_value_get_object (value), 0, 0, 0, 0);
+      break;
     case PROP_TEAROFF_STATE:
       gtk_menu_set_tearoff_state (menu, g_value_get_boolean (value));
       break;
     case PROP_TEAROFF_TITLE:
       gtk_menu_set_title (menu, g_value_get_string (value));
-      break;     
+      break;
+    case PROP_MONITOR:
+      gtk_menu_set_monitor (menu, g_value_get_int (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -721,12 +821,27 @@ gtk_menu_get_property (GObject     *object,
   
   switch (prop_id)
     {
+    case PROP_ACTIVE:
+      g_value_set_uint (value, g_list_index (GTK_MENU_SHELL (menu)->children, gtk_menu_get_active (menu)));
+      break;
+    case PROP_ACCEL_GROUP:
+      g_value_set_object (value, gtk_menu_get_accel_group (menu));
+      break;
+    case PROP_ACCEL_PATH:
+      g_value_set_string (value, gtk_menu_get_accel_path (menu));
+      break;
+    case PROP_ATTACH_WIDGET:
+      g_value_set_object (value, gtk_menu_get_attach_widget (menu));
+      break;
     case PROP_TEAROFF_STATE:
       g_value_set_boolean (value, gtk_menu_get_tearoff_state (menu));
       break;
     case PROP_TEAROFF_TITLE:
       g_value_set_string (value, gtk_menu_get_title (menu));
       break;
+    case PROP_MONITOR:
+      g_value_set_int (value, gtk_menu_get_monitor (menu));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -902,14 +1017,10 @@ gtk_menu_init (GtkMenu *menu)
 static void
 gtk_menu_destroy (GtkObject *object)
 {
-  GtkMenu *menu;
+  GtkMenu *menu = GTK_MENU (object);
   GtkMenuAttachData *data;
   GtkMenuPrivate *priv; 
 
-  g_return_if_fail (GTK_IS_MENU (object));
-
-  menu = GTK_MENU (object);
-
   gtk_menu_remove_scroll_timeout (menu);
   
   data = g_object_get_data (G_OBJECT (object), attach_data_key);
@@ -960,16 +1071,6 @@ gtk_menu_destroy (GtkObject *object)
   GTK_OBJECT_CLASS (gtk_menu_parent_class)->destroy (object);
 }
 
-static void
-gtk_menu_finalize (GObject *object)
-{
-  GtkMenu *menu = GTK_MENU (object);
-
-  g_free (menu->accel_path);
-  
-  G_OBJECT_CLASS (gtk_menu_parent_class)->finalize (object);
-}
-
 static void
 menu_change_screen (GtkMenu   *menu,
                    GdkScreen *new_screen)
@@ -1028,7 +1129,7 @@ gtk_menu_attach_to_widget (GtkMenu               *menu,
   
   g_object_ref_sink (menu);
   
-  data = g_new (GtkMenuAttachData, 1);
+  data = g_slice_new (GtkMenuAttachData);
   data->attach_widget = attach_widget;
   
   g_signal_connect (attach_widget, "screen_changed",
@@ -1042,8 +1143,9 @@ gtk_menu_attach_to_widget (GtkMenu               *menu,
     {
       list = g_list_prepend (list, menu);
     }
-  g_object_set_data_full (G_OBJECT (attach_widget), I_(ATTACHED_MENUS), list, (GtkDestroyNotify) g_list_free);
-  
+  g_object_set_data_full (G_OBJECT (attach_widget), I_(ATTACHED_MENUS), list,
+                          (GDestroyNotify) g_list_free);
+
   if (GTK_WIDGET_STATE (menu) != GTK_STATE_NORMAL)
     gtk_widget_set_state (GTK_WIDGET (menu), GTK_STATE_NORMAL);
   
@@ -1095,14 +1197,15 @@ gtk_menu_detach (GtkMenu *menu)
   list = g_object_steal_data (G_OBJECT (data->attach_widget), ATTACHED_MENUS);
   list = g_list_remove (list, menu);
   if (list)
-    g_object_set_data_full (G_OBJECT (data->attach_widget), I_(ATTACHED_MENUS), list, (GtkDestroyNotify) g_list_free);
+    g_object_set_data_full (G_OBJECT (data->attach_widget), I_(ATTACHED_MENUS), list,
+                            (GDestroyNotify) g_list_free);
   else
     g_object_set_data (G_OBJECT (data->attach_widget), I_(ATTACHED_MENUS), NULL);
   
   if (GTK_WIDGET_REALIZED (menu))
     gtk_widget_unrealize (GTK_WIDGET (menu));
   
-  g_free (data);
+  g_slice_free (GtkMenuAttachData, data);
   
   /* Fallback title for menu comes from attach widget */
   gtk_menu_update_title (menu);
@@ -1114,13 +1217,10 @@ static void
 gtk_menu_remove (GtkContainer *container,
                 GtkWidget    *widget)
 {
-  GtkMenu *menu;
+  GtkMenu *menu = GTK_MENU (container);
 
-  g_return_if_fail (GTK_IS_MENU (container));
   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
 
-  menu = GTK_MENU (container);
-
   /* Clear out old_active_menu_item if it matches the item we are removing
    */
   if (menu->old_active_menu_item == widget)
@@ -1393,7 +1493,7 @@ gtk_menu_popup (GtkMenu               *menu,
     }
 
   /* Set transient for to get the right window group and parent relationship */
-  if (parent_toplevel && GTK_IS_WINDOW (parent_toplevel))
+  if (GTK_IS_WINDOW (parent_toplevel))
     gtk_window_set_transient_for (GTK_WINDOW (menu->toplevel),
                                  GTK_WINDOW (parent_toplevel));
   
@@ -1449,13 +1549,6 @@ gtk_menu_popup (GtkMenu              *menu,
    */
   gtk_widget_show (menu->toplevel);
 
-  /* flush the X event queue for the popup to become realized and
-   * mapped, since grabbing requires a mapped window. (this only works
-   * for popups, regular windows need gtk_widget_show_now() to sync
-   * with window manager interaction).
-   */
-  gdk_flush ();
-
   if (xgrab_shell == widget)
     popup_grab_on_window (widget->window, activate_time, grab_keyboard); /* Should always succeed */
   gtk_grab_add (GTK_WIDGET (menu));
@@ -1653,6 +1746,10 @@ gtk_menu_real_can_activate_accel (GtkWidget *widget,
  * Assigning accel paths to menu items then enables the user to change
  * their accelerators at runtime. More details about accelerator paths
  * and their default setups can be found at gtk_accel_map_add_entry().
+ * 
+ * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
+ * pass a static string, you can save some memory by interning it first with 
+ * g_intern_static_string().
  */
 void
 gtk_menu_set_accel_path (GtkMenu     *menu,
@@ -1662,12 +1759,28 @@ gtk_menu_set_accel_path (GtkMenu     *menu,
   if (accel_path)
     g_return_if_fail (accel_path[0] == '<' && strchr (accel_path, '/')); /* simplistic check */
 
-  g_free (menu->accel_path);
-  menu->accel_path = g_strdup (accel_path);
+  /* FIXME: accel_path should be defined as const gchar* */
+  menu->accel_path = (gchar*)g_intern_string (accel_path);
   if (menu->accel_path)
     _gtk_menu_refresh_accel_paths (menu, FALSE);
 }
 
+/**
+ * gtk_menu_get_accel_path
+ * @menu: a valid #GtkMenu
+ *
+ * Retrieves the accelerator path set on the menu.
+ *
+ * Since: 2.14
+ */
+const gchar*
+gtk_menu_get_accel_path (GtkMenu     *menu)
+{
+  g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
+
+  return menu->accel_path;
+}
+
 typedef struct {
   GtkMenu *menu;
   gboolean group_changed;
@@ -1826,7 +1939,7 @@ gtk_menu_set_tearoff_state (GtkMenu  *menu,
            {
              GtkWidget *toplevel;
 
-             menu->tearoff_window = gtk_widget_new (GTK_TYPE_WINDOW,
+             menu->tearoff_window = g_object_new (GTK_TYPE_WINDOW,
                                                     "type", GTK_WINDOW_TOPLEVEL,
                                                     "screen", gtk_widget_get_screen (menu->toplevel),
                                                     "app-paintable", TRUE,
@@ -2176,11 +2289,7 @@ menu_grab_transfer_window_destroy (GtkMenu *menu)
 static void
 gtk_menu_unrealize (GtkWidget *widget)
 {
-  GtkMenu *menu;
-
-  g_return_if_fail (GTK_IS_MENU (widget));
-
-  menu = GTK_MENU (widget);
+  GtkMenu *menu = GTK_MENU (widget);
 
   menu_grab_transfer_window_destroy (menu);
 
@@ -2523,7 +2632,7 @@ gtk_menu_paint (GtkWidget      *widget,
                         widget->window,
                         priv->upper_arrow_state,
                          GTK_SHADOW_OUT,
-                        &event->area, widget, "menu",
+                        &event->area, widget, "menu_scroll_arrow_up",
                          upper.x,
                          upper.y,
                          upper.width,
@@ -2547,7 +2656,7 @@ gtk_menu_paint (GtkWidget      *widget,
                         widget->window,
                         priv->lower_arrow_state,
                          GTK_SHADOW_OUT,
-                        &event->area, widget, "menu",
+                        &event->area, widget, "menu_scroll_arrow_down",
                          lower.x,
                          lower.y,
                          lower.width,
@@ -4936,6 +5045,28 @@ gtk_menu_set_monitor (GtkMenu *menu,
   priv->monitor_num = monitor_num;
 }
 
+/**
+ * gtk_menu_get_monitor:
+ * @menu: a #GtkMenu
+ *
+ * Retrieves the number of the monitor on which to show the menu.
+ *
+ * Returns: the number of the monitor on which the menu should
+ *    be popped up or -1
+ *
+ * Since: 2.14
+ **/
+gint
+gtk_menu_get_monitor (GtkMenu *menu)
+{
+  GtkMenuPrivate *priv;
+  g_return_val_if_fail (GTK_IS_MENU (menu), -1);
+
+  priv = gtk_menu_get_private (menu);
+  
+  return priv->monitor_num;
+}
+
 /**
  * gtk_menu_get_for_attach_widget:
  * @widget: a #GtkWidget