]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtktearoffmenuitem.c
Don't use *DISABLE_DEPRECATED guards
[~andy/gtk] / gtk / gtktearoffmenuitem.c
index fb76f1010b1a4d7997dea7eeea2dedc6973bef8a..340062c83a14dd7bffd13858b6858fd3b5e23ea1 100644 (file)
  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
  *
  * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
+ * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
-#include "gtkmenu.h"
-#include "gtksignal.h"
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gtkmenuprivate.h"
+#include "gtkmenuitemprivate.h"
 #include "gtktearoffmenuitem.h"
+#include "gtkintl.h"
+
+
+/**
+ * SECTION:gtktearoffmenuitem
+ * @Short_description: A menu item used to tear off and reattach its menu
+ * @Title: GtkTearoffMenuItem
+ * @See_also: #GtkMenu
+ *
+ * A #GtkTearoffMenuItem is a special #GtkMenuItem which is used to
+ * tear off and reattach its menu.
+ *
+ * When its menu is shown normally, the #GtkTearoffMenuItem is drawn as a
+ * dotted line indicating that the menu can be torn off.  Activating it
+ * causes its menu to be torn off and displayed in its own window
+ * as a tearoff menu.
+ *
+ * When its menu is shown as a tearoff menu, the #GtkTearoffMenuItem is drawn
+ * as a dotted line which has a left pointing arrow graphic indicating that
+ * the tearoff menu can be reattached.  Activating it will erase the tearoff
+ * menu window.
+ *
+ * <note>#GtkTearoffMenuItem is deprecated and should not be used in newly
+ * written code. Menus are not meant to be torn around.</note>
+ */
+
 
 #define ARROW_SIZE 10
 #define TEAR_LENGTH 5
 #define BORDER_SPACING  3
 
-static void gtk_tearoff_menu_item_class_init (GtkTearoffMenuItemClass *klass);
-static void gtk_tearoff_menu_item_init       (GtkTearoffMenuItem      *tearoff_menu_item);
-static void gtk_tearoff_menu_item_size_request (GtkWidget             *widget,
-                                               GtkRequisition        *requisition);
-static void gtk_tearoff_menu_item_draw       (GtkWidget             *widget,
-                                             GdkRectangle          *area);
-static gint gtk_tearoff_menu_item_expose     (GtkWidget             *widget,
-                                             GdkEventExpose        *event);
-static void gtk_tearoff_menu_item_activate   (GtkMenuItem           *menu_item);
-static gint gtk_tearoff_menu_item_delete_cb  (GtkMenuItem           *menu_item,
-                                             GdkEventAny           *event);
-
-GtkType
-gtk_tearoff_menu_item_get_type (void)
+struct _GtkTearoffMenuItemPrivate
 {
-  static GtkType tearoff_menu_item_type = 0;
-
-  if (!tearoff_menu_item_type)
-    {
-      GtkTypeInfo tearoff_menu_item_info =
-      {
-        "GtkTearoffMenuItem",
-        sizeof (GtkTearoffMenuItem),
-        sizeof (GtkTearoffMenuItemClass),
-        (GtkClassInitFunc) gtk_tearoff_menu_item_class_init,
-        (GtkObjectInitFunc) gtk_tearoff_menu_item_init,
-        /* reserved_1 */ NULL,
-        /* reserved_2 */ NULL,
-        (GtkClassInitFunc) NULL,
-      };
-
-      tearoff_menu_item_type = gtk_type_unique (gtk_menu_item_get_type (), &tearoff_menu_item_info);
-    }
-
-  return tearoff_menu_item_type;
-}
-
+  guint torn_off : 1;
+};
+
+static void gtk_tearoff_menu_item_get_preferred_width  (GtkWidget      *widget,
+                                                        gint           *minimum,
+                                                        gint           *natural);
+static void gtk_tearoff_menu_item_get_preferred_height (GtkWidget      *widget,
+                                                        gint           *minimum,
+                                                        gint           *natural);
+static gboolean gtk_tearoff_menu_item_draw             (GtkWidget      *widget,
+                                                        cairo_t        *cr);
+static void gtk_tearoff_menu_item_activate             (GtkMenuItem    *menu_item);
+static void gtk_tearoff_menu_item_parent_set           (GtkWidget      *widget,
+                                                        GtkWidget      *previous);
+
+G_DEFINE_TYPE (GtkTearoffMenuItem, gtk_tearoff_menu_item, GTK_TYPE_MENU_ITEM)
+
+/**
+ * gtk_tearoff_menu_item_new:
+ *
+ * Creates a new #GtkTearoffMenuItem.
+ *
+ * Returns: a new #GtkTearoffMenuItem.
+ *
+ * Deprecated: 3.4: #GtkTearoffMenuItem is deprecated and should not be
+ *     used in newly written code.
+ */
 GtkWidget*
 gtk_tearoff_menu_item_new (void)
 {
-  return GTK_WIDGET (gtk_type_new (gtk_tearoff_menu_item_get_type ()));
+  return g_object_new (GTK_TYPE_TEAROFF_MENU_ITEM, NULL);
 }
 
 static void
 gtk_tearoff_menu_item_class_init (GtkTearoffMenuItemClass *klass)
 {
-  GtkObjectClass *object_class;
   GtkWidgetClass *widget_class;
   GtkMenuItemClass *menu_item_class;
 
-  object_class = (GtkObjectClass*) klass;
   widget_class = (GtkWidgetClass*) klass;
   menu_item_class = (GtkMenuItemClass*) klass;
 
   widget_class->draw = gtk_tearoff_menu_item_draw;
-  widget_class->expose_event = gtk_tearoff_menu_item_expose;
-  widget_class->size_request = gtk_tearoff_menu_item_size_request;
+  widget_class->get_preferred_width = gtk_tearoff_menu_item_get_preferred_width;
+  widget_class->get_preferred_height = gtk_tearoff_menu_item_get_preferred_height;
+  widget_class->parent_set = gtk_tearoff_menu_item_parent_set;
+
+  gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_TEAR_OFF_MENU_ITEM);
 
   menu_item_class->activate = gtk_tearoff_menu_item_activate;
+
+  g_type_class_add_private (klass, sizeof (GtkTearoffMenuItemPrivate));
 }
 
 static void
 gtk_tearoff_menu_item_init (GtkTearoffMenuItem *tearoff_menu_item)
 {
-  tearoff_menu_item->torn_off = FALSE;
+  GtkTearoffMenuItemPrivate *priv;
+
+  tearoff_menu_item->priv = G_TYPE_INSTANCE_GET_PRIVATE (tearoff_menu_item,
+                                                         GTK_TYPE_TEAROFF_MENU_ITEM,
+                                                         GtkTearoffMenuItemPrivate);
+  priv = tearoff_menu_item->priv;
+
+  priv->torn_off = FALSE;
 }
 
 static void
-gtk_tearoff_menu_item_size_request (GtkWidget      *widget,
-                                   GtkRequisition *requisition)
+gtk_tearoff_menu_item_get_preferred_width (GtkWidget      *widget,
+                                           gint           *minimum,
+                                           gint           *natural)
 {
-  GtkTearoffMenuItem *tearoff;
+  GtkStyleContext *context;
+  guint border_width;
+  GtkBorder padding;
+  GtkStateFlags state;
+
+  context = gtk_widget_get_style_context (widget);
+  state = gtk_widget_get_state_flags (widget);
 
-  tearoff = GTK_TEAROFF_MENU_ITEM (widget);
-  
-  requisition->width = (GTK_CONTAINER (widget)->border_width +
-                       widget->style->klass->xthickness +
-                       BORDER_SPACING) * 2;
-  requisition->height = (GTK_CONTAINER (widget)->border_width +
-                        widget->style->klass->ythickness) * 2;
+  gtk_style_context_get_padding (context, state, &padding);
+  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
 
-  if (tearoff->torn_off)
+  *minimum = *natural = (border_width + BORDER_SPACING) * 2 + padding.left + padding.right;
+}
+
+static void
+gtk_tearoff_menu_item_get_preferred_height (GtkWidget      *widget,
+                                            gint           *minimum,
+                                            gint           *natural)
+{
+  GtkStyleContext *context;
+  GtkBorder padding;
+  GtkStateFlags state;
+  GtkWidget *parent;
+  guint border_width;
+
+  context = gtk_widget_get_style_context (widget);
+  state = gtk_widget_get_state_flags (widget);
+
+  gtk_style_context_get_padding (context, state, &padding);
+  border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+
+  *minimum = *natural = (border_width * 2) + padding.top + padding.bottom;
+
+  parent = gtk_widget_get_parent (widget);
+  if (GTK_IS_MENU (parent) && GTK_MENU (parent)->priv->torn_off)
     {
-      requisition->height += ARROW_SIZE;
+      *minimum += ARROW_SIZE;
+      *natural += ARROW_SIZE;
     }
   else
     {
-      requisition->height += widget->style->klass->ythickness;
+      *minimum += padding.top + 4;
+      *natural += padding.top + 4;
     }
 }
 
-static void
-gtk_tearoff_menu_item_paint (GtkWidget   *widget,
-                            GdkRectangle *area)
+static gboolean
+gtk_tearoff_menu_item_draw (GtkWidget *widget,
+                            cairo_t   *cr)
 {
   GtkMenuItem *menu_item;
-  GtkTearoffMenuItem *tearoff_item;
-  GtkShadowType shadow_type;
-  gint width, height;
-  gint x, y;
+  GtkStateFlags state;
+  GtkStyleContext *context;
+  GtkBorder padding;
+  gint x, y, width, height;
   gint right_max;
+  guint border_width;
+  GtkTextDirection direction;
+  GtkWidget *parent;
+  gdouble angle;
+
+  menu_item = GTK_MENU_ITEM (widget);
+  context = gtk_widget_get_style_context (widget);
+  direction = gtk_widget_get_direction (widget);
+  state = gtk_widget_get_state_flags (widget);
+
+  border_width = gtk_container_get_border_width (GTK_CONTAINER (menu_item));
+  x = border_width;
+  y = border_width;
+  width = gtk_widget_get_allocated_width (widget) - border_width * 2;
+  height = gtk_widget_get_allocated_height (widget) - border_width * 2;
+  right_max = x + width;
+
+  gtk_style_context_save (context);
+  gtk_style_context_set_state (context, state);
+  gtk_style_context_get_padding (context, state, &padding);
+
+  if (state & GTK_STATE_FLAG_PRELIGHT)
+    {
+      gtk_render_background (context, cr, x, y, width, height);
+      gtk_render_frame (context, cr, x, y, width, height);
+    }
 
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_TEAROFF_MENU_ITEM (widget));
+  parent = gtk_widget_get_parent (widget);
+  if (GTK_IS_MENU (parent) && GTK_MENU (parent)->priv->torn_off)
+    {
+      gint arrow_x;
+
+      if (menu_item->priv->toggle_size > ARROW_SIZE)
+        {
+          if (direction == GTK_TEXT_DIR_LTR)
+            {
+              arrow_x = x + (menu_item->priv->toggle_size - ARROW_SIZE)/2;
+              angle = (3 * G_PI) / 2;
+            }
+          else
+            {
+              arrow_x = x + width - menu_item->priv->toggle_size + (menu_item->priv->toggle_size - ARROW_SIZE)/2;
+              angle = G_PI / 2;
+            }
+          x += menu_item->priv->toggle_size + BORDER_SPACING;
+        }
+      else
+        {
+          if (direction == GTK_TEXT_DIR_LTR)
+            {
+              arrow_x = ARROW_SIZE / 2;
+              angle = (3 * G_PI) / 2;
+            }
+          else
+            {
+              arrow_x = x + width - 2 * ARROW_SIZE + ARROW_SIZE / 2;
+              angle = G_PI / 2;
+            }
+          x += 2 * ARROW_SIZE;
+        }
+
+      gtk_render_arrow (context, cr, angle,
+                        arrow_x, height / 2 - 5,
+                        ARROW_SIZE);
+    }
 
-  if (GTK_WIDGET_DRAWABLE (widget))
+  while (x < right_max)
     {
-      menu_item = GTK_MENU_ITEM (widget);
-      tearoff_item = GTK_TEAROFF_MENU_ITEM (widget);
-
-      x = GTK_CONTAINER (menu_item)->border_width;
-      y = GTK_CONTAINER (menu_item)->border_width;
-      width = widget->allocation.width - x * 2;
-      height = widget->allocation.height - y * 2;
-      right_max = x + width;
-
-      if (widget->state == GTK_STATE_PRELIGHT)
-       gtk_paint_box (widget->style,
-                      widget->window,
-                      GTK_STATE_PRELIGHT,
-                      GTK_SHADOW_OUT,
-                      area, widget, "menuitem",
-                      x, y, width, height);
-       else
-        gdk_window_clear_area (widget->window, area->x, area->y, area->width, area->height);
-
-      if (tearoff_item->torn_off)
-       {
-         gint arrow_x;
-
-         if (widget->state == GTK_STATE_PRELIGHT)
-           shadow_type = GTK_SHADOW_IN;
-         else
-           shadow_type = GTK_SHADOW_OUT;
-
-         if (menu_item->toggle_size > ARROW_SIZE)
-           {
-             arrow_x = x + (menu_item->toggle_size - ARROW_SIZE)/2;
-             x += menu_item->toggle_size + BORDER_SPACING;
-           }
-         else
-           {
-             arrow_x = ARROW_SIZE / 2;
-             x += 2 * ARROW_SIZE;
-           }
-         
-         gtk_draw_arrow (widget->style, widget->window,
-                         widget->state, shadow_type, GTK_ARROW_LEFT, FALSE,
-                         arrow_x, y + height / 2 - 5, 
-                         ARROW_SIZE, ARROW_SIZE);
-       }
-
-      while (x < right_max)
-       {
-         gtk_draw_hline (widget->style, widget->window, GTK_STATE_NORMAL,
-                         x, MIN (x+TEAR_LENGTH, right_max),
-                         y + (height - widget->style->klass->ythickness)/2);
-         x += 2 * TEAR_LENGTH;
-       }
+      gint x1, x2;
+
+      if (direction == GTK_TEXT_DIR_LTR)
+        {
+          x1 = x;
+          x2 = MIN (x + TEAR_LENGTH, right_max);
+        }
+      else
+        {
+          x1 = right_max - x;
+          x2 = MAX (right_max - x - TEAR_LENGTH, 0);
+        }
+
+      gtk_render_line (context, cr,
+                       x1, y + (height - padding.bottom) / 2,
+                       x2, y + (height - padding.bottom) / 2);
+      x += 2 * TEAR_LENGTH;
     }
-}
 
-static void
-gtk_tearoff_menu_item_draw (GtkWidget    *widget,
-                         GdkRectangle *area)
-{
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_TEAROFF_MENU_ITEM (widget));
-  g_return_if_fail (area != NULL);
+  gtk_style_context_restore (context);
 
-  gtk_tearoff_menu_item_paint (widget, area);
+  return FALSE;
 }
 
-static gint
-gtk_tearoff_menu_item_expose (GtkWidget      *widget,
-                           GdkEventExpose *event)
+static void
+gtk_tearoff_menu_item_activate (GtkMenuItem *menu_item)
 {
-  g_return_val_if_fail (widget != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_TEAROFF_MENU_ITEM (widget), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
+  GtkWidget *parent;
 
-  gtk_tearoff_menu_item_paint (widget, &event->area);
+  parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
+  if (GTK_IS_MENU (parent))
+    {
+      GtkMenu *menu = GTK_MENU (parent);
 
-  return FALSE;
+      gtk_widget_queue_resize (GTK_WIDGET (menu_item));
+      gtk_menu_set_tearoff_state (GTK_MENU (parent),
+                                  !menu->priv->torn_off);
+    }
 }
 
-static gint
-gtk_tearoff_menu_item_delete_cb (GtkMenuItem *menu_item, GdkEventAny *event)
+static void
+tearoff_state_changed (GtkMenu            *menu,
+                       GParamSpec         *pspec,
+                       gpointer            data)
 {
-  gtk_tearoff_menu_item_activate (menu_item);
-  return TRUE;
+  GtkTearoffMenuItem *tearoff_menu_item = GTK_TEAROFF_MENU_ITEM (data);
+  GtkTearoffMenuItemPrivate *priv = tearoff_menu_item->priv;
+
+  priv->torn_off = gtk_menu_get_tearoff_state (menu);
 }
 
 static void
-gtk_tearoff_menu_item_activate (GtkMenuItem *menu_item)
+gtk_tearoff_menu_item_parent_set (GtkWidget *widget,
+                                  GtkWidget *previous)
 {
-  GtkTearoffMenuItem *tearoff_menu_item;
+  GtkTearoffMenuItem *tearoff_menu_item = GTK_TEAROFF_MENU_ITEM (widget);
+  GtkTearoffMenuItemPrivate *priv = tearoff_menu_item->priv;
+  GtkMenu *menu;
+  GtkWidget *parent;
 
-  g_return_if_fail (menu_item != NULL);
-  g_return_if_fail (GTK_IS_TEAROFF_MENU_ITEM (menu_item));
+  parent = gtk_widget_get_parent (widget);
+  menu = GTK_IS_MENU (parent) ? GTK_MENU (parent) : NULL;
 
-  tearoff_menu_item = GTK_TEAROFF_MENU_ITEM (menu_item);
-  tearoff_menu_item->torn_off = !tearoff_menu_item->torn_off;
+  if (previous)
+    g_signal_handlers_disconnect_by_func (previous,
+                                          tearoff_state_changed,
+                                          tearoff_menu_item);
 
-  if (GTK_IS_MENU (GTK_WIDGET (menu_item)->parent))
+  if (menu)
     {
-      GtkMenu *menu = GTK_MENU (GTK_WIDGET (menu_item)->parent);
-      gboolean need_connect;
-      
-       need_connect = (tearoff_menu_item->torn_off && !menu->tearoff_window);
-
-       gtk_menu_set_tearoff_state (GTK_MENU (GTK_WIDGET (menu_item)->parent),
-                                   tearoff_menu_item->torn_off);
-
-       if (need_connect)
-         gtk_signal_connect_object (GTK_OBJECT (menu->tearoff_window),  
-                                    "delete_event",
-                                    GTK_SIGNAL_FUNC (gtk_tearoff_menu_item_delete_cb),
-                                    GTK_OBJECT (menu_item));
+      priv->torn_off = gtk_menu_get_tearoff_state (menu);
+      g_signal_connect (menu, "notify::tearoff-state",
+                        G_CALLBACK (tearoff_state_changed),
+                        tearoff_menu_item);
     }
-  
-  gtk_widget_queue_resize (GTK_WIDGET (menu_item));
 }
-