gtkmenu.c 201 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
 */

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.
39 40 41 42 43 44 45
 * 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.  
 *
46 47
 * ## Connecting the popup signal handler.
 *
48
 * |[<!-- language="C" -->
49
 *   // connect our handler which will popup the menu
50 51
 *   g_signal_connect_swapped (window, "button_press_event",
 *	G_CALLBACK (my_popup_handler), menu);
52
 * ]|
53
 *
54 55
 * ## Signal handler which displays a popup menu.
 *
56
 * |[<!-- language="C" -->
57 58 59 60 61 62 63 64 65 66
 * 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);
 *
67 68
 *   // The "widget" is the menu that was supplied when 
 *   // g_signal_connect_swapped() was called.
69 70 71 72 73
 *   menu = GTK_MENU (widget);
 *
 *   if (event->type == GDK_BUTTON_PRESS)
 *     {
 *       event_button = (GdkEventButton *) event;
74
 *       if (event_button->button == GDK_BUTTON_SECONDARY)
75 76 77 78 79 80
 *         {
 *           gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 
 *                           event_button->button, event_button->time);
 *           return TRUE;
 *         }
 *     }
81
 *
82 83
 *   return FALSE;
 * }
84
 * ]|
85 86 87
 *
 * # CSS nodes
 *
88 89 90
 * |[<!-- language="plain" -->
 * menu
 * ├── arrow.top
91 92 93
 * ├── <child>
 * ┊
 * ├── <child>
94 95 96
 * ╰── arrow.bottom
 * ]|
 *
97 98 99
 * The main CSS node of GtkMenu has name menu, and there are two subnodes
 * with name arrow, for scrolling menu arrows. These subnodes get the
 * .top and .bottom style classes.
100 101
 */

102
#include "config.h"
103

104
#include <string.h>
105 106 107

#include  <gobject/gvaluecollector.h>

Matthias Clasen's avatar
Matthias Clasen committed
108
#include "gtkaccellabel.h"
109
#include "gtkaccelmap.h"
110
#include "gtkadjustment.h"
111
#include "gtkbindings.h"
112
#include "gtkbuiltiniconprivate.h"
113
#include "gtkcheckmenuitem.h"
114
#include "gtkcheckmenuitemprivate.h"
Elliot Lee's avatar
Elliot Lee committed
115
#include "gtkmain.h"
116
#include "gtkmarshalers.h"
117
#include "gtkmenuprivate.h"
118
#include "gtkmenuitemprivate.h"
119
#include "gtkmenushellprivate.h"
120
#include "gtkwindow.h"
Matthias Clasen's avatar
Matthias Clasen committed
121
#include "gtkbox.h"
122
#include "gtkscrollbar.h"
Havoc Pennington's avatar
Havoc Pennington committed
123
#include "gtksettings.h"
124
#include "gtkprivate.h"
125
#include "gtkwidgetpath.h"
126
#include "gtkwidgetprivate.h"
127
#include "gtkdnd.h"
128
#include "gtkintl.h"
129
#include "gtktypebuiltins.h"
130
#include "gtkwidgetprivate.h"
131
#include "gtkwindowprivate.h"
132 133 134
#include "gtkcssnodeprivate.h"
#include "gtkstylecontextprivate.h"
#include "gtkcssstylepropertyprivate.h"
135

136
#include "deprecated/gtktearoffmenuitem.h"
137 138


139
#include "a11y/gtkmenuaccessible.h"
140
#include "gdk/gdk-private.h"
Elliot Lee's avatar
Elliot Lee committed
141

142
#define NAVIGATION_REGION_OVERSHOOT 50  /* How much the navigation region
143 144
                                         * extends below the submenu
                                         */
145

146 147 148 149 150
#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
151

152 153 154
#define MENU_POPUP_DELAY     225
#define MENU_POPDOWN_DELAY  1000

155
#define ATTACH_INFO_KEY "gtk-menu-child-attach-info-key"
156
#define ATTACHED_MENUS "gtk-attached-menus"
157

158 159
typedef struct _GtkMenuAttachData  GtkMenuAttachData;
typedef struct _GtkMenuPopdownData GtkMenuPopdownData;
160 161 162 163 164 165 166

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

167 168 169 170 171 172
struct _GtkMenuPopdownData
{
  GtkMenu *menu;
  GdkDevice *device;
};

173 174
typedef struct
{
175 176 177 178 179 180 181 182 183
  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;
184

185 186
enum {
  MOVE_SCROLL,
187
  POPPED_UP,
188 189 190
  LAST_SIGNAL
};

191 192
enum {
  PROP_0,
Tim Janik's avatar
Tim Janik committed
193 194 195 196
  PROP_ACTIVE,
  PROP_ACCEL_GROUP,
  PROP_ACCEL_PATH,
  PROP_ATTACH_WIDGET,
197
  PROP_TEAROFF_STATE,
Tim Janik's avatar
Tim Janik committed
198
  PROP_TEAROFF_TITLE,
199
  PROP_MONITOR,
200 201 202 203 204
  PROP_RESERVE_TOGGLE_SIZE,
  PROP_ANCHOR_HINTS,
  PROP_RECT_ANCHOR_DX,
  PROP_RECT_ANCHOR_DY,
  PROP_MENU_TYPE_HINT
205
};
206

207
enum {
208 209 210 211 212 213 214 215
  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,
216 217 218
                                            guint             prop_id,
                                            const GValue     *value,
                                            GParamSpec       *pspec);
219
static void     gtk_menu_get_property      (GObject          *object,
220 221 222
                                            guint             prop_id,
                                            GValue           *value,
                                            GParamSpec       *pspec);
223
static void     gtk_menu_finalize          (GObject          *object);
224 225 226 227 228 229 230 231 232 233
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);
234
static void     gtk_menu_destroy           (GtkWidget        *widget);
235 236 237
static void     gtk_menu_realize           (GtkWidget        *widget);
static void     gtk_menu_unrealize         (GtkWidget        *widget);
static void     gtk_menu_size_allocate     (GtkWidget        *widget,
238
                                            GtkAllocation    *allocation);
239
static void     gtk_menu_show              (GtkWidget        *widget);
Benjamin Otte's avatar
Benjamin Otte committed
240 241
static gboolean gtk_menu_draw              (GtkWidget        *widget,
                                            cairo_t          *cr);
242
static gboolean gtk_menu_key_press         (GtkWidget        *widget,
243
                                            GdkEventKey      *event);
244
static gboolean gtk_menu_scroll            (GtkWidget        *widget,
245
                                            GdkEventScroll   *event);
246
static gboolean gtk_menu_button_press      (GtkWidget        *widget,
247
                                            GdkEventButton   *event);
248
static gboolean gtk_menu_button_release    (GtkWidget        *widget,
249
                                            GdkEventButton   *event);
250
static gboolean gtk_menu_motion_notify     (GtkWidget        *widget,
251
                                            GdkEventMotion   *event);
252
static gboolean gtk_menu_enter_notify      (GtkWidget        *widget,
253
                                            GdkEventCrossing *event);
254
static gboolean gtk_menu_leave_notify      (GtkWidget        *widget,
255
                                            GdkEventCrossing *event);
256
static void     gtk_menu_scroll_to         (GtkMenu          *menu,
257
                                            gint              offset);
258
static void     gtk_menu_grab_notify       (GtkWidget        *widget,
259
                                            gboolean          was_grabbed);
260 261 262
static gboolean gtk_menu_captured_event    (GtkWidget        *widget,
                                            GdkEvent         *event);

263

264 265 266
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);
267

268
static void     gtk_menu_scroll_item_visible (GtkMenuShell    *menu_shell,
269
                                              GtkWidget       *menu_item);
270
static void     gtk_menu_select_item       (GtkMenuShell     *menu_shell,
271
                                            GtkWidget        *menu_item);
272
static void     gtk_menu_real_insert       (GtkMenuShell     *menu_shell,
273 274
                                            GtkWidget        *child,
                                            gint              position);
275
static void     gtk_menu_scrollbar_changed (GtkAdjustment    *adjustment,
276
                                            GtkMenu          *menu);
277
static void     gtk_menu_handle_scrolling  (GtkMenu          *menu,
278 279 280
                                            gint              event_x,
                                            gint              event_y,
                                            gboolean          enter,
281
                                            gboolean          motion);
282
static void     gtk_menu_set_tearoff_hints (GtkMenu          *menu,
283
                                            gint             width);
284
static gboolean gtk_menu_focus             (GtkWidget        *widget,
285
                                            GtkDirectionType direction);
286
static gint     gtk_menu_get_popup_delay   (GtkMenuShell     *menu_shell);
287 288
static void     gtk_menu_move_current      (GtkMenuShell     *menu_shell,
                                            GtkMenuDirectionType direction);
289
static void     gtk_menu_real_move_scroll  (GtkMenu          *menu,
290
                                            GtkScrollType     type);
291 292 293 294

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,
295 296
                                                        gint              event_x,
                                                        gint              event_y);
297
static void     gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
298 299
                                                        GtkMenuItem      *menu_item,
                                                        GdkEventCrossing *event);
300
 
301
static void gtk_menu_deactivate     (GtkMenuShell      *menu_shell);
302
static void gtk_menu_show_all       (GtkWidget         *widget);
303 304
static void gtk_menu_position       (GtkMenu           *menu,
                                     gboolean           set_scroll_offset);
305 306 307
static void gtk_menu_reparent       (GtkMenu           *menu,
                                     GtkWidget         *new_parent,
                                     gboolean           unrealize);
308
static void gtk_menu_remove         (GtkContainer      *menu,
309
                                     GtkWidget         *widget);
Elliot Lee's avatar
Elliot Lee committed
310

311 312
static void gtk_menu_update_title   (GtkMenu           *menu);

313 314 315
static void       menu_grab_transfer_window_destroy (GtkMenu *menu);
static GdkWindow *menu_grab_transfer_window_get     (GtkMenu *menu);

316 317
static gboolean gtk_menu_real_can_activate_accel (GtkWidget *widget,
                                                  guint      signal_id);
318
static void _gtk_menu_refresh_accel_paths (GtkMenu *menu,
319
                                           gboolean group_changed);
320

321 322 323 324 325 326 327 328 329 330
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);
331 332


333
static const gchar attach_data_key[] = "gtk-menu-attach-data";
334

335 336
static guint menu_signals[LAST_SIGNAL] = { 0 };

337
G_DEFINE_TYPE_WITH_PRIVATE (GtkMenu, gtk_menu, GTK_TYPE_MENU_SHELL)
Elliot Lee's avatar
Elliot Lee committed
338

339 340 341
static void
menu_queue_resize (GtkMenu *menu)
{
342
  GtkMenuPrivate *priv = menu->priv;
343 344 345 346 347

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

348 349 350 351 352 353
static void
attach_info_free (AttachInfo *info)
{
  g_slice_free (AttachInfo, info);
}

354 355 356 357 358 359 360 361
static AttachInfo *
get_attach_info (GtkWidget *child)
{
  GObject *object = G_OBJECT (child);
  AttachInfo *ai = g_object_get_data (object, ATTACH_INFO_KEY);

  if (!ai)
    {
362
      ai = g_slice_new0 (AttachInfo);
363 364
      g_object_set_data_full (object, I_(ATTACH_INFO_KEY), ai,
                              (GDestroyNotify) attach_info_free);
365 366 367 368 369 370 371 372 373
    }

  return ai;
}

static gboolean
is_grid_attached (AttachInfo *ai)
{
  return (ai->left_attach >= 0 &&
374 375 376
          ai->right_attach >= 0 &&
          ai->top_attach >= 0 &&
          ai->bottom_attach >= 0);
377 378 379 380 381
}

static void
menu_ensure_layout (GtkMenu *menu)
{
382
  GtkMenuPrivate *priv = menu->priv;
383 384 385 386 387 388 389

  if (!priv->have_layout)
    {
      GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
      GList *l;
      gchar *row_occupied;
      gint current_row;
390
      gint max_right_attach;
391 392 393 394 395 396 397
      gint max_bottom_attach;

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

398
      for (l = menu_shell->priv->children; l; l = l->next)
399 400 401 402 403 404 405 406 407 408 409 410
        {
          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 */
411 412
      row_occupied = g_malloc0 (max_bottom_attach);

413
      for (l = menu_shell->priv->children; l; l = l->next)
414 415 416
        {
          GtkWidget *child = l->data;
          AttachInfo *ai = get_attach_info (child);
417

418 419 420
          if (is_grid_attached (ai))
            {
              gint i;
421

422 423 424 425
              for (i = ai->top_attach; i < ai->bottom_attach; i++)
                row_occupied[i] = TRUE;
            }
        }
426 427 428 429

      /* Lay non-grid-items out in those rows
       */
      current_row = 0;
430
      for (l = menu_shell->priv->children; l; l = l->next)
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
        {
          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;
            }
        }
455 456 457 458 459 460 461 462 463 464 465 466 467

      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)
{
468
  GtkMenuPrivate *priv = menu->priv;
469 470 471 472 473 474 475 476 477

  menu_ensure_layout (menu);

  return priv->n_columns;
}

static gint
gtk_menu_get_n_rows (GtkMenu *menu)
{
478
  GtkMenuPrivate *priv = menu->priv;
479 480 481 482 483 484 485 486

  menu_ensure_layout (menu);

  return priv->n_rows;
}

static void
get_effective_child_attach (GtkWidget *child,
487 488 489 490
                            int       *l,
                            int       *r,
                            int       *t,
                            int       *b)
491
{
492
  GtkMenu *menu = GTK_MENU (gtk_widget_get_parent (child));
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
  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
510 511 512
static void
gtk_menu_class_init (GtkMenuClass *class)
{
513 514 515 516
  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);
517
  GtkBindingSet *binding_set;
Tim Janik's avatar
Tim Janik committed
518
  
519 520
  gobject_class->set_property = gtk_menu_set_property;
  gobject_class->get_property = gtk_menu_get_property;
521
  gobject_class->finalize = gtk_menu_finalize;
522

523
  widget_class->destroy = gtk_menu_destroy;
524 525 526 527
  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
528
  widget_class->draw = gtk_menu_draw;
529
  widget_class->scroll_event = gtk_menu_scroll;
530 531 532 533 534 535 536 537
  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;
  widget_class->focus = gtk_menu_focus;
538
  widget_class->can_activate_accel = gtk_menu_real_can_activate_accel;
539
  widget_class->grab_notify = gtk_menu_grab_notify;
540 541 542
  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;
543 544 545 546 547 548 549 550 551 552 553 554

  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;

555 556 557 558 559
  /**
   * GtkMenu::move-scroll:
   * @menu: a #GtkMenu
   * @scroll_type: a #GtkScrollType
   */
560
  menu_signals[MOVE_SCROLL] =
561
    g_signal_new_class_handler (I_("move-scroll"),
562
                                G_OBJECT_CLASS_TYPE (gobject_class),
563 564 565
                                G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                                G_CALLBACK (gtk_menu_real_move_scroll),
                                NULL, NULL,
566
                                NULL,
567 568
                                G_TYPE_NONE, 1,
                                GTK_TYPE_SCROLL_TYPE);
Tim Janik's avatar
Tim Janik committed
569

570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
  /**
   * GtkMenu::popped-up:
   * @menu: the #GtkMenu that popped up
   * @flipped_rect: (nullable): the position of @menu after any possible
   *                flipping or %NULL if the backend can't obtain it
   * @final_rect: (nullable): the final position of @menu or %NULL if the
   *              backend can't obtain it
   * @flipped_x: %TRUE if the anchors were flipped horizontally
   * @flipped_y: %TRUE if the anchors were flipped vertically
   *
   * Emitted when the position of @menu is finalized after being popped up
   * using gtk_menu_popup_at_rect (), gtk_menu_popup_at_widget (), or
   * gtk_menu_popup_at_pointer ().
   *
   * @menu might be flipped over the anchor rectangle in order to keep it
   * on-screen, in which case @flipped_x and @flipped_y will be set to %TRUE
   * accordingly.
   *
   * @flipped_rect is the ideal position of @menu after any possible flipping,
   * but before any possible sliding. @final_rect is @flipped_rect, but possibly
   * translated in the case that flipping is still ineffective in keeping @menu
   * on-screen.
   *
   * ![](popup-slide.png)
   *
   * The blue menu is @menu's ideal position, the green menu is @flipped_rect,
   * and the red menu is @final_rect.
   *
   * See gtk_menu_popup_at_rect (), gtk_menu_popup_at_widget (),
   * gtk_menu_popup_at_pointer (), #GtkMenu:anchor-hints,
   * #GtkMenu:rect-anchor-dx, #GtkMenu:rect-anchor-dy, and
   * #GtkMenu:menu-type-hint.
   *
   * Since: 3.22
   * Stability: Unstable
   */
  menu_signals[POPPED_UP] =
    g_signal_new_class_handler (I_("popped-up"),
                                G_OBJECT_CLASS_TYPE (gobject_class),
                                G_SIGNAL_RUN_FIRST,
                                NULL,
                                NULL,
                                NULL,
                                _gtk_marshal_VOID__POINTER_POINTER_BOOLEAN_BOOLEAN,
                                G_TYPE_NONE,
                                4,
                                G_TYPE_POINTER,
                                G_TYPE_POINTER,
                                G_TYPE_BOOLEAN,
                                G_TYPE_BOOLEAN);

Tim Janik's avatar
Tim Janik committed
621 622 623
  /**
   * GtkMenu:active:
   *
624 625
   * The index of the currently selected menu item, or -1 if no
   * menu item is selected.
Tim Janik's avatar
Tim Janik committed
626
   *
627
   * Since: 2.14
Tim Janik's avatar
Tim Janik committed
628 629 630
   **/
  g_object_class_install_property (gobject_class,
                                   PROP_ACTIVE,
631
                                   g_param_spec_int ("active",
632 633 634 635
                                                     P_("Active"),
                                                     P_("The currently selected menu item"),
                                                     -1, G_MAXINT, -1,
                                                     GTK_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
636 637 638 639 640 641

  /**
   * GtkMenu:accel-group:
   *
   * The accel group holding accelerators for the menu.
   *
642
   * Since: 2.14
Tim Janik's avatar
Tim Janik committed
643 644 645 646
   **/
  g_object_class_install_property (gobject_class,
                                   PROP_ACCEL_GROUP,
                                   g_param_spec_object ("accel-group",
647 648 649 650
                                                        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
651 652 653 654 655 656

  /**
   * GtkMenu:accel-path:
   *
   * An accel path used to conveniently construct accel paths of child items.
   *
657
   * Since: 2.14
Tim Janik's avatar
Tim Janik committed
658 659 660 661
   **/
  g_object_class_install_property (gobject_class,
                                   PROP_ACCEL_PATH,
                                   g_param_spec_string ("accel-path",
662 663 664 665
                                                        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
666 667 668 669

  /**
   * GtkMenu:attach-widget:
   *
Matthias Clasen's avatar
Matthias Clasen committed
670 671 672
   * 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
673
   *
674
   * Since: 2.14
Tim Janik's avatar
Tim Janik committed
675 676
   **/
  g_object_class_install_property (gobject_class,
Matthias Clasen's avatar
Matthias Clasen committed
677 678
                                   PROP_ATTACH_WIDGET,
                                   g_param_spec_object ("attach-widget",
679 680 681 682
                                                        P_("Attach Widget"),
                                                        P_("The widget the menu is attached to"),
                                                        GTK_TYPE_WIDGET,
                                                        GTK_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
683

684 685 686 687 688 689 690 691
  /**
   * GtkMenu:tearoff-title:
   *
   * A title that may be displayed by the window manager when this
   * menu is torn-off.
   *
   * Deprecated: 3.10
   **/
692 693 694
  g_object_class_install_property (gobject_class,
                                   PROP_TEAROFF_TITLE,
                                   g_param_spec_string ("tearoff-title",
695 696
                                                        P_("Tearoff Title"),
                                                        P_("A title that may be displayed by the window manager when this menu is torn-off"),
697
                                                        NULL,
698
                                                        GTK_PARAM_READWRITE | G_PARAM_DEPRECATED));
699 700 701 702 703 704 705

  /**
   * GtkMenu:tearoff-state:
   *
   * A boolean that indicates whether the menu is torn-off.
   *
   * Since: 2.6
706 707
   *
   * Deprecated: 3.10
708 709 710 711
   **/
  g_object_class_install_property (gobject_class,
                                   PROP_TEAROFF_STATE,
                                   g_param_spec_boolean ("tearoff-state",
712 713 714
                                                         P_("Tearoff State"),
                                                         P_("A boolean that indicates whether the menu is torn-off"),
                                                         FALSE,
715
                                                         GTK_PARAM_READWRITE | G_PARAM_DEPRECATED));
Manish Singh's avatar
Manish Singh committed
716

Tim Janik's avatar
Tim Janik committed
717 718 719 720 721
  /**
   * GtkMenu:monitor:
   *
   * The monitor the menu will be popped up on.
   *
722
   * Since: 2.14
Tim Janik's avatar
Tim Janik committed
723 724 725 726
   **/
  g_object_class_install_property (gobject_class,
                                   PROP_MONITOR,
                                   g_param_spec_int ("monitor",
727 728 729
                                                     P_("Monitor"),
                                                     P_("The monitor the menu will be popped up on"),
                                                     -1, G_MAXINT, -1,
730
                                                     GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
Tim Janik's avatar
Tim Janik committed
731

732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
  /**
   * 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",
748 749 750
                                                         P_("Reserve Toggle Size"),
                                                         P_("A boolean that indicates whether the menu reserves space for toggles and icons"),
                                                         TRUE,
751
                                                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
752

753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
  /**
   * GtkMenu:anchor-hints:
   *
   * Positioning hints for aligning the menu relative to a rectangle.
   *
   * These hints determine how the menu should be positioned in the case that
   * the menu would fall off-screen if placed in its ideal position.
   *
   * ![](popup-flip.png)
   *
   * For example, %GDK_ANCHOR_FLIP_Y will replace %GDK_GRAVITY_NORTH_WEST with
   * %GDK_GRAVITY_SOUTH_WEST and vice versa if the menu extends beyond the
   * bottom edge of the monitor.
   *
   * See gtk_menu_popup_at_rect (), gtk_menu_popup_at_widget (),
   * gtk_menu_popup_at_pointer (), #GtkMenu:rect-anchor-dx,
   * #GtkMenu:rect-anchor-dy, #GtkMenu:menu-type-hint, and #GtkMenu::popped-up.
   *
   * Since: 3.22
   * Stability: Unstable
   */
  g_object_class_install_property (gobject_class,
                                   PROP_ANCHOR_HINTS,
                                   g_param_spec_flags ("anchor-hints",
                                                       P_("Anchor hints"),
                                                       P_("Positioning hints for when the menu might fall off-screen"),
                                                       GDK_TYPE_ANCHOR_HINTS,
                                                       GDK_ANCHOR_FLIP |
                                                       GDK_ANCHOR_SLIDE |
                                                       GDK_ANCHOR_RESIZE,
                                                       G_PARAM_READWRITE |
                                                       G_PARAM_CONSTRUCT |
                                                       G_PARAM_STATIC_NAME |
                                                       G_PARAM_STATIC_NICK |
787 788
                                                       G_PARAM_STATIC_BLURB |
                                                       G_PARAM_EXPLICIT_NOTIFY));
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814

  /**
   * GtkMenu:rect-anchor-dx:
   *
   * Horizontal offset to apply to the menu, i.e. the rectangle or widget
   * anchor.
   *
   * See gtk_menu_popup_at_rect (), gtk_menu_popup_at_widget (),
   * gtk_menu_popup_at_pointer (), #GtkMenu:anchor-hints,
   * #GtkMenu:rect-anchor-dy, #GtkMenu:menu-type-hint, and #GtkMenu::popped-up.
   *
   * Since: 3.22
   * Stability: Unstable
   */
  g_object_class_install_property (gobject_class,
                                   PROP_RECT_ANCHOR_DX,
                                   g_param_spec_int ("rect-anchor-dx",
                                                     P_("Rect anchor dx"),
                                                     P_("Rect anchor horizontal offset"),
                                                     G_MININT,
                                                     G_MAXINT,
                                                     0,
                                                     G_PARAM_READWRITE |
                                                     G_PARAM_CONSTRUCT |
                                                     G_PARAM_STATIC_NAME |
                                                     G_PARAM_STATIC_NICK |
815 816
                                                     G_PARAM_STATIC_BLURB |
                                                     G_PARAM_EXPLICIT_NOTIFY));
817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841

  /**
   * GtkMenu:rect-anchor-dy:
   *
   * Vertical offset to apply to the menu, i.e. the rectangle or widget anchor.
   *
   * See gtk_menu_popup_at_rect (), gtk_menu_popup_at_widget (),
   * gtk_menu_popup_at_pointer (), #GtkMenu:anchor-hints,
   * #GtkMenu:rect-anchor-dx, #GtkMenu:menu-type-hint, and #GtkMenu::popped-up.
   *
   * Since: 3.22
   * Stability: Unstable
   */
  g_object_class_install_property (gobject_class,
                                   PROP_RECT_ANCHOR_DY,
                                   g_param_spec_int ("rect-anchor-dy",
                                                     P_("Rect anchor dy"),
                                                     P_("Rect anchor vertical offset"),
                                                     G_MININT,
                                                     G_MAXINT,
                                                     0,
                                                     G_PARAM_READWRITE |
                                                     G_PARAM_CONSTRUCT |
                                                     G_PARAM_STATIC_NAME |
                                                     G_PARAM_STATIC_NICK |
842 843
                                                     G_PARAM_STATIC_BLURB |
                                                     G_PARAM_EXPLICIT_NOTIFY));
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867

  /**
   * GtkMenu:menu-type-hint:
   *
   * The #GdkWindowTypeHint to use for the menu's #GdkWindow.
   *
   * See gtk_menu_popup_at_rect (), gtk_menu_popup_at_widget (),
   * gtk_menu_popup_at_pointer (), #GtkMenu:anchor-hints,
   * #GtkMenu:rect-anchor-dx, #GtkMenu:rect-anchor-dy, and #GtkMenu::popped-up.
   *
   * Since: 3.22
   * Stability: Unstable
   */
  g_object_class_install_property (gobject_class,
                                   PROP_MENU_TYPE_HINT,
                                   g_param_spec_enum ("menu-type-hint",
                                                      P_("Menu type hint"),
                                                      P_("Menu window type hint"),
                                                      GDK_TYPE_WINDOW_TYPE_HINT,
                                                      GDK_WINDOW_TYPE_HINT_POPUP_MENU,
                                                      G_PARAM_READWRITE |
                                                      G_PARAM_CONSTRUCT |
                                                      G_PARAM_STATIC_NAME |
                                                      G_PARAM_STATIC_NICK |
868 869
                                                      G_PARAM_STATIC_BLURB |
                                                      G_PARAM_EXPLICIT_NOTIFY));
870

871 872 873 874 875 876 877 878 879
  /**
   * GtkMenu:horizontal-padding:
   *
   * Extra space at the left and right edges of the menu.
   *
   * Deprecated: 3.8: use the standard padding CSS property (through objects
   *   like #GtkStyleContext and #GtkCssProvider); the value of this style
   *   property is ignored.
   */
880 881 882 883 884 885 886
  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,
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
                                                             GTK_PARAM_READABLE |
                                                             G_PARAM_DEPRECATED));

  /**
   * GtkMenu:vertical-padding:
   *
   * Extra space at the top and bottom of the menu.
   *
   * Deprecated: 3.8: use the standard padding CSS property (through objects
   *   like #GtkStyleContext and #GtkCssProvider); the value of this style
   *   property is ignored.
   */
  gtk_widget_class_install_style_property (widget_class,
                                           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 |
                                                             G_PARAM_DEPRECATED));
908

909
  gtk_widget_class_install_style_property (widget_class,
910 911 912 913 914 915 916
                                           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));
917 918

  gtk_widget_class_install_style_property (widget_class,
919 920 921 922 923 924 925
                                           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));
926

927 928 929 930 931 932 933
  /**
   * GtkMenu:double-arrows:
   *
   * When %TRUE, both arrows are shown when scrolling.
   *
   * Deprecated: 3.20: the value of this style property is ignored.
   **/
934 935 936 937 938
  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,
939
                                                                 GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
940

941 942 943 944 945 946
  /**
   * GtkMenu:arrow-placement:
   *
   * Indicates where scroll arrows should be placed.
   *
   * Since: 2.16
947 948
   *
   * Deprecated: 3.20: the value of this style property is ignored.
949 950 951 952 953 954 955
   **/
  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,
956
                                                              GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
957

958 959
 gtk_container_class_install_child_property (container_class,
                                             CHILD_PROP_LEFT_ATTACH,
960
                                              g_param_spec_int ("left-attach",
961 962
                                                               P_("Left Attach"),
                                                               P_("The column number to attach the left side of the child to"),
963
                                                                -1, INT_MAX, -1,
964
                                                               GTK_PARAM_READWRITE));
965 966 967

 gtk_container_class_install_child_property (container_class,
                                             CHILD_PROP_RIGHT_ATTACH,
968
                                              g_param_spec_int ("right-attach",
969 970
                                                               P_("Right Attach"),
                                                               P_("The column number to attach the right side of the child to"),
971
                                                                -1, INT_MAX, -1,
972
                                                               GTK_PARAM_READWRITE));
973 974 975

 gtk_container_class_install_child_property (container_class,
                                             CHILD_PROP_TOP_ATTACH,
976
                                              g_param_spec_int ("top-attach",
977 978
                                                               P_("Top Attach"),
                                                               P_("The row number to attach the top of the child to"),
979
                                                                -1, INT_MAX, -1,
980
                                                               GTK_PARAM_READWRITE));
981 982 983

 gtk_container_class_install_child_property (container_class,
                                             CHILD_PROP_BOTTOM_ATTACH,
984
                                              g_param_spec_int ("bottom-attach",
985 986
                                                               P_("Bottom Attach"),
                                                               P_("The row number to attach the bottom of the child to"),
987
                                                                -1, INT_MAX, -1,
988
                                                               GTK_PARAM_READWRITE));
989

990
 /**
991
  * GtkMenu:arrow-scaling:
992 993 994 995
  *
  * Arbitrary constant to scale down the size of the scroll arrow.
  *
  * Since: 2.16
996 997 998
  *
  * Deprecated: 3.20: use the standard min-width/min-height CSS properties on
  *   the arrow node; the value of this style property is ignored.
999 1000 1001 1002 1003 1004
  */
  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,
1005
                                                               GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
1006

1007 1008
  binding_set = gtk_binding_set_by_class (class);
  gtk_binding_entry_add_signal (binding_set,
1009 1010 1011 1012
                                GDK_KEY_Up, 0,
                                I_("move-current"), 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_PREV);
1013
  gtk_binding_entry_add_signal (binding_set,
1014 1015 1016 1017
                                GDK_KEY_KP_Up, 0,
                                "move-current", 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_PREV);
1018
  gtk_binding_entry_add_signal (binding_set,
1019 1020 1021 1022
                                GDK_KEY_Down, 0,
                                "move-current", 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_NEXT);
1023
  gtk_binding_entry_add_signal (binding_set,
1024 1025 1026 1027
                                GDK_KEY_KP_Down, 0,
                                "move-current", 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_NEXT);
1028
  gtk_binding_entry_add_signal (binding_set,
1029 1030 1031 1032
                                GDK_KEY_Left, 0,
                                "move-current", 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_PARENT);
1033
  gtk_binding_entry_add_signal (binding_set,
1034 1035 1036 1037
                                GDK_KEY_KP_Left, 0,
                                "move-current", 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_PARENT);
1038
  gtk_binding_entry_add_signal (binding_set,
1039 1040 1041 1042
                                GDK_KEY_Right, 0,
                                "move-current", 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_CHILD);
1043
  gtk_binding_entry_add_signal (binding_set,
1044 1045 1046 1047
                                GDK_KEY_KP_Right, 0,
                                "move-current", 1,
                                GTK_TYPE_MENU_DIRECTION_TYPE,
                                GTK_MENU_DIR_CHILD);
1048
  gtk_binding_entry_add_signal (binding_set,
1049 1050 1051 1052
                                GDK_KEY_Home, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_START);
1053
  gtk_binding_entry_add_signal (binding_set,
1054 1055 1056 1057
                                GDK_KEY_KP_Home, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_START);
1058
  gtk_binding_entry_add_signal (binding_set,
1059 1060 1061 1062
                                GDK_KEY_End, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_END);
1063
  gtk_binding_entry_add_signal (binding_set,
1064 1065 1066 1067
                                GDK_KEY_KP_End, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_END);
1068
  gtk_binding_entry_add_signal (binding_set,
1069 1070 1071 1072
                                GDK_KEY_Page_Up, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_PAGE_UP);
1073
  gtk_binding_entry_add_signal (binding_set,
1074 1075 1076 1077
                                GDK_KEY_KP_Page_Up, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_PAGE_UP);
1078
  gtk_binding_entry_add_signal (binding_set,
1079 1080 1081 1082
                                GDK_KEY_Page_Down, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_PAGE_DOWN);
1083
  gtk_binding_entry_add_signal (binding_set,
1084 1085 1086 1087
                                GDK_KEY_KP_Page_Down, 0,
                                "move-scroll", 1,
                                GTK_TYPE_SCROLL_TYPE,
                                GTK_SCROLL_PAGE_DOWN);
1088

1089
  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_MENU_ACCESSIBLE);
1090
  gtk_widget_class_set_css_name (widget_class, "menu");
Elliot Lee's avatar
Elliot Lee committed
1091 1092
}

1093

1094
static void
1095
gtk_menu_set_property (GObject      *object,
1096 1097 1098
                       guint         prop_id,
                       const GValue *value,
                       GParamSpec   *pspec)
1099
{
1100 1101
  GtkMenu *menu = GTK_MENU (object);

1102 1103
  switch (prop_id)
    {
Tim Janik's avatar
Tim Janik committed
1104
    case PROP_ACTIVE:
1105
      gtk_menu_set_active (menu, g_value_get_int (value));
Tim Janik's avatar
Tim Janik committed
1106 1107 1108 1109 1110 1111 1112 1113
      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
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
      {
        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
1125
      break;
1126
    case PROP_TEAROFF_STATE:
1127
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1128
      gtk_menu_set_tearoff_state (menu, g_value_get_boolean (value));
1129
G_GNUC_END_IGNORE_DEPRECATIONS;
1130
      break;
1131
    case PROP_TEAROFF_TITLE:
1132
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1133
      gtk_menu_set_title (menu, g_value_get_string (value));
1134
G_GNUC_END_IGNORE_DEPRECATIONS;
Tim Janik's avatar
Tim Janik committed
1135 1136 1137 1138
      break;
    case PROP_MONITOR:
      gtk_menu_set_monitor (menu, g_value_get_int (value));
      break;
1139 1140 1141
    case PROP_RESERVE_TOGGLE_SIZE:
      gtk_menu_set_reserve_toggle_size (menu, g_value_get_boolean (value));
      break;
1142
    case PROP_ANCHOR_HINTS:
1143 1144 1145 1146 1147
      if (menu->priv->anchor_hints != g_value_get_flags (value))
        {
          menu->priv->anchor_hints = g_value_get_flags (value);
          g_object_notify_by_pspec (object, pspec);
        }
1148 1149
      break;
    case PROP_RECT_ANCHOR_DX:
1150 1151 1152 1153 1154
      if (menu->priv->rect_anchor_dx != g_value_get_int (value))
        {
          menu->priv->rect_anchor_dx = g_value_get_int (value);
          g_object_notify_by_pspec (object, pspec);
        }
1155 1156
      break;
    case PROP_RECT_ANCHOR_DY:
1157 1158 1159 1160 1161
      if (menu->priv->rect_anchor_dy != g_value_get_int (value))
        {
          menu->priv->rect_anchor_dy = g_value_get_int (value);
          g_object_notify_by_pspec (object, pspec);
        }
1162 1163
      break;
    case PROP_MENU_TYPE_HINT:
1164 1165 1166 1167 1168
      if (menu->priv->menu_type_hint != g_value_get_enum (value))
        {
          menu->priv->menu_type_hint = g_value_get_enum (value);
          g_object_notify_by_pspec (object, pspec);
        }
1169
      break;
1170 1171 1172 1173 1174 1175
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

1176
static void
1177
gtk_menu_get_property (GObject     *object,
1178 1179 1180
                       guint        prop_id,
                       GValue      *value,
                       GParamSpec  *pspec)
1181
{
1182 1183
  GtkMenu *menu = GTK_MENU (object);

1184 1185
  switch (prop_id)
    {
Tim Janik's avatar
Tim Janik committed
1186
    case PROP_ACTIVE:
1187
      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
1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
      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;
1198
    case PROP_TEAROFF_STATE:
1199
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1200
      g_value_set_boolean (value, gtk_menu_get_tearoff_state (menu));
1201
G_GNUC_END_IGNORE_DEPRECATIONS;
1202
      break;
1203
    case PROP_TEAROFF_TITLE:
1204
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1205
      g_value_set_string (value, gtk_menu_get_title (menu));
1206
G_GNUC_END_IGNORE_DEPRECATIONS;
1207
      break;
Tim Janik's avatar
Tim Janik committed
1208 1209 1210
    case PROP_MONITOR:
      g_value_set_int (value, gtk_menu_get_monitor (menu));
      break;
1211 1212 1213
    case PROP_RESERVE_TOGGLE_SIZE:
      g_value_set_boolean (value, gtk_menu_get_reserve_toggle_size (menu));
      break;
1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225
    case PROP_ANCHOR_HINTS:
      g_value_set_flags (value, menu->priv->anchor_hints);
      break;
    case PROP_RECT_ANCHOR_DX:
      g_value_set_int (value, menu->priv->rect_anchor_dx);
      break;
    case PROP_RECT_ANCHOR_DY:
      g_value_set_int (value, menu->priv->rect_anchor_dy);
      break;
    case PROP_MENU_TYPE_HINT:
      g_value_set_enum (value, menu->priv->menu_type_hint);
      break;
1226 1227 1228 1229 1230 1231
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

1232 1233 1234 1235 1236 1237 1238 1239
static void
gtk_menu_set_child_property (GtkContainer *container,
                             GtkWidget    *child,
                             guint         property_id,
                             const GValue *value,
                             GParamSpec   *pspec)
{
  GtkMenu *menu = GTK_MENU (container);
1240
  AttachInfo *ai = get_attach_info (child);
1241 1242 1243

  switch (property_id)
    {
1244 1245 1246 1247 1248 1249 1250
    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:
1251
      ai->top_attach = g_value_get_int (value);
1252 1253 1254 1255 1256 1257 1258 1259
      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;
1260 1261
    }

1262
  menu_queue_resize (menu);
1263 1264 1265 1266 1267 1268 1269 1270 1271
}

static void
gtk_menu_get_child_property (GtkContainer *container,
                             GtkWidget    *child,
                             guint         property_id,
                             GValue       *value,
                             GParamSpec   *pspec)
{
1272
  AttachInfo *ai = get_attach_info (child);
1273 1274 1275

  switch (property_id)
    {
1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291
    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;
1292 1293 1294
    }
}

1295
static gboolean
1296
gtk_menu_window_event (GtkWidget *window,
1297 1298
                       GdkEvent  *event,
                       GtkWidget *menu)
1299 1300 1301
{
  gboolean handled = FALSE;

Manish Singh's avatar
Manish Singh committed
1302 1303
  g_object_ref (window);
  g_object_ref (menu);
1304 1305 1306 1307 1308

  switch (event->type)
    {
    case GDK_KEY_PRESS:
    case GDK_KEY_RELEASE:
1309
      handled = gtk_widget_event (menu, event);
1310
      break;
1311 1312 1313 1314 1315 1316 1317 1318 1319
    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;
1320 1321 1322 1323
    default:
      break;
    }

Manish Singh's avatar
Manish Singh committed
1324 1325
  g_object_unref (window);
  g_object_unref (menu);
1326 1327 1328 1329

  return handled;
}

Elliot Lee's avatar
Elliot Lee committed
1330 1331 1332
static void
gtk_menu_init (GtkMenu *menu)
{
1333
  GtkMenuPrivate *priv;
1334
  GtkCssNode *top_arrow_node, *bottom_arrow_node, *widget_node;
1335

1336
  priv = gtk_menu_get_instance_private (menu);
1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348

  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);
1349

Matthias Clasen's avatar
Matthias Clasen committed
1350
  _gtk_window_request_csd (GTK_WINDOW (priv->toplevel));
1351 1352
  gtk_style_context_add_class (gtk_widget_get_style_context (priv->toplevel),
                               GTK_STYLE_CLASS_POPUP);
1353

Owen Taylor's avatar
Owen Taylor committed
1354 1355 1356
  /* Refloat the menu, so that reference counting for the menu isn't
   * affected by it being a child of the toplevel
   */
1357
  g_object_force_floating (G_OBJECT (menu));
1358
  priv->needs_destruction_ref = TRUE;
Owen Taylor's avatar
Owen Taylor committed
1359

1360
  priv->monitor_num = -1;
1361
  priv->drag_start_y = -1;
1362

1363 1364 1365 1366 1367
  priv->anchor_hints = GDK_ANCHOR_FLIP | GDK_ANCHOR_SLIDE | GDK_ANCHOR_RESIZE;
  priv->rect_anchor_dx = 0;
  priv->rect_anchor_dy = 0;
  priv->menu_type_hint = GDK_WINDOW_TYPE_HINT_POPUP_MENU;

1368
  _gtk_widget_set_captured_event_handler (GTK_WIDGET (menu), gtk_menu_captured_event);
1369 1370

  widget_node = gtk_widget_get_css_node (GTK_WIDGET (menu));
1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387
  priv->top_arrow_gadget = gtk_builtin_icon_new ("arrow",
                                                 GTK_WIDGET (menu),
                                                 NULL, NULL);
  gtk_css_gadget_add_class (priv->top_arrow_gadget, GTK_STYLE_CLASS_TOP);
  top_arrow_node = gtk_css_gadget_get_node (priv->top_arrow_gadget);
  gtk_css_node_set_parent (top_arrow_node, widget_node);
  gtk_css_node_set_visible (top_arrow_node, FALSE);
  gtk_css_node_set_state (top_arrow_node, gtk_css_node_get_state (widget_node));

  priv->bottom_arrow_gadget = gtk_builtin_icon_new ("arrow",
                                                    GTK_WIDGET (menu),
                                                    NULL, NULL);
  gtk_css_gadget_add_class (priv->bottom_arrow_gadget, GTK_STYLE_CLASS_BOTTOM);
  bottom_arrow_node = gtk_css_gadget_get_node (priv->bottom_arrow_gadget);
  gtk_css_node_set_parent (bottom_arrow_node, widget_node);
  gtk_css_node_set_visible (bottom_arrow_node, FALSE);
  gtk_css_node_set_state (bottom_arrow_node, gtk_css_node_get_state (widget_node));
1388 1389
}

1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406
static void
moved_to_rect_cb (GdkWindow          *window,
                  const GdkRectangle *flipped_rect,
                  const GdkRectangle *final_rect,
                  gboolean            flipped_x,
                  gboolean            flipped_y,
                  GtkMenu            *menu)
{
  g_signal_emit (menu,
                 menu_signals[POPPED_UP],
                 0,
                 flipped_rect,
                 final_rect,
                 flipped_x,
                 flipped_y);
}

1407
static void
1408
gtk_menu_destroy (GtkWidget *widget)
1409
{
1410
  GtkMenu *menu = GTK_MENU (widget);
1411
  GtkMenuPrivate *priv = menu->priv;
1412
  GtkMenuAttachData *data;
1413

1414
  gtk_menu_remove_scroll_timeout (menu);
1415

1416
  data = g_object_get_data (G_OBJECT (widget), attach_data_key);
1417
  if (data)
1418
    gtk_menu_detach (menu);
1419

1420 1421
  gtk_menu_stop_navigating_submenu (menu);

1422
  g_clear_object (&priv->old_active_menu_item);
1423

Owen Taylor's avatar
Owen Taylor committed
1424
  /* Add back the reference count for being a child */
1425
  if (priv->needs_destruction_ref)
1426
    {
1427
      priv->needs_destruction_ref = FALSE;
1428
      g_object_ref (widget);
1429
    }
1430

1431
  g_clear_object (&priv->accel_group);
1432

1433
  if (priv->toplevel)
1434 1435 1436 1437
    {
      g_signal_handlers_disconnect_by_func (priv->toplevel, moved_to_rect_cb, menu);
      gtk_widget_destroy (priv->toplevel);
    }
1438

1439 1440
  if (priv->tearoff_window)
    gtk_widget_destroy (priv->tearoff_window);
1441

1442
  g_clear_pointer (&priv->heights, g_free);
1443

1444
  g_clear_pointer (&priv->title, g_free);
1445