gtkmenu.c 183 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Elliot Lee's avatar
Elliot Lee committed
2 3 4
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10
 * 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
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
16
 */
17 18

/*
19
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20 21
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
22
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 24
 */

Pavel Holejsovsky's avatar
Pavel Holejsovsky committed
25 26 27 28 29 30 31 32 33 34 35 36 37
/**
 * SECTION:gtkmenu
 * @Short_description: A menu widget
 * @Title: GtkMenu
 *
 * A #GtkMenu is a #GtkMenuShell that implements a drop down menu
 * consisting of a list of #GtkMenuItem objects which can be navigated
 * and activated by the user to perform application functions.
 *
 * A #GtkMenu is most commonly dropped down by activating a
 * #GtkMenuItem in a #GtkMenuBar or popped up by activating a
 * #GtkMenuItem in another #GtkMenu.
 *
38
 * A #GtkMenu can also be popped up by activating a #GtkComboBox.
Pavel Holejsovsky's avatar
Pavel Holejsovsky committed
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
 * Other composite widgets such as the #GtkNotebook can pop up a
 * #GtkMenu as well.
 *
 * Applications can display a #GtkMenu as a popup menu by calling the 
 * gtk_menu_popup() function.  The example below shows how an application
 * can pop up a menu when the 3rd mouse button is pressed.  
 *
 * <example>
 * <title>Connecting the popup signal handler.</title>
 * <programlisting>
 *   /<!---->* connect our handler which will popup the menu *<!---->/
 *   g_signal_connect_swapped (window, "button_press_event",
 *	G_CALLBACK (my_popup_handler), menu);
 * </programlisting>
 * </example>
 *
 * <example>
 * <title>Signal handler which displays a popup menu.</title>
 * <programlisting>
 * static gint
 * my_popup_handler (GtkWidget *widget, GdkEvent *event)
 * {
 *   GtkMenu *menu;
 *   GdkEventButton *event_button;
 *
 *   g_return_val_if_fail (widget != NULL, FALSE);
 *   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
 *   g_return_val_if_fail (event != NULL, FALSE);
 *
 *   /<!---->* The "widget" is the menu that was supplied when 
 *    * g_signal_connect_swapped() was called.
 *    *<!---->/
 *   menu = GTK_MENU (widget);
 *
 *   if (event->type == GDK_BUTTON_PRESS)
 *     {
 *       event_button = (GdkEventButton *) event;
76
 *       if (event_button->button == GDK_BUTTON_SECONDARY)
Pavel Holejsovsky's avatar
Pavel Holejsovsky committed
77 78 79 80 81 82 83 84 85 86 87 88 89
 *         {
 *           gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 
 *                           event_button->button, event_button->time);
 *           return TRUE;
 *         }
 *     }
 * 
 *   return FALSE;
 * }
 * </programlisting>
 * </example>
 */

90
#include "config.h"
91

92
#include <string.h>
93 94 95

#include  <gobject/gvaluecollector.h>

Matthias Clasen's avatar
Matthias Clasen committed
96
#include "gtkaccellabel.h"
97
#include "gtkaccelmap.h"
98
#include "gtkadjustment.h"
99
#include "gtkbindings.h"
100
#include "gtkcheckmenuitem.h"
Elliot Lee's avatar
Elliot Lee committed
101
#include "gtkmain.h"
102
#include "gtkmarshalers.h"
103
#include "gtkmenuprivate.h"
104
#include "gtkmenuitemprivate.h"
105
#include "gtkmenushellprivate.h"
106
#include "gtkwindow.h"
Matthias Clasen's avatar
Matthias Clasen committed
107
#include "gtkbox.h"
Javier Jardón's avatar
Javier Jardón committed
108
#include "gtkscrollbar.h"
Havoc Pennington's avatar
Havoc Pennington committed
109
#include "gtksettings.h"
110
#include "gtkprivate.h"
111
#include "gtkwidgetpath.h"
112
#include "gtkwidgetprivate.h"
113
#include "gtkdnd.h"
114
#include "gtkintl.h"
115
#include "gtktypebuiltins.h"
116
#include "gtkwidgetprivate.h"
117

118
#include "deprecated/gtktearoffmenuitem.h"
119 120


121
#include "a11y/gtkmenuaccessible.h"
Elliot Lee's avatar
Elliot Lee committed
122

123
#define NAVIGATION_REGION_OVERSHOOT 50  /* How much the navigation region
124 125
                                         * extends below the submenu
                                         */
126

127 128 129 130 131
#define MENU_SCROLL_STEP1      8
#define MENU_SCROLL_STEP2     15
#define MENU_SCROLL_FAST_ZONE  8
#define MENU_SCROLL_TIMEOUT1  50
#define MENU_SCROLL_TIMEOUT2  20
132

133
#define ATTACH_INFO_KEY "gtk-menu-child-attach-info-key"
134
#define ATTACHED_MENUS "gtk-attached-menus"
135

136 137
typedef struct _GtkMenuAttachData  GtkMenuAttachData;
typedef struct _GtkMenuPopdownData GtkMenuPopdownData;
Tim Janik's avatar
Tim Janik committed
138 139 140 141 142 143 144

struct _GtkMenuAttachData
{
  GtkWidget *attach_widget;
  GtkMenuDetachFunc detacher;
};

145 146 147 148 149 150
struct _GtkMenuPopdownData
{
  GtkMenu *menu;
  GdkDevice *device;
};

151 152
typedef struct
{
153 154 155 156 157 158 159 160 161
  gint left_attach;
  gint right_attach;
  gint top_attach;
  gint bottom_attach;
  gint effective_left_attach;
  gint effective_right_attach;
  gint effective_top_attach;
  gint effective_bottom_attach;
} AttachInfo;
162

163 164 165 166 167
enum {
  MOVE_SCROLL,
  LAST_SIGNAL
};

168 169
enum {
  PROP_0,
Tim Janik's avatar
Tim Janik committed
170 171 172 173
  PROP_ACTIVE,
  PROP_ACCEL_GROUP,
  PROP_ACCEL_PATH,
  PROP_ATTACH_WIDGET,
174
  PROP_TEAROFF_STATE,
Tim Janik's avatar
Tim Janik committed
175
  PROP_TEAROFF_TITLE,
176 177
  PROP_MONITOR,
  PROP_RESERVE_TOGGLE_SIZE
178
};
Tim Janik's avatar
Tim Janik committed
179

180
enum {
181 182 183 184 185 186 187 188
  CHILD_PROP_0,
  CHILD_PROP_LEFT_ATTACH,
  CHILD_PROP_RIGHT_ATTACH,
  CHILD_PROP_TOP_ATTACH,
  CHILD_PROP_BOTTOM_ATTACH
};

static void     gtk_menu_set_property      (GObject          *object,
189 190 191
                                            guint             prop_id,
                                            const GValue     *value,
                                            GParamSpec       *pspec);
192
static void     gtk_menu_get_property      (GObject          *object,
193 194 195
                                            guint             prop_id,
                                            GValue           *value,
                                            GParamSpec       *pspec);
196 197 198 199 200 201 202 203 204 205
static void     gtk_menu_set_child_property(GtkContainer     *container,
                                            GtkWidget        *child,
                                            guint             property_id,
                                            const GValue     *value,
                                            GParamSpec       *pspec);
static void     gtk_menu_get_child_property(GtkContainer     *container,
                                            GtkWidget        *child,
                                            guint             property_id,
                                            GValue           *value,
                                            GParamSpec       *pspec);
206
static void     gtk_menu_destroy           (GtkWidget        *widget);
207 208 209
static void     gtk_menu_realize           (GtkWidget        *widget);
static void     gtk_menu_unrealize         (GtkWidget        *widget);
static void     gtk_menu_size_allocate     (GtkWidget        *widget,
210
                                            GtkAllocation    *allocation);
211
static void     gtk_menu_show              (GtkWidget        *widget);
Benjamin Otte's avatar
Benjamin Otte committed
212 213
static gboolean gtk_menu_draw              (GtkWidget        *widget,
                                            cairo_t          *cr);
214
static gboolean gtk_menu_key_press         (GtkWidget        *widget,
215
                                            GdkEventKey      *event);
216
static gboolean gtk_menu_scroll            (GtkWidget        *widget,
217
                                            GdkEventScroll   *event);
218
static gboolean gtk_menu_button_press      (GtkWidget        *widget,
219
                                            GdkEventButton   *event);
220
static gboolean gtk_menu_button_release    (GtkWidget        *widget,
221
                                            GdkEventButton   *event);
222
static gboolean gtk_menu_motion_notify     (GtkWidget        *widget,
223
                                            GdkEventMotion   *event);
224
static gboolean gtk_menu_enter_notify      (GtkWidget        *widget,
225
                                            GdkEventCrossing *event);
226
static gboolean gtk_menu_leave_notify      (GtkWidget        *widget,
227
                                            GdkEventCrossing *event);
228
static void     gtk_menu_scroll_to         (GtkMenu          *menu,
229
                                            gint              offset);
230
static void     gtk_menu_grab_notify       (GtkWidget        *widget,
231
                                            gboolean          was_grabbed);
232 233 234
static gboolean gtk_menu_captured_event    (GtkWidget        *widget,
                                            GdkEvent         *event);

235

236 237 238
static void     gtk_menu_stop_scrolling         (GtkMenu  *menu);
static void     gtk_menu_remove_scroll_timeout  (GtkMenu  *menu);
static gboolean gtk_menu_scroll_timeout         (gpointer  data);
239

240
static void     gtk_menu_scroll_item_visible (GtkMenuShell    *menu_shell,
241
                                              GtkWidget       *menu_item);
242
static void     gtk_menu_select_item       (GtkMenuShell     *menu_shell,
243
                                            GtkWidget        *menu_item);
244
static void     gtk_menu_real_insert       (GtkMenuShell     *menu_shell,
245 246
                                            GtkWidget        *child,
                                            gint              position);
247
static void     gtk_menu_scrollbar_changed (GtkAdjustment    *adjustment,
248
                                            GtkMenu          *menu);
249
static void     gtk_menu_handle_scrolling  (GtkMenu          *menu,
250 251 252
                                            gint              event_x,
                                            gint              event_y,
                                            gboolean          enter,
253
                                            gboolean          motion);
254
static void     gtk_menu_set_tearoff_hints (GtkMenu          *menu,
255
                                            gint             width);
256
static void     gtk_menu_style_updated     (GtkWidget        *widget);
257
static gboolean gtk_menu_focus             (GtkWidget        *widget,
258
                                            GtkDirectionType direction);
259
static gint     gtk_menu_get_popup_delay   (GtkMenuShell     *menu_shell);
260 261
static void     gtk_menu_move_current      (GtkMenuShell     *menu_shell,
                                            GtkMenuDirectionType direction);
262
static void     gtk_menu_real_move_scroll  (GtkMenu          *menu,
263
                                            GtkScrollType     type);
264 265 266 267

static void     gtk_menu_stop_navigating_submenu       (GtkMenu          *menu);
static gboolean gtk_menu_stop_navigating_submenu_cb    (gpointer          user_data);
static gboolean gtk_menu_navigating_submenu            (GtkMenu          *menu,
268 269
                                                        gint              event_x,
                                                        gint              event_y);
270
static void     gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
271 272
                                                        GtkMenuItem      *menu_item,
                                                        GdkEventCrossing *event);
273
 
274
static void gtk_menu_deactivate     (GtkMenuShell      *menu_shell);
275
static void gtk_menu_show_all       (GtkWidget         *widget);
276 277
static void gtk_menu_position       (GtkMenu           *menu,
                                     gboolean           set_scroll_offset);
278 279 280
static void gtk_menu_reparent       (GtkMenu           *menu,
                                     GtkWidget         *new_parent,
                                     gboolean           unrealize);
281
static void gtk_menu_remove         (GtkContainer      *menu,
282
                                     GtkWidget         *widget);
Elliot Lee's avatar
Elliot Lee committed
283

284 285
static void gtk_menu_update_title   (GtkMenu           *menu);

286 287 288
static void       menu_grab_transfer_window_destroy (GtkMenu *menu);
static GdkWindow *menu_grab_transfer_window_get     (GtkMenu *menu);

289 290
static gboolean gtk_menu_real_can_activate_accel (GtkWidget *widget,
                                                  guint      signal_id);
291
static void _gtk_menu_refresh_accel_paths (GtkMenu *menu,
292
                                           gboolean group_changed);
293

294 295 296 297 298 299 300 301 302 303
static void gtk_menu_get_preferred_width            (GtkWidget           *widget,
                                                     gint                *minimum_size,
                                                     gint                *natural_size);
static void gtk_menu_get_preferred_height           (GtkWidget           *widget,
                                                     gint                *minimum_size,
                                                     gint                *natural_size);
static void gtk_menu_get_preferred_height_for_width (GtkWidget           *widget,
                                                     gint                 for_size,
                                                     gint                *minimum_size,
                                                     gint                *natural_size);
304 305


306
static const gchar attach_data_key[] = "gtk-menu-attach-data";
Tim Janik's avatar
Tim Janik committed
307

308 309
static guint menu_signals[LAST_SIGNAL] = { 0 };

310
G_DEFINE_TYPE (GtkMenu, gtk_menu, GTK_TYPE_MENU_SHELL)
Elliot Lee's avatar
Elliot Lee committed
311

312 313 314
static void
menu_queue_resize (GtkMenu *menu)
{
315
  GtkMenuPrivate *priv = menu->priv;
316 317 318 319 320

  priv->have_layout = FALSE;
  gtk_widget_queue_resize (GTK_WIDGET (menu));
}

321 322 323 324 325 326
static void
attach_info_free (AttachInfo *info)
{
  g_slice_free (AttachInfo, info);
}

327 328 329 330 331 332 333 334
static AttachInfo *
get_attach_info (GtkWidget *child)
{
  GObject *object = G_OBJECT (child);
  AttachInfo *ai = g_object_get_data (object, ATTACH_INFO_KEY);

  if (!ai)
    {
335
      ai = g_slice_new0 (AttachInfo);
336 337
      g_object_set_data_full (object, I_(ATTACH_INFO_KEY), ai,
                              (GDestroyNotify) attach_info_free);
338 339 340 341 342 343 344 345 346
    }

  return ai;
}

static gboolean
is_grid_attached (AttachInfo *ai)
{
  return (ai->left_attach >= 0 &&
347 348 349
          ai->right_attach >= 0 &&
          ai->top_attach >= 0 &&
          ai->bottom_attach >= 0);
350 351 352 353 354
}

static void
menu_ensure_layout (GtkMenu *menu)
{
355
  GtkMenuPrivate *priv = menu->priv;
356 357 358 359 360 361 362

  if (!priv->have_layout)
    {
      GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
      GList *l;
      gchar *row_occupied;
      gint current_row;
363
      gint max_right_attach;
364 365 366 367 368 369 370
      gint max_bottom_attach;

      /* Find extents of gridded portion
       */
      max_right_attach = 1;
      max_bottom_attach = 0;

371
      for (l = menu_shell->priv->children; l; l = l->next)
372 373 374 375 376 377 378 379 380 381 382 383
        {
          GtkWidget *child = l->data;
          AttachInfo *ai = get_attach_info (child);

          if (is_grid_attached (ai))
            {
              max_bottom_attach = MAX (max_bottom_attach, ai->bottom_attach);
              max_right_attach = MAX (max_right_attach, ai->right_attach);
            }
        }

      /* Find empty rows */
384 385
      row_occupied = g_malloc0 (max_bottom_attach);

386
      for (l = menu_shell->priv->children; l; l = l->next)
387 388 389
        {
          GtkWidget *child = l->data;
          AttachInfo *ai = get_attach_info (child);
390

391 392 393
          if (is_grid_attached (ai))
            {
              gint i;
394

395 396 397 398
              for (i = ai->top_attach; i < ai->bottom_attach; i++)
                row_occupied[i] = TRUE;
            }
        }
399 400 401 402

      /* Lay non-grid-items out in those rows
       */
      current_row = 0;
403
      for (l = menu_shell->priv->children; l; l = l->next)
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
        {
          GtkWidget *child = l->data;
          AttachInfo *ai = get_attach_info (child);

          if (!is_grid_attached (ai))
            {
              while (current_row < max_bottom_attach && row_occupied[current_row])
                current_row++;

              ai->effective_left_attach = 0;
              ai->effective_right_attach = max_right_attach;
              ai->effective_top_attach = current_row;
              ai->effective_bottom_attach = current_row + 1;

              current_row++;
            }
          else
            {
              ai->effective_left_attach = ai->left_attach;
              ai->effective_right_attach = ai->right_attach;
              ai->effective_top_attach = ai->top_attach;
              ai->effective_bottom_attach = ai->bottom_attach;
            }
        }
428 429 430 431 432 433 434 435 436 437 438 439 440

      g_free (row_occupied);

      priv->n_rows = MAX (current_row, max_bottom_attach);
      priv->n_columns = max_right_attach;
      priv->have_layout = TRUE;
    }
}


static gint
gtk_menu_get_n_columns (GtkMenu *menu)
{
441
  GtkMenuPrivate *priv = menu->priv;
442 443 444 445 446 447 448 449 450

  menu_ensure_layout (menu);

  return priv->n_columns;
}

static gint
gtk_menu_get_n_rows (GtkMenu *menu)
{
451
  GtkMenuPrivate *priv = menu->priv;
452 453 454 455 456 457 458 459

  menu_ensure_layout (menu);

  return priv->n_rows;
}

static void
get_effective_child_attach (GtkWidget *child,
460 461 462 463
                            int       *l,
                            int       *r,
                            int       *t,
                            int       *b)
464
{
465
  GtkMenu *menu = GTK_MENU (gtk_widget_get_parent (child));
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
  AttachInfo *ai;
  
  menu_ensure_layout (menu);

  ai = get_attach_info (child);

  if (l)
    *l = ai->effective_left_attach;
  if (r)
    *r = ai->effective_right_attach;
  if (t)
    *t = ai->effective_top_attach;
  if (b)
    *b = ai->effective_bottom_attach;

}

Elliot Lee's avatar
Elliot Lee committed
483 484 485
static void
gtk_menu_class_init (GtkMenuClass *class)
{
486 487 488 489
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
  GtkMenuShellClass *menu_shell_class = GTK_MENU_SHELL_CLASS (class);
490
  GtkBindingSet *binding_set;
Tim Janik's avatar
Tim Janik committed
491
  
492 493 494
  gobject_class->set_property = gtk_menu_set_property;
  gobject_class->get_property = gtk_menu_get_property;

495
  widget_class->destroy = gtk_menu_destroy;
496 497 498 499
  widget_class->realize = gtk_menu_realize;
  widget_class->unrealize = gtk_menu_unrealize;
  widget_class->size_allocate = gtk_menu_size_allocate;
  widget_class->show = gtk_menu_show;
Benjamin Otte's avatar
Benjamin Otte committed
500
  widget_class->draw = gtk_menu_draw;
501
  widget_class->scroll_event = gtk_menu_scroll;
502 503 504 505 506 507 508
  widget_class->key_press_event = gtk_menu_key_press;
  widget_class->button_press_event = gtk_menu_button_press;
  widget_class->button_release_event = gtk_menu_button_release;
  widget_class->motion_notify_event = gtk_menu_motion_notify;
  widget_class->show_all = gtk_menu_show_all;
  widget_class->enter_notify_event = gtk_menu_enter_notify;
  widget_class->leave_notify_event = gtk_menu_leave_notify;
509
  widget_class->style_updated = gtk_menu_style_updated;
510
  widget_class->focus = gtk_menu_focus;
511
  widget_class->can_activate_accel = gtk_menu_real_can_activate_accel;
512
  widget_class->grab_notify = gtk_menu_grab_notify;
513 514 515
  widget_class->get_preferred_width = gtk_menu_get_preferred_width;
  widget_class->get_preferred_height = gtk_menu_get_preferred_height;
  widget_class->get_preferred_height_for_width = gtk_menu_get_preferred_height_for_width;
516 517 518 519 520 521 522 523 524 525 526 527

  container_class->remove = gtk_menu_remove;
  container_class->get_child_property = gtk_menu_get_child_property;
  container_class->set_child_property = gtk_menu_set_child_property;
  
  menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
  menu_shell_class->deactivate = gtk_menu_deactivate;
  menu_shell_class->select_item = gtk_menu_select_item;
  menu_shell_class->insert = gtk_menu_real_insert;
  menu_shell_class->get_popup_delay = gtk_menu_get_popup_delay;
  menu_shell_class->move_current = gtk_menu_move_current;

Pavel Holejsovsky's avatar
Pavel Holejsovsky committed
528 529 530 531 532
  /**
   * GtkMenu::move-scroll:
   * @menu: a #GtkMenu
   * @scroll_type: a #GtkScrollType
   */
533
  menu_signals[MOVE_SCROLL] =
534
    g_signal_new_class_handler (I_("move-scroll"),
535
                                G_OBJECT_CLASS_TYPE (gobject_class),
536 537 538 539 540 541
                                G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                                G_CALLBACK (gtk_menu_real_move_scroll),
                                NULL, NULL,
                                _gtk_marshal_VOID__ENUM,
                                G_TYPE_NONE, 1,
                                GTK_TYPE_SCROLL_TYPE);
Tim Janik's avatar
Tim Janik committed
542 543 544 545

  /**
   * GtkMenu:active:
   *
Matthias Clasen's avatar
Matthias Clasen committed
546 547
   * The index of the currently selected menu item, or -1 if no
   * menu item is selected.
Tim Janik's avatar
Tim Janik committed
548
   *
549
   * Since: 2.14
Tim Janik's avatar
Tim Janik committed
550 551 552
   **/
  g_object_class_install_property (gobject_class,
                                   PROP_ACTIVE,
Matthias Clasen's avatar
Matthias Clasen committed
553
                                   g_param_spec_int ("active",
554 555 556 557
                                                     P_("Active"),
                                                     P_("The currently selected menu item"),
                                                     -1, G_MAXINT, -1,
                                                     GTK_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
558 559 560 561 562 563

  /**
   * GtkMenu:accel-group:
   *
   * The accel group holding accelerators for the menu.
   *
564
   * Since: 2.14
Tim Janik's avatar
Tim Janik committed
565 566 567 568
   **/
  g_object_class_install_property (gobject_class,
                                   PROP_ACCEL_GROUP,
                                   g_param_spec_object ("accel-group",
569 570 571 572
                                                        P_("Accel Group"),
                                                        P_("The accel group holding accelerators for the menu"),
                                                        GTK_TYPE_ACCEL_GROUP,
                                                        GTK_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
573 574 575 576 577 578

  /**
   * GtkMenu:accel-path:
   *
   * An accel path used to conveniently construct accel paths of child items.
   *
579
   * Since: 2.14
Tim Janik's avatar
Tim Janik committed
580 581 582 583
   **/
  g_object_class_install_property (gobject_class,
                                   PROP_ACCEL_PATH,
                                   g_param_spec_string ("accel-path",
584 585 586 587
                                                        P_("Accel Path"),
                                                        P_("An accel path used to conveniently construct accel paths of child items"),
                                                        NULL,
                                                        GTK_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
588 589 590 591

  /**
   * GtkMenu:attach-widget:
   *
Matthias Clasen's avatar
Matthias Clasen committed
592 593 594
   * The widget the menu is attached to. Setting this property attaches
   * the menu without a #GtkMenuDetachFunc. If you need to use a detacher,
   * use gtk_menu_attach_to_widget() directly.
Tim Janik's avatar
Tim Janik committed
595
   *
596
   * Since: 2.14
Tim Janik's avatar
Tim Janik committed
597 598
   **/
  g_object_class_install_property (gobject_class,
Matthias Clasen's avatar
Matthias Clasen committed
599 600
                                   PROP_ATTACH_WIDGET,
                                   g_param_spec_object ("attach-widget",
601 602 603 604
                                                        P_("Attach Widget"),
                                                        P_("The widget the menu is attached to"),
                                                        GTK_TYPE_WIDGET,
                                                        GTK_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
605

606 607 608
  g_object_class_install_property (gobject_class,
                                   PROP_TEAROFF_TITLE,
                                   g_param_spec_string ("tearoff-title",
609 610
                                                        P_("Tearoff Title"),
                                                        P_("A title that may be displayed by the window manager when this menu is torn-off"),
611
                                                        NULL,
612
                                                        GTK_PARAM_READWRITE));
613 614 615 616 617 618 619 620 621 622 623

  /**
   * GtkMenu:tearoff-state:
   *
   * A boolean that indicates whether the menu is torn-off.
   *
   * Since: 2.6
   **/
  g_object_class_install_property (gobject_class,
                                   PROP_TEAROFF_STATE,
                                   g_param_spec_boolean ("tearoff-state",
624 625 626 627
                                                         P_("Tearoff State"),
                                                         P_("A boolean that indicates whether the menu is torn-off"),
                                                         FALSE,
                                                         GTK_PARAM_READWRITE));
Manish Singh's avatar
Manish Singh committed
628

Tim Janik's avatar
Tim Janik committed
629 630 631 632 633
  /**
   * GtkMenu:monitor:
   *
   * The monitor the menu will be popped up on.
   *
634
   * Since: 2.14
Tim Janik's avatar
Tim Janik committed
635 636 637 638
   **/
  g_object_class_install_property (gobject_class,
                                   PROP_MONITOR,
                                   g_param_spec_int ("monitor",
639 640 641 642
                                                     P_("Monitor"),
                                                     P_("The monitor the menu will be popped up on"),
                                                     -1, G_MAXINT, -1,
                                                     GTK_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
643

644
  gtk_widget_class_install_style_property (widget_class,
645 646 647 648 649 650 651
                                           g_param_spec_int ("vertical-padding",
                                                             P_("Vertical Padding"),
                                                             P_("Extra space at the top and bottom of the menu"),
                                                             0,
                                                             G_MAXINT,
                                                             1,
                                                             GTK_PARAM_READABLE));
652

653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
  /**
   * GtkMenu:reserve-toggle-size:
   *
   * A boolean that indicates whether the menu reserves space for
   * toggles and icons, regardless of their actual presence.
   *
   * This property should only be changed from its default value
   * for special-purposes such as tabular menus. Regular menus that
   * are connected to a menu bar or context menus should reserve
   * toggle space for consistency.
   *
   * Since: 2.18
   */
  g_object_class_install_property (gobject_class,
                                   PROP_RESERVE_TOGGLE_SIZE,
                                   g_param_spec_boolean ("reserve-toggle-size",
669 670 671 672
                                                         P_("Reserve Toggle Size"),
                                                         P_("A boolean that indicates whether the menu reserves space for toggles and icons"),
                                                         TRUE,
                                                         GTK_PARAM_READWRITE));
673

674 675 676 677 678 679 680 681 682
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_int ("horizontal-padding",
                                                             P_("Horizontal Padding"),
                                                             P_("Extra space at the left and right edges of the menu"),
                                                             0,
                                                             G_MAXINT,
                                                             0,
                                                             GTK_PARAM_READABLE));

683
  gtk_widget_class_install_style_property (widget_class,
684 685 686 687 688 689 690
                                           g_param_spec_int ("vertical-offset",
                                                             P_("Vertical Offset"),
                                                             P_("When the menu is a submenu, position it this number of pixels offset vertically"),
                                                             G_MININT,
                                                             G_MAXINT,
                                                             0,
                                                             GTK_PARAM_READABLE));
691 692

  gtk_widget_class_install_style_property (widget_class,
693 694 695 696 697 698 699
                                           g_param_spec_int ("horizontal-offset",
                                                             P_("Horizontal Offset"),
                                                             P_("When the menu is a submenu, position it this number of pixels offset horizontally"),
                                                             G_MININT,
                                                             G_MAXINT,
                                                             -2,
                                                             GTK_PARAM_READABLE));
700

701 702 703 704 705 706 707
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_boolean ("double-arrows",
                                                                 P_("Double Arrows"),
                                                                 P_("When scrolling, always show both arrows."),
                                                                 TRUE,
                                                                 GTK_PARAM_READABLE));

708 709 710 711 712 713 714 715 716 717 718 719 720 721
  /**
   * GtkMenu:arrow-placement:
   *
   * Indicates where scroll arrows should be placed.
   *
   * Since: 2.16
   **/
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_enum ("arrow-placement",
                                                              P_("Arrow Placement"),
                                                              P_("Indicates where scroll arrows should be placed"),
                                                              GTK_TYPE_ARROW_PLACEMENT,
                                                              GTK_ARROWS_BOTH,
                                                              GTK_PARAM_READABLE));
722

723 724
 gtk_container_class_install_child_property (container_class,
                                             CHILD_PROP_LEFT_ATTACH,
725
                                              g_param_spec_int ("left-attach",
726 727
                                                               P_("Left Attach"),
                                                               P_("The column number to attach the left side of the child to"),
728
                                                                -1, INT_MAX, -1,
729
                                                               GTK_PARAM_READWRITE));
730 731 732

 gtk_container_class_install_child_property (container_class,
                                             CHILD_PROP_RIGHT_ATTACH,
733
                                              g_param_spec_int ("right-attach",
734 735
                                                               P_("Right Attach"),
                                                               P_("The column number to attach the right side of the child to"),
736
                                                                -1, INT_MAX, -1,
737
                                                               GTK_PARAM_READWRITE));
738 739 740

 gtk_container_class_install_child_property (container_class,
                                             CHILD_PROP_TOP_ATTACH,
741
                                              g_param_spec_int ("top-attach",
742 743
                                                               P_("Top Attach"),
                                                               P_("The row number to attach the top of the child to"),
744
                                                                -1, INT_MAX, -1,
745
                                                               GTK_PARAM_READWRITE));
746 747 748

 gtk_container_class_install_child_property (container_class,
                                             CHILD_PROP_BOTTOM_ATTACH,
749
                                              g_param_spec_int ("bottom-attach",
750 751
                                                               P_("Bottom Attach"),
                                                               P_("The row number to attach the bottom of the child to"),
752
                                                                -1, INT_MAX, -1,
753
                                                               GTK_PARAM_READWRITE));
754

755
 /**
Matthias Clasen's avatar
Matthias Clasen committed
756
  * GtkMenu:arrow-scaling:
757 758 759 760 761 762 763 764 765 766 767 768
  *
  * Arbitrary constant to scale down the size of the scroll arrow.
  *
  * Since: 2.16
  */
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_float ("arrow-scaling",
                                                               P_("Arrow Scaling"),
                                                               P_("Arbitrary constant to scale down the size of the scroll arrow"),
                                                               0.0, 1.0, 0.7,
                                                               GTK_PARAM_READABLE));

769 770
  binding_set = gtk_binding_set_by_class (class);
  gtk_binding_entry_add_signal (binding_set,
771 772 773 774
                                GDK_KEY_Up, 0,
                                I_("move-current"), 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_PREV);
775
  gtk_binding_entry_add_signal (binding_set,
776 777 778 779
                                GDK_KEY_KP_Up, 0,
                                "move-current", 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_PREV);
780
  gtk_binding_entry_add_signal (binding_set,
781 782 783 784
                                GDK_KEY_Down, 0,
                                "move-current", 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_NEXT);
785
  gtk_binding_entry_add_signal (binding_set,
786 787 788 789
                                GDK_KEY_KP_Down, 0,
                                "move-current", 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_NEXT);
790
  gtk_binding_entry_add_signal (binding_set,
791 792 793 794
                                GDK_KEY_Left, 0,
                                "move-current", 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_PARENT);
795
  gtk_binding_entry_add_signal (binding_set,
796 797 798 799
                                GDK_KEY_KP_Left, 0,
                                "move-current", 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_PARENT);
800
  gtk_binding_entry_add_signal (binding_set,
801 802 803 804
                                GDK_KEY_Right, 0,
                                "move-current", 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_CHILD);
805
  gtk_binding_entry_add_signal (binding_set,
806 807 808 809
                                GDK_KEY_KP_Right, 0,
                                "move-current", 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_CHILD);
810
  gtk_binding_entry_add_signal (binding_set,
811 812 813 814
                                GDK_KEY_Home, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_START);
815
  gtk_binding_entry_add_signal (binding_set,
816 817 818 819
                                GDK_KEY_KP_Home, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_START);
820
  gtk_binding_entry_add_signal (binding_set,
821 822 823 824
                                GDK_KEY_End, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_END);
825
  gtk_binding_entry_add_signal (binding_set,
826 827 828 829
                                GDK_KEY_KP_End, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_END);
830
  gtk_binding_entry_add_signal (binding_set,
831 832 833 834
                                GDK_KEY_Page_Up, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_PAGE_UP);
835
  gtk_binding_entry_add_signal (binding_set,
836 837 838 839
                                GDK_KEY_KP_Page_Up, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_PAGE_UP);
840
  gtk_binding_entry_add_signal (binding_set,
841 842 843 844
                                GDK_KEY_Page_Down, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_PAGE_DOWN);
845
  gtk_binding_entry_add_signal (binding_set,
846 847 848 849
                                GDK_KEY_KP_Page_Down, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_PAGE_DOWN);
850

851
  g_type_class_add_private (gobject_class, sizeof (GtkMenuPrivate));
852 853

  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_MENU_ACCESSIBLE);
Elliot Lee's avatar
Elliot Lee committed
854 855
}

856

857
static void
858
gtk_menu_set_property (GObject      *object,
859 860 861
                       guint         prop_id,
                       const GValue *value,
                       GParamSpec   *pspec)
862
{
863 864
  GtkMenu *menu = GTK_MENU (object);

865 866
  switch (prop_id)
    {
Tim Janik's avatar
Tim Janik committed
867
    case PROP_ACTIVE:
Matthias Clasen's avatar
Matthias Clasen committed
868
      gtk_menu_set_active (menu, g_value_get_int (value));
Tim Janik's avatar
Tim Janik committed
869 870 871 872 873 874 875 876
      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:
Matthias Clasen's avatar
Matthias Clasen committed
877 878 879 880 881 882 883 884 885 886 887
      {
        GtkWidget *widget;

        widget = gtk_menu_get_attach_widget (menu);
        if (widget)
          gtk_menu_detach (menu);

        widget = (GtkWidget*) g_value_get_object (value); 
        if (widget)
          gtk_menu_attach_to_widget (menu, widget, NULL);
      }
Tim Janik's avatar
Tim Janik committed
888
      break;
889 890 891
    case PROP_TEAROFF_STATE:
      gtk_menu_set_tearoff_state (menu, g_value_get_boolean (value));
      break;
892 893
    case PROP_TEAROFF_TITLE:
      gtk_menu_set_title (menu, g_value_get_string (value));
Tim Janik's avatar
Tim Janik committed
894 895 896 897
      break;
    case PROP_MONITOR:
      gtk_menu_set_monitor (menu, g_value_get_int (value));
      break;
898 899 900
    case PROP_RESERVE_TOGGLE_SIZE:
      gtk_menu_set_reserve_toggle_size (menu, g_value_get_boolean (value));
      break;
901 902 903 904 905 906
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

907
static void
908
gtk_menu_get_property (GObject     *object,
909 910 911
                       guint        prop_id,
                       GValue      *value,
                       GParamSpec  *pspec)
912
{
913 914
  GtkMenu *menu = GTK_MENU (object);

915 916
  switch (prop_id)
    {
Tim Janik's avatar
Tim Janik committed
917
    case PROP_ACTIVE:
918
      g_value_set_int (value, g_list_index (GTK_MENU_SHELL (menu)->priv->children, gtk_menu_get_active (menu)));
Tim Janik's avatar
Tim Janik committed
919 920 921 922 923 924 925 926 927 928
      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;
929 930 931
    case PROP_TEAROFF_STATE:
      g_value_set_boolean (value, gtk_menu_get_tearoff_state (menu));
      break;
932
    case PROP_TEAROFF_TITLE:
933
      g_value_set_string (value, gtk_menu_get_title (menu));
934
      break;
Tim Janik's avatar
Tim Janik committed
935 936 937
    case PROP_MONITOR:
      g_value_set_int (value, gtk_menu_get_monitor (menu));
      break;
938 939 940
    case PROP_RESERVE_TOGGLE_SIZE:
      g_value_set_boolean (value, gtk_menu_get_reserve_toggle_size (menu));
      break;
941 942 943 944 945 946
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

947 948 949 950 951 952 953 954
static void
gtk_menu_set_child_property (GtkContainer *container,
                             GtkWidget    *child,
                             guint         property_id,
                             const GValue *value,
                             GParamSpec   *pspec)
{
  GtkMenu *menu = GTK_MENU (container);
955
  AttachInfo *ai = get_attach_info (child);
956 957 958

  switch (property_id)
    {
959 960 961 962 963 964 965
    case CHILD_PROP_LEFT_ATTACH:
      ai->left_attach = g_value_get_int (value);
      break;
    case CHILD_PROP_RIGHT_ATTACH:
      ai->right_attach = g_value_get_int (value);
      break;
    case CHILD_PROP_TOP_ATTACH:
966
      ai->top_attach = g_value_get_int (value);
967 968 969 970 971 972 973 974
      break;
    case CHILD_PROP_BOTTOM_ATTACH:
      ai->bottom_attach = g_value_get_int (value);
      break;

    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
      return;
975 976
    }

977
  menu_queue_resize (menu);
978 979 980 981 982 983 984 985 986
}

static void
gtk_menu_get_child_property (GtkContainer *container,
                             GtkWidget    *child,
                             guint         property_id,
                             GValue       *value,
                             GParamSpec   *pspec)
{
987
  AttachInfo *ai = get_attach_info (child);
988 989 990

  switch (property_id)
    {
991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
    case CHILD_PROP_LEFT_ATTACH:
      g_value_set_int (value, ai->left_attach);
      break;
    case CHILD_PROP_RIGHT_ATTACH:
      g_value_set_int (value, ai->right_attach);
      break;
    case CHILD_PROP_TOP_ATTACH:
      g_value_set_int (value, ai->top_attach);
      break;
    case CHILD_PROP_BOTTOM_ATTACH:
      g_value_set_int (value, ai->bottom_attach);
      break;
      
    default:
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
      return;
1007 1008 1009
    }
}

1010
static gboolean
1011
gtk_menu_window_event (GtkWidget *window,
1012 1013
                       GdkEvent  *event,
                       GtkWidget *menu)
1014 1015 1016
{
  gboolean handled = FALSE;

Manish Singh's avatar
Manish Singh committed
1017 1018
  g_object_ref (window);
  g_object_ref (menu);
1019 1020 1021 1022 1023

  switch (event->type)
    {
    case GDK_KEY_PRESS:
    case GDK_KEY_RELEASE:
1024
      handled = gtk_widget_event (menu, event);
1025
      break;
1026 1027 1028 1029 1030 1031 1032 1033 1034
    case GDK_WINDOW_STATE:
      /* Window for the menu has been closed by the display server or by GDK.
       * Update the internal state as if the user had clicked outside the
       * menu
       */
      if (event->window_state.new_window_state & GDK_WINDOW_STATE_WITHDRAWN &&
          event->window_state.changed_mask & GDK_WINDOW_STATE_WITHDRAWN)
        gtk_menu_shell_deactivate (GTK_MENU_SHELL(menu));
      break;
1035 1036 1037 1038
    default:
      break;
    }

Manish Singh's avatar
Manish Singh committed
1039 1040
  g_object_unref (window);
  g_object_unref (menu);
1041 1042 1043 1044

  return handled;
}

Elliot Lee's avatar
Elliot Lee committed
1045 1046 1047
static void
gtk_menu_init (GtkMenu *menu)
{
1048
  GtkMenuPrivate *priv;
1049
  GtkStyleContext *context;
1050

1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
  priv = G_TYPE_INSTANCE_GET_PRIVATE (menu, GTK_TYPE_MENU, GtkMenuPrivate);

  menu->priv = priv;

  priv->toplevel = g_object_connect (g_object_new (GTK_TYPE_WINDOW,
                                                   "type", GTK_WINDOW_POPUP,
                                                   "child", menu,
                                                   NULL),
                                     "signal::event", gtk_menu_window_event, menu,
                                     "signal::destroy", gtk_widget_destroyed, &priv->toplevel,
                                     NULL);
  gtk_window_set_resizable (GTK_WINDOW (priv->toplevel), FALSE);
  gtk_window_set_mnemonic_modifier (GTK_WINDOW (priv->toplevel), 0);
1064

Owen Taylor's avatar
Owen Taylor committed
1065 1066 1067
  /* Refloat the menu, so that reference counting for the menu isn't
   * affected by it being a child of the toplevel
   */
1068
  g_object_force_floating (G_OBJECT (menu));
1069
  priv->needs_destruction_ref = TRUE;
Owen Taylor's avatar
Owen Taylor committed
1070

Matthias Clasen's avatar
Matthias Clasen committed
1071
  priv->monitor_num = -1;
1072
  priv->drag_start_y = -1;
1073 1074 1075

  context = gtk_widget_get_style_context (GTK_WIDGET (menu));
  gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENU);
1076 1077

  _gtk_widget_set_captured_event_handler (GTK_WIDGET (menu), gtk_menu_captured_event);
1078 1079 1080
}

static void
1081
gtk_menu_destroy (GtkWidget *widget)
1082
{
1083
  GtkMenu *menu = GTK_MENU (widget);
1084
  GtkMenuPrivate *priv = menu->priv;
Tim Janik's avatar
Tim Janik committed
1085
  GtkMenuAttachData *data;
1086

1087
  gtk_menu_remove_scroll_timeout (menu);
1088

1089
  data = g_object_get_data (G_OBJECT (widget), attach_data_key);
Tim Janik's avatar
Tim Janik committed
1090
  if (data)
1091
    gtk_menu_detach (menu);
1092

1093 1094
  gtk_menu_stop_navigating_submenu (menu);

1095 1096
  if (priv->old_active_menu_item)
    g_clear_object (&priv->old_active_menu_item);
1097

Owen Taylor's avatar
Owen Taylor committed
1098
  /* Add back the reference count for being a child */
1099
  if (priv->needs_destruction_ref)
1100
    {
1101
      priv->needs_destruction_ref = FALSE;
1102
      g_object_ref (widget);
1103
    }
1104

1105 1106
  if (priv->accel_group)
    g_clear_object (&priv->accel_group);
1107

1108 1109
  if (priv->toplevel)
    gtk_widget_destroy (priv->toplevel);
1110

1111 1112
  if (priv->tearoff_window)
    gtk_widget_destroy (priv->tearoff_window);
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125

  if (priv->heights)
    {
      g_free (priv->heights);
      priv->heights = NULL;
    }

  if (priv->title)
    {
      g_free (priv->title);
      priv->title = NULL;
    }

1126 1127
  if (priv->position_func_data_destroy)
    {
1128 1129
      priv->position_func_data_destroy (priv->position_func_data);
      priv->position_func_data = NULL;
1130 1131 1132
      priv->position_func_data_destroy = NULL;
    }

1133
  GTK_WIDGET_CLASS (gtk_menu_parent_class)->destroy (widget);
Tim Janik's avatar
Tim Janik committed
1134 1135
}

1136 1137
static void
menu_change_screen (GtkMenu   *menu,
1138
                    GdkScreen *new_screen)
1139
{
1140
  GtkMenuPrivate *priv = menu->priv;
1141

Matthias Clasen's avatar
Matthias Clasen committed
1142
  if (gtk_widget_has_screen (GTK_WIDGET (menu)))
1143
    {
Matthias Clasen's avatar
Matthias Clasen committed
1144
      if (new_screen == gtk_widget_get_screen (GTK_WIDGET (menu)))
1145
        return;
1146 1147
    }

1148
  if (priv->torn_off)
1149
    {
1150
      gtk_window_set_screen (GTK_WINDOW (priv->tearoff_window), new_screen);
1151
      gtk_menu_position (menu, TRUE);
1152 1153
    }

1154 1155
  gtk_window_set_screen (GTK_WINDOW (priv->toplevel), new_screen);
  priv->monitor_num = -1;
1156 1157
}

1158 1159
static void
attach_widget_screen_changed (GtkWidget *attach_widget,
1160 1161
                              GdkScreen *previous_screen,
                              GtkMenu   *menu)
1162 1163 1164
{
  if (gtk_widget_has_screen (attach_widget) &&
      !g_object_get_data (G_OBJECT (menu), "gtk-menu-explicit-screen"))
1165
    menu_change_screen (menu, gtk_widget_get_screen (attach_widget));
1166 1167
}

Pavel Holejsovsky's avatar
Pavel Holejsovsky committed
1168
/**
1169
 * gtk_menu_attach_to_widget:
Pavel Holejsovsky's avatar
Pavel Holejsovsky committed
1170 1171
 * @menu: a #GtkMenu
 * @attach_widget: the #GtkWidget that the menu will be attached to
1172 1173
 * @detacher: (scope async)(allow-none): the user supplied callback function
 *             that will be called when the menu calls gtk_menu_detach()
Pavel Holejsovsky's avatar
Pavel Holejsovsky committed
1174 1175 1176 1177 1178
 *
 * Attaches the menu to the widget and provides a callback function
 * that will be invoked when the menu calls gtk_menu_detach() during
 * its destruction.
 */
Tim Janik's avatar
Tim Janik committed
1179
void
1180 1181 1182
gtk_menu_attach_to_widget (GtkMenu           *menu,
                           GtkWidget         *attach_widget,
                           GtkMenuDetachFunc  detacher)
Tim Janik's avatar
Tim Janik committed
1183 1184
{
  GtkMenuAttachData *data;
1185
  GList *list;
1186

Tim Janik's avatar
Tim Janik committed
1187 1188
  g_return_if_fail (GTK_IS_MENU (menu));
  g_return_if_fail (GTK_IS_WIDGET (attach_widget));
1189 1190

  /* keep this function in sync with gtk_widget_set_parent() */
Manish Singh's avatar
Manish Singh committed
1191
  data = g_object_get_data (G_OBJECT (menu), attach_data_key);
Tim Janik's avatar
Tim Janik committed
1192 1193 1194
  if (data)
    {
      g_warning ("gtk_menu_attach_to_widget(): menu already attached to %s",
1195
                 g_type_name (G_TYPE_FROM_INSTANCE (data->attach_widget)));