]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenubutton.c
notebook: restore previous behaviour wrt. unparenting of tab labels
[~andy/gtk] / gtk / gtkmenubutton.c
1 /* GTK - The GIMP Toolkit
2  *
3  * Copyright (C) 2003 Ricardo Fernandez Pascual
4  * Copyright (C) 2004 Paolo Borelli
5  * Copyright (C) 2012 Bastien Nocera
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 /**
22  * SECTION:gtkmenubutton
23  * @short_description: A widget that shows a menu when clicked on
24  * @title: GtkMenuButton
25  *
26  * The #GtkMenuButton widget is used to display a menu when clicked on.
27  * This menu can be provided either as a #GtkMenu, or an abstract #GMenuModel.
28  *
29  * The #GtkMenuButton widget can hold any valid child widget.  That is, it can hold
30  * almost any other standard #GtkWidget. The most commonly used child is the
31  * provided #GtkArrow.
32  */
33
34 #include "config.h"
35
36 #include "gtkmenubutton.h"
37 #include "gtkmenubuttonprivate.h"
38 #include "gtkarrow.h"
39
40 #include "gtkprivate.h"
41 #include "gtkintl.h"
42
43 struct _GtkMenuButtonPrivate
44 {
45   GtkWidget *menu;
46   GMenuModel *model;
47
48   GtkMenuButtonShowMenuCallback func;
49   gpointer user_data;
50
51   GtkArrowType arrow_type;
52   GtkWidget *align_widget;
53   gpointer arrow_widget;
54 };
55
56 enum
57 {
58   PROP_0,
59   PROP_MENU,
60   PROP_MODEL,
61   PROP_ALIGN_WIDGET,
62   PROP_DIRECTION
63 };
64
65 G_DEFINE_TYPE(GtkMenuButton, gtk_menu_button, GTK_TYPE_TOGGLE_BUTTON)
66
67 static void gtk_menu_button_dispose (GObject *object);
68
69 static void
70 gtk_menu_button_set_property (GObject      *object,
71                               guint         property_id,
72                               const GValue *value,
73                               GParamSpec   *pspec)
74 {
75   GtkMenuButton *self = GTK_MENU_BUTTON (object);
76
77   switch (property_id)
78     {
79       case PROP_MENU:
80         gtk_menu_button_set_menu (self, g_value_get_object (value));
81         break;
82       case PROP_MODEL:
83         gtk_menu_button_set_menu_model (self, g_value_get_object (value));
84         break;
85       case PROP_ALIGN_WIDGET:
86         gtk_menu_button_set_align_widget (self, g_value_get_object (value));
87         break;
88       case PROP_DIRECTION:
89         gtk_menu_button_set_direction (self, g_value_get_enum (value));
90         break;
91       default:
92         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
93     }
94 }
95
96 static void
97 gtk_menu_button_get_property (GObject    *object,
98                               guint       property_id,
99                               GValue     *value,
100                               GParamSpec *pspec)
101 {
102   GtkMenuButtonPrivate *priv = GTK_MENU_BUTTON (object)->priv;
103
104   switch (property_id)
105     {
106       case PROP_MENU:
107         g_value_set_object (value, priv->menu);
108         break;
109       case PROP_MODEL:
110         g_value_set_object (value, priv->model);
111         break;
112       case PROP_ALIGN_WIDGET:
113         g_value_set_object (value, priv->align_widget);
114         break;
115       case PROP_DIRECTION:
116         g_value_set_enum (value, priv->arrow_type);
117         break;
118       default:
119         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
120     }
121 }
122
123 static void
124 gtk_menu_button_state_flags_changed (GtkWidget    *widget,
125                                      GtkStateFlags previous_state_flags)
126 {
127   GtkMenuButton *button = GTK_MENU_BUTTON (widget);
128   GtkMenuButtonPrivate *priv = button->priv;
129
130   if (!gtk_widget_is_sensitive (widget) && priv->menu)
131     gtk_menu_shell_deactivate (GTK_MENU_SHELL (priv->menu));
132 }
133
134 static void
135 menu_position_down_func (GtkMenu       *menu,
136                          gint          *x,
137                          gint          *y,
138                          gboolean      *push_in,
139                          GtkMenuButton *menu_button)
140 {
141   GtkMenuButtonPrivate *priv = menu_button->priv;
142   GtkWidget *widget = GTK_WIDGET (menu_button);
143   GtkRequisition menu_req;
144   GtkTextDirection direction;
145   GdkRectangle monitor;
146   gint monitor_num;
147   GdkScreen *screen;
148   GdkWindow *window;
149   GtkAllocation allocation, arrow_allocation;
150   GtkWidget *toplevel;
151
152   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (priv->menu));
153   gtk_window_set_type_hint (GTK_WINDOW (toplevel), GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU);
154
155   gtk_widget_get_preferred_size (GTK_WIDGET (priv->menu),
156                                  &menu_req, NULL);
157
158   direction = gtk_widget_get_direction (widget);
159   window = gtk_widget_get_window (priv->align_widget ? priv->align_widget : widget);
160
161   screen = gtk_widget_get_screen (GTK_WIDGET (menu));
162   monitor_num = gdk_screen_get_monitor_at_window (screen, window);
163   if (monitor_num < 0)
164     monitor_num = 0;
165   gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
166
167   gtk_widget_get_allocation (priv->align_widget ? priv->align_widget : widget, &allocation);
168   gtk_widget_get_allocation (widget, &arrow_allocation);
169
170   gdk_window_get_origin (window, x, y);
171   *x += allocation.x;
172   *y += allocation.y;
173
174   if (direction == GTK_TEXT_DIR_LTR)
175     *x += MAX (allocation.width - menu_req.width, 0);
176   else if (menu_req.width > allocation.width)
177     *x -= menu_req.width - allocation.width;
178
179   if ((*y + arrow_allocation.height + menu_req.height) <= monitor.y + monitor.height)
180     *y += arrow_allocation.height;
181   else if ((*y - menu_req.height) >= monitor.y)
182     *y -= menu_req.height;
183   else if (monitor.y + monitor.height - (*y + arrow_allocation.height) > *y)
184     *y += arrow_allocation.height;
185   else
186     *y -= menu_req.height;
187
188   *push_in = FALSE;
189 }
190
191 static void
192 menu_position_up_func (GtkMenu       *menu,
193                        gint          *x,
194                        gint          *y,
195                        gboolean      *push_in,
196                        GtkMenuButton *menu_button)
197 {
198   GtkMenuButtonPrivate *priv = menu_button->priv;
199   GtkWidget *widget = GTK_WIDGET (menu_button);
200   GtkRequisition menu_req;
201   GtkTextDirection direction;
202   GdkRectangle monitor;
203   gint monitor_num;
204   GdkScreen *screen;
205   GdkWindow *window;
206   GtkAllocation allocation, arrow_allocation;
207
208   gtk_widget_get_preferred_size (GTK_WIDGET (priv->menu),
209                                  &menu_req, NULL);
210
211   direction = gtk_widget_get_direction (widget);
212   window = gtk_widget_get_window (priv->align_widget ? priv->align_widget : widget);
213
214   screen = gtk_widget_get_screen (GTK_WIDGET (menu));
215   monitor_num = gdk_screen_get_monitor_at_window (screen, window);
216   if (monitor_num < 0)
217     monitor_num = 0;
218   gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
219
220   gtk_widget_get_allocation (priv->align_widget ? priv->align_widget : widget, &allocation);
221   gtk_widget_get_allocation (widget, &arrow_allocation);
222
223   gdk_window_get_origin (window, x, y);
224   *x += allocation.x;
225   *y += allocation.y;
226
227   if (direction == GTK_TEXT_DIR_LTR)
228     *x += MAX (allocation.width - menu_req.width, 0);
229   else if (menu_req.width > allocation.width)
230     *x -= menu_req.width - allocation.width;
231
232   *y -= menu_req.height;
233
234   /* If we're going to clip the top, pop down instead */
235   if (*y < monitor.y)
236     {
237       menu_position_down_func (menu, x, y, push_in, menu_button);
238       return;
239     }
240
241   *push_in = FALSE;
242 }
243
244 static void
245 menu_position_side_func (GtkMenu       *menu,
246                          gint          *x,
247                          gint          *y,
248                          gboolean      *push_in,
249                          GtkMenuButton *menu_button)
250 {
251   GtkMenuButtonPrivate *priv = menu_button->priv;
252   GtkAllocation toggle_allocation;
253   GtkWidget *widget = GTK_WIDGET (menu_button);
254   GtkRequisition menu_req;
255   GdkRectangle monitor;
256   gint monitor_num;
257   GdkScreen *screen;
258   GdkWindow *window;
259
260   gtk_widget_get_preferred_size (GTK_WIDGET (priv->menu),
261                                  &menu_req, NULL);
262
263   window = gtk_widget_get_window (widget);
264
265   screen = gtk_widget_get_screen (GTK_WIDGET (menu));
266   monitor_num = gdk_screen_get_monitor_at_window (screen, window);
267   if (monitor_num < 0)
268     monitor_num = 0;
269   gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
270
271   gdk_window_get_origin (gtk_button_get_event_window (GTK_BUTTON (menu_button)), x, y);
272
273   gtk_widget_get_allocation (widget, &toggle_allocation);
274
275   if (priv->arrow_type == GTK_ARROW_RIGHT)
276     *x += toggle_allocation.width;
277   else
278     *x -= menu_req.width;
279
280   if (*y + menu_req.height > monitor.y + monitor.height &&
281       *y + toggle_allocation.height - monitor.y > monitor.y + monitor.height - *y)
282     *y += toggle_allocation.height - menu_req.height;
283
284   *push_in = FALSE;
285 }
286
287 static void
288 popup_menu (GtkMenuButton  *menu_button,
289             GdkEventButton *event)
290 {
291   GtkMenuButtonPrivate *priv = menu_button->priv;
292   GtkMenuPositionFunc func;
293
294   if (priv->func)
295     priv->func (priv->user_data);
296
297   if (!priv->menu)
298     return;
299
300   switch (priv->arrow_type)
301     {
302       case GTK_ARROW_UP:
303         func = (GtkMenuPositionFunc) menu_position_up_func;
304         break;
305       case GTK_ARROW_LEFT:
306       case GTK_ARROW_RIGHT:
307         func = (GtkMenuPositionFunc) menu_position_side_func;
308         break;
309       default:
310         func = (GtkMenuPositionFunc) menu_position_down_func;
311         break;
312   }
313
314   gtk_menu_popup_for_device (GTK_MENU (priv->menu),
315                              event ? event->device : NULL,
316                              NULL, NULL,
317                              func,
318                              GTK_WIDGET (menu_button),
319                              NULL,
320                              event ? event->button : 0,
321                              event ? event->time : gtk_get_current_event_time ());
322 }
323
324 static void
325 gtk_menu_button_toggled (GtkToggleButton *button)
326 {
327   GtkMenuButton *menu_button = GTK_MENU_BUTTON (button);
328   GtkMenuButtonPrivate *priv = menu_button->priv;
329
330   if (!priv->menu)
331     return;
332
333   if (gtk_toggle_button_get_active (button) &&
334       !gtk_widget_get_visible (GTK_WIDGET (priv->menu)))
335     {
336       /* we get here only when the menu is activated by a key
337        * press, so that we can select the first menu item
338        */
339       popup_menu (menu_button, NULL);
340       gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->menu), FALSE);
341     }
342 }
343
344 static gboolean
345 gtk_menu_button_button_press_event (GtkWidget      *widget,
346                                     GdkEventButton *event)
347 {
348   if (event->button == GDK_BUTTON_PRIMARY)
349     {
350       popup_menu (GTK_MENU_BUTTON (widget), event);
351       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
352
353       return TRUE;
354     }
355
356   return GTK_WIDGET_CLASS (gtk_menu_button_parent_class)->button_press_event (widget, event);
357 }
358
359 static void
360 gtk_menu_button_class_init (GtkMenuButtonClass *klass)
361 {
362   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
363   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
364   GtkToggleButtonClass *toggle_button_class = GTK_TOGGLE_BUTTON_CLASS (klass);
365
366   g_type_class_add_private (klass, sizeof (GtkMenuButtonPrivate));
367
368   gobject_class->set_property = gtk_menu_button_set_property;
369   gobject_class->get_property = gtk_menu_button_get_property;
370   gobject_class->dispose = gtk_menu_button_dispose;
371
372   widget_class->state_flags_changed = gtk_menu_button_state_flags_changed;
373   widget_class->button_press_event = gtk_menu_button_button_press_event;
374
375   toggle_button_class->toggled = gtk_menu_button_toggled;
376
377   /**
378    * GtkMenuButton:menu:
379    *
380    * The #GtkMenu that will be popped up when the button is clicked.
381    *
382    * Since: 3.6
383    */
384   g_object_class_install_property (gobject_class,
385                                    PROP_MENU,
386                                    g_param_spec_object ("menu",
387                                                         P_("menu"),
388                                                         P_("The dropdown menu."),
389                                                         GTK_TYPE_MENU,
390                                                         G_PARAM_READWRITE));
391   /**
392    * GtkMenuButton:menu-model:
393    *
394    * The #GMenuModel from which the menu to pop up will be created.
395    * See gtk_menu_button_set_menu_model() for the interaction with the
396    * #GtkMenuButton:menu property.
397    *
398    * Since: 3.6
399    */
400   g_object_class_install_property (gobject_class,
401                                    PROP_MODEL,
402                                    g_param_spec_object ("menu-model",
403                                                         P_("menu-model"),
404                                                         P_("The dropdown menu's model."),
405                                                         G_TYPE_MENU_MODEL,
406                                                         G_PARAM_READWRITE));
407   /**
408    * GtkMenuButton:align-widget:
409    *
410    * The #GtkWidget to use to align the popup menu with.
411    *
412    * Since: 3.6
413    */
414   g_object_class_install_property (gobject_class,
415                                    PROP_ALIGN_WIDGET,
416                                    g_param_spec_object ("align-widget",
417                                                         P_("align-widget"),
418                                                         P_("The parent widget which the menu should align with."),
419                                                         GTK_TYPE_CONTAINER,
420                                                         G_PARAM_READWRITE));
421   /**
422    * GtkMenuButton:direction:
423    *
424    * The #GtkArrowType representing the direction in which the
425    * menu will be popped out.
426    *
427    * Since: 3.6
428    */
429   g_object_class_install_property (gobject_class,
430                                    PROP_DIRECTION,
431                                    g_param_spec_enum ("direction",
432                                                       P_("direction"),
433                                                       P_("The direction the arrow should point."),
434                                                       GTK_TYPE_ARROW_TYPE,
435                                                       GTK_ARROW_DOWN,
436                                                       G_PARAM_READWRITE));
437 }
438
439 static void
440 add_arrow (GtkMenuButton *menu_button)
441 {
442   GtkWidget *arrow;
443
444   arrow = gtk_arrow_new (menu_button->priv->arrow_type, GTK_SHADOW_NONE);
445   gtk_container_add (GTK_CONTAINER (menu_button), arrow);
446   gtk_widget_show (arrow);
447   menu_button->priv->arrow_widget = arrow;
448 }
449
450 static void
451 gtk_menu_button_init (GtkMenuButton *menu_button)
452 {
453   GtkMenuButtonPrivate *priv;
454
455   priv = G_TYPE_INSTANCE_GET_PRIVATE (menu_button, GTK_TYPE_MENU_BUTTON, GtkMenuButtonPrivate);
456   menu_button->priv = priv;
457   priv->arrow_type = GTK_ARROW_DOWN;
458
459   add_arrow (menu_button);
460
461   gtk_widget_set_sensitive (GTK_WIDGET (menu_button), FALSE);
462 }
463
464 /**
465  * gtk_menu_button_new:
466  *
467  * Creates a new #GtkMenuButton widget with downwards-pointing
468  * arrow as the only child. You can replace the child widget
469  * with another #GtkWidget should you wish to.
470  *
471  * Returns: The newly created #GtkMenuButton widget.
472  *
473  * Since: 3.6
474  */
475 GtkWidget *
476 gtk_menu_button_new (void)
477 {
478   return g_object_new (GTK_TYPE_MENU_BUTTON, NULL);
479 }
480
481 /* Callback for the "deactivate" signal on the pop-up menu.
482  * This is used so that we unset the state of the toggle button
483  * when the pop-up menu disappears.
484  */
485 static int
486 menu_deactivate_cb (GtkMenuShell  *menu_shell,
487                     GtkMenuButton *menu_button)
488 {
489   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menu_button), FALSE);
490
491   return TRUE;
492 }
493
494 static void
495 menu_detacher (GtkWidget *widget,
496                GtkMenu   *menu)
497 {
498   GtkMenuButtonPrivate *priv = GTK_MENU_BUTTON (widget)->priv;
499
500   g_return_if_fail (priv->menu == (GtkWidget *) menu);
501
502   priv->menu = NULL;
503 }
504
505 /* This function is used in GtkMenuToolButton, the call back will
506  * be called when GtkMenuToolButton would have emitted the "show-menu"
507  * signal.
508  */
509 void
510 _gtk_menu_button_set_menu_with_func (GtkMenuButton                 *menu_button,
511                                      GtkWidget                     *menu,
512                                      GtkMenuButtonShowMenuCallback  func,
513                                      gpointer                       user_data)
514 {
515   GtkMenuButtonPrivate *priv;
516
517   g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button));
518   g_return_if_fail (GTK_IS_MENU (menu) || menu == NULL);
519
520   priv = menu_button->priv;
521   priv->func = func;
522   priv->user_data = user_data;
523
524   if (priv->menu == GTK_WIDGET (menu))
525     return;
526
527   if (priv->menu)
528     {
529       if (gtk_widget_get_visible (GTK_WIDGET (priv->menu)))
530         gtk_menu_shell_deactivate (GTK_MENU_SHELL (priv->menu));
531     }
532
533   if (priv->menu)
534     {
535       g_signal_handlers_disconnect_by_func (priv->menu,
536                                             menu_deactivate_cb,
537                                             menu_button);
538       gtk_menu_detach (GTK_MENU (priv->menu));
539     }
540
541   priv->menu = menu;
542
543   if (priv->menu)
544     {
545       gtk_menu_attach_to_widget (GTK_MENU (priv->menu), GTK_WIDGET (menu_button),
546                                  menu_detacher);
547
548       gtk_widget_set_sensitive (GTK_WIDGET (menu_button), TRUE);
549
550       g_signal_connect (priv->menu, "deactivate",
551                         G_CALLBACK (menu_deactivate_cb), menu_button);
552     }
553   else
554     {
555       gtk_widget_set_sensitive (GTK_WIDGET (menu_button), FALSE);
556     }
557
558   g_object_notify (G_OBJECT (menu_button), "menu");
559   g_object_notify (G_OBJECT (menu_button), "menu-model");
560 }
561
562 /**
563  * gtk_menu_button_set_menu:
564  * @menu_button: a #GtkMenuButton
565  * @menu: (allow-none): a #GtkMenu
566  *
567  * Sets the #GtkMenu that will be popped up when the button is clicked,
568  * or %NULL to disable the button. If #GtkMenuButton:menu-model is set,
569  * it will be set to %NULL.
570  *
571  * Since: 3.6
572  */
573 void
574 gtk_menu_button_set_menu (GtkMenuButton *menu_button,
575                           GtkWidget     *menu)
576 {
577   GtkMenuButtonPrivate *priv;
578
579   g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button));
580   g_return_if_fail (GTK_IS_MENU (menu) || menu == NULL);
581
582   priv = menu_button->priv;
583   g_clear_object (&priv->model);
584
585   _gtk_menu_button_set_menu_with_func (menu_button, menu, NULL, NULL);
586 }
587
588 /**
589  * gtk_menu_button_get_menu:
590  * @menu_button: a #GtkMenuButton
591  *
592  * Returns the #GtkMenu that pops out of the button.
593  *
594  * Returns: (transfer none): a #GtkMenu or %NULL.
595  *
596  * Since: 3.6
597  */
598 GtkMenu *
599 gtk_menu_button_get_menu (GtkMenuButton *menu_button)
600 {
601   g_return_val_if_fail (GTK_IS_MENU_BUTTON (menu_button), NULL);
602
603   return GTK_MENU (menu_button->priv->menu);
604 }
605
606 /**
607  * gtk_menu_button_set_menu_model:
608  * @menu_button: a #GtkMenuButton
609  * @menu_model: (allow-none): a #GMenuModel
610  *
611  * Sets the #GMenuModel from which the #GtkMenuButton:menu property will be
612  * filled in, or %NULL to disable the button.
613  *
614  * The #GtkMenu will be created with gtk_menu_new_from_model(), so actions
615  * will be connected as documented there.
616  *
617  * If you #GtkMenuButton:menu * is already set, then its content will be lost
618  * and replaced by our newly created #GtkMenu.
619  *
620  * Since: 3.6
621  */
622 void
623 gtk_menu_button_set_menu_model (GtkMenuButton *menu_button,
624                                 GMenuModel    *menu_model)
625 {
626   GtkMenuButtonPrivate *priv;
627   GtkWidget *menu;
628
629   g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button));
630   g_return_if_fail (G_IS_MENU_MODEL (menu_model) || menu_model == NULL);
631
632   priv = menu_button->priv;
633   g_clear_object (&priv->model);
634
635   if (menu_model == NULL)
636     {
637       gtk_menu_button_set_menu (menu_button, NULL);
638       return;
639     }
640
641   priv->model = g_object_ref (menu_model);
642   menu = gtk_menu_new_from_model (menu_model);
643   gtk_widget_show_all (menu);
644   gtk_menu_button_set_menu (menu_button, menu);
645 }
646
647 /**
648  * gtk_menu_button_get_menu_model:
649  * @menu_button: a #GtkMenuButton
650  *
651  * Returns the #GMenuModel used to generate the menu.
652  *
653  * Returns: (transfer none): a #GMenuModel or %NULL.
654  *
655  * Since: 3.6
656  */
657 GMenuModel *
658 gtk_menu_button_get_menu_model (GtkMenuButton *menu_button)
659 {
660   g_return_val_if_fail (GTK_IS_MENU_BUTTON (menu_button), NULL);
661
662   return menu_button->priv->model;
663 }
664
665 /**
666  * gtk_menu_button_set_align_widget:
667  * @menu_button: a #GtkMenuButton
668  * @align_widget: (allow-none): a #GtkWidget
669  *
670  * Sets the #GtkWidget to use to line the menu with when popped up. Note that
671  * the @align_widget must contain the #GtkMenuButton itself.
672  *
673  * Setting it to %NULL means that the popup menu will be aligned with the
674  * button itself.
675  *
676  * Since: 3.6
677  */
678 void
679 gtk_menu_button_set_align_widget (GtkMenuButton *menu_button,
680                                   GtkWidget     *align_widget)
681 {
682   GtkMenuButtonPrivate *priv;
683
684   g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button));
685   g_return_if_fail (align_widget == NULL || gtk_widget_is_ancestor (GTK_WIDGET (menu_button), align_widget));
686
687   priv = menu_button->priv;
688   if (priv->align_widget == align_widget)
689     return;
690
691   priv->align_widget = align_widget;
692
693   if (priv->align_widget)
694     g_object_add_weak_pointer (G_OBJECT (priv->align_widget), (gpointer *) &priv->align_widget);
695
696   g_object_notify (G_OBJECT (menu_button), "align-widget");
697 }
698
699 /**
700  * gtk_menu_button_get_align_widget:
701  * @menu_button: a #GtkMenuButton
702  *
703  * Returns the parent #GtkWidget to use to line up with menu.
704  *
705  * Returns: (transfer none): a #GtkWidget value or %NULL.
706  *
707  * Since: 3.6
708  */
709 GtkWidget *
710 gtk_menu_button_get_align_widget (GtkMenuButton *menu_button)
711 {
712   g_return_val_if_fail (GTK_IS_MENU_BUTTON (menu_button), NULL);
713
714   return menu_button->priv->align_widget;
715 }
716
717 /**
718  * gtk_menu_button_set_direction:
719  * @menu_button: a #GtkMenuButton
720  * @direction: a #GtkArrowType
721  *
722  * Sets the direction in which the menu will be popped up, as
723  * well as changing the arrow's direction. The child will not
724  * be changed to an arrow if it was customized.
725  *
726  * If the menu when popped out would have collided with screen edges,
727  * we will do our best to keep it inside the screen and fully visible.
728  *
729  * If you pass GTK_ARROW_NONE for a @direction, the menu will behave
730  * as if you passed GTK_ARROW_DOWN (although you won't see any arrows).
731  *
732  * Since: 3.6
733  */
734 void
735 gtk_menu_button_set_direction (GtkMenuButton *menu_button,
736                                GtkArrowType   direction)
737 {
738   GtkMenuButtonPrivate *priv = menu_button->priv;
739   GtkWidget *child;
740
741   g_return_if_fail (GTK_IS_MENU_BUTTON (menu_button));
742
743   if (priv->arrow_type == direction)
744     return;
745
746   priv->arrow_type = direction;
747
748   /* Is it custom content? We don't change that */
749   child = gtk_bin_get_child (GTK_BIN (menu_button));
750   if (priv->arrow_widget != child)
751     return;
752
753   gtk_arrow_set (GTK_ARROW (child), priv->arrow_type, GTK_SHADOW_NONE);
754 }
755
756 /**
757  * gtk_menu_button_get_direction:
758  * @menu_button: a #GtkMenuButton
759  *
760  * Returns the direction the menu will be pointing at when popped up.
761  *
762  * Returns: a #GtkArrowType value.
763  *
764  * Since: 3.6
765  */
766 GtkArrowType
767 gtk_menu_button_get_direction (GtkMenuButton *menu_button)
768 {
769   g_return_val_if_fail (GTK_IS_MENU_BUTTON (menu_button), GTK_ARROW_DOWN);
770
771   return menu_button->priv->arrow_type;
772 }
773
774 static void
775 gtk_menu_button_dispose (GObject *object)
776 {
777   GtkMenuButtonPrivate *priv = GTK_MENU_BUTTON (object)->priv;
778
779   if (priv->menu)
780     {
781       g_signal_handlers_disconnect_by_func (priv->menu,
782                                             menu_deactivate_cb,
783                                             object);
784       gtk_menu_detach (GTK_MENU (priv->menu));
785     }
786
787   g_clear_object (&priv->model);
788
789   G_OBJECT_CLASS (gtk_menu_button_parent_class)->dispose (object);
790 }