Bug Summary

File:ctk/ctkmenu.c
Warning:line 432, column 33
Out of bound memory access (access exceeds upper limit of memory block)

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name ctkmenu.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/rootdir/ctk -resource-dir /usr/lib/llvm-16/lib/clang/16 -D HAVE_CONFIG_H -I . -I .. -D G_LOG_DOMAIN="Ctk" -D G_LOG_USE_STRUCTURED=1 -D CTK_VERSION="3.25.5" -D CTK_BINARY_VERSION="3.0.0" -D CTK_COMPILATION -D CTK_PRINT_BACKEND_ENABLE_UNSUPPORTED -D CTK_LIBDIR="/usr/lib" -D CTK_LOCALEDIR="/usr/share/locale" -D CTK_DATADIR="/usr/share" -D CTK_DATA_PREFIX="/usr" -D CTK_SYSCONFDIR="/usr/etc" -D CTK_HOST="x86_64-pc-linux-gnu" -D CTK_PRINT_BACKENDS="file,cups" -D X11_DATA_PREFIX="/usr" -D ISO_CODES_PREFIX="" -I .. -I ../ctk -I .. -I ../cdk -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -D G_ENABLE_DEBUG -D G_ENABLE_CONSISTENCY_CHECKS -D GLIB_MIN_REQUIRED_VERSION=GLIB_VERSION_2_66 -D GLIB_MAX_ALLOWED_VERSION=GLIB_VERSION_2_66 -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/atk-1.0 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/gio-unix-2.0 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/pango-1.0 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -D PIC -internal-isystem /usr/lib/llvm-16/lib/clang/16/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdebug-compilation-dir=/rootdir/ctk -ferror-limit 19 -fvisibility=hidden -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2024-09-19-170505-43637-1 -x c ctkmenu.c
1/* CTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * 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.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/*
19 * Modified by the CTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the CTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * CTK+ at ftp://ftp.ctk.org/pub/ctk/.
23 */
24
25/**
26 * SECTION:ctkmenu
27 * @Short_description: A menu widget
28 * @Title: CtkMenu
29 *
30 * A #CtkMenu is a #CtkMenuShell that implements a drop down menu
31 * consisting of a list of #CtkMenuItem objects which can be navigated
32 * and activated by the user to perform application functions.
33 *
34 * A #CtkMenu is most commonly dropped down by activating a
35 * #CtkMenuItem in a #CtkMenuBar or popped up by activating a
36 * #CtkMenuItem in another #CtkMenu.
37 *
38 * A #CtkMenu can also be popped up by activating a #CtkComboBox.
39 * Other composite widgets such as the #CtkNotebook can pop up a
40 * #CtkMenu as well.
41 *
42 * Applications can display a #CtkMenu as a popup menu by calling the
43 * ctk_menu_popup() function. The example below shows how an application
44 * can pop up a menu when the 3rd mouse button is pressed.
45 *
46 * ## Connecting the popup signal handler.
47 *
48 * |[<!-- language="C" -->
49 * // connect our handler which will popup the menu
50 * g_signal_connect_swapped (window, "button_press_event",
51 * G_CALLBACK (my_popup_handler), menu);
52 * ]|
53 *
54 * ## Signal handler which displays a popup menu.
55 *
56 * |[<!-- language="C" -->
57 * static gint
58 * my_popup_handler (CtkWidget *widget, CdkEvent *event)
59 * {
60 * CtkMenu *menu;
61 * CdkEventButton *event_button;
62 *
63 * g_return_val_if_fail (widget != NULL, FALSE);
64 * g_return_val_if_fail (CTK_IS_MENU (widget), FALSE);
65 * g_return_val_if_fail (event != NULL, FALSE);
66 *
67 * // The "widget" is the menu that was supplied when
68 * // g_signal_connect_swapped() was called.
69 * menu = CTK_MENU (widget);
70 *
71 * if (event->type == CDK_BUTTON_PRESS)
72 * {
73 * event_button = (CdkEventButton *) event;
74 * if (event_button->button == CDK_BUTTON_SECONDARY)
75 * {
76 * ctk_menu_popup (menu, NULL, NULL, NULL, NULL,
77 * event_button->button, event_button->time);
78 * return TRUE;
79 * }
80 * }
81 *
82 * return FALSE;
83 * }
84 * ]|
85 *
86 * # CSS nodes
87 *
88 * |[<!-- language="plain" -->
89 * menu
90 * ├── arrow.top
91 * ├── <child>
92 * ┊
93 * ├── <child>
94 * ╰── arrow.bottom
95 * ]|
96 *
97 * The main CSS node of CtkMenu has name menu, and there are two subnodes
98 * with name arrow, for scrolling menu arrows. These subnodes get the
99 * .top and .bottom style classes.
100 */
101
102#include "config.h"
103
104#include <math.h>
105#include <string.h>
106
107#include <gobject/gvaluecollector.h>
108
109#include "ctkaccellabel.h"
110#include "ctkaccelmap.h"
111#include "ctkadjustment.h"
112#include "ctkbindings.h"
113#include "ctkbuiltiniconprivate.h"
114#include "ctkcheckmenuitem.h"
115#include "ctkcheckmenuitemprivate.h"
116#include "ctkmain.h"
117#include "ctkmarshalers.h"
118#include "ctkmenuprivate.h"
119#include "ctkmenuitemprivate.h"
120#include "ctkmenushellprivate.h"
121#include "ctkwindow.h"
122#include "ctkbox.h"
123#include "ctkscrollbar.h"
124#include "ctksettings.h"
125#include "ctkprivate.h"
126#include "ctkwidgetpath.h"
127#include "ctkwidgetprivate.h"
128#include "ctkdnd.h"
129#include "ctkintl.h"
130#include "ctktypebuiltins.h"
131#include "ctkwidgetprivate.h"
132#include "ctkwindowprivate.h"
133#include "ctkcssnodeprivate.h"
134#include "ctkstylecontextprivate.h"
135#include "ctkcssstylepropertyprivate.h"
136#include "ctktooltipprivate.h"
137
138#include "ctktearoffmenuitem.h"
139
140
141#include "a11y/ctkmenuaccessible.h"
142#include "cdk/cdk-private.h"
143
144#define NAVIGATION_REGION_OVERSHOOT50 50 /* How much the navigation region
145 * extends below the submenu
146 */
147
148#define MENU_SCROLL_STEP18 8
149#define MENU_SCROLL_STEP215 15
150#define MENU_SCROLL_FAST_ZONE8 8
151#define MENU_SCROLL_TIMEOUT150 50
152#define MENU_SCROLL_TIMEOUT220 20
153
154#define MENU_POPUP_DELAY225 225
155#define MENU_POPDOWN_DELAY1000 1000
156
157#define ATTACH_INFO_KEY"ctk-menu-child-attach-info-key" "ctk-menu-child-attach-info-key"
158#define ATTACHED_MENUS"ctk-attached-menus" "ctk-attached-menus"
159
160typedef struct _CtkMenuAttachData CtkMenuAttachData;
161typedef struct _CtkMenuPopdownData CtkMenuPopdownData;
162
163struct _CtkMenuAttachData
164{
165 CtkWidget *attach_widget;
166 CtkMenuDetachFunc detacher;
167};
168
169struct _CtkMenuPopdownData
170{
171 CtkMenu *menu;
172 CdkDevice *device;
173};
174
175typedef struct
176{
177 gint left_attach;
178 gint right_attach;
179 gint top_attach;
180 gint bottom_attach;
181 gint effective_left_attach;
182 gint effective_right_attach;
183 gint effective_top_attach;
184 gint effective_bottom_attach;
185} AttachInfo;
186
187enum {
188 MOVE_SCROLL,
189 POPPED_UP,
190 LAST_SIGNAL
191};
192
193enum {
194 PROP_0,
195 PROP_ACTIVE,
196 PROP_ACCEL_GROUP,
197 PROP_ACCEL_PATH,
198 PROP_ATTACH_WIDGET,
199 PROP_TEAROFF_STATE,
200 PROP_TEAROFF_TITLE,
201 PROP_MONITOR,
202 PROP_RESERVE_TOGGLE_SIZE,
203 PROP_ANCHOR_HINTS,
204 PROP_RECT_ANCHOR_DX,
205 PROP_RECT_ANCHOR_DY,
206 PROP_MENU_TYPE_HINT
207};
208
209enum {
210 CHILD_PROP_0,
211 CHILD_PROP_LEFT_ATTACH,
212 CHILD_PROP_RIGHT_ATTACH,
213 CHILD_PROP_TOP_ATTACH,
214 CHILD_PROP_BOTTOM_ATTACH
215};
216
217typedef enum _CtkMenuScrollFlag
218{
219 CTK_MENU_SCROLL_FLAG_NONE = 0,
220 CTK_MENU_SCROLL_FLAG_ADAPT = 1 << 0,
221} CtkMenuScrollFlag;
222
223static void ctk_menu_set_property (GObject *object,
224 guint prop_id,
225 const GValue *value,
226 GParamSpec *pspec);
227static void ctk_menu_get_property (GObject *object,
228 guint prop_id,
229 GValue *value,
230 GParamSpec *pspec);
231static void ctk_menu_finalize (GObject *object);
232static void ctk_menu_set_child_property(CtkContainer *container,
233 CtkWidget *child,
234 guint property_id,
235 const GValue *value,
236 GParamSpec *pspec);
237static void ctk_menu_get_child_property(CtkContainer *container,
238 CtkWidget *child,
239 guint property_id,
240 GValue *value,
241 GParamSpec *pspec);
242static void ctk_menu_destroy (CtkWidget *widget);
243static void ctk_menu_realize (CtkWidget *widget);
244static void ctk_menu_unrealize (CtkWidget *widget);
245static void ctk_menu_size_allocate (CtkWidget *widget,
246 CtkAllocation *allocation);
247static void ctk_menu_show (CtkWidget *widget);
248static gboolean ctk_menu_draw (CtkWidget *widget,
249 cairo_t *cr);
250static gboolean ctk_menu_key_press (CtkWidget *widget,
251 CdkEventKey *event);
252static gboolean ctk_menu_scroll (CtkWidget *widget,
253 CdkEventScroll *event);
254static gboolean ctk_menu_button_press (CtkWidget *widget,
255 CdkEventButton *event);
256static gboolean ctk_menu_button_release (CtkWidget *widget,
257 CdkEventButton *event);
258static gboolean ctk_menu_motion_notify (CtkWidget *widget,
259 CdkEventMotion *event);
260static gboolean ctk_menu_enter_notify (CtkWidget *widget,
261 CdkEventCrossing *event);
262static gboolean ctk_menu_leave_notify (CtkWidget *widget,
263 CdkEventCrossing *event);
264static void ctk_menu_scroll_to (CtkMenu *menu,
265 gint offset,
266 CtkMenuScrollFlag flags);
267static void ctk_menu_grab_notify (CtkWidget *widget,
268 gboolean was_grabbed);
269static gboolean ctk_menu_captured_event (CtkWidget *widget,
270 CdkEvent *event);
271
272
273static void ctk_menu_stop_scrolling (CtkMenu *menu);
274static void ctk_menu_remove_scroll_timeout (CtkMenu *menu);
275static gboolean ctk_menu_scroll_timeout (gpointer data);
276
277static void ctk_menu_scroll_item_visible (CtkMenuShell *menu_shell,
278 CtkWidget *menu_item);
279static void ctk_menu_select_item (CtkMenuShell *menu_shell,
280 CtkWidget *menu_item);
281static void ctk_menu_real_insert (CtkMenuShell *menu_shell,
282 CtkWidget *child,
283 gint position);
284static void ctk_menu_scrollbar_changed (CtkAdjustment *adjustment,
285 CtkMenu *menu);
286static void ctk_menu_handle_scrolling (CtkMenu *menu,
287 gint event_x,
288 gint event_y,
289 gboolean enter,
290 gboolean motion);
291static void ctk_menu_set_tearoff_hints (CtkMenu *menu,
292 gint width);
293static gboolean ctk_menu_focus (CtkWidget *widget,
294 CtkDirectionType direction);
295static gint ctk_menu_get_popup_delay (CtkMenuShell *menu_shell);
296static void ctk_menu_move_current (CtkMenuShell *menu_shell,
297 CtkMenuDirectionType direction);
298static void ctk_menu_real_move_scroll (CtkMenu *menu,
299 CtkScrollType type);
300
301static void ctk_menu_stop_navigating_submenu (CtkMenu *menu);
302static gboolean ctk_menu_stop_navigating_submenu_cb (gpointer user_data);
303static gboolean ctk_menu_navigating_submenu (CtkMenu *menu,
304 gint event_x,
305 gint event_y);
306static void ctk_menu_set_submenu_navigation_region (CtkMenu *menu,
307 CtkMenuItem *menu_item,
308 CdkEventCrossing *event);
309
310static void ctk_menu_deactivate (CtkMenuShell *menu_shell);
311static void ctk_menu_show_all (CtkWidget *widget);
312static void ctk_menu_position (CtkMenu *menu,
313 gboolean set_scroll_offset);
314static void ctk_menu_reparent (CtkMenu *menu,
315 CtkWidget *new_parent,
316 gboolean unrealize);
317static void ctk_menu_remove (CtkContainer *menu,
318 CtkWidget *widget);
319
320static void ctk_menu_update_title (CtkMenu *menu);
321
322static void menu_grab_transfer_window_destroy (CtkMenu *menu);
323static CdkWindow *menu_grab_transfer_window_get (CtkMenu *menu);
324
325static gboolean ctk_menu_real_can_activate_accel (CtkWidget *widget,
326 guint signal_id);
327static void _ctk_menu_refresh_accel_paths (CtkMenu *menu,
328 gboolean group_changed);
329
330static void ctk_menu_get_preferred_width (CtkWidget *widget,
331 gint *minimum_size,
332 gint *natural_size);
333static void ctk_menu_get_preferred_height (CtkWidget *widget,
334 gint *minimum_size,
335 gint *natural_size);
336static void ctk_menu_get_preferred_height_for_width (CtkWidget *widget,
337 gint for_size,
338 gint *minimum_size,
339 gint *natural_size);
340
341
342static const gchar attach_data_key[] = "ctk-menu-attach-data";
343
344static guint menu_signals[LAST_SIGNAL] = { 0 };
345
346G_DEFINE_TYPE_WITH_PRIVATE (CtkMenu, ctk_menu, CTK_TYPE_MENU_SHELL)static void ctk_menu_init (CtkMenu *self); static void ctk_menu_class_init
(CtkMenuClass *klass); static GType ctk_menu_get_type_once (
void); static gpointer ctk_menu_parent_class = ((void*)0); static
gint CtkMenu_private_offset; static void ctk_menu_class_intern_init
(gpointer klass) { ctk_menu_parent_class = g_type_class_peek_parent
(klass); if (CtkMenu_private_offset != 0) g_type_class_adjust_private_offset
(klass, &CtkMenu_private_offset); ctk_menu_class_init ((
CtkMenuClass*) klass); } __attribute__ ((__unused__)) static inline
gpointer ctk_menu_get_instance_private (CtkMenu *self) { return
(((gpointer) ((guint8*) (self) + (glong) (CtkMenu_private_offset
)))); } GType ctk_menu_get_type (void) { static GType static_g_define_type_id
= 0; if ((__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id
) == sizeof (gpointer), "Expression evaluates to false"); (void
) (0 ? (gpointer) * (&static_g_define_type_id) : ((void*)
0)); (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id
) == sizeof (gpointer), "Expression evaluates to false"); __typeof__
(*(&static_g_define_type_id)) gapg_temp_newval; __typeof__
((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id
); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5)
; gapg_temp_newval; })) && g_once_init_enter_pointer (
&static_g_define_type_id)); })) ) { GType g_define_type_id
= ctk_menu_get_type_once (); (__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id
) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer
((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id
)); })) ; } return static_g_define_type_id; } __attribute__ (
(__noinline__)) static GType ctk_menu_get_type_once (void) { GType
g_define_type_id = g_type_register_static_simple ((ctk_menu_shell_get_type
()), g_intern_static_string ("CtkMenu"), sizeof (CtkMenuClass
), (GClassInitFunc)(void (*)(void)) ctk_menu_class_intern_init
, sizeof (CtkMenu), (GInstanceInitFunc)(void (*)(void)) ctk_menu_init
, (GTypeFlags) 0); { {{ CtkMenu_private_offset = g_type_add_instance_private
(g_define_type_id, sizeof (CtkMenuPrivate)); };} } return g_define_type_id
; }
347
348static void
349menu_queue_resize (CtkMenu *menu)
350{
351 CtkMenuPrivate *priv = menu->priv;
352
353 priv->have_layout = FALSE(0);
354 ctk_widget_queue_resize (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
355}
356
357static void
358attach_info_free (AttachInfo *info)
359{
360 g_slice_free (AttachInfo, info)do { if (1) g_slice_free1 (sizeof (AttachInfo), (info)); else
(void) ((AttachInfo*) 0 == (info)); } while (0)
;
361}
362
363static AttachInfo *
364get_attach_info (CtkWidget *child)
365{
366 GObject *object = G_OBJECT (child)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((child)), (((GType) ((20) << (2))))))))
;
367 AttachInfo *ai = g_object_get_data (object, ATTACH_INFO_KEY"ctk-menu-child-attach-info-key");
368
369 if (!ai)
370 {
371 ai = g_slice_new0 (AttachInfo)((AttachInfo*) g_slice_alloc0 (sizeof (AttachInfo)));
372 g_object_set_data_full (object, I_(ATTACH_INFO_KEY)g_intern_static_string ("ctk-menu-child-attach-info-key"), ai,
373 (GDestroyNotify) attach_info_free);
374 }
375
376 return ai;
377}
378
379static gboolean
380is_grid_attached (AttachInfo *ai)
381{
382 return (ai->left_attach >= 0 &&
383 ai->right_attach >= 0 &&
384 ai->top_attach >= 0 &&
385 ai->bottom_attach >= 0);
386}
387
388static void
389menu_ensure_layout (CtkMenu *menu)
390{
391 CtkMenuPrivate *priv = menu->priv;
392
393 if (!priv->have_layout)
8
Assuming field 'have_layout' is 0
9
Taking true branch
394 {
395 CtkMenuShell *menu_shell = CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
;
396 GList *l;
397 gchar *row_occupied;
398 gint current_row;
399 gint max_right_attach;
400 gint max_bottom_attach;
401
402 /* Find extents of gridded portion
403 */
404 max_right_attach = 1;
405 max_bottom_attach = 0;
406
407 for (l = menu_shell->priv->children; l; l = l->next)
10
Loop condition is true. Entering loop body
12
Loop condition is false. Execution continues on line 420
408 {
409 CtkWidget *child = l->data;
410 AttachInfo *ai = get_attach_info (child);
411
412 if (is_grid_attached (ai))
11
Taking false branch
413 {
414 max_bottom_attach = MAX (max_bottom_attach, ai->bottom_attach)(((max_bottom_attach) > (ai->bottom_attach)) ? (max_bottom_attach
) : (ai->bottom_attach))
;
415 max_right_attach = MAX (max_right_attach, ai->right_attach)(((max_right_attach) > (ai->right_attach)) ? (max_right_attach
) : (ai->right_attach))
;
416 }
417 }
418
419 /* Find empty rows */
420 row_occupied = g_malloc0 (max_bottom_attach);
421
422 for (l = menu_shell->priv->children; l; l = l->next)
13
Loop condition is true. Entering loop body
423 {
424 CtkWidget *child = l->data;
425 AttachInfo *ai = get_attach_info (child);
426
427 if (is_grid_attached (ai))
14
Taking true branch
428 {
429 gint i;
430
431 for (i = ai->top_attach; i < ai->bottom_attach; i++)
15
Assuming 'i' is < field 'bottom_attach'
16
Loop condition is true. Entering loop body
432 row_occupied[i] = TRUE(!(0));
17
Out of bound memory access (access exceeds upper limit of memory block)
433 }
434 }
435
436 /* Lay non-grid-items out in those rows
437 */
438 current_row = 0;
439 for (l = menu_shell->priv->children; l; l = l->next)
440 {
441 CtkWidget *child = l->data;
442 AttachInfo *ai = get_attach_info (child);
443
444 if (!is_grid_attached (ai))
445 {
446 while (current_row < max_bottom_attach && row_occupied[current_row])
447 current_row++;
448
449 ai->effective_left_attach = 0;
450 ai->effective_right_attach = max_right_attach;
451 ai->effective_top_attach = current_row;
452 ai->effective_bottom_attach = current_row + 1;
453
454 current_row++;
455 }
456 else
457 {
458 ai->effective_left_attach = ai->left_attach;
459 ai->effective_right_attach = ai->right_attach;
460 ai->effective_top_attach = ai->top_attach;
461 ai->effective_bottom_attach = ai->bottom_attach;
462 }
463 }
464
465 g_free (row_occupied);
466
467 priv->n_rows = MAX (current_row, max_bottom_attach)(((current_row) > (max_bottom_attach)) ? (current_row) : (
max_bottom_attach))
;
468 priv->n_columns = max_right_attach;
469 priv->have_layout = TRUE(!(0));
470 }
471}
472
473
474static gint
475ctk_menu_get_n_columns (CtkMenu *menu)
476{
477 CtkMenuPrivate *priv = menu->priv;
478
479 menu_ensure_layout (menu);
480
481 return priv->n_columns;
482}
483
484static gint
485ctk_menu_get_n_rows (CtkMenu *menu)
486{
487 CtkMenuPrivate *priv = menu->priv;
488
489 menu_ensure_layout (menu);
490
491 return priv->n_rows;
492}
493
494static void
495get_effective_child_attach (CtkWidget *child,
496 int *l,
497 int *r,
498 int *t,
499 int *b)
500{
501 CtkMenu *menu = CTK_MENU (ctk_widget_get_parent (child))((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((ctk_widget_get_parent (child))), ((ctk_menu_get_type ())
)))))
;
502 AttachInfo *ai;
503
504 menu_ensure_layout (menu);
7
Calling 'menu_ensure_layout'
505
506 ai = get_attach_info (child);
507
508 if (l)
509 *l = ai->effective_left_attach;
510 if (r)
511 *r = ai->effective_right_attach;
512 if (t)
513 *t = ai->effective_top_attach;
514 if (b)
515 *b = ai->effective_bottom_attach;
516
517}
518
519static void
520ctk_menu_class_init (CtkMenuClass *class)
521{
522 GObjectClass *gobject_class = G_OBJECT_CLASS (class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((class)), (((GType) ((20) << (2))))))))
;
523 CtkWidgetClass *widget_class = CTK_WIDGET_CLASS (class)((((CtkWidgetClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((class)), ((ctk_widget_get_type ()))))))
;
524 CtkContainerClass *container_class = CTK_CONTAINER_CLASS (class)((((CtkContainerClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((class)), ((ctk_container_get_type ()))))))
;
525 CtkMenuShellClass *menu_shell_class = CTK_MENU_SHELL_CLASS (class)((((CtkMenuShellClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((class)), ((ctk_menu_shell_get_type ()))))))
;
526 CtkBindingSet *binding_set;
527
528 gobject_class->set_property = ctk_menu_set_property;
529 gobject_class->get_property = ctk_menu_get_property;
530 gobject_class->finalize = ctk_menu_finalize;
531
532 widget_class->destroy = ctk_menu_destroy;
533 widget_class->realize = ctk_menu_realize;
534 widget_class->unrealize = ctk_menu_unrealize;
535 widget_class->size_allocate = ctk_menu_size_allocate;
536 widget_class->show = ctk_menu_show;
537 widget_class->draw = ctk_menu_draw;
538 widget_class->scroll_event = ctk_menu_scroll;
539 widget_class->key_press_event = ctk_menu_key_press;
540 widget_class->button_press_event = ctk_menu_button_press;
541 widget_class->button_release_event = ctk_menu_button_release;
542 widget_class->motion_notify_event = ctk_menu_motion_notify;
543 widget_class->show_all = ctk_menu_show_all;
544 widget_class->enter_notify_event = ctk_menu_enter_notify;
545 widget_class->leave_notify_event = ctk_menu_leave_notify;
546 widget_class->focus = ctk_menu_focus;
547 widget_class->can_activate_accel = ctk_menu_real_can_activate_accel;
548 widget_class->grab_notify = ctk_menu_grab_notify;
549 widget_class->get_preferred_width = ctk_menu_get_preferred_width;
550 widget_class->get_preferred_height = ctk_menu_get_preferred_height;
551 widget_class->get_preferred_height_for_width = ctk_menu_get_preferred_height_for_width;
552
553 container_class->remove = ctk_menu_remove;
554 container_class->get_child_property = ctk_menu_get_child_property;
555 container_class->set_child_property = ctk_menu_set_child_property;
556
557 menu_shell_class->submenu_placement = CTK_LEFT_RIGHT;
558 menu_shell_class->deactivate = ctk_menu_deactivate;
559 menu_shell_class->select_item = ctk_menu_select_item;
560 menu_shell_class->insert = ctk_menu_real_insert;
561 menu_shell_class->get_popup_delay = ctk_menu_get_popup_delay;
562 menu_shell_class->move_current = ctk_menu_move_current;
563
564 /**
565 * CtkMenu::move-scroll:
566 * @menu: a #CtkMenu
567 * @scroll_type: a #CtkScrollType
568 */
569 menu_signals[MOVE_SCROLL] =
570 g_signal_new_class_handler (I_("move-scroll")g_intern_static_string ("move-scroll"),
571 G_OBJECT_CLASS_TYPE (gobject_class)((((GTypeClass*) (gobject_class))->g_type)),
572 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
573 G_CALLBACK (ctk_menu_real_move_scroll)((GCallback) (ctk_menu_real_move_scroll)),
574 NULL((void*)0), NULL((void*)0),
575 NULL((void*)0),
576 G_TYPE_NONE((GType) ((1) << (2))), 1,
577 CTK_TYPE_SCROLL_TYPE(ctk_scroll_type_get_type ()));
578
579 /**
580 * CtkMenu::popped-up:
581 * @menu: the #CtkMenu that popped up
582 * @flipped_rect: (nullable): the position of @menu after any possible
583 * flipping or %NULL if the backend can't obtain it
584 * @final_rect: (nullable): the final position of @menu or %NULL if the
585 * backend can't obtain it
586 * @flipped_x: %TRUE if the anchors were flipped horizontally
587 * @flipped_y: %TRUE if the anchors were flipped vertically
588 *
589 * Emitted when the position of @menu is finalized after being popped up
590 * using ctk_menu_popup_at_rect (), ctk_menu_popup_at_widget (), or
591 * ctk_menu_popup_at_pointer ().
592 *
593 * @menu might be flipped over the anchor rectangle in order to keep it
594 * on-screen, in which case @flipped_x and @flipped_y will be set to %TRUE
595 * accordingly.
596 *
597 * @flipped_rect is the ideal position of @menu after any possible flipping,
598 * but before any possible sliding. @final_rect is @flipped_rect, but possibly
599 * translated in the case that flipping is still ineffective in keeping @menu
600 * on-screen.
601 *
602 * ![](popup-slide.png)
603 *
604 * The blue menu is @menu's ideal position, the green menu is @flipped_rect,
605 * and the red menu is @final_rect.
606 *
607 * See ctk_menu_popup_at_rect (), ctk_menu_popup_at_widget (),
608 * ctk_menu_popup_at_pointer (), #CtkMenu:anchor-hints,
609 * #CtkMenu:rect-anchor-dx, #CtkMenu:rect-anchor-dy, and
610 * #CtkMenu:menu-type-hint.
611 *
612 * Since: 3.22
613 */
614 menu_signals[POPPED_UP] =
615 g_signal_new_class_handler (I_("popped-up")g_intern_static_string ("popped-up"),
616 G_OBJECT_CLASS_TYPE (gobject_class)((((GTypeClass*) (gobject_class))->g_type)),
617 G_SIGNAL_RUN_FIRST,
618 NULL((void*)0),
619 NULL((void*)0),
620 NULL((void*)0),
621 _ctk_marshal_VOID__POINTER_POINTER_BOOLEAN_BOOLEAN,
622 G_TYPE_NONE((GType) ((1) << (2))),
623 4,
624 G_TYPE_POINTER((GType) ((17) << (2))),
625 G_TYPE_POINTER((GType) ((17) << (2))),
626 G_TYPE_BOOLEAN((GType) ((5) << (2))),
627 G_TYPE_BOOLEAN((GType) ((5) << (2))));
628
629 /**
630 * CtkMenu:active:
631 *
632 * The index of the currently selected menu item, or -1 if no
633 * menu item is selected.
634 *
635 * Since: 2.14
636 **/
637 g_object_class_install_property (gobject_class,
638 PROP_ACTIVE,
639 g_param_spec_int ("active",
640 P_("Active")g_dgettext("ctk30" "-properties","Active"),
641 P_("The currently selected menu item")g_dgettext("ctk30" "-properties","The currently selected menu item"
)
,
642 -1, G_MAXINT2147483647, -1,
643 CTK_PARAM_READWRITEG_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
644
645 /**
646 * CtkMenu:accel-group:
647 *
648 * The accel group holding accelerators for the menu.
649 *
650 * Since: 2.14
651 **/
652 g_object_class_install_property (gobject_class,
653 PROP_ACCEL_GROUP,
654 g_param_spec_object ("accel-group",
655 P_("Accel Group")g_dgettext("ctk30" "-properties","Accel Group"),
656 P_("The accel group holding accelerators for the menu")g_dgettext("ctk30" "-properties","The accel group holding accelerators for the menu"
)
,
657 CTK_TYPE_ACCEL_GROUP(ctk_accel_group_get_type ()),
658 CTK_PARAM_READWRITEG_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
659
660 /**
661 * CtkMenu:accel-path:
662 *
663 * An accel path used to conveniently construct accel paths of child items.
664 *
665 * Since: 2.14
666 **/
667 g_object_class_install_property (gobject_class,
668 PROP_ACCEL_PATH,
669 g_param_spec_string ("accel-path",
670 P_("Accel Path")g_dgettext("ctk30" "-properties","Accel Path"),
671 P_("An accel path used to conveniently construct accel paths of child items")g_dgettext("ctk30" "-properties","An accel path used to conveniently construct accel paths of child items"
)
,
672 NULL((void*)0),
673 CTK_PARAM_READWRITEG_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
674
675 /**
676 * CtkMenu:attach-widget:
677 *
678 * The widget the menu is attached to. Setting this property attaches
679 * the menu without a #CtkMenuDetachFunc. If you need to use a detacher,
680 * use ctk_menu_attach_to_widget() directly.
681 *
682 * Since: 2.14
683 **/
684 g_object_class_install_property (gobject_class,
685 PROP_ATTACH_WIDGET,
686 g_param_spec_object ("attach-widget",
687 P_("Attach Widget")g_dgettext("ctk30" "-properties","Attach Widget"),
688 P_("The widget the menu is attached to")g_dgettext("ctk30" "-properties","The widget the menu is attached to"
)
,
689 CTK_TYPE_WIDGET(ctk_widget_get_type ()),
690 CTK_PARAM_READWRITEG_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
691
692 /**
693 * CtkMenu:tearoff-title:
694 *
695 * A title that may be displayed by the window manager when this
696 * menu is torn-off.
697 *
698 * Deprecated: 3.10
699 **/
700 g_object_class_install_property (gobject_class,
701 PROP_TEAROFF_TITLE,
702 g_param_spec_string ("tearoff-title",
703 P_("Tearoff Title")g_dgettext("ctk30" "-properties","Tearoff Title"),
704 P_("A title that may be displayed by the window manager when this menu is torn-off")g_dgettext("ctk30" "-properties","A title that may be displayed by the window manager when this menu is torn-off"
)
,
705 NULL((void*)0),
706 CTK_PARAM_READWRITEG_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
707
708 /**
709 * CtkMenu:tearoff-state:
710 *
711 * A boolean that indicates whether the menu is torn-off.
712 *
713 * Since: 2.6
714 *
715 * Deprecated: 3.10
716 **/
717 g_object_class_install_property (gobject_class,
718 PROP_TEAROFF_STATE,
719 g_param_spec_boolean ("tearoff-state",
720 P_("Tearoff State")g_dgettext("ctk30" "-properties","Tearoff State"),
721 P_("A boolean that indicates whether the menu is torn-off")g_dgettext("ctk30" "-properties","A boolean that indicates whether the menu is torn-off"
)
,
722 FALSE(0),
723 CTK_PARAM_READWRITEG_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
724
725 /**
726 * CtkMenu:monitor:
727 *
728 * The monitor the menu will be popped up on.
729 *
730 * Since: 2.14
731 **/
732 g_object_class_install_property (gobject_class,
733 PROP_MONITOR,
734 g_param_spec_int ("monitor",
735 P_("Monitor")g_dgettext("ctk30" "-properties","Monitor"),
736 P_("The monitor the menu will be popped up on")g_dgettext("ctk30" "-properties","The monitor the menu will be popped up on"
)
,
737 -1, G_MAXINT2147483647, -1,
738 CTK_PARAM_READWRITEG_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB|G_PARAM_EXPLICIT_NOTIFY));
739
740 /**
741 * CtkMenu:reserve-toggle-size:
742 *
743 * A boolean that indicates whether the menu reserves space for
744 * toggles and icons, regardless of their actual presence.
745 *
746 * This property should only be changed from its default value
747 * for special-purposes such as tabular menus. Regular menus that
748 * are connected to a menu bar or context menus should reserve
749 * toggle space for consistency.
750 *
751 * Since: 2.18
752 */
753 g_object_class_install_property (gobject_class,
754 PROP_RESERVE_TOGGLE_SIZE,
755 g_param_spec_boolean ("reserve-toggle-size",
756 P_("Reserve Toggle Size")g_dgettext("ctk30" "-properties","Reserve Toggle Size"),
757 P_("A boolean that indicates whether the menu reserves space for toggles and icons")g_dgettext("ctk30" "-properties","A boolean that indicates whether the menu reserves space for toggles and icons"
)
,
758 TRUE(!(0)),
759 CTK_PARAM_READWRITEG_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB|G_PARAM_EXPLICIT_NOTIFY));
760
761 /**
762 * CtkMenu:anchor-hints:
763 *
764 * Positioning hints for aligning the menu relative to a rectangle.
765 *
766 * These hints determine how the menu should be positioned in the case that
767 * the menu would fall off-screen if placed in its ideal position.
768 *
769 * ![](popup-flip.png)
770 *
771 * For example, %CDK_ANCHOR_FLIP_Y will replace %CDK_GRAVITY_NORTH_WEST with
772 * %CDK_GRAVITY_SOUTH_WEST and vice versa if the menu extends beyond the
773 * bottom edge of the monitor.
774 *
775 * See ctk_menu_popup_at_rect (), ctk_menu_popup_at_widget (),
776 * ctk_menu_popup_at_pointer (), #CtkMenu:rect-anchor-dx,
777 * #CtkMenu:rect-anchor-dy, #CtkMenu:menu-type-hint, and #CtkMenu::popped-up.
778 *
779 * Since: 3.22
780 */
781 g_object_class_install_property (gobject_class,
782 PROP_ANCHOR_HINTS,
783 g_param_spec_flags ("anchor-hints",
784 P_("Anchor hints")g_dgettext("ctk30" "-properties","Anchor hints"),
785 P_("Positioning hints for when the menu might fall off-screen")g_dgettext("ctk30" "-properties","Positioning hints for when the menu might fall off-screen"
)
,
786 CDK_TYPE_ANCHOR_HINTS(cdk_anchor_hints_get_type ()),
787 CDK_ANCHOR_FLIP |
788 CDK_ANCHOR_SLIDE |
789 CDK_ANCHOR_RESIZE,
790 G_PARAM_READWRITE |
791 G_PARAM_CONSTRUCT |
792 G_PARAM_STATIC_NAME |
793 G_PARAM_STATIC_NICK |
794 G_PARAM_STATIC_BLURB |
795 G_PARAM_EXPLICIT_NOTIFY));
796
797 /**
798 * CtkMenu:rect-anchor-dx:
799 *
800 * Horizontal offset to apply to the menu, i.e. the rectangle or widget
801 * anchor.
802 *
803 * See ctk_menu_popup_at_rect (), ctk_menu_popup_at_widget (),
804 * ctk_menu_popup_at_pointer (), #CtkMenu:anchor-hints,
805 * #CtkMenu:rect-anchor-dy, #CtkMenu:menu-type-hint, and #CtkMenu::popped-up.
806 *
807 * Since: 3.22
808 */
809 g_object_class_install_property (gobject_class,
810 PROP_RECT_ANCHOR_DX,
811 g_param_spec_int ("rect-anchor-dx",
812 P_("Rect anchor dx")g_dgettext("ctk30" "-properties","Rect anchor dx"),
813 P_("Rect anchor horizontal offset")g_dgettext("ctk30" "-properties","Rect anchor horizontal offset"
)
,
814 G_MININT(-2147483647 -1),
815 G_MAXINT2147483647,
816 0,
817 G_PARAM_READWRITE |
818 G_PARAM_CONSTRUCT |
819 G_PARAM_STATIC_NAME |
820 G_PARAM_STATIC_NICK |
821 G_PARAM_STATIC_BLURB |
822 G_PARAM_EXPLICIT_NOTIFY));
823
824 /**
825 * CtkMenu:rect-anchor-dy:
826 *
827 * Vertical offset to apply to the menu, i.e. the rectangle or widget anchor.
828 *
829 * See ctk_menu_popup_at_rect (), ctk_menu_popup_at_widget (),
830 * ctk_menu_popup_at_pointer (), #CtkMenu:anchor-hints,
831 * #CtkMenu:rect-anchor-dx, #CtkMenu:menu-type-hint, and #CtkMenu::popped-up.
832 *
833 * Since: 3.22
834 */
835 g_object_class_install_property (gobject_class,
836 PROP_RECT_ANCHOR_DY,
837 g_param_spec_int ("rect-anchor-dy",
838 P_("Rect anchor dy")g_dgettext("ctk30" "-properties","Rect anchor dy"),
839 P_("Rect anchor vertical offset")g_dgettext("ctk30" "-properties","Rect anchor vertical offset"
)
,
840 G_MININT(-2147483647 -1),
841 G_MAXINT2147483647,
842 0,
843 G_PARAM_READWRITE |
844 G_PARAM_CONSTRUCT |
845 G_PARAM_STATIC_NAME |
846 G_PARAM_STATIC_NICK |
847 G_PARAM_STATIC_BLURB |
848 G_PARAM_EXPLICIT_NOTIFY));
849
850 /**
851 * CtkMenu:menu-type-hint:
852 *
853 * The #CdkWindowTypeHint to use for the menu's #CdkWindow.
854 *
855 * See ctk_menu_popup_at_rect (), ctk_menu_popup_at_widget (),
856 * ctk_menu_popup_at_pointer (), #CtkMenu:anchor-hints,
857 * #CtkMenu:rect-anchor-dx, #CtkMenu:rect-anchor-dy, and #CtkMenu::popped-up.
858 *
859 * Since: 3.22
860 */
861 g_object_class_install_property (gobject_class,
862 PROP_MENU_TYPE_HINT,
863 g_param_spec_enum ("menu-type-hint",
864 P_("Menu type hint")g_dgettext("ctk30" "-properties","Menu type hint"),
865 P_("Menu window type hint")g_dgettext("ctk30" "-properties","Menu window type hint"),
866 CDK_TYPE_WINDOW_TYPE_HINT(cdk_window_type_hint_get_type ()),
867 CDK_WINDOW_TYPE_HINT_POPUP_MENU,
868 G_PARAM_READWRITE |
869 G_PARAM_CONSTRUCT |
870 G_PARAM_STATIC_NAME |
871 G_PARAM_STATIC_NICK |
872 G_PARAM_STATIC_BLURB |
873 G_PARAM_EXPLICIT_NOTIFY));
874
875 /**
876 * CtkMenu:horizontal-padding:
877 *
878 * Extra space at the left and right edges of the menu.
879 *
880 * Deprecated: 3.8: use the standard padding CSS property (through objects
881 * like #CtkStyleContext and #CtkCssProvider); the value of this style
882 * property is ignored.
883 */
884 ctk_widget_class_install_style_property (widget_class,
885 g_param_spec_int ("horizontal-padding",
886 P_("Horizontal Padding")g_dgettext("ctk30" "-properties","Horizontal Padding"),
887 P_("Extra space at the left and right edges of the menu")g_dgettext("ctk30" "-properties","Extra space at the left and right edges of the menu"
)
,
888 0,
889 G_MAXINT2147483647,
890 0,
891 CTK_PARAM_READABLEG_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB |
892 G_PARAM_DEPRECATED));
893
894 /**
895 * CtkMenu:vertical-padding:
896 *
897 * Extra space at the top and bottom of the menu.
898 *
899 * Deprecated: 3.8: use the standard padding CSS property (through objects
900 * like #CtkStyleContext and #CtkCssProvider); the value of this style
901 * property is ignored.
902 */
903 ctk_widget_class_install_style_property (widget_class,
904 g_param_spec_int ("vertical-padding",
905 P_("Vertical Padding")g_dgettext("ctk30" "-properties","Vertical Padding"),
906 P_("Extra space at the top and bottom of the menu")g_dgettext("ctk30" "-properties","Extra space at the top and bottom of the menu"
)
,
907 0,
908 G_MAXINT2147483647,
909 1,
910 CTK_PARAM_READABLEG_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB |
911 G_PARAM_DEPRECATED));
912
913 ctk_widget_class_install_style_property (widget_class,
914 g_param_spec_int ("vertical-offset",
915 P_("Vertical Offset")g_dgettext("ctk30" "-properties","Vertical Offset"),
916 P_("When the menu is a submenu, position it this number of pixels offset vertically")g_dgettext("ctk30" "-properties","When the menu is a submenu, position it this number of pixels offset vertically"
)
,
917 G_MININT(-2147483647 -1),
918 G_MAXINT2147483647,
919 0,
920 CTK_PARAM_READABLEG_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
921
922 ctk_widget_class_install_style_property (widget_class,
923 g_param_spec_int ("horizontal-offset",
924 P_("Horizontal Offset")g_dgettext("ctk30" "-properties","Horizontal Offset"),
925 P_("When the menu is a submenu, position it this number of pixels offset horizontally")g_dgettext("ctk30" "-properties","When the menu is a submenu, position it this number of pixels offset horizontally"
)
,
926 G_MININT(-2147483647 -1),
927 G_MAXINT2147483647,
928 -2,
929 CTK_PARAM_READABLEG_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
930
931 /**
932 * CtkMenu:double-arrows:
933 *
934 * When %TRUE, both arrows are shown when scrolling.
935 *
936 * Deprecated: 3.20: the value of this style property is ignored.
937 **/
938 ctk_widget_class_install_style_property (widget_class,
939 g_param_spec_boolean ("double-arrows",
940 P_("Double Arrows")g_dgettext("ctk30" "-properties","Double Arrows"),
941 P_("When scrolling, always show both arrows.")g_dgettext("ctk30" "-properties","When scrolling, always show both arrows."
)
,
942 TRUE(!(0)),
943 CTK_PARAM_READABLEG_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB|G_PARAM_DEPRECATED));
944
945 /**
946 * CtkMenu:arrow-placement:
947 *
948 * Indicates where scroll arrows should be placed.
949 *
950 * Since: 2.16
951 *
952 * Deprecated: 3.20: the value of this style property is ignored.
953 **/
954 ctk_widget_class_install_style_property (widget_class,
955 g_param_spec_enum ("arrow-placement",
956 P_("Arrow Placement")g_dgettext("ctk30" "-properties","Arrow Placement"),
957 P_("Indicates where scroll arrows should be placed")g_dgettext("ctk30" "-properties","Indicates where scroll arrows should be placed"
)
,
958 CTK_TYPE_ARROW_PLACEMENT(ctk_arrow_placement_get_type ()),
959 CTK_ARROWS_BOTH,
960 CTK_PARAM_READABLEG_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB|G_PARAM_DEPRECATED));
961
962 ctk_container_class_install_child_property (container_class,
963 CHILD_PROP_LEFT_ATTACH,
964 g_param_spec_int ("left-attach",
965 P_("Left Attach")g_dgettext("ctk30" "-properties","Left Attach"),
966 P_("The column number to attach the left side of the child to")g_dgettext("ctk30" "-properties","The column number to attach the left side of the child to"
)
,
967 -1, INT_MAX2147483647, -1,
968 CTK_PARAM_READWRITEG_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
969
970 ctk_container_class_install_child_property (container_class,
971 CHILD_PROP_RIGHT_ATTACH,
972 g_param_spec_int ("right-attach",
973 P_("Right Attach")g_dgettext("ctk30" "-properties","Right Attach"),
974 P_("The column number to attach the right side of the child to")g_dgettext("ctk30" "-properties","The column number to attach the right side of the child to"
)
,
975 -1, INT_MAX2147483647, -1,
976 CTK_PARAM_READWRITEG_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
977
978 ctk_container_class_install_child_property (container_class,
979 CHILD_PROP_TOP_ATTACH,
980 g_param_spec_int ("top-attach",
981 P_("Top Attach")g_dgettext("ctk30" "-properties","Top Attach"),
982 P_("The row number to attach the top of the child to")g_dgettext("ctk30" "-properties","The row number to attach the top of the child to"
)
,
983 -1, INT_MAX2147483647, -1,
984 CTK_PARAM_READWRITEG_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
985
986 ctk_container_class_install_child_property (container_class,
987 CHILD_PROP_BOTTOM_ATTACH,
988 g_param_spec_int ("bottom-attach",
989 P_("Bottom Attach")g_dgettext("ctk30" "-properties","Bottom Attach"),
990 P_("The row number to attach the bottom of the child to")g_dgettext("ctk30" "-properties","The row number to attach the bottom of the child to"
)
,
991 -1, INT_MAX2147483647, -1,
992 CTK_PARAM_READWRITEG_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
993
994 /**
995 * CtkMenu:arrow-scaling:
996 *
997 * Arbitrary constant to scale down the size of the scroll arrow.
998 *
999 * Since: 2.16
1000 *
1001 * Deprecated: 3.20: use the standard min-width/min-height CSS properties on
1002 * the arrow node; the value of this style property is ignored.
1003 */
1004 ctk_widget_class_install_style_property (widget_class,
1005 g_param_spec_float ("arrow-scaling",
1006 P_("Arrow Scaling")g_dgettext("ctk30" "-properties","Arrow Scaling"),
1007 P_("Arbitrary constant to scale down the size of the scroll arrow")g_dgettext("ctk30" "-properties","Arbitrary constant to scale down the size of the scroll arrow"
)
,
1008 0.0, 1.0, 0.7,
1009 CTK_PARAM_READABLEG_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB|G_PARAM_DEPRECATED));
1010
1011 binding_set = ctk_binding_set_by_class (class);
1012 ctk_binding_entry_add_signal (binding_set,
1013 CDK_KEY_Up0xff52, 0,
1014 I_("move-current")g_intern_static_string ("move-current"), 1,
1015 CTK_TYPE_MENU_DIRECTION_TYPE(ctk_menu_direction_type_get_type ()),
1016 CTK_MENU_DIR_PREV);
1017 ctk_binding_entry_add_signal (binding_set,
1018 CDK_KEY_KP_Up0xff97, 0,
1019 "move-current", 1,
1020 CTK_TYPE_MENU_DIRECTION_TYPE(ctk_menu_direction_type_get_type ()),
1021 CTK_MENU_DIR_PREV);
1022 ctk_binding_entry_add_signal (binding_set,
1023 CDK_KEY_Down0xff54, 0,
1024 "move-current", 1,
1025 CTK_TYPE_MENU_DIRECTION_TYPE(ctk_menu_direction_type_get_type ()),
1026 CTK_MENU_DIR_NEXT);
1027 ctk_binding_entry_add_signal (binding_set,
1028 CDK_KEY_KP_Down0xff99, 0,
1029 "move-current", 1,
1030 CTK_TYPE_MENU_DIRECTION_TYPE(ctk_menu_direction_type_get_type ()),
1031 CTK_MENU_DIR_NEXT);
1032 ctk_binding_entry_add_signal (binding_set,
1033 CDK_KEY_Left0xff51, 0,
1034 "move-current", 1,
1035 CTK_TYPE_MENU_DIRECTION_TYPE(ctk_menu_direction_type_get_type ()),
1036 CTK_MENU_DIR_PARENT);
1037 ctk_binding_entry_add_signal (binding_set,
1038 CDK_KEY_KP_Left0xff96, 0,
1039 "move-current", 1,
1040 CTK_TYPE_MENU_DIRECTION_TYPE(ctk_menu_direction_type_get_type ()),
1041 CTK_MENU_DIR_PARENT);
1042 ctk_binding_entry_add_signal (binding_set,
1043 CDK_KEY_Right0xff53, 0,
1044 "move-current", 1,
1045 CTK_TYPE_MENU_DIRECTION_TYPE(ctk_menu_direction_type_get_type ()),
1046 CTK_MENU_DIR_CHILD);
1047 ctk_binding_entry_add_signal (binding_set,
1048 CDK_KEY_KP_Right0xff98, 0,
1049 "move-current", 1,
1050 CTK_TYPE_MENU_DIRECTION_TYPE(ctk_menu_direction_type_get_type ()),
1051 CTK_MENU_DIR_CHILD);
1052 ctk_binding_entry_add_signal (binding_set,
1053 CDK_KEY_Home0xff50, 0,
1054 "move-scroll", 1,
1055 CTK_TYPE_SCROLL_TYPE(ctk_scroll_type_get_type ()),
1056 CTK_SCROLL_START);
1057 ctk_binding_entry_add_signal (binding_set,
1058 CDK_KEY_KP_Home0xff95, 0,
1059 "move-scroll", 1,
1060 CTK_TYPE_SCROLL_TYPE(ctk_scroll_type_get_type ()),
1061 CTK_SCROLL_START);
1062 ctk_binding_entry_add_signal (binding_set,
1063 CDK_KEY_End0xff57, 0,
1064 "move-scroll", 1,
1065 CTK_TYPE_SCROLL_TYPE(ctk_scroll_type_get_type ()),
1066 CTK_SCROLL_END);
1067 ctk_binding_entry_add_signal (binding_set,
1068 CDK_KEY_KP_End0xff9c, 0,
1069 "move-scroll", 1,
1070 CTK_TYPE_SCROLL_TYPE(ctk_scroll_type_get_type ()),
1071 CTK_SCROLL_END);
1072 ctk_binding_entry_add_signal (binding_set,
1073 CDK_KEY_Page_Up0xff55, 0,
1074 "move-scroll", 1,
1075 CTK_TYPE_SCROLL_TYPE(ctk_scroll_type_get_type ()),
1076 CTK_SCROLL_PAGE_UP);
1077 ctk_binding_entry_add_signal (binding_set,
1078 CDK_KEY_KP_Page_Up0xff9a, 0,
1079 "move-scroll", 1,
1080 CTK_TYPE_SCROLL_TYPE(ctk_scroll_type_get_type ()),
1081 CTK_SCROLL_PAGE_UP);
1082 ctk_binding_entry_add_signal (binding_set,
1083 CDK_KEY_Page_Down0xff56, 0,
1084 "move-scroll", 1,
1085 CTK_TYPE_SCROLL_TYPE(ctk_scroll_type_get_type ()),
1086 CTK_SCROLL_PAGE_DOWN);
1087 ctk_binding_entry_add_signal (binding_set,
1088 CDK_KEY_KP_Page_Down0xff9b, 0,
1089 "move-scroll", 1,
1090 CTK_TYPE_SCROLL_TYPE(ctk_scroll_type_get_type ()),
1091 CTK_SCROLL_PAGE_DOWN);
1092
1093 ctk_widget_class_set_accessible_type (widget_class, CTK_TYPE_MENU_ACCESSIBLE(ctk_menu_accessible_get_type ()));
1094 ctk_widget_class_set_css_name (widget_class, "menu");
1095}
1096
1097
1098static void
1099ctk_menu_set_property (GObject *object,
1100 guint prop_id,
1101 const GValue *value,
1102 GParamSpec *pspec)
1103{
1104 CtkMenu *menu = CTK_MENU (object)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((object)), ((ctk_menu_get_type ()))))))
;
1105
1106 switch (prop_id)
1107 {
1108 case PROP_ACTIVE:
1109 ctk_menu_set_active (menu, g_value_get_int (value));
1110 break;
1111 case PROP_ACCEL_GROUP:
1112 ctk_menu_set_accel_group (menu, g_value_get_object (value));
1113 break;
1114 case PROP_ACCEL_PATH:
1115 ctk_menu_set_accel_path (menu, g_value_get_string (value));
1116 break;
1117 case PROP_ATTACH_WIDGET:
1118 {
1119 CtkWidget *widget;
1120
1121 widget = ctk_menu_get_attach_widget (menu);
1122 if (widget)
1123 ctk_menu_detach (menu);
1124
1125 widget = (CtkWidget*) g_value_get_object (value);
1126 if (widget)
1127 ctk_menu_attach_to_widget (menu, widget, NULL((void*)0));
1128 }
1129 break;
1130 case PROP_TEAROFF_STATE:
1131 ctk_menu_set_tearoff_state (menu, g_value_get_boolean (value));
1132 break;
1133 case PROP_TEAROFF_TITLE:
1134 ctk_menu_set_title (menu, g_value_get_string (value));
1135 break;
1136 case PROP_MONITOR:
1137 ctk_menu_set_monitor (menu, g_value_get_int (value));
1138 break;
1139 case PROP_RESERVE_TOGGLE_SIZE:
1140 ctk_menu_set_reserve_toggle_size (menu, g_value_get_boolean (value));
1141 break;
1142 case PROP_ANCHOR_HINTS:
1143 if (menu->priv->anchor_hints != g_value_get_flags (value))
1144 {
1145 menu->priv->anchor_hints = g_value_get_flags (value);
1146 g_object_notify_by_pspec (object, pspec);
1147 }
1148 break;
1149 case PROP_RECT_ANCHOR_DX:
1150 if (menu->priv->rect_anchor_dx != g_value_get_int (value))
1151 {
1152 menu->priv->rect_anchor_dx = g_value_get_int (value);
1153 g_object_notify_by_pspec (object, pspec);
1154 }
1155 break;
1156 case PROP_RECT_ANCHOR_DY:
1157 if (menu->priv->rect_anchor_dy != g_value_get_int (value))
1158 {
1159 menu->priv->rect_anchor_dy = g_value_get_int (value);
1160 g_object_notify_by_pspec (object, pspec);
1161 }
1162 break;
1163 case PROP_MENU_TYPE_HINT:
1164 if (menu->priv->menu_type_hint != g_value_get_enum (value))
1165 {
1166 menu->priv->menu_type_hint = g_value_get_enum (value);
1167 g_object_notify_by_pspec (object, pspec);
1168 }
1169 break;
1170 default:
1171 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec)do { GObject *_glib__object = (GObject*) ((object)); GParamSpec
*_glib__pspec = (GParamSpec*) ((pspec)); guint _glib__property_id
= ((prop_id)); g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'"
, "ctkmenu.c", 1171, ("property"), _glib__property_id, _glib__pspec
->name, g_type_name ((((((GTypeClass*) (((GTypeInstance*) (
_glib__pspec))->g_class))->g_type)))), (g_type_name (((
(((GTypeClass*) (((GTypeInstance*) (_glib__object))->g_class
))->g_type)))))); } while (0)
;
1172 break;
1173 }
1174}
1175
1176static void
1177ctk_menu_get_property (GObject *object,
1178 guint prop_id,
1179 GValue *value,
1180 GParamSpec *pspec)
1181{
1182 CtkMenu *menu = CTK_MENU (object)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((object)), ((ctk_menu_get_type ()))))))
;
1183
1184 switch (prop_id)
1185 {
1186 case PROP_ACTIVE:
1187 g_value_set_int (value, g_list_index (CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
->priv->children, ctk_menu_get_active (menu)));
1188 break;
1189 case PROP_ACCEL_GROUP:
1190 g_value_set_object (value, ctk_menu_get_accel_group (menu));
1191 break;
1192 case PROP_ACCEL_PATH:
1193 g_value_set_string (value, ctk_menu_get_accel_path (menu));
1194 break;
1195 case PROP_ATTACH_WIDGET:
1196 g_value_set_object (value, ctk_menu_get_attach_widget (menu));
1197 break;
1198 case PROP_TEAROFF_STATE:
1199 g_value_set_boolean (value, ctk_menu_get_tearoff_state (menu));
1200 break;
1201 case PROP_TEAROFF_TITLE:
1202 g_value_set_string (value, ctk_menu_get_title (menu));
1203 break;
1204 case PROP_MONITOR:
1205 g_value_set_int (value, ctk_menu_get_monitor (menu));
1206 break;
1207 case PROP_RESERVE_TOGGLE_SIZE:
1208 g_value_set_boolean (value, ctk_menu_get_reserve_toggle_size (menu));
1209 break;
1210 case PROP_ANCHOR_HINTS:
1211 g_value_set_flags (value, menu->priv->anchor_hints);
1212 break;
1213 case PROP_RECT_ANCHOR_DX:
1214 g_value_set_int (value, menu->priv->rect_anchor_dx);
1215 break;
1216 case PROP_RECT_ANCHOR_DY:
1217 g_value_set_int (value, menu->priv->rect_anchor_dy);
1218 break;
1219 case PROP_MENU_TYPE_HINT:
1220 g_value_set_enum (value, menu->priv->menu_type_hint);
1221 break;
1222 default:
1223 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec)do { GObject *_glib__object = (GObject*) ((object)); GParamSpec
*_glib__pspec = (GParamSpec*) ((pspec)); guint _glib__property_id
= ((prop_id)); g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'"
, "ctkmenu.c", 1223, ("property"), _glib__property_id, _glib__pspec
->name, g_type_name ((((((GTypeClass*) (((GTypeInstance*) (
_glib__pspec))->g_class))->g_type)))), (g_type_name (((
(((GTypeClass*) (((GTypeInstance*) (_glib__object))->g_class
))->g_type)))))); } while (0)
;
1224 break;
1225 }
1226}
1227
1228static void
1229ctk_menu_set_child_property (CtkContainer *container,
1230 CtkWidget *child,
1231 guint property_id,
1232 const GValue *value,
1233 GParamSpec *pspec)
1234{
1235 CtkMenu *menu = CTK_MENU (container)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((container)), ((ctk_menu_get_type ()))))))
;
1236 AttachInfo *ai = get_attach_info (child);
1237
1238 switch (property_id)
1239 {
1240 case CHILD_PROP_LEFT_ATTACH:
1241 ai->left_attach = g_value_get_int (value);
1242 break;
1243 case CHILD_PROP_RIGHT_ATTACH:
1244 ai->right_attach = g_value_get_int (value);
1245 break;
1246 case CHILD_PROP_TOP_ATTACH:
1247 ai->top_attach = g_value_get_int (value);
1248 break;
1249 case CHILD_PROP_BOTTOM_ATTACH:
1250 ai->bottom_attach = g_value_get_int (value);
1251 break;
1252
1253 default:
1254 CTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec)do { GObject *_glib__object = (GObject*) ((container)); GParamSpec
*_glib__pspec = (GParamSpec*) ((pspec)); guint _glib__property_id
= ((property_id)); g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'"
, "ctkmenu.c", 1254, ("child property"), _glib__property_id, _glib__pspec
->name, g_type_name ((((((GTypeClass*) (((GTypeInstance*) (
_glib__pspec))->g_class))->g_type)))), (g_type_name (((
(((GTypeClass*) (((GTypeInstance*) (_glib__object))->g_class
))->g_type)))))); } while (0)
;
1255 return;
1256 }
1257
1258 menu_queue_resize (menu);
1259}
1260
1261static void
1262ctk_menu_get_child_property (CtkContainer *container,
1263 CtkWidget *child,
1264 guint property_id,
1265 GValue *value,
1266 GParamSpec *pspec)
1267{
1268 AttachInfo *ai = get_attach_info (child);
1269
1270 switch (property_id)
1271 {
1272 case CHILD_PROP_LEFT_ATTACH:
1273 g_value_set_int (value, ai->left_attach);
1274 break;
1275 case CHILD_PROP_RIGHT_ATTACH:
1276 g_value_set_int (value, ai->right_attach);
1277 break;
1278 case CHILD_PROP_TOP_ATTACH:
1279 g_value_set_int (value, ai->top_attach);
1280 break;
1281 case CHILD_PROP_BOTTOM_ATTACH:
1282 g_value_set_int (value, ai->bottom_attach);
1283 break;
1284
1285 default:
1286 CTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec)do { GObject *_glib__object = (GObject*) ((container)); GParamSpec
*_glib__pspec = (GParamSpec*) ((pspec)); guint _glib__property_id
= ((property_id)); g_warning ("%s:%d: invalid %s id %u for \"%s\" of type '%s' in '%s'"
, "ctkmenu.c", 1286, ("child property"), _glib__property_id, _glib__pspec
->name, g_type_name ((((((GTypeClass*) (((GTypeInstance*) (
_glib__pspec))->g_class))->g_type)))), (g_type_name (((
(((GTypeClass*) (((GTypeInstance*) (_glib__object))->g_class
))->g_type)))))); } while (0)
;
1287 return;
1288 }
1289}
1290
1291static gboolean
1292ctk_menu_window_event (CtkWidget *window,
1293 CdkEvent *event,
1294 CtkWidget *menu)
1295{
1296 gboolean handled = FALSE(0);
1297
1298 g_object_ref (window)((__typeof__ (window)) (g_object_ref) (window));
1299 g_object_ref (menu)((__typeof__ (menu)) (g_object_ref) (menu));
1300
1301 switch (event->type)
1302 {
1303 case CDK_KEY_PRESS:
1304 case CDK_KEY_RELEASE:
1305 handled = ctk_widget_event (menu, event);
1306 break;
1307 case CDK_WINDOW_STATE:
1308 /* Window for the menu has been closed by the display server or by CDK.
1309 * Update the internal state as if the user had clicked outside the
1310 * menu
1311 */
1312 if (event->window_state.new_window_state & CDK_WINDOW_STATE_WITHDRAWN &&
1313 event->window_state.changed_mask & CDK_WINDOW_STATE_WITHDRAWN)
1314 ctk_menu_shell_deactivate (CTK_MENU_SHELL(menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
);
1315 break;
1316 default:
1317 break;
1318 }
1319
1320 g_object_unref (window);
1321 g_object_unref (menu);
1322
1323 return handled;
1324}
1325
1326static void
1327ctk_menu_init (CtkMenu *menu)
1328{
1329 CtkMenuPrivate *priv;
1330 CtkCssNode *top_arrow_node, *bottom_arrow_node, *widget_node;
1331
1332 priv = ctk_menu_get_instance_private (menu);
1333
1334 menu->priv = priv;
1335
1336 priv->toplevel = ctk_window_new (CTK_WINDOW_POPUP);
1337 ctk_container_add (CTK_CONTAINER (priv->toplevel)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_container_get_type ()))))))
, CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
1338 g_signal_connect (priv->toplevel, "event", G_CALLBACK (ctk_menu_window_event), menu)g_signal_connect_data ((priv->toplevel), ("event"), (((GCallback
) (ctk_menu_window_event))), (menu), ((void*)0), (GConnectFlags
) 0)
;
1339 g_signal_connect (priv->toplevel, "destroy", G_CALLBACK (ctk_widget_destroyed), &priv->toplevel)g_signal_connect_data ((priv->toplevel), ("destroy"), (((GCallback
) (ctk_widget_destroyed))), (&priv->toplevel), ((void*
)0), (GConnectFlags) 0)
;
1340 ctk_window_set_resizable (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
, FALSE(0));
1341 ctk_window_set_mnemonic_modifier (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
, 0);
1342
1343 _ctk_window_request_csd (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
);
1344 ctk_style_context_add_class (ctk_widget_get_style_context (priv->toplevel),
1345 CTK_STYLE_CLASS_POPUP"popup");
1346
1347 /* Refloat the menu, so that reference counting for the menu isn't
1348 * affected by it being a child of the toplevel
1349 */
1350 g_object_force_floating (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
);
1351 priv->needs_destruction_ref = TRUE(!(0));
1352
1353 priv->monitor_num = -1;
1354 priv->drag_start_y = -1;
1355
1356 priv->anchor_hints = CDK_ANCHOR_FLIP | CDK_ANCHOR_SLIDE | CDK_ANCHOR_RESIZE;
1357 priv->rect_anchor_dx = 0;
1358 priv->rect_anchor_dy = 0;
1359 priv->menu_type_hint = CDK_WINDOW_TYPE_HINT_POPUP_MENU;
1360
1361 _ctk_widget_set_captured_event_handler (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
, ctk_menu_captured_event);
1362
1363 widget_node = ctk_widget_get_css_node (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
1364 priv->top_arrow_gadget = ctk_builtin_icon_new ("arrow",
1365 CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
,
1366 NULL((void*)0), NULL((void*)0));
1367 ctk_css_gadget_add_class (priv->top_arrow_gadget, CTK_STYLE_CLASS_TOP"top");
1368 top_arrow_node = ctk_css_gadget_get_node (priv->top_arrow_gadget);
1369 ctk_css_node_set_parent (top_arrow_node, widget_node);
1370 ctk_css_node_set_visible (top_arrow_node, FALSE(0));
1371 ctk_css_node_set_state (top_arrow_node, ctk_css_node_get_state (widget_node));
1372
1373 priv->bottom_arrow_gadget = ctk_builtin_icon_new ("arrow",
1374 CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
,
1375 NULL((void*)0), NULL((void*)0));
1376 ctk_css_gadget_add_class (priv->bottom_arrow_gadget, CTK_STYLE_CLASS_BOTTOM"bottom");
1377 bottom_arrow_node = ctk_css_gadget_get_node (priv->bottom_arrow_gadget);
1378 ctk_css_node_set_parent (bottom_arrow_node, widget_node);
1379 ctk_css_node_set_visible (bottom_arrow_node, FALSE(0));
1380 ctk_css_node_set_state (bottom_arrow_node, ctk_css_node_get_state (widget_node));
1381}
1382
1383static void
1384moved_to_rect_cb (CdkWindow *window G_GNUC_UNUSED__attribute__ ((__unused__)),
1385 const CdkRectangle *flipped_rect,
1386 const CdkRectangle *final_rect,
1387 gboolean flipped_x,
1388 gboolean flipped_y,
1389 CtkMenu *menu)
1390{
1391 CtkMenuPrivate *priv = menu->priv;
1392
1393 ctk_window_fixate_size (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
);
1394
1395 if (!priv->emulated_move_to_rect)
1396 g_signal_emit (menu,
1397 menu_signals[POPPED_UP],
1398 0,
1399 flipped_rect,
1400 final_rect,
1401 flipped_x,
1402 flipped_y);
1403}
1404
1405static void
1406ctk_menu_destroy (CtkWidget *widget)
1407{
1408 CtkMenu *menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
1409 CtkMenuPrivate *priv = menu->priv;
1410 CtkMenuAttachData *data;
1411
1412 ctk_menu_remove_scroll_timeout (menu);
1413
1414 data = g_object_get_data (G_OBJECT (widget)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), (((GType) ((20) << (2))))))))
, attach_data_key);
1415 if (data)
1416 ctk_menu_detach (menu);
1417
1418 ctk_menu_stop_navigating_submenu (menu);
1419
1420 g_clear_object (&priv->old_active_menu_item)do { _Static_assert (sizeof *((&priv->old_active_menu_item
)) == sizeof (gpointer), "Expression evaluates to false"); __typeof__
(((&priv->old_active_menu_item))) _pp = ((&priv->
old_active_menu_item)); __typeof__ (*((&priv->old_active_menu_item
))) _ptr = *_pp; *_pp = ((void*)0); if (_ptr) (g_object_unref
) (_ptr); } while (0)
;
1421
1422 /* Add back the reference count for being a child */
1423 if (priv->needs_destruction_ref)
1424 {
1425 priv->needs_destruction_ref = FALSE(0);
1426 g_object_ref (widget)((__typeof__ (widget)) (g_object_ref) (widget));
1427 }
1428
1429 g_clear_object (&priv->accel_group)do { _Static_assert (sizeof *((&priv->accel_group)) ==
sizeof (gpointer), "Expression evaluates to false"); __typeof__
(((&priv->accel_group))) _pp = ((&priv->accel_group
)); __typeof__ (*((&priv->accel_group))) _ptr = *_pp; *
_pp = ((void*)0); if (_ptr) (g_object_unref) (_ptr); } while (
0)
;
1430
1431 if (priv->toplevel)
1432 {
1433 g_signal_handlers_disconnect_by_func (priv->toplevel, moved_to_rect_cb, menu)g_signal_handlers_disconnect_matched ((priv->toplevel), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), (moved_to_rect_cb), (menu))
;
1434 ctk_widget_destroy (priv->toplevel);
1435 }
1436
1437 if (priv->tearoff_window)
1438 ctk_widget_destroy (priv->tearoff_window);
1439
1440 g_clear_pointer (&priv->heights, g_free)do { _Static_assert (sizeof *(&priv->heights) == sizeof
(gpointer), "Expression evaluates to false"); __typeof__ ((&
priv->heights)) _pp = (&priv->heights); __typeof__ (
*(&priv->heights)) _ptr = *_pp; *_pp = ((void*)0); if (
_ptr) (g_free) (_ptr); } while (0)
;
1441
1442 g_clear_pointer (&priv->title, g_free)do { _Static_assert (sizeof *(&priv->title) == sizeof (
gpointer), "Expression evaluates to false"); __typeof__ ((&
priv->title)) _pp = (&priv->title); __typeof__ (*(&
priv->title)) _ptr = *_pp; *_pp = ((void*)0); if (_ptr) (g_free
) (_ptr); } while (0)
;
1443
1444 if (priv->position_func_data_destroy)
1445 {
1446 priv->position_func_data_destroy (priv->position_func_data);
1447 priv->position_func_data = NULL((void*)0);
1448 priv->position_func_data_destroy = NULL((void*)0);
1449 }
1450
1451 CTK_WIDGET_CLASS (ctk_menu_parent_class)((((CtkWidgetClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), ((ctk_widget_get_type ()))))))
->destroy (widget);
1452}
1453
1454static void
1455ctk_menu_finalize (GObject *object)
1456{
1457 CtkMenu *menu = CTK_MENU (object)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((object)), ((ctk_menu_get_type ()))))))
;
1458 CtkMenuPrivate *priv = menu->priv;
1459
1460 g_clear_object (&priv->top_arrow_gadget)do { _Static_assert (sizeof *((&priv->top_arrow_gadget
)) == sizeof (gpointer), "Expression evaluates to false"); __typeof__
(((&priv->top_arrow_gadget))) _pp = ((&priv->top_arrow_gadget
)); __typeof__ (*((&priv->top_arrow_gadget))) _ptr = *
_pp; *_pp = ((void*)0); if (_ptr) (g_object_unref) (_ptr); } while
(0)
;
1461 g_clear_object (&priv->bottom_arrow_gadget)do { _Static_assert (sizeof *((&priv->bottom_arrow_gadget
)) == sizeof (gpointer), "Expression evaluates to false"); __typeof__
(((&priv->bottom_arrow_gadget))) _pp = ((&priv->
bottom_arrow_gadget)); __typeof__ (*((&priv->bottom_arrow_gadget
))) _ptr = *_pp; *_pp = ((void*)0); if (_ptr) (g_object_unref
) (_ptr); } while (0)
;
1462
1463 G_OBJECT_CLASS (ctk_menu_parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), (((GType) ((20) << (2))))
))))
->finalize (object);
1464}
1465
1466static void
1467menu_change_screen (CtkMenu *menu,
1468 CdkScreen *new_screen)
1469{
1470 CtkMenuPrivate *priv = menu->priv;
1471
1472 if (ctk_widget_has_screen (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
))
1473 {
1474 if (new_screen == ctk_widget_get_screen (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
))
1475 return;
1476 }
1477
1478 if (priv->torn_off)
1479 {
1480 ctk_window_set_screen (CTK_WINDOW (priv->tearoff_window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->tearoff_window)), ((ctk_window_get_type ())))))
)
, new_screen);
1481 ctk_menu_position (menu, TRUE(!(0)));
1482 }
1483
1484 ctk_window_set_screen (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
, new_screen);
1485 priv->monitor_num = -1;
1486}
1487
1488static void
1489attach_widget_screen_changed (CtkWidget *attach_widget,
1490 CdkScreen *previous_screen G_GNUC_UNUSED__attribute__ ((__unused__)),
1491 CtkMenu *menu)
1492{
1493 if (ctk_widget_has_screen (attach_widget) &&
1494 !g_object_get_data (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, "ctk-menu-explicit-screen"))
1495 menu_change_screen (menu, ctk_widget_get_screen (attach_widget));
1496}
1497
1498static void
1499menu_toplevel_attached_to (CtkWindow *toplevel G_GNUC_UNUSED__attribute__ ((__unused__)),
1500 GParamSpec *pspec G_GNUC_UNUSED__attribute__ ((__unused__)),
1501 CtkMenu *menu)
1502{
1503 CtkMenuAttachData *data;
1504
1505 data = g_object_get_data (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, attach_data_key);
1506
1507 g_return_if_fail (data)do { if ((data)) { } else { g_return_if_fail_warning ("Ctk", (
(const char*) (__func__)), "data"); return; } } while (0)
;
1508
1509 ctk_menu_detach (menu);
1510}
1511
1512/**
1513 * ctk_menu_attach_to_widget:
1514 * @menu: a #CtkMenu
1515 * @attach_widget: the #CtkWidget that the menu will be attached to
1516 * @detacher: (scope async)(allow-none): the user supplied callback function
1517 * that will be called when the menu calls ctk_menu_detach()
1518 *
1519 * Attaches the menu to the widget and provides a callback function
1520 * that will be invoked when the menu calls ctk_menu_detach() during
1521 * its destruction.
1522 *
1523 * If the menu is attached to the widget then it will be destroyed
1524 * when the widget is destroyed, as if it was a child widget.
1525 * An attached menu will also move between screens correctly if the
1526 * widgets moves between screens.
1527 */
1528void
1529ctk_menu_attach_to_widget (CtkMenu *menu,
1530 CtkWidget *attach_widget,
1531 CtkMenuDetachFunc detacher)
1532{
1533 CtkMenuAttachData *data;
1534 GList *list;
1535
1536 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
1537 g_return_if_fail (CTK_IS_WIDGET (attach_widget))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((attach_widget)); GType __t = ((ctk_widget_get_type ()));
gboolean __r; if (!__inst) __r = (0); else if (__inst->g_class
&& __inst->g_class->g_type == __t) __r = (!(0)
); else __r = g_type_check_instance_is_a (__inst, __t); __r; }
)))))) { } else { g_return_if_fail_warning ("Ctk", ((const char
*) (__func__)), "CTK_IS_WIDGET (attach_widget)"); return; } }
while (0)
;
1538
1539 /* keep this function in sync with ctk_widget_set_parent() */
1540 data = g_object_get_data (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, attach_data_key);
1541 if (data)
1542 {
1543 g_warning ("ctk_menu_attach_to_widget(): menu already attached to %s",
1544 g_type_name (G_TYPE_FROM_INSTANCE (data->attach_widget)((((GTypeClass*) (((GTypeInstance*) (data->attach_widget))
->g_class))->g_type))
));
1545 return;
1546 }
1547
1548 g_object_ref_sink (menu)((__typeof__ (menu)) (g_object_ref_sink) (menu));
1549
1550 data = g_slice_new (CtkMenuAttachData)((CtkMenuAttachData*) g_slice_alloc (sizeof (CtkMenuAttachData
)))
;
1551 data->attach_widget = attach_widget;
1552
1553 g_signal_connect (attach_widget, "screen-changed",g_signal_connect_data ((attach_widget), ("screen-changed"), (
((GCallback) (attach_widget_screen_changed))), (menu), ((void
*)0), (GConnectFlags) 0)
1554 G_CALLBACK (attach_widget_screen_changed), menu)g_signal_connect_data ((attach_widget), ("screen-changed"), (
((GCallback) (attach_widget_screen_changed))), (menu), ((void
*)0), (GConnectFlags) 0)
;
1555 attach_widget_screen_changed (attach_widget, NULL((void*)0), menu);
1556
1557 data->detacher = detacher;
1558 g_object_set_data (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, I_(attach_data_key)g_intern_static_string (attach_data_key), data);
1559 list = g_object_steal_data (G_OBJECT (attach_widget)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((attach_widget)), (((GType) ((20) << (2))))))))
, ATTACHED_MENUS"ctk-attached-menus");
1560 if (!g_list_find (list, menu))
1561 list = g_list_prepend (list, menu);
1562
1563 g_object_set_data_full (G_OBJECT (attach_widget)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((attach_widget)), (((GType) ((20) << (2))))))))
, I_(ATTACHED_MENUS)g_intern_static_string ("ctk-attached-menus"), list,
1564 (GDestroyNotify) g_list_free);
1565
1566 /* Attach the widget to the toplevel window. */
1567 ctk_window_set_attached_to (CTK_WINDOW (menu->priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu->priv->toplevel)), ((ctk_window_get_type ()))
))))
, attach_widget);
1568 g_signal_connect (CTK_WINDOW (menu->priv->toplevel), "notify::attached-to",g_signal_connect_data ((((((CtkWindow*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((menu->priv->toplevel)), ((ctk_window_get_type
()))))))), ("notify::attached-to"), (((GCallback) (menu_toplevel_attached_to
))), (menu), ((void*)0), (GConnectFlags) 0)
1569 G_CALLBACK (menu_toplevel_attached_to), menu)g_signal_connect_data ((((((CtkWindow*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((menu->priv->toplevel)), ((ctk_window_get_type
()))))))), ("notify::attached-to"), (((GCallback) (menu_toplevel_attached_to
))), (menu), ((void*)0), (GConnectFlags) 0)
;
1570
1571 _ctk_widget_update_parent_muxer (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
1572
1573 /* Fallback title for menu comes from attach widget */
1574 ctk_menu_update_title (menu);
1575
1576 g_object_notify (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, "attach-widget");
1577}
1578
1579/**
1580 * ctk_menu_get_attach_widget:
1581 * @menu: a #CtkMenu
1582 *
1583 * Returns the #CtkWidget that the menu is attached to.
1584 *
1585 * Returns: (transfer none): the #CtkWidget that the menu is attached to
1586 */
1587CtkWidget*
1588ctk_menu_get_attach_widget (CtkMenu *menu)
1589{
1590 CtkMenuAttachData *data;
1591
1592 g_return_val_if_fail (CTK_IS_MENU (menu), NULL)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return (((void*)0)); } } while (0)
;
1593
1594 data = g_object_get_data (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, attach_data_key);
1595 if (data)
1596 return data->attach_widget;
1597 return NULL((void*)0);
1598}
1599
1600/**
1601 * ctk_menu_detach:
1602 * @menu: a #CtkMenu
1603 *
1604 * Detaches the menu from the widget to which it had been attached.
1605 * This function will call the callback function, @detacher, provided
1606 * when the ctk_menu_attach_to_widget() function was called.
1607 */
1608void
1609ctk_menu_detach (CtkMenu *menu)
1610{
1611 CtkWindow *toplevel;
1612 CtkMenuAttachData *data;
1613 GList *list;
1614
1615 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
1616 toplevel = CTK_WINDOW (menu->priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu->priv->toplevel)), ((ctk_window_get_type ()))
))))
;
1617
1618 /* keep this function in sync with ctk_widget_unparent() */
1619 data = g_object_get_data (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, attach_data_key);
1620 if (!data)
1621 {
1622 g_warning ("ctk_menu_detach(): menu is not attached");
1623 return;
1624 }
1625 g_object_set_data (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, I_(attach_data_key)g_intern_static_string (attach_data_key), NULL((void*)0));
1626
1627 /* Detach the toplevel window. */
1628 if (toplevel)
1629 {
1630 g_signal_handlers_disconnect_by_func (toplevel,g_signal_handlers_disconnect_matched ((toplevel), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), ((gpointer) menu_toplevel_attached_to), (menu))
1631 (gpointer) menu_toplevel_attached_to,g_signal_handlers_disconnect_matched ((toplevel), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), ((gpointer) menu_toplevel_attached_to), (menu))
1632 menu)g_signal_handlers_disconnect_matched ((toplevel), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), ((gpointer) menu_toplevel_attached_to), (menu))
;
1633 if (ctk_window_get_attached_to (toplevel) == data->attach_widget)
1634 ctk_window_set_attached_to (toplevel, NULL((void*)0));
1635 }
1636
1637 g_signal_handlers_disconnect_by_func (data->attach_widget,g_signal_handlers_disconnect_matched ((data->attach_widget
), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA
), 0, 0, ((void*)0), ((gpointer) attach_widget_screen_changed
), (menu))
1638 (gpointer) attach_widget_screen_changed,g_signal_handlers_disconnect_matched ((data->attach_widget
), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA
), 0, 0, ((void*)0), ((gpointer) attach_widget_screen_changed
), (menu))
1639 menu)g_signal_handlers_disconnect_matched ((data->attach_widget
), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA
), 0, 0, ((void*)0), ((gpointer) attach_widget_screen_changed
), (menu))
;
1640
1641 if (data->detacher)
1642 data->detacher (data->attach_widget, menu);
1643 list = g_object_steal_data (G_OBJECT (data->attach_widget)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((data->attach_widget)), (((GType) ((20) << (2)))
)))))
, ATTACHED_MENUS"ctk-attached-menus");
1644 list = g_list_remove (list, menu);
1645 if (list)
1646 g_object_set_data_full (G_OBJECT (data->attach_widget)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((data->attach_widget)), (((GType) ((20) << (2)))
)))))
, I_(ATTACHED_MENUS)g_intern_static_string ("ctk-attached-menus"), list,
1647 (GDestroyNotify) g_list_free);
1648 else
1649 g_object_set_data (G_OBJECT (data->attach_widget)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((data->attach_widget)), (((GType) ((20) << (2)))
)))))
, I_(ATTACHED_MENUS)g_intern_static_string ("ctk-attached-menus"), NULL((void*)0));
1650
1651 if (ctk_widget_get_realized (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
))
1652 ctk_widget_unrealize (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
1653
1654 g_slice_free (CtkMenuAttachData, data)do { if (1) g_slice_free1 (sizeof (CtkMenuAttachData), (data)
); else (void) ((CtkMenuAttachData*) 0 == (data)); } while (0
)
;
1655
1656 _ctk_widget_update_parent_muxer (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
1657
1658 /* Fallback title for menu comes from attach widget */
1659 ctk_menu_update_title (menu);
1660
1661 g_object_notify (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, "attach-widget");
1662 g_object_unref (menu);
1663}
1664
1665static void
1666ctk_menu_remove (CtkContainer *container,
1667 CtkWidget *widget)
1668{
1669 CtkMenu *menu = CTK_MENU (container)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((container)), ((ctk_menu_get_type ()))))))
;
1670 CtkMenuPrivate *priv = menu->priv;
1671
1672 /* Clear out old_active_menu_item if it matches the item we are removing */
1673 if (priv->old_active_menu_item == widget)
1674 g_clear_object (&priv->old_active_menu_item)do { _Static_assert (sizeof *((&priv->old_active_menu_item
)) == sizeof (gpointer), "Expression evaluates to false"); __typeof__
(((&priv->old_active_menu_item))) _pp = ((&priv->
old_active_menu_item)); __typeof__ (*((&priv->old_active_menu_item
))) _ptr = *_pp; *_pp = ((void*)0); if (_ptr) (g_object_unref
) (_ptr); } while (0)
;
1675
1676 CTK_CONTAINER_CLASS (ctk_menu_parent_class)((((CtkContainerClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), ((ctk_container_get_type ()))))
))
->remove (container, widget);
1677
1678 g_object_set_data (G_OBJECT (widget)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), (((GType) ((20) << (2))))))))
, I_(ATTACH_INFO_KEY)g_intern_static_string ("ctk-menu-child-attach-info-key"), NULL((void*)0));
1679
1680 menu_queue_resize (menu);
1681}
1682
1683/**
1684 * ctk_menu_new:
1685 *
1686 * Creates a new #CtkMenu
1687 *
1688 * Returns: a new #CtkMenu
1689 */
1690CtkWidget*
1691ctk_menu_new (void)
1692{
1693 return g_object_new (CTK_TYPE_MENU(ctk_menu_get_type ()), NULL((void*)0));
1694}
1695
1696static void
1697ctk_menu_real_insert (CtkMenuShell *menu_shell,
1698 CtkWidget *child,
1699 gint position)
1700{
1701 CtkMenu *menu = CTK_MENU (menu_shell)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu_shell)), ((ctk_menu_get_type ()))))))
;
1702 CtkMenuPrivate *priv = menu->priv;
1703 AttachInfo *ai = get_attach_info (child);
1704 CtkCssNode *widget_node, *child_node;
1705
1706 ai->left_attach = -1;
1707 ai->right_attach = -1;
1708 ai->top_attach = -1;
1709 ai->bottom_attach = -1;
1710
1711 if (ctk_widget_get_realized (CTK_WIDGET (menu_shell)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu_shell)), ((ctk_widget_get_type ()))))))
))
1712 ctk_widget_set_parent_window (child, priv->bin_window);
1713
1714 widget_node = ctk_widget_get_css_node (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
1715 child_node = ctk_widget_get_css_node (child);
1716 ctk_css_node_insert_before (widget_node, child_node,
1717 ctk_css_gadget_get_node (priv->bottom_arrow_gadget));
1718
1719 CTK_MENU_SHELL_CLASS (ctk_menu_parent_class)((((CtkMenuShellClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), ((ctk_menu_shell_get_type ())))
)))
->insert (menu_shell, child, position);
1720
1721 menu_queue_resize (menu);
1722}
1723
1724static void
1725ctk_menu_tearoff_bg_copy (CtkMenu *menu)
1726{
1727 CtkMenuPrivate *priv = menu->priv;
1728 gint width, height;
1729
1730 if (priv->torn_off)
1731 {
1732 CdkWindow *window;
1733 cairo_surface_t *surface;
1734 cairo_pattern_t *pattern;
1735 cairo_t *cr;
1736
1737 priv->tearoff_active = FALSE(0);
1738 priv->saved_scroll_offset = priv->scroll_offset;
1739
1740 window = ctk_widget_get_window (priv->tearoff_window);
1741 width = cdk_window_get_width (window);
1742 height = cdk_window_get_height (window);
1743
1744 surface = cdk_window_create_similar_surface (window,
1745 CAIRO_CONTENT_COLOR,
1746 width,
1747 height);
1748
1749 cr = cairo_create (surface);
1750 cdk_cairo_set_source_window (cr, window, 0, 0);
1751 cairo_paint (cr);
1752 cairo_destroy (cr);
1753
1754 ctk_widget_set_size_request (priv->tearoff_window, width, height);
1755
1756 pattern = cairo_pattern_create_for_surface (surface);
1757 cdk_window_set_background_pattern (window, pattern);
1758
1759 cairo_pattern_destroy (pattern);
1760 cairo_surface_destroy (surface);
1761 }
1762}
1763
1764static gboolean
1765popup_grab_on_window (CdkWindow *window,
1766 CdkDevice *pointer)
1767{
1768 CdkGrabStatus status;
1769 CdkSeat *seat;
1770
1771 seat = cdk_device_get_seat (pointer);
1772
1773/* Let CtkMenu use pointer emulation instead of touch events under X11. */
1774#define CDK_SEAT_CAPABILITY_NO_TOUCH(CDK_SEAT_CAPABILITY_POINTER | CDK_SEAT_CAPABILITY_TABLET_STYLUS
| CDK_SEAT_CAPABILITY_KEYBOARD)
(CDK_SEAT_CAPABILITY_POINTER | \
1775 CDK_SEAT_CAPABILITY_TABLET_STYLUS | \
1776 CDK_SEAT_CAPABILITY_KEYBOARD)
1777 status = cdk_seat_grab (seat, window,
1778 CDK_SEAT_CAPABILITY_NO_TOUCH(CDK_SEAT_CAPABILITY_POINTER | CDK_SEAT_CAPABILITY_TABLET_STYLUS
| CDK_SEAT_CAPABILITY_KEYBOARD)
, TRUE(!(0)),
1779 NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0));
1780
1781 return status == CDK_GRAB_SUCCESS;
1782}
1783
1784static void
1785associate_menu_grab_transfer_window (CtkMenu *menu)
1786{
1787 CtkMenuPrivate *priv = menu->priv;
1788 CdkWindow *toplevel_window;
1789 CdkWindow *transfer_window;
1790
1791 toplevel_window = ctk_widget_get_window (priv->toplevel);
1792 transfer_window = g_object_get_data (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, "ctk-menu-transfer-window");
1793
1794 if (toplevel_window == NULL((void*)0) || transfer_window == NULL((void*)0))
1795 return;
1796
1797 g_object_set_data (G_OBJECT (toplevel_window)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((toplevel_window)), (((GType) ((20) << (2))))))))
, I_("cdk-attached-grab-window")g_intern_static_string ("cdk-attached-grab-window"), transfer_window);
1798}
1799
1800static void
1801ctk_menu_popup_internal (CtkMenu *menu,
1802 CdkDevice *device,
1803 CtkWidget *parent_menu_shell,
1804 CtkWidget *parent_menu_item,
1805 CtkMenuPositionFunc func,
1806 gpointer data,
1807 GDestroyNotify destroy,
1808 guint button,
1809 guint32 activate_time)
1810{
1811 CtkMenuPrivate *priv = menu->priv;
1812 CtkWidget *widget;
1813 CtkWidget *xgrab_shell;
1814 CtkWidget *parent;
1815 CdkEvent *current_event;
1816 CtkMenuShell *menu_shell;
1817 gboolean grab_keyboard;
1818 CtkWidget *parent_toplevel;
1819 CdkDevice *pointer, *source_device = NULL((void*)0);
1820 CdkDisplay *display;
1821
1822 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
1823 g_return_if_fail (device == NULL || CDK_IS_DEVICE (device))do { if ((device == ((void*)0) || (((__extension__ ({ GTypeInstance
*__inst = (GTypeInstance*) ((device)); GType __t = ((cdk_device_get_type
())); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("Ctk", ((const
char*) (__func__)), "device == NULL || CDK_IS_DEVICE (device)"
); return; } } while (0)
;
1824
1825 _ctk_tooltip_hide_in_display (ctk_widget_get_display (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
));
1826 display = ctk_widget_get_display (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
1827
1828 if (device == NULL((void*)0))
1829 device = ctk_get_current_event_device ();
1830
1831 if (device && cdk_device_get_display (device) != display)
1832 device = NULL((void*)0);
1833
1834 if (device == NULL((void*)0))
1835 device = cdk_seat_get_pointer (cdk_display_get_default_seat (display));
1836
1837 widget = CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
;
1838 menu_shell = CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
;
1839
1840 if (cdk_device_get_source (device) == CDK_SOURCE_KEYBOARD)
1841 pointer = cdk_device_get_associated_device (device);
1842 else
1843 pointer = device;
1844
1845 menu_shell->priv->parent_menu_shell = parent_menu_shell;
1846
1847 priv->seen_item_enter = FALSE(0);
1848
1849 /* Find the last viewable ancestor, and make an X grab on it
1850 */
1851 parent = CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
;
1852 xgrab_shell = NULL((void*)0);
1853 while (parent)
1854 {
1855 gboolean viewable = TRUE(!(0));
1856 CtkWidget *tmp = parent;
1857
1858 while (tmp)
1859 {
1860 if (!ctk_widget_get_mapped (tmp))
1861 {
1862 viewable = FALSE(0);
1863 break;
1864 }
1865 tmp = ctk_widget_get_parent (tmp);
1866 }
1867
1868 if (viewable)
1869 xgrab_shell = parent;
1870
1871 parent = CTK_MENU_SHELL (parent)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((parent)), ((ctk_menu_shell_get_type ()))))))
->priv->parent_menu_shell;
1872 }
1873
1874 /* We want to receive events generated when we map the menu;
1875 * unfortunately, since there is probably already an implicit
1876 * grab in place from the button that the user used to pop up
1877 * the menu, we won't receive then -- in particular, the EnterNotify
1878 * when the menu pops up under the pointer.
1879 *
1880 * If we are grabbing on a parent menu shell, no problem; just grab
1881 * on that menu shell first before popping up the window with
1882 * owner_events = TRUE.
1883 *
1884 * When grabbing on the menu itself, things get more convoluted --
1885 * we do an explicit grab on a specially created window with
1886 * owner_events = TRUE, which we override further down with a
1887 * grab on the menu. (We can't grab on the menu until it is mapped;
1888 * we probably could just leave the grab on the other window,
1889 * with a little reorganization of the code in ctkmenu*).
1890 */
1891 grab_keyboard = ctk_menu_shell_get_take_focus (menu_shell);
1892 ctk_window_set_accept_focus (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
, grab_keyboard);
1893
1894 if (xgrab_shell && xgrab_shell != widget)
1895 {
1896 if (popup_grab_on_window (ctk_widget_get_window (xgrab_shell), pointer))
1897 {
1898 _ctk_menu_shell_set_grab_device (CTK_MENU_SHELL (xgrab_shell)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((xgrab_shell)), ((ctk_menu_shell_get_type ()))))))
, pointer);
1899 CTK_MENU_SHELL (xgrab_shell)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((xgrab_shell)), ((ctk_menu_shell_get_type ()))))))
->priv->have_xgrab = TRUE(!(0));
1900 }
1901 }
1902 else
1903 {
1904 CdkWindow *transfer_window;
1905
1906 xgrab_shell = widget;
1907 transfer_window = menu_grab_transfer_window_get (menu);
1908 if (popup_grab_on_window (transfer_window, pointer))
1909 {
1910 _ctk_menu_shell_set_grab_device (CTK_MENU_SHELL (xgrab_shell)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((xgrab_shell)), ((ctk_menu_shell_get_type ()))))))
, pointer);
1911 CTK_MENU_SHELL (xgrab_shell)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((xgrab_shell)), ((ctk_menu_shell_get_type ()))))))
->priv->have_xgrab = TRUE(!(0));
1912 }
1913 }
1914
1915 if (!CTK_MENU_SHELL (xgrab_shell)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((xgrab_shell)), ((ctk_menu_shell_get_type ()))))))
->priv->have_xgrab)
1916 {
1917 /* We failed to make our pointer/keyboard grab.
1918 * Rather than leaving the user with a stuck up window,
1919 * we just abort here. Presumably the user will try again.
1920 */
1921 menu_shell->priv->parent_menu_shell = NULL((void*)0);
1922 menu_grab_transfer_window_destroy (menu);
1923 return;
1924 }
1925
1926 _ctk_menu_shell_set_grab_device (CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
, pointer);
1927 menu_shell->priv->active = TRUE(!(0));
1928 menu_shell->priv->button = button;
1929
1930 /* If we are popping up the menu from something other than, a button
1931 * press then, as a heuristic, we ignore enter events for the menu
1932 * until we get a MOTION_NOTIFY.
1933 */
1934
1935 current_event = ctk_get_current_event ();
1936 if (current_event)
1937 {
1938 if ((current_event->type != CDK_BUTTON_PRESS) &&
1939 (current_event->type != CDK_ENTER_NOTIFY))
1940 menu_shell->priv->ignore_enter = TRUE(!(0));
1941
1942 source_device = cdk_event_get_source_device (current_event);
1943 cdk_event_free (current_event);
1944 }
1945 else
1946 menu_shell->priv->ignore_enter = TRUE(!(0));
1947
1948 if (priv->torn_off)
1949 {
1950 ctk_menu_tearoff_bg_copy (menu);
1951
1952 ctk_menu_reparent (menu, priv->toplevel, FALSE(0));
1953 }
1954
1955 parent_toplevel = NULL((void*)0);
1956 if (parent_menu_shell)
1957 parent_toplevel = ctk_widget_get_toplevel (parent_menu_shell);
1958 else if (!g_object_get_data (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, "ctk-menu-explicit-screen"))
1959 {
1960 CtkWidget *attach_widget = ctk_menu_get_attach_widget (menu);
1961 if (attach_widget)
1962 parent_toplevel = ctk_widget_get_toplevel (attach_widget);
1963 }
1964
1965 /* Set transient for to get the right window group and parent */
1966 if (CTK_IS_WINDOW (parent_toplevel)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(parent_toplevel)); GType __t = ((ctk_window_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
)
1967 ctk_window_set_transient_for (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
,
1968 CTK_WINDOW (parent_toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((parent_toplevel)), ((ctk_window_get_type ()))))))
);
1969
1970 priv->parent_menu_item = parent_menu_item;
1971 priv->position_func = func;
1972 priv->position_func_data = data;
1973 priv->position_func_data_destroy = destroy;
1974 menu_shell->priv->activate_time = activate_time;
1975
1976 /* We need to show the menu here rather in the init function
1977 * because code expects to be able to tell if the menu is onscreen
1978 * by looking at ctk_widget_get_visible (menu)
1979 */
1980 ctk_widget_show (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
1981
1982 /* Position the menu, possibly changing the size request
1983 */
1984 ctk_menu_position (menu, TRUE(!(0)));
1985
1986 associate_menu_grab_transfer_window (menu);
1987
1988 ctk_menu_scroll_to (menu, priv->scroll_offset, CTK_MENU_SCROLL_FLAG_NONE);
1989
1990 /* if no item is selected, select the first one */
1991 if (!menu_shell->priv->active_menu_item &&
1992 source_device && cdk_device_get_source (source_device) == CDK_SOURCE_TOUCHSCREEN)
1993 ctk_menu_shell_select_first (menu_shell, TRUE(!(0)));
1994
1995 /* Once everything is set up correctly, map the toplevel */
1996 ctk_window_force_resize (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
);
1997 ctk_widget_show (priv->toplevel);
1998
1999 if (xgrab_shell == widget)
2000 popup_grab_on_window (ctk_widget_get_window (widget), pointer); /* Should always succeed */
2001
2002 ctk_grab_add (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
2003
2004 if (parent_menu_shell)
2005 {
2006 gboolean keyboard_mode;
2007
2008 keyboard_mode = _ctk_menu_shell_get_keyboard_mode (CTK_MENU_SHELL (parent_menu_shell)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((parent_menu_shell)), ((ctk_menu_shell_get_type ()))))))
);
2009 _ctk_menu_shell_set_keyboard_mode (menu_shell, keyboard_mode);
2010 }
2011 else if (menu_shell->priv->button == 0) /* a keynav-activated context menu */
2012 _ctk_menu_shell_set_keyboard_mode (menu_shell, TRUE(!(0)));
2013
2014 _ctk_menu_shell_update_mnemonics (menu_shell);
2015}
2016
2017/**
2018 * ctk_menu_popup_for_device:
2019 * @menu: a #CtkMenu
2020 * @device: (allow-none): a #CdkDevice
2021 * @parent_menu_shell: (allow-none): the menu shell containing the triggering
2022 * menu item, or %NULL
2023 * @parent_menu_item: (allow-none): the menu item whose activation triggered
2024 * the popup, or %NULL
2025 * @func: (allow-none): a user supplied function used to position the menu,
2026 * or %NULL
2027 * @data: (allow-none): user supplied data to be passed to @func
2028 * @destroy: (allow-none): destroy notify for @data
2029 * @button: the mouse button which was pressed to initiate the event
2030 * @activate_time: the time at which the activation event occurred
2031 *
2032 * Displays a menu and makes it available for selection.
2033 *
2034 * Applications can use this function to display context-sensitive menus,
2035 * and will typically supply %NULL for the @parent_menu_shell,
2036 * @parent_menu_item, @func, @data and @destroy parameters. The default
2037 * menu positioning function will position the menu at the current position
2038 * of @device (or its corresponding pointer).
2039 *
2040 * The @button parameter should be the mouse button pressed to initiate
2041 * the menu popup. If the menu popup was initiated by something other than
2042 * a mouse button press, such as a mouse button release or a keypress,
2043 * @button should be 0.
2044 *
2045 * The @activate_time parameter is used to conflict-resolve initiation of
2046 * concurrent requests for mouse/keyboard grab requests. To function
2047 * properly, this needs to be the time stamp of the user event (such as
2048 * a mouse click or key press) that caused the initiation of the popup.
2049 * Only if no such event is available, ctk_get_current_event_time() can
2050 * be used instead.
2051 *
2052 * Note that this function does not work very well on CDK backends that
2053 * do not have global coordinates, such as Wayland or Mir. You should
2054 * probably use one of the ctk_menu_popup_at_ variants, which do not
2055 * have this problem.
2056 *
2057 * Since: 3.0
2058 */
2059void
2060ctk_menu_popup_for_device (CtkMenu *menu,
2061 CdkDevice *device,
2062 CtkWidget *parent_menu_shell,
2063 CtkWidget *parent_menu_item,
2064 CtkMenuPositionFunc func,
2065 gpointer data,
2066 GDestroyNotify destroy,
2067 guint button,
2068 guint32 activate_time)
2069{
2070 CtkMenuPrivate *priv;
2071
2072 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
2073
2074 priv = menu->priv;
2075 priv->rect_window = NULL((void*)0);
2076 priv->widget = NULL((void*)0);
2077
2078 ctk_menu_popup_internal (menu,
2079 device,
2080 parent_menu_shell,
2081 parent_menu_item,
2082 func,
2083 data,
2084 destroy,
2085 button,
2086 activate_time);
2087}
2088
2089/**
2090 * ctk_menu_popup:
2091 * @menu: a #CtkMenu
2092 * @parent_menu_shell: (allow-none): the menu shell containing the
2093 * triggering menu item, or %NULL
2094 * @parent_menu_item: (allow-none): the menu item whose activation
2095 * triggered the popup, or %NULL
2096 * @func: (scope async) (allow-none): a user supplied function used to position
2097 * the menu, or %NULL
2098 * @data: user supplied data to be passed to @func.
2099 * @button: the mouse button which was pressed to initiate the event.
2100 * @activate_time: the time at which the activation event occurred.
2101 *
2102 * Displays a menu and makes it available for selection.
2103 *
2104 * Applications can use this function to display context-sensitive
2105 * menus, and will typically supply %NULL for the @parent_menu_shell,
2106 * @parent_menu_item, @func and @data parameters. The default menu
2107 * positioning function will position the menu at the current mouse
2108 * cursor position.
2109 *
2110 * The @button parameter should be the mouse button pressed to initiate
2111 * the menu popup. If the menu popup was initiated by something other
2112 * than a mouse button press, such as a mouse button release or a keypress,
2113 * @button should be 0.
2114 *
2115 * The @activate_time parameter is used to conflict-resolve initiation
2116 * of concurrent requests for mouse/keyboard grab requests. To function
2117 * properly, this needs to be the timestamp of the user event (such as
2118 * a mouse click or key press) that caused the initiation of the popup.
2119 * Only if no such event is available, ctk_get_current_event_time() can
2120 * be used instead.
2121 *
2122 * Note that this function does not work very well on CDK backends that
2123 * do not have global coordinates, such as Wayland or Mir. You should
2124 * probably use one of the ctk_menu_popup_at_ variants, which do not
2125 * have this problem.
2126 */
2127void
2128ctk_menu_popup (CtkMenu *menu,
2129 CtkWidget *parent_menu_shell,
2130 CtkWidget *parent_menu_item,
2131 CtkMenuPositionFunc func,
2132 gpointer data,
2133 guint button,
2134 guint32 activate_time)
2135{
2136 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
2137
2138 ctk_menu_popup_for_device (menu,
2139 NULL((void*)0),
2140 parent_menu_shell,
2141 parent_menu_item,
2142
2143 func, data, NULL((void*)0),
2144 button, activate_time);
2145}
2146
2147static CdkDevice *
2148get_device_for_event (const CdkEvent *event)
2149{
2150 CdkDevice *device = NULL((void*)0);
2151 CdkSeat *seat = NULL((void*)0);
2152 CdkScreen *screen = NULL((void*)0);
2153 CdkDisplay *display = NULL((void*)0);
2154
2155 device = cdk_event_get_device (event);
2156
2157 if (device)
2158 return device;
2159
2160 seat = cdk_event_get_seat (event);
2161
2162 if (!seat)
2163 {
2164 screen = cdk_event_get_screen (event);
2165
2166 if (screen)
2167 display = cdk_screen_get_display (screen);
2168
2169 if (!display)
2170 {
2171 g_warning ("no display for event, using default");
2172 display = cdk_display_get_default ();
2173 }
2174
2175 if (display)
2176 seat = cdk_display_get_default_seat (display);
2177 }
2178
2179 return seat ? cdk_seat_get_pointer (seat) : NULL((void*)0);
2180}
2181
2182/**
2183 * ctk_menu_popup_at_rect:
2184 * @menu: the #CtkMenu to pop up
2185 * @rect_window: (not nullable): the #CdkWindow @rect is relative to
2186 * @rect: (not nullable): the #CdkRectangle to align @menu with
2187 * @rect_anchor: the point on @rect to align with @menu's anchor point
2188 * @menu_anchor: the point on @menu to align with @rect's anchor point
2189 * @trigger_event: (nullable): the #CdkEvent that initiated this request or
2190 * %NULL if it's the current event
2191 *
2192 * Displays @menu and makes it available for selection.
2193 *
2194 * See ctk_menu_popup_at_widget () and ctk_menu_popup_at_pointer (), which
2195 * handle more common cases for popping up menus.
2196 *
2197 * @menu will be positioned at @rect, aligning their anchor points. @rect is
2198 * relative to the top-left corner of @rect_window. @rect_anchor and
2199 * @menu_anchor determine anchor points on @rect and @menu to pin together.
2200 * @menu can optionally be offset by #CtkMenu:rect-anchor-dx and
2201 * #CtkMenu:rect-anchor-dy.
2202 *
2203 * Anchors should be specified under the assumption that the text direction is
2204 * left-to-right; they will be flipped horizontally automatically if the text
2205 * direction is right-to-left.
2206 *
2207 * Other properties that influence the behaviour of this function are
2208 * #CtkMenu:anchor-hints and #CtkMenu:menu-type-hint. Connect to the
2209 * #CtkMenu::popped-up signal to find out how it was actually positioned.
2210 *
2211 * Since: 3.22
2212 */
2213void
2214ctk_menu_popup_at_rect (CtkMenu *menu,
2215 CdkWindow *rect_window,
2216 const CdkRectangle *rect,
2217 CdkGravity rect_anchor,
2218 CdkGravity menu_anchor,
2219 const CdkEvent *trigger_event)
2220{
2221 CtkMenuPrivate *priv;
2222 CdkEvent *current_event = NULL((void*)0);
2223 CdkDevice *device = NULL((void*)0);
2224 guint button = 0;
2225 guint32 activate_time = CDK_CURRENT_TIME0L;
2226
2227 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
2228 g_return_if_fail (CDK_IS_WINDOW (rect_window))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((rect_window)); GType __t = ((cdk_window_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CDK_IS_WINDOW (rect_window)"); return; } } while (0)
;
2229 g_return_if_fail (rect)do { if ((rect)) { } else { g_return_if_fail_warning ("Ctk", (
(const char*) (__func__)), "rect"); return; } } while (0)
;
2230
2231 priv = menu->priv;
2232 priv->rect_window = rect_window;
2233 priv->rect = *rect;
2234 priv->widget = NULL((void*)0);
2235 priv->rect_anchor = rect_anchor;
2236 priv->menu_anchor = menu_anchor;
2237
2238 if (!trigger_event)
2239 {
2240 current_event = ctk_get_current_event ();
2241 trigger_event = current_event;
2242 }
2243
2244 if (trigger_event)
2245 {
2246 device = get_device_for_event (trigger_event);
2247 cdk_event_get_button (trigger_event, &button);
2248 activate_time = cdk_event_get_time (trigger_event);
2249 }
2250 else
2251 g_warning ("no trigger event for menu popup");
2252
2253 ctk_menu_popup_internal (menu,
2254 device,
2255 NULL((void*)0),
2256 NULL((void*)0),
2257 NULL((void*)0),
2258 NULL((void*)0),
2259 NULL((void*)0),
2260 button,
2261 activate_time);
2262
2263 g_clear_pointer (&current_event, cdk_event_free)do { _Static_assert (sizeof *(&current_event) == sizeof (
gpointer), "Expression evaluates to false"); __typeof__ ((&
current_event)) _pp = (&current_event); __typeof__ (*(&
current_event)) _ptr = *_pp; *_pp = ((void*)0); if (_ptr) (cdk_event_free
) (_ptr); } while (0)
;
2264}
2265
2266/**
2267 * ctk_menu_popup_at_widget:
2268 * @menu: the #CtkMenu to pop up
2269 * @widget: (not nullable): the #CtkWidget to align @menu with
2270 * @widget_anchor: the point on @widget to align with @menu's anchor point
2271 * @menu_anchor: the point on @menu to align with @widget's anchor point
2272 * @trigger_event: (nullable): the #CdkEvent that initiated this request or
2273 * %NULL if it's the current event
2274 *
2275 * Displays @menu and makes it available for selection.
2276 *
2277 * See ctk_menu_popup_at_pointer () to pop up a menu at the master pointer.
2278 * ctk_menu_popup_at_rect () also allows you to position a menu at an arbitrary
2279 * rectangle.
2280 *
2281 * ![](popup-anchors.png)
2282 *
2283 * @menu will be positioned at @widget, aligning their anchor points.
2284 * @widget_anchor and @menu_anchor determine anchor points on @widget and @menu
2285 * to pin together. @menu can optionally be offset by #CtkMenu:rect-anchor-dx
2286 * and #CtkMenu:rect-anchor-dy.
2287 *
2288 * Anchors should be specified under the assumption that the text direction is
2289 * left-to-right; they will be flipped horizontally automatically if the text
2290 * direction is right-to-left.
2291 *
2292 * Other properties that influence the behaviour of this function are
2293 * #CtkMenu:anchor-hints and #CtkMenu:menu-type-hint. Connect to the
2294 * #CtkMenu::popped-up signal to find out how it was actually positioned.
2295 *
2296 * Since: 3.22
2297 */
2298void
2299ctk_menu_popup_at_widget (CtkMenu *menu,
2300 CtkWidget *widget,
2301 CdkGravity widget_anchor,
2302 CdkGravity menu_anchor,
2303 const CdkEvent *trigger_event)
2304{
2305 CtkMenuPrivate *priv;
2306 CdkEvent *current_event = NULL((void*)0);
2307 CdkDevice *device = NULL((void*)0);
2308 guint button = 0;
2309 guint32 activate_time = CDK_CURRENT_TIME0L;
2310 CtkWidget *parent_menu_shell = NULL((void*)0);
2311 CtkWidget *parent_menu_item = NULL((void*)0);
2312
2313 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
2314 g_return_if_fail (CTK_IS_WIDGET (widget))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((widget)); GType __t = ((ctk_widget_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_WIDGET (widget)"); return; } } while (0)
;
2315
2316 priv = menu->priv;
2317 priv->rect_window = NULL((void*)0);
2318 priv->widget = widget;
2319 priv->rect_anchor = widget_anchor;
2320 priv->menu_anchor = menu_anchor;
2321
2322 if (!trigger_event)
2323 {
2324 current_event = ctk_get_current_event ();
2325 trigger_event = current_event;
2326 }
2327
2328 if (trigger_event)
2329 {
2330 device = get_device_for_event (trigger_event);
2331 cdk_event_get_button (trigger_event, &button);
2332 activate_time = cdk_event_get_time (trigger_event);
2333 }
2334 else
2335 g_warning ("no trigger event for menu popup");
2336
2337 if (CTK_IS_MENU_ITEM (priv->widget)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(priv->widget)); GType __t = ((ctk_menu_item_get_type ()))
; gboolean __r; if (!__inst) __r = (0); else if (__inst->g_class
&& __inst->g_class->g_type == __t) __r = (!(0)
); else __r = g_type_check_instance_is_a (__inst, __t); __r; }
))))
)
2338 {
2339 parent_menu_item = priv->widget;
2340
2341 if (CTK_IS_MENU_SHELL (ctk_widget_get_parent (parent_menu_item))(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(ctk_widget_get_parent (parent_menu_item))); GType __t = ((ctk_menu_shell_get_type
())); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; }))))
)
2342 parent_menu_shell = ctk_widget_get_parent (parent_menu_item);
2343 }
2344
2345 ctk_menu_popup_internal (menu,
2346 device,
2347 parent_menu_shell,
2348 parent_menu_item,
2349 NULL((void*)0),
2350 NULL((void*)0),
2351 NULL((void*)0),
2352 button,
2353 activate_time);
2354
2355 g_clear_pointer (&current_event, cdk_event_free)do { _Static_assert (sizeof *(&current_event) == sizeof (
gpointer), "Expression evaluates to false"); __typeof__ ((&
current_event)) _pp = (&current_event); __typeof__ (*(&
current_event)) _ptr = *_pp; *_pp = ((void*)0); if (_ptr) (cdk_event_free
) (_ptr); } while (0)
;
2356}
2357
2358/**
2359 * ctk_menu_popup_at_pointer:
2360 * @menu: the #CtkMenu to pop up
2361 * @trigger_event: (nullable): the #CdkEvent that initiated this request or
2362 * %NULL if it's the current event
2363 *
2364 * Displays @menu and makes it available for selection.
2365 *
2366 * See ctk_menu_popup_at_widget () to pop up a menu at a widget.
2367 * ctk_menu_popup_at_rect () also allows you to position a menu at an arbitrary
2368 * rectangle.
2369 *
2370 * @menu will be positioned at the pointer associated with @trigger_event.
2371 *
2372 * Properties that influence the behaviour of this function are
2373 * #CtkMenu:anchor-hints, #CtkMenu:rect-anchor-dx, #CtkMenu:rect-anchor-dy, and
2374 * #CtkMenu:menu-type-hint. Connect to the #CtkMenu::popped-up signal to find
2375 * out how it was actually positioned.
2376 *
2377 * Since: 3.22
2378 */
2379void
2380ctk_menu_popup_at_pointer (CtkMenu *menu,
2381 const CdkEvent *trigger_event)
2382{
2383 CdkEvent *current_event = NULL((void*)0);
2384 CdkWindow *rect_window = NULL((void*)0);
2385 CdkDevice *device = NULL((void*)0);
2386 CdkRectangle rect = { 0, 0, 1, 1 };
2387
2388 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
2389
2390 if (!trigger_event)
2391 {
2392 current_event = ctk_get_current_event ();
2393 trigger_event = current_event;
2394 }
2395
2396 if (trigger_event)
2397 {
2398 rect_window = cdk_event_get_window (trigger_event);
2399
2400 if (rect_window)
2401 {
2402 device = get_device_for_event (trigger_event);
2403
2404 if (device && cdk_device_get_source (device) == CDK_SOURCE_KEYBOARD)
2405 device = cdk_device_get_associated_device (device);
2406
2407 if (device)
2408 cdk_window_get_device_position (rect_window, device, &rect.x, &rect.y, NULL((void*)0));
2409 }
2410 }
2411 else
2412 g_warning ("no trigger event for menu popup");
2413
2414 ctk_menu_popup_at_rect (menu,
2415 rect_window,
2416 &rect,
2417 CDK_GRAVITY_SOUTH_EAST,
2418 CDK_GRAVITY_NORTH_WEST,
2419 trigger_event);
2420
2421 g_clear_pointer (&current_event, cdk_event_free)do { _Static_assert (sizeof *(&current_event) == sizeof (
gpointer), "Expression evaluates to false"); __typeof__ ((&
current_event)) _pp = (&current_event); __typeof__ (*(&
current_event)) _ptr = *_pp; *_pp = ((void*)0); if (_ptr) (cdk_event_free
) (_ptr); } while (0)
;
2422}
2423
2424static void
2425get_arrows_border (CtkMenu *menu,
2426 CtkBorder *border)
2427{
2428 CtkMenuPrivate *priv = menu->priv;
2429 gint top_arrow_height, bottom_arrow_height;
2430
2431 ctk_css_gadget_get_preferred_size (priv->top_arrow_gadget,
2432 CTK_ORIENTATION_VERTICAL,
2433 -1,
2434 &top_arrow_height, NULL((void*)0),
2435 NULL((void*)0), NULL((void*)0));
2436 ctk_css_gadget_get_preferred_size (priv->bottom_arrow_gadget,
2437 CTK_ORIENTATION_VERTICAL,
2438 -1,
2439 &bottom_arrow_height, NULL((void*)0),
2440 NULL((void*)0), NULL((void*)0));
2441
2442 border->top = priv->upper_arrow_visible ? top_arrow_height : 0;
2443 border->bottom = priv->lower_arrow_visible ? bottom_arrow_height : 0;
2444 border->left = border->right = 0;
2445}
2446
2447/**
2448 * ctk_menu_update_scroll_offset:
2449 * @menu: the #CtkMenu that popped up
2450 * @flipped_rect: (nullable): the position of @menu after any possible flipping
2451 * or %NULL if unknown
2452 * @final_rect: (nullable): the final position of @menu or %NULL if unknown
2453 * @flipped_x: %TRUE if the anchors were flipped horizontally
2454 * @flipped_y: %TRUE if the anchors were flipped vertically
2455 * @user_data: user data
2456 *
2457 * Updates the scroll offset of @menu based on the amount of sliding done while
2458 * positioning @menu. Connect this to the #CtkMenu::popped-up signal to keep the
2459 * contents of the menu vertically aligned with their ideal position, for combo
2460 * boxes for example.
2461 *
2462 * Since: 3.22
2463 * Stability: Private
2464 */
2465void
2466ctk_menu_update_scroll_offset (CtkMenu *menu,
2467 const CdkRectangle *flipped_rect,
2468 const CdkRectangle *final_rect,
2469 gboolean flipped_x G_GNUC_UNUSED__attribute__ ((__unused__)),
2470 gboolean flipped_y G_GNUC_UNUSED__attribute__ ((__unused__)),
2471 gpointer user_data G_GNUC_UNUSED__attribute__ ((__unused__)))
2472{
2473 CtkBorder arrows_border;
2474
2475 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
2476
2477 if (!flipped_rect || !final_rect)
2478 return;
2479
2480 get_arrows_border (menu, &arrows_border);
2481 menu->priv->scroll_offset = arrows_border.top + (final_rect->y - flipped_rect->y);
2482 ctk_menu_scroll_to (menu, menu->priv->scroll_offset,
2483 CTK_MENU_SCROLL_FLAG_ADAPT);
2484}
2485
2486/**
2487 * ctk_menu_popdown:
2488 * @menu: a #CtkMenu
2489 *
2490 * Removes the menu from the screen.
2491 */
2492void
2493ctk_menu_popdown (CtkMenu *menu)
2494{
2495 CtkMenuPrivate *priv;
2496 CtkMenuShell *menu_shell;
2497 CdkDevice *pointer;
2498
2499 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
2500
2501 menu_shell = CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
;
2502 priv = menu->priv;
2503
2504 menu_shell->priv->parent_menu_shell = NULL((void*)0);
2505 menu_shell->priv->active = FALSE(0);
2506 menu_shell->priv->ignore_enter = FALSE(0);
2507
2508 priv->have_position = FALSE(0);
2509
2510 ctk_menu_stop_scrolling (menu);
2511 ctk_menu_stop_navigating_submenu (menu);
2512
2513 if (menu_shell->priv->active_menu_item)
2514 {
2515 if (priv->old_active_menu_item)
2516 g_object_unref (priv->old_active_menu_item);
2517 priv->old_active_menu_item = menu_shell->priv->active_menu_item;
2518 g_object_ref (priv->old_active_menu_item)((__typeof__ (priv->old_active_menu_item)) (g_object_ref) (
priv->old_active_menu_item))
;
2519 }
2520
2521 ctk_menu_shell_deselect (menu_shell);
2522
2523 /* The X Grab, if present, will automatically be removed
2524 * when we hide the window
2525 */
2526 if (priv->toplevel)
2527 {
2528 ctk_widget_hide (priv->toplevel);
2529 ctk_window_set_transient_for (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
, NULL((void*)0));
2530 }
2531
2532 pointer = _ctk_menu_shell_get_grab_device (menu_shell);
2533
2534 if (priv->torn_off)
2535 {
2536 ctk_widget_set_size_request (priv->tearoff_window, -1, -1);
2537
2538 if (ctk_bin_get_child (CTK_BIN (priv->toplevel)((((CtkBin*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_bin_get_type ()))))))
))
2539 {
2540 ctk_menu_reparent (menu, priv->tearoff_hbox, TRUE(!(0)));
2541 }
2542 else
2543 {
2544 /* We popped up the menu from the tearoff, so we need to
2545 * release the grab - we aren't actually hiding the menu.
2546 */
2547 if (menu_shell->priv->have_xgrab && pointer)
2548 cdk_seat_ungrab (cdk_device_get_seat (pointer));
2549 }
2550
2551 /* ctk_menu_popdown is called each time a menu item is selected from
2552 * a torn off menu. Only scroll back to the saved position if the
2553 * non-tearoff menu was popped down.
2554 */
2555 if (!priv->tearoff_active)
2556 ctk_menu_scroll_to (menu, priv->saved_scroll_offset,
2557 CTK_MENU_SCROLL_FLAG_NONE);
2558 priv->tearoff_active = TRUE(!(0));
2559 }
2560 else
2561 ctk_widget_hide (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
2562
2563 menu_shell->priv->have_xgrab = FALSE(0);
2564
2565 ctk_grab_remove (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
2566
2567 _ctk_menu_shell_set_grab_device (menu_shell, NULL((void*)0));
2568
2569 menu_grab_transfer_window_destroy (menu);
2570}
2571
2572/**
2573 * ctk_menu_get_active:
2574 * @menu: a #CtkMenu
2575 *
2576 * Returns the selected menu item from the menu. This is used by the
2577 * #CtkComboBox.
2578 *
2579 * Returns: (transfer none): the #CtkMenuItem that was last selected
2580 * in the menu. If a selection has not yet been made, the
2581 * first menu item is selected.
2582 */
2583CtkWidget*
2584ctk_menu_get_active (CtkMenu *menu)
2585{
2586 CtkMenuPrivate *priv;
2587 CtkWidget *child;
2588 GList *children;
2589
2590 g_return_val_if_fail (CTK_IS_MENU (menu), NULL)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return (((void*)0)); } } while (0)
;
2591
2592 priv = menu->priv;
2593
2594 if (!priv->old_active_menu_item)
2595 {
2596 child = NULL((void*)0);
2597 children = CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
->priv->children;
2598
2599 while (children)
2600 {
2601 child = children->data;
2602 children = children->next;
2603
2604 if (ctk_bin_get_child (CTK_BIN (child)((((CtkBin*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((child)), ((ctk_bin_get_type ()))))))
))
2605 break;
2606 child = NULL((void*)0);
2607 }
2608
2609 priv->old_active_menu_item = child;
2610 if (priv->old_active_menu_item)
2611 g_object_ref (priv->old_active_menu_item)((__typeof__ (priv->old_active_menu_item)) (g_object_ref) (
priv->old_active_menu_item))
;
2612 }
2613
2614 return priv->old_active_menu_item;
2615}
2616
2617/**
2618 * ctk_menu_set_active:
2619 * @menu: a #CtkMenu
2620 * @index: the index of the menu item to select. Index values are
2621 * from 0 to n-1
2622 *
2623 * Selects the specified menu item within the menu. This is used by
2624 * the #CtkComboBox and should not be used by anyone else.
2625 */
2626void
2627ctk_menu_set_active (CtkMenu *menu,
2628 guint index)
2629{
2630 CtkMenuPrivate *priv;
2631 CtkWidget *child;
2632 GList *tmp_list;
2633
2634 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
2635
2636 priv = menu->priv;
2637
2638 tmp_list = g_list_nth (CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
->priv->children, index);
2639 if (tmp_list)
2640 {
2641 child = tmp_list->data;
2642 if (ctk_bin_get_child (CTK_BIN (child)((((CtkBin*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((child)), ((ctk_bin_get_type ()))))))
))
2643 {
2644 if (priv->old_active_menu_item)
2645 g_object_unref (priv->old_active_menu_item);
2646 priv->old_active_menu_item = child;
2647 g_object_ref (priv->old_active_menu_item)((__typeof__ (priv->old_active_menu_item)) (g_object_ref) (
priv->old_active_menu_item))
;
2648 }
2649 }
2650 g_object_notify (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, "active");
2651}
2652
2653/**
2654 * ctk_menu_set_accel_group:
2655 * @menu: a #CtkMenu
2656 * @accel_group: (allow-none): the #CtkAccelGroup to be associated
2657 * with the menu.
2658 *
2659 * Set the #CtkAccelGroup which holds global accelerators for the
2660 * menu. This accelerator group needs to also be added to all windows
2661 * that this menu is being used in with ctk_window_add_accel_group(),
2662 * in order for those windows to support all the accelerators
2663 * contained in this group.
2664 */
2665void
2666ctk_menu_set_accel_group (CtkMenu *menu,
2667 CtkAccelGroup *accel_group)
2668{
2669 CtkMenuPrivate *priv;
2670
2671 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
2672 g_return_if_fail (!accel_group || CTK_IS_ACCEL_GROUP (accel_group))do { if ((!accel_group || (((__extension__ ({ GTypeInstance *
__inst = (GTypeInstance*) ((accel_group)); GType __t = ((ctk_accel_group_get_type
())); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("Ctk", ((const
char*) (__func__)), "!accel_group || CTK_IS_ACCEL_GROUP (accel_group)"
); return; } } while (0)
;
2673
2674 priv = menu->priv;
2675
2676 if (priv->accel_group != accel_group)
2677 {
2678 if (priv->accel_group)
2679 g_object_unref (priv->accel_group);
2680 priv->accel_group = accel_group;
2681 if (priv->accel_group)
2682 g_object_ref (priv->accel_group)((__typeof__ (priv->accel_group)) (g_object_ref) (priv->
accel_group))
;
2683 _ctk_menu_refresh_accel_paths (menu, TRUE(!(0)));
2684 }
2685}
2686
2687/**
2688 * ctk_menu_get_accel_group:
2689 * @menu: a #CtkMenu
2690 *
2691 * Gets the #CtkAccelGroup which holds global accelerators for the
2692 * menu. See ctk_menu_set_accel_group().
2693 *
2694 * Returns: (transfer none): the #CtkAccelGroup associated with the menu
2695 */
2696CtkAccelGroup*
2697ctk_menu_get_accel_group (CtkMenu *menu)
2698{
2699 g_return_val_if_fail (CTK_IS_MENU (menu), NULL)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return (((void*)0)); } } while (0)
;
2700
2701 return menu->priv->accel_group;
2702}
2703
2704static gboolean
2705ctk_menu_real_can_activate_accel (CtkWidget *widget,
2706 guint signal_id)
2707{
2708 /* Menu items chain here to figure whether they can activate their
2709 * accelerators. Unlike ordinary widgets, menus allow accel
2710 * activation even if invisible since that's the usual case for
2711 * submenus/popup-menus. however, the state of the attach widget
2712 * affects the "activeness" of the menu.
2713 */
2714 CtkWidget *awidget = ctk_menu_get_attach_widget (CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
);
2715
2716 if (awidget)
2717 return ctk_widget_can_activate_accel (awidget, signal_id);
2718 else
2719 return ctk_widget_is_sensitive (widget);
2720}
2721
2722/**
2723 * ctk_menu_set_accel_path:
2724 * @menu: a valid #CtkMenu
2725 * @accel_path: (nullable): a valid accelerator path, or %NULL to unset the path
2726 *
2727 * Sets an accelerator path for this menu from which accelerator paths
2728 * for its immediate children, its menu items, can be constructed.
2729 * The main purpose of this function is to spare the programmer the
2730 * inconvenience of having to call ctk_menu_item_set_accel_path() on
2731 * each menu item that should support runtime user changable accelerators.
2732 * Instead, by just calling ctk_menu_set_accel_path() on their parent,
2733 * each menu item of this menu, that contains a label describing its
2734 * purpose, automatically gets an accel path assigned.
2735 *
2736 * For example, a menu containing menu items “New” and “Exit”, will, after
2737 * `ctk_menu_set_accel_path (menu, "<Gnumeric-Sheet>/File");` has been
2738 * called, assign its items the accel paths: `"<Gnumeric-Sheet>/File/New"`
2739 * and `"<Gnumeric-Sheet>/File/Exit"`.
2740 *
2741 * Assigning accel paths to menu items then enables the user to change
2742 * their accelerators at runtime. More details about accelerator paths
2743 * and their default setups can be found at ctk_accel_map_add_entry().
2744 *
2745 * Note that @accel_path string will be stored in a #GQuark. Therefore,
2746 * if you pass a static string, you can save some memory by interning
2747 * it first with g_intern_static_string().
2748 */
2749void
2750ctk_menu_set_accel_path (CtkMenu *menu,
2751 const gchar *accel_path)
2752{
2753 CtkMenuPrivate *priv;
2754
2755 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
2756
2757 priv = menu->priv;
2758
2759 if (accel_path)
2760 g_return_if_fail (accel_path[0] == '<' && strchr (accel_path, '/'))do { if ((accel_path[0] == '<' && strchr (accel_path
, '/'))) { } else { g_return_if_fail_warning ("Ctk", ((const char
*) (__func__)), "accel_path[0] == '<' && strchr (accel_path, '/')"
); return; } } while (0)
; /* simplistic check */
2761
2762 priv->accel_path = g_intern_string (accel_path);
2763 if (priv->accel_path)
2764 _ctk_menu_refresh_accel_paths (menu, FALSE(0));
2765}
2766
2767/**
2768 * ctk_menu_get_accel_path:
2769 * @menu: a valid #CtkMenu
2770 *
2771 * Retrieves the accelerator path set on the menu.
2772 *
2773 * Returns: the accelerator path set on the menu.
2774 *
2775 * Since: 2.14
2776 */
2777const gchar*
2778ctk_menu_get_accel_path (CtkMenu *menu)
2779{
2780 g_return_val_if_fail (CTK_IS_MENU (menu), NULL)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return (((void*)0)); } } while (0)
;
2781
2782 return menu->priv->accel_path;
2783}
2784
2785typedef struct {
2786 CtkMenu *menu;
2787 gboolean group_changed;
2788} AccelPropagation;
2789
2790static void
2791refresh_accel_paths_foreach (CtkWidget *widget,
2792 gpointer data)
2793{
2794 CtkMenuPrivate *priv;
2795 AccelPropagation *prop = data;
2796
2797 if (CTK_IS_MENU_ITEM (widget)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(widget)); GType __t = ((ctk_menu_item_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
) /* should always be true */
2798 {
2799 priv = prop->menu->priv;
2800 _ctk_menu_item_refresh_accel_path (CTK_MENU_ITEM (widget)((((CtkMenuItem*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_item_get_type ()))))))
,
2801 priv->accel_path,
2802 priv->accel_group,
2803 prop->group_changed);
2804 }
2805}
2806
2807static void
2808_ctk_menu_refresh_accel_paths (CtkMenu *menu,
2809 gboolean group_changed)
2810{
2811 CtkMenuPrivate *priv = menu->priv;
2812
2813 if (priv->accel_path)
2814 {
2815 AccelPropagation prop;
2816
2817 prop.menu = menu;
2818 prop.group_changed = group_changed;
2819 ctk_container_foreach (CTK_CONTAINER (menu)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_container_get_type ()))))))
,
2820 refresh_accel_paths_foreach,
2821 &prop);
2822 }
2823}
2824
2825/**
2826 * ctk_menu_reposition:
2827 * @menu: a #CtkMenu
2828 *
2829 * Repositions the menu according to its position function.
2830 */
2831void
2832ctk_menu_reposition (CtkMenu *menu)
2833{
2834 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
2835
2836 if (!menu->priv->torn_off && ctk_widget_is_drawable (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
))
2837 ctk_menu_position (menu, FALSE(0));
2838}
2839
2840static void
2841ctk_menu_scrollbar_changed (CtkAdjustment *adjustment,
2842 CtkMenu *menu)
2843{
2844 double value;
2845
2846 value = ctk_adjustment_get_value (adjustment);
2847 if (menu->priv->scroll_offset != value)
2848 ctk_menu_scroll_to (menu, value, CTK_MENU_SCROLL_FLAG_NONE);
2849}
2850
2851static void
2852ctk_menu_set_tearoff_hints (CtkMenu *menu,
2853 gint width)
2854{
2855 CtkMenuPrivate *priv = menu->priv;
2856 CdkGeometry geometry_hints;
2857
2858 if (!priv->tearoff_window)
2859 return;
2860
2861 if (ctk_widget_get_visible (priv->tearoff_scrollbar))
2862 {
2863 CtkRequisition requisition;
2864
2865 ctk_widget_get_preferred_size (priv->tearoff_scrollbar,
2866 &requisition, NULL((void*)0));
2867 width += requisition.width;
2868 }
2869
2870 geometry_hints.min_width = width;
2871 geometry_hints.max_width = width;
2872
2873 geometry_hints.min_height = 0;
2874 geometry_hints.max_height = priv->requested_height;
2875
2876 ctk_window_set_geometry_hints (CTK_WINDOW (priv->tearoff_window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->tearoff_window)), ((ctk_window_get_type ())))))
)
,
2877 NULL((void*)0),
2878 &geometry_hints,
2879 CDK_HINT_MAX_SIZE|CDK_HINT_MIN_SIZE);
2880}
2881
2882static void
2883ctk_menu_update_title (CtkMenu *menu)
2884{
2885 CtkMenuPrivate *priv = menu->priv;
2886
2887 if (priv->tearoff_window)
2888 {
2889 const gchar *title;
2890 CtkWidget *attach_widget;
2891
2892 title = ctk_menu_get_title (menu);
2893
2894 if (!title)
2895 {
2896 attach_widget = ctk_menu_get_attach_widget (menu);
2897 if (CTK_IS_MENU_ITEM (attach_widget)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(attach_widget)); GType __t = ((ctk_menu_item_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
)
2898 {
2899 CtkWidget *child = ctk_bin_get_child (CTK_BIN (attach_widget)((((CtkBin*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((attach_widget)), ((ctk_bin_get_type ()))))))
);
2900 if (CTK_IS_LABEL (child)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(child)); GType __t = ((ctk_label_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
)
2901 title = ctk_label_get_text (CTK_LABEL (child)((((CtkLabel*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((child)), ((ctk_label_get_type ()))))))
);
2902 }
2903 }
2904
2905 if (title)
2906 ctk_window_set_title (CTK_WINDOW (priv->tearoff_window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->tearoff_window)), ((ctk_window_get_type ())))))
)
, title);
2907 }
2908}
2909
2910static CtkWidget*
2911ctk_menu_get_toplevel (CtkWidget *menu)
2912{
2913 CtkWidget *attach, *toplevel;
2914
2915 attach = ctk_menu_get_attach_widget (CTK_MENU (menu)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_get_type ()))))))
);
2916
2917 if (CTK_IS_MENU_ITEM (attach)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(attach)); GType __t = ((ctk_menu_item_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
)
2918 attach = ctk_widget_get_parent (attach);
2919
2920 if (CTK_IS_MENU (attach)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(attach)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
)
2921 return ctk_menu_get_toplevel (attach);
2922 else if (CTK_IS_WIDGET (attach)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(attach)); GType __t = ((ctk_widget_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
)
2923 {
2924 toplevel = ctk_widget_get_toplevel (attach);
2925 if (ctk_widget_is_toplevel (toplevel))
2926 return toplevel;
2927 }
2928
2929 return NULL((void*)0);
2930}
2931
2932static void
2933tearoff_window_destroyed (CtkWidget *widget G_GNUC_UNUSED__attribute__ ((__unused__)),
2934 CtkMenu *menu)
2935{
2936 ctk_menu_set_tearoff_state (menu, FALSE(0));
2937}
2938
2939/**
2940 * ctk_menu_set_tearoff_state:
2941 * @menu: a #CtkMenu
2942 * @torn_off: If %TRUE, menu is displayed as a tearoff menu.
2943 *
2944 * Changes the tearoff state of the menu. A menu is normally
2945 * displayed as drop down menu which persists as long as the menu is
2946 * active. It can also be displayed as a tearoff menu which persists
2947 * until it is closed or reattached.
2948 */
2949void
2950ctk_menu_set_tearoff_state (CtkMenu *menu,
2951 gboolean torn_off)
2952{
2953 CtkMenuPrivate *priv;
2954 gint height;
2955
2956 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
2957
2958 priv = menu->priv;
2959
2960 torn_off = !!torn_off;
2961 if (priv->torn_off != torn_off)
2962 {
2963 priv->torn_off = torn_off;
2964 priv->tearoff_active = torn_off;
2965
2966 if (priv->torn_off)
2967 {
2968 if (ctk_widget_get_visible (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
))
2969 ctk_menu_popdown (menu);
2970
2971 if (!priv->tearoff_window)
2972 {
2973 CtkWidget *toplevel;
2974
2975 priv->tearoff_window = g_object_new (CTK_TYPE_WINDOW(ctk_window_get_type ()),
2976 "type", CTK_WINDOW_TOPLEVEL,
2977 "screen", ctk_widget_get_screen (priv->toplevel),
2978 "app-paintable", TRUE(!(0)),
2979 NULL((void*)0));
2980
2981 ctk_window_set_type_hint (CTK_WINDOW (priv->tearoff_window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->tearoff_window)), ((ctk_window_get_type ())))))
)
,
2982 CDK_WINDOW_TYPE_HINT_MENU);
2983 ctk_window_set_mnemonic_modifier (CTK_WINDOW (priv->tearoff_window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->tearoff_window)), ((ctk_window_get_type ())))))
)
, 0);
2984 g_signal_connect (priv->tearoff_window, "destroy",g_signal_connect_data ((priv->tearoff_window), ("destroy")
, (((GCallback) (tearoff_window_destroyed))), (menu), ((void*
)0), (GConnectFlags) 0)
2985 G_CALLBACK (tearoff_window_destroyed), menu)g_signal_connect_data ((priv->tearoff_window), ("destroy")
, (((GCallback) (tearoff_window_destroyed))), (menu), ((void*
)0), (GConnectFlags) 0)
;
2986 g_signal_connect (priv->tearoff_window, "event",g_signal_connect_data ((priv->tearoff_window), ("event"), (
((GCallback) (ctk_menu_window_event))), (menu), ((void*)0), (
GConnectFlags) 0)
2987 G_CALLBACK (ctk_menu_window_event), menu)g_signal_connect_data ((priv->tearoff_window), ("event"), (
((GCallback) (ctk_menu_window_event))), (menu), ((void*)0), (
GConnectFlags) 0)
;
2988
2989 ctk_menu_update_title (menu);
2990
2991 ctk_widget_realize (priv->tearoff_window);
2992
2993 toplevel = ctk_menu_get_toplevel (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
2994 if (toplevel != NULL((void*)0))
2995 ctk_window_set_transient_for (CTK_WINDOW (priv->tearoff_window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->tearoff_window)), ((ctk_window_get_type ())))))
)
,
2996 CTK_WINDOW (toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((toplevel)), ((ctk_window_get_type ()))))))
);
2997
2998 priv->tearoff_hbox = ctk_box_new (CTK_ORIENTATION_HORIZONTAL, 0);
2999 ctk_container_add (CTK_CONTAINER (priv->tearoff_window)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->tearoff_window)), ((ctk_container_get_type ()))
))))
,
3000 priv->tearoff_hbox);
3001
3002 height = cdk_window_get_height (ctk_widget_get_window (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
));
3003 priv->tearoff_adjustment = ctk_adjustment_new (0,
3004 0, priv->requested_height,
3005 MENU_SCROLL_STEP215,
3006 height/2,
3007 height);
3008 g_object_connect (priv->tearoff_adjustment,
3009 "signal::value-changed", ctk_menu_scrollbar_changed, menu,
3010 NULL((void*)0));
3011 priv->tearoff_scrollbar = ctk_scrollbar_new (CTK_ORIENTATION_VERTICAL, priv->tearoff_adjustment);
3012
3013 ctk_box_pack_end (CTK_BOX (priv->tearoff_hbox)((((CtkBox*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->tearoff_hbox)), ((ctk_box_get_type ()))))))
,
3014 priv->tearoff_scrollbar,
3015 FALSE(0), FALSE(0), 0);
3016
3017 if (ctk_adjustment_get_upper (priv->tearoff_adjustment) > height)
3018 ctk_widget_show (priv->tearoff_scrollbar);
3019
3020 ctk_widget_show (priv->tearoff_hbox);
3021 }
3022
3023 ctk_menu_reparent (menu, priv->tearoff_hbox, FALSE(0));
3024
3025 /* Update menu->requisition */
3026 ctk_widget_get_preferred_size (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
, NULL((void*)0), NULL((void*)0));
3027
3028 ctk_menu_set_tearoff_hints (menu, cdk_window_get_width (ctk_widget_get_window (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
)));
3029
3030 ctk_widget_realize (priv->tearoff_window);
3031 ctk_menu_position (menu, TRUE(!(0)));
3032
3033 ctk_widget_show (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
3034 ctk_widget_show (priv->tearoff_window);
3035
3036 ctk_menu_scroll_to (menu, 0, CTK_MENU_SCROLL_FLAG_NONE);
3037
3038 }
3039 else
3040 {
3041 ctk_widget_hide (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
3042 ctk_widget_hide (priv->tearoff_window);
3043 if (CTK_IS_CONTAINER (priv->toplevel)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(priv->toplevel)); GType __t = ((ctk_container_get_type ()
)); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; }))))
)
3044 ctk_menu_reparent (menu, priv->toplevel, FALSE(0));
3045 ctk_widget_destroy (priv->tearoff_window);
3046
3047 priv->tearoff_window = NULL((void*)0);
3048 priv->tearoff_hbox = NULL((void*)0);
3049 priv->tearoff_scrollbar = NULL((void*)0);
3050 priv->tearoff_adjustment = NULL((void*)0);
3051 }
3052
3053 g_object_notify (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, "tearoff-state");
3054 }
3055}
3056
3057/**
3058 * ctk_menu_get_tearoff_state:
3059 * @menu: a #CtkMenu
3060 *
3061 * Returns whether the menu is torn off.
3062 * See ctk_menu_set_tearoff_state().
3063 *
3064 * Returns: %TRUE if the menu is currently torn off.
3065 */
3066gboolean
3067ctk_menu_get_tearoff_state (CtkMenu *menu)
3068{
3069 g_return_val_if_fail (CTK_IS_MENU (menu), FALSE)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return ((0)); } } while (0)
;
3070
3071 return menu->priv->torn_off;
3072}
3073
3074/**
3075 * ctk_menu_set_title:
3076 * @menu: a #CtkMenu
3077 * @title: (nullable): a string containing the title for the menu, or %NULL to
3078 * inherit the title of the parent menu item, if any
3079 *
3080 * Sets the title string for the menu.
3081 *
3082 * The title is displayed when the menu is shown as a tearoff
3083 * menu. If @title is %NULL, the menu will see if it is attached
3084 * to a parent menu item, and if so it will try to use the same
3085 * text as that menu item’s label.
3086 */
3087void
3088ctk_menu_set_title (CtkMenu *menu,
3089 const gchar *title)
3090{
3091 CtkMenuPrivate *priv;
3092 char *old_title;
3093
3094 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
3095
3096 priv = menu->priv;
3097
3098 old_title = priv->title;
3099 priv->title = g_strdup (title)g_strdup_inline (title);
3100 g_free (old_title);
3101
3102 ctk_menu_update_title (menu);
3103 g_object_notify (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, "tearoff-title");
3104}
3105
3106/**
3107 * ctk_menu_get_title:
3108 * @menu: a #CtkMenu
3109 *
3110 * Returns the title of the menu. See ctk_menu_set_title().
3111 *
3112 * Returns: the title of the menu, or %NULL if the menu
3113 * has no title set on it. This string is owned by CTK+
3114 * and should not be modified or freed.
3115 **/
3116const gchar *
3117ctk_menu_get_title (CtkMenu *menu)
3118{
3119 g_return_val_if_fail (CTK_IS_MENU (menu), NULL)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return (((void*)0)); } } while (0)
;
3120
3121 return menu->priv->title;
3122}
3123
3124/**
3125 * ctk_menu_reorder_child:
3126 * @menu: a #CtkMenu
3127 * @child: the #CtkMenuItem to move
3128 * @position: the new position to place @child.
3129 * Positions are numbered from 0 to n - 1
3130 *
3131 * Moves @child to a new @position in the list of @menu
3132 * children.
3133 */
3134void
3135ctk_menu_reorder_child (CtkMenu *menu,
3136 CtkWidget *child,
3137 gint position)
3138{
3139 CtkMenuShell *menu_shell;
3140
3141 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
3142 g_return_if_fail (CTK_IS_MENU_ITEM (child))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((child)); GType __t = ((ctk_menu_item_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU_ITEM (child)"); return; } } while (0)
;
3143
3144 menu_shell = CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
;
3145
3146 if (g_list_find (menu_shell->priv->children, child))
3147 {
3148 menu_shell->priv->children = g_list_remove (menu_shell->priv->children, child);
3149 menu_shell->priv->children = g_list_insert (menu_shell->priv->children, child, position);
3150
3151 menu_queue_resize (menu);
3152 }
3153}
3154
3155static void
3156get_menu_padding (CtkWidget *widget,
3157 CtkBorder *padding)
3158{
3159 CtkStyleContext *context;
3160
3161 context = ctk_widget_get_style_context (widget);
3162
3163 ctk_style_context_get_padding (context,
3164 ctk_style_context_get_state (context),
3165 padding);
3166}
3167
3168static void
3169get_menu_margin (CtkWidget *widget,
3170 CtkBorder *margin)
3171{
3172 CtkStyleContext *context;
3173
3174 context = ctk_widget_get_style_context (widget);
3175
3176 ctk_style_context_get_margin (context,
3177 ctk_style_context_get_state (context),
3178 margin);
3179}
3180
3181static void
3182ctk_menu_realize (CtkWidget *widget)
3183{
3184 CtkMenu *menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
3185 CtkMenuPrivate *priv = menu->priv;
3186 CtkAllocation allocation;
3187 CdkWindow *window;
3188 CdkWindowAttr attributes;
3189 gint attributes_mask;
3190 gint border_width;
3191 CtkWidget *child;
3192 GList *children;
3193 CtkBorder arrow_border, padding;
3194
3195 g_return_if_fail (CTK_IS_MENU (widget))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((widget)); GType __t = ((ctk_menu_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (widget)"); return; } } while (0)
;
3196
3197 ctk_widget_set_realized (widget, TRUE(!(0)));
3198
3199 ctk_widget_get_allocation (widget, &allocation);
3200
3201 attributes.window_type = CDK_WINDOW_CHILD;
3202 attributes.x = allocation.x;
3203 attributes.y = allocation.y;
3204 attributes.width = allocation.width;
3205 attributes.height = allocation.height;
3206 attributes.wclass = CDK_INPUT_OUTPUT;
3207 attributes.visual = ctk_widget_get_visual (widget);
3208 attributes.event_mask = ctk_widget_get_events (widget);
3209 attributes.event_mask |= (CDK_KEY_PRESS_MASK |
3210 CDK_ENTER_NOTIFY_MASK | CDK_LEAVE_NOTIFY_MASK );
3211
3212 attributes_mask = CDK_WA_X | CDK_WA_Y | CDK_WA_VISUAL;
3213
3214 window = cdk_window_new (ctk_widget_get_parent_window (widget),
3215 &attributes, attributes_mask);
3216 ctk_widget_set_window (widget, window);
3217 ctk_widget_register_window (widget, window);
3218
3219 get_menu_padding (widget, &padding);
3220 border_width = ctk_container_get_border_width (CTK_CONTAINER (widget)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_container_get_type ()))))))
);
3221
3222 ctk_widget_get_allocation (widget, &allocation);
3223
3224 attributes.x = border_width + padding.left;
3225 attributes.y = border_width + padding.top;
3226 attributes.width = allocation.width -
3227 (2 * border_width) - padding.left - padding.right;
3228 attributes.height = allocation.height -
3229 (2 * border_width) - padding.top - padding.bottom;
3230
3231 get_arrows_border (menu, &arrow_border);
3232 attributes.y += arrow_border.top;
3233 attributes.height -= arrow_border.top;
3234 attributes.height -= arrow_border.bottom;
3235
3236 attributes.width = MAX (1, attributes.width)(((1) > (attributes.width)) ? (1) : (attributes.width));
3237 attributes.height = MAX (1, attributes.height)(((1) > (attributes.height)) ? (1) : (attributes.height));
3238
3239 priv->view_window = cdk_window_new (window,
3240 &attributes, attributes_mask);
3241 ctk_widget_register_window (widget, priv->view_window);
3242
3243 ctk_widget_get_allocation (widget, &allocation);
3244
3245 attributes.x = 0;
3246 attributes.y = - priv->scroll_offset;
3247 attributes.width = allocation.width + (2 * border_width) +
3248 padding.left + padding.right;
3249 attributes.height = priv->requested_height - (2 * border_width) +
3250 padding.top + padding.bottom;
3251
3252 attributes.width = MAX (1, attributes.width)(((1) > (attributes.width)) ? (1) : (attributes.width));
3253 attributes.height = MAX (1, attributes.height)(((1) > (attributes.height)) ? (1) : (attributes.height));
3254
3255 priv->bin_window = cdk_window_new (priv->view_window,
3256 &attributes, attributes_mask);
3257 ctk_widget_register_window (widget, priv->bin_window);
3258
3259 children = CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
->priv->children;
3260 while (children)
3261 {
3262 child = children->data;
3263 children = children->next;
3264
3265 ctk_widget_set_parent_window (child, priv->bin_window);
3266 }
3267
3268 if (CTK_MENU_SHELL (widget)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_shell_get_type ()))))))
->priv->active_menu_item)
3269 ctk_menu_scroll_item_visible (CTK_MENU_SHELL (widget)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_shell_get_type ()))))))
,
3270 CTK_MENU_SHELL (widget)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_shell_get_type ()))))))
->priv->active_menu_item);
3271
3272 cdk_window_show (priv->bin_window);
3273 cdk_window_show (priv->view_window);
3274}
3275
3276static gboolean
3277ctk_menu_focus (CtkWidget *widget G_GNUC_UNUSED__attribute__ ((__unused__)),
3278 CtkDirectionType direction G_GNUC_UNUSED__attribute__ ((__unused__)))
3279{
3280 /* A menu or its menu items cannot have focus */
3281 return FALSE(0);
3282}
3283
3284/* See notes in ctk_menu_popup() for information
3285 * about the “grab transfer window”
3286 */
3287static CdkWindow *
3288menu_grab_transfer_window_get (CtkMenu *menu)
3289{
3290 CdkWindow *window = g_object_get_data (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, "ctk-menu-transfer-window");
3291 if (!window)
3292 {
3293 CdkWindowAttr attributes;
3294 gint attributes_mask;
3295 CdkWindow *parent;
3296
3297 attributes.x = -100;
3298 attributes.y = -100;
3299 attributes.width = 10;
3300 attributes.height = 10;
3301 attributes.window_type = CDK_WINDOW_TEMP;
3302 attributes.wclass = CDK_INPUT_ONLY;
3303 attributes.override_redirect = TRUE(!(0));
3304 attributes.event_mask = 0;
3305
3306 attributes_mask = CDK_WA_X | CDK_WA_Y | CDK_WA_NOREDIR;
3307
3308 parent = cdk_screen_get_root_window (ctk_widget_get_screen (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
));
3309 window = cdk_window_new (parent,
3310 &attributes, attributes_mask);
3311 ctk_widget_register_window (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
, window);
3312
3313 cdk_window_show (window);
3314
3315 g_object_set_data (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, I_("ctk-menu-transfer-window")g_intern_static_string ("ctk-menu-transfer-window"), window);
3316 }
3317
3318 return window;
3319}
3320
3321static void
3322menu_grab_transfer_window_destroy (CtkMenu *menu)
3323{
3324 CtkMenuPrivate *priv = menu->priv;
3325 CdkWindow *window = g_object_get_data (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, "ctk-menu-transfer-window");
3326 if (window)
3327 {
3328 CdkWindow *toplevel_window;
3329
3330 ctk_widget_unregister_window (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
, window);
3331 cdk_window_destroy (window);
3332 g_object_set_data (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, I_("ctk-menu-transfer-window")g_intern_static_string ("ctk-menu-transfer-window"), NULL((void*)0));
3333
3334 toplevel_window = ctk_widget_get_window (priv->toplevel);
3335
3336 if (toplevel_window != NULL((void*)0))
3337 g_object_set_data (G_OBJECT (toplevel_window)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((toplevel_window)), (((GType) ((20) << (2))))))))
, I_("cdk-attached-grab-window")g_intern_static_string ("cdk-attached-grab-window"), NULL((void*)0));
3338 }
3339}
3340
3341static void
3342ctk_menu_unrealize (CtkWidget *widget)
3343{
3344 CtkMenu *menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
3345 CtkMenuPrivate *priv = menu->priv;
3346
3347 menu_grab_transfer_window_destroy (menu);
3348
3349 ctk_widget_unregister_window (widget, priv->view_window);
3350 cdk_window_destroy (priv->view_window);
3351 priv->view_window = NULL((void*)0);
3352
3353 ctk_widget_unregister_window (widget, priv->bin_window);
3354 cdk_window_destroy (priv->bin_window);
3355 priv->bin_window = NULL((void*)0);
3356
3357 CTK_WIDGET_CLASS (ctk_menu_parent_class)((((CtkWidgetClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), ((ctk_widget_get_type ()))))))
->unrealize (widget);
3358}
3359
3360static gint
3361calculate_line_heights (CtkMenu *menu,
3362 gint for_width,
3363 guint **ret_min_heights,
3364 guint **ret_nat_heights)
3365{
3366 CtkBorder padding;
3367 CtkMenuPrivate *priv;
3368 CtkMenuShell *menu_shell;
3369 CtkWidget *child, *widget;
3370 GList *children;
3371 guint border_width;
3372 guint n_columns;
3373 gint n_heights;
3374 guint *min_heights;
3375 guint *nat_heights;
3376 gint avail_width;
3377
3378 priv = menu->priv;
3379 widget = CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
;
3380 menu_shell = CTK_MENU_SHELL (widget)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_shell_get_type ()))))))
;
3381
3382 min_heights = g_new0 (guint, ctk_menu_get_n_rows (menu))((guint *) g_malloc0_n ((ctk_menu_get_n_rows (menu)), sizeof (
guint)))
;
3383 nat_heights = g_new0 (guint, ctk_menu_get_n_rows (menu))((guint *) g_malloc0_n ((ctk_menu_get_n_rows (menu)), sizeof (
guint)))
;
3384 n_heights = ctk_menu_get_n_rows (menu);
3385 n_columns = ctk_menu_get_n_columns (menu);
3386 avail_width = for_width - (2 * priv->toggle_size + priv->accel_size) * n_columns;
3387
3388 get_menu_padding (widget, &padding);
3389
3390 border_width = ctk_container_get_border_width (CTK_CONTAINER (menu)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_container_get_type ()))))))
);
3391 avail_width -= (border_width) * 2 + padding.left + padding.right;
3392
3393 for (children = menu_shell->priv->children; children; children = children->next)
3394 {
3395 gint part;
3396 gint toggle_size;
3397 gint l, r, t, b;
3398 gint child_min, child_nat;
3399
3400 child = children->data;
3401
3402 if (!ctk_widget_get_visible (child))
3403 continue;
3404
3405 get_effective_child_attach (child, &l, &r, &t, &b);
3406
3407 part = avail_width / (r - l);
3408
3409 ctk_widget_get_preferred_height_for_width (child, part,
3410 &child_min, &child_nat);
3411
3412 ctk_menu_item_toggle_size_request (CTK_MENU_ITEM (child)((((CtkMenuItem*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((child)), ((ctk_menu_item_get_type ()))))))
, &toggle_size);
3413
3414 part = MAX (child_min, toggle_size)(((child_min) > (toggle_size)) ? (child_min) : (toggle_size
))
/ (b - t);
3415 min_heights[t] = MAX (min_heights[t], part)(((min_heights[t]) > (part)) ? (min_heights[t]) : (part));
3416
3417 part = MAX (child_nat, toggle_size)(((child_nat) > (toggle_size)) ? (child_nat) : (toggle_size
))
/ (b - t);
3418 nat_heights[t] = MAX (nat_heights[t], part)(((nat_heights[t]) > (part)) ? (nat_heights[t]) : (part));
3419 }
3420
3421 if (ret_min_heights)
3422 *ret_min_heights = min_heights;
3423 else
3424 g_free (min_heights);
3425
3426 if (ret_nat_heights)
3427 *ret_nat_heights = nat_heights;
3428 else
3429 g_free (nat_heights);
3430
3431 return n_heights;
3432}
3433
3434static void
3435ctk_menu_size_allocate (CtkWidget *widget,
3436 CtkAllocation *allocation)
3437{
3438 CtkMenu *menu;
3439 CtkMenuPrivate *priv;
3440 CtkMenuShell *menu_shell;
3441 CtkWidget *child;
3442 CtkAllocation arrow_allocation, child_allocation;
3443 CtkAllocation clip;
3444 GList *children;
3445 gint x, y, i;
3446 gint width, height;
3447 guint border_width;
3448 CtkBorder arrow_border, padding;
3449
3450 g_return_if_fail (CTK_IS_MENU (widget))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((widget)); GType __t = ((ctk_menu_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (widget)"); return; } } while (0)
;
3451 g_return_if_fail (allocation != NULL)do { if ((allocation != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "allocation != NULL"); return
; } } while (0)
;
3452
3453 menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
3454 menu_shell = CTK_MENU_SHELL (widget)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_shell_get_type ()))))))
;
3455 priv = menu->priv;
3456
3457 ctk_widget_set_allocation (widget, allocation);
3458
3459 get_menu_padding (widget, &padding);
3460 border_width = ctk_container_get_border_width (CTK_CONTAINER (menu)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_container_get_type ()))))))
);
3461
3462 g_free (priv->heights);
3463 priv->heights_length = calculate_line_heights (menu,
3464 allocation->width,
3465 &priv->heights,
3466 NULL((void*)0));
3467
3468 /* refresh our cached height request */
3469 priv->requested_height = (2 * border_width) + padding.top + padding.bottom;
3470 for (i = 0; i < priv->heights_length; i++)
3471 priv->requested_height += priv->heights[i];
3472
3473 x = border_width + padding.left;
3474 y = border_width + padding.top;
3475 width = allocation->width - (2 * border_width) - padding.left - padding.right;
3476 height = allocation->height - (2 * border_width) - padding.top - padding.bottom;
3477
3478 if (menu_shell->priv->active)
3479 ctk_menu_scroll_to (menu, priv->scroll_offset, CTK_MENU_SCROLL_FLAG_NONE);
3480
3481 get_arrows_border (menu, &arrow_border);
3482
3483 arrow_allocation.x = x;
3484 arrow_allocation.y = y;
3485 arrow_allocation.width = width;
3486 arrow_allocation.height = arrow_border.top;
3487
3488 if (priv->upper_arrow_visible)
3489 ctk_css_gadget_allocate (priv->top_arrow_gadget,
3490 &arrow_allocation, -1,
3491 &clip);
3492
3493 arrow_allocation.y = height - y - arrow_border.bottom;
3494 arrow_allocation.height = arrow_border.bottom;
3495
3496 if (priv->lower_arrow_visible)
3497 ctk_css_gadget_allocate (priv->bottom_arrow_gadget,
3498 &arrow_allocation, -1,
3499 &clip);
3500
3501 if (!priv->tearoff_active)
3502 {
3503 y += arrow_border.top;
3504 height -= arrow_border.top;
3505 height -= arrow_border.bottom;
3506 }
3507
3508 width = MAX (1, width)(((1) > (width)) ? (1) : (width));
3509 height = MAX (1, height)(((1) > (height)) ? (1) : (height));
3510
3511 if (ctk_widget_get_realized (widget))
3512 {
3513 cdk_window_move_resize (ctk_widget_get_window (widget),
3514 allocation->x, allocation->y,
3515 allocation->width, allocation->height);
3516
3517 cdk_window_move_resize (priv->view_window, x, y, width, height);
3518 }
3519
3520 if (menu_shell->priv->children)
3521 {
3522 gint base_width = width / ctk_menu_get_n_columns (menu);
3523
3524 children = menu_shell->priv->children;
3525 while (children)
3526 {
3527 child = children->data;
3528 children = children->next;
3529
3530 if (ctk_widget_get_visible (child))
3531 {
3532 gint l, r, t, b;
3533
3534 get_effective_child_attach (child, &l, &r, &t, &b);
3535
3536 if (ctk_widget_get_direction (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
) == CTK_TEXT_DIR_RTL)
3537 {
3538 guint tmp;
3539 tmp = ctk_menu_get_n_columns (menu) - l;
3540 l = ctk_menu_get_n_columns (menu) - r;
3541 r = tmp;
3542 }
3543
3544 child_allocation.width = (r - l) * base_width;
3545 child_allocation.height = 0;
3546 child_allocation.x = l * base_width;
3547 child_allocation.y = 0;
3548
3549 for (i = 0; i < b; i++)
3550 {
3551 if (i < t)
3552 child_allocation.y += priv->heights[i];
3553 else
3554 child_allocation.height += priv->heights[i];
3555 }
3556
3557 ctk_menu_item_toggle_size_allocate (CTK_MENU_ITEM (child)((((CtkMenuItem*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((child)), ((ctk_menu_item_get_type ()))))))
,
3558 priv->toggle_size);
3559
3560 ctk_widget_size_allocate (child, &child_allocation);
3561 ctk_widget_queue_draw (child);
3562 }
3563 }
3564
3565 /* Resize the item window */
3566 if (ctk_widget_get_realized (widget))
3567 {
3568 gint w, h;
3569
3570 h = 0;
3571 for (i = 0; i < ctk_menu_get_n_rows (menu); i++)
3572 h += priv->heights[i];
3573
3574 w = ctk_menu_get_n_columns (menu) * base_width;
3575 cdk_window_resize (priv->bin_window, w, h);
3576 }
3577
3578 if (priv->tearoff_active)
3579 {
3580 if (height >= priv->requested_height)
3581 {
3582 if (ctk_widget_get_visible (priv->tearoff_scrollbar))
3583 {
3584 ctk_widget_hide (priv->tearoff_scrollbar);
3585 ctk_menu_set_tearoff_hints (menu, allocation->width);
3586
3587 ctk_menu_scroll_to (menu, 0, CTK_MENU_SCROLL_FLAG_NONE);
3588 }
3589 }
3590 else
3591 {
3592 ctk_adjustment_configure (priv->tearoff_adjustment,
3593 ctk_adjustment_get_value (priv->tearoff_adjustment),
3594 0,
3595 priv->requested_height,
3596 ctk_adjustment_get_step_increment (priv->tearoff_adjustment),
3597 ctk_adjustment_get_page_increment (priv->tearoff_adjustment),
3598 allocation->height);
3599
3600 if (!ctk_widget_get_visible (priv->tearoff_scrollbar))
3601 {
3602 ctk_widget_show (priv->tearoff_scrollbar);
3603 ctk_menu_set_tearoff_hints (menu, allocation->width);
3604 }
3605 }
3606 }
3607 }
3608}
3609
3610static gboolean
3611ctk_menu_draw (CtkWidget *widget,
3612 cairo_t *cr)
3613{
3614 CtkMenu *menu;
3615 CtkMenuPrivate *priv;
3616 CtkStyleContext *context;
3617 gint width, height;
3618
3619 menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
3620 priv = menu->priv;
3621 context = ctk_widget_get_style_context (widget);
3622
3623 width = ctk_widget_get_allocated_width (widget);
3624 height = ctk_widget_get_allocated_height (widget);
3625
3626 if (ctk_cairo_should_draw_window (cr, ctk_widget_get_window (widget)))
3627 {
3628 ctk_render_background (context, cr, 0, 0,
3629 width, height);
3630 ctk_render_frame (context, cr, 0, 0,
3631 width, height);
3632
3633 if (priv->upper_arrow_visible && !priv->tearoff_active)
3634 ctk_css_gadget_draw (priv->top_arrow_gadget, cr);
3635
3636 if (priv->lower_arrow_visible && !priv->tearoff_active)
3637 ctk_css_gadget_draw (priv->bottom_arrow_gadget, cr);
3638 }
3639
3640 if (ctk_cairo_should_draw_window (cr, priv->bin_window))
3641 {
3642 int x, y;
3643
3644 cdk_window_get_position (priv->view_window, &x, &y);
3645 cairo_rectangle (cr,
3646 x, y,
3647 cdk_window_get_width (priv->view_window),
3648 cdk_window_get_height (priv->view_window));
3649 cairo_clip (cr);
3650
3651 CTK_WIDGET_CLASS (ctk_menu_parent_class)((((CtkWidgetClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), ((ctk_widget_get_type ()))))))
->draw (widget, cr);
3652 }
3653
3654 return FALSE(0);
3655}
3656
3657static void
3658ctk_menu_show (CtkWidget *widget)
3659{
3660 CtkMenu *menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
3661
3662 _ctk_menu_refresh_accel_paths (menu, FALSE(0));
3663
3664 CTK_WIDGET_CLASS (ctk_menu_parent_class)((((CtkWidgetClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), ((ctk_widget_get_type ()))))))
->show (widget);
3665}
3666
3667
3668static void
3669ctk_menu_get_preferred_width (CtkWidget *widget,
3670 gint *minimum_size,
3671 gint *natural_size)
3672{
3673 CtkMenu *menu;
3674 CtkMenuShell *menu_shell;
3675 CtkMenuPrivate *priv;
3676 CtkWidget *child;
3677 GList *children;
3678 guint max_toggle_size;
3679 guint max_accel_width;
3680 guint border_width;
3681 gint child_min, child_nat;
3682 gint min_width, nat_width;
3683 CtkBorder padding;
3684
3685 menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
3686 menu_shell = CTK_MENU_SHELL (widget)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_shell_get_type ()))))))
;
3687 priv = menu->priv;
3688
3689 min_width = nat_width = 0;
3690
3691 max_toggle_size = 0;
3692 max_accel_width = 0;
3693
3694 children = menu_shell->priv->children;
3695 while (children)
3696 {
3697 gint part;
3698 gint toggle_size;
3699 gint l, r, t, b;
3700
3701 child = children->data;
3702 children = children->next;
3703
3704 if (! ctk_widget_get_visible (child))
3705 continue;
3706
3707 get_effective_child_attach (child, &l, &r, &t, &b);
3708
3709 /* It's important to size_request the child
3710 * before doing the toggle size request, in
3711 * case the toggle size request depends on the size
3712 * request of a child of the child (e.g. for ImageMenuItem)
3713 */
3714 ctk_widget_get_preferred_width (child, &child_min, &child_nat);
3715
3716 ctk_menu_item_toggle_size_request (CTK_MENU_ITEM (child)((((CtkMenuItem*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((child)), ((ctk_menu_item_get_type ()))))))
, &toggle_size);
3717 max_toggle_size = MAX (max_toggle_size, toggle_size)(((max_toggle_size) > (toggle_size)) ? (max_toggle_size) :
(toggle_size))
;
3718 max_accel_width = MAX (max_accel_width,(((max_accel_width) > (((((CtkMenuItem*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((child)), ((ctk_menu_item_get_type ()))))
))->priv->accelerator_width)) ? (max_accel_width) : (((
((CtkMenuItem*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((child)), ((ctk_menu_item_get_type ()))))))->priv->
accelerator_width))
3719 CTK_MENU_ITEM (child)->priv->accelerator_width)(((max_accel_width) > (((((CtkMenuItem*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((child)), ((ctk_menu_item_get_type ()))))
))->priv->accelerator_width)) ? (max_accel_width) : (((
((CtkMenuItem*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((child)), ((ctk_menu_item_get_type ()))))))->priv->
accelerator_width))
;
3720
3721 part = child_min / (r - l);
3722 min_width = MAX (min_width, part)(((min_width) > (part)) ? (min_width) : (part));
3723
3724 part = child_nat / (r - l);
3725 nat_width = MAX (nat_width, part)(((nat_width) > (part)) ? (nat_width) : (part));
3726 }
3727
3728 /* If the menu doesn't include any images or check items
3729 * reserve the space so that all menus are consistent.
3730 * We only do this for 'ordinary' menus, not for combobox
3731 * menus or multi-column menus
3732 */
3733 if (max_toggle_size == 0 &&
3734 ctk_menu_get_n_columns (menu) == 1 &&
3735 !priv->no_toggle_size)
3736 {
3737 CtkWidget *menu_item;
3738 CtkCssGadget *indicator_gadget;
3739 gint indicator_width;
3740
3741 /* Create a CtkCheckMenuItem, to query indicator size */
3742 menu_item = ctk_check_menu_item_new ();
3743 indicator_gadget = _ctk_check_menu_item_get_indicator_gadget
3744 (CTK_CHECK_MENU_ITEM (menu_item)((((CtkCheckMenuItem*) (void *) g_type_check_instance_cast ((
GTypeInstance*) ((menu_item)), ((ctk_check_menu_item_get_type
()))))))
);
3745
3746 ctk_css_gadget_get_preferred_size (indicator_gadget,
3747 CTK_ORIENTATION_HORIZONTAL,
3748 -1,
3749 &indicator_width, NULL((void*)0),
3750 NULL((void*)0), NULL((void*)0));
3751 max_toggle_size = indicator_width;
3752
3753 ctk_widget_destroy (menu_item);
3754 g_object_ref_sink (menu_item)((__typeof__ (menu_item)) (g_object_ref_sink) (menu_item));
3755 g_object_unref (menu_item);
3756 }
3757
3758 min_width += 2 * max_toggle_size + max_accel_width;
3759 min_width *= ctk_menu_get_n_columns (menu);
3760
3761 nat_width += 2 * max_toggle_size + max_accel_width;
3762 nat_width *= ctk_menu_get_n_columns (menu);
3763
3764 get_menu_padding (widget, &padding);
3765 border_width = ctk_container_get_border_width (CTK_CONTAINER (menu)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_container_get_type ()))))))
);
3766 min_width += (2 * border_width) + padding.left + padding.right;
3767 nat_width += (2 * border_width) + padding.left + padding.right;
3768
3769 priv->toggle_size = max_toggle_size;
3770 priv->accel_size = max_accel_width;
3771
3772 *minimum_size = min_width;
3773 *natural_size = nat_width;
3774
3775 /* Don't resize the tearoff if it is not active,
3776 * because it won't redraw (it is only a background pixmap).
3777 */
3778 if (priv->tearoff_active)
3779 ctk_menu_set_tearoff_hints (menu, min_width);
3780}
3781
3782static void
3783ctk_menu_get_preferred_height (CtkWidget *widget,
3784 gint *minimum_size,
3785 gint *natural_size)
3786{
3787 gint min_width, nat_width;
3788
3789 /* Menus are height-for-width only, just return the height
3790 * for the minimum width
3791 */
3792 CTK_WIDGET_GET_CLASS (widget)((((CtkWidgetClass*) (((GTypeInstance*) ((widget)))->g_class
))))
->get_preferred_width (widget, &min_width, &nat_width);
3793 CTK_WIDGET_GET_CLASS (widget)((((CtkWidgetClass*) (((GTypeInstance*) ((widget)))->g_class
))))
->get_preferred_height_for_width (widget, min_width, minimum_size, natural_size);
3794}
3795
3796static void
3797ctk_menu_get_preferred_height_for_width (CtkWidget *widget,
3798 gint for_size,
3799 gint *minimum_size,
3800 gint *natural_size)
3801{
3802 CtkBorder padding, arrow_border;
3803 CtkMenu *menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
3804 CtkMenuPrivate *priv = menu->priv;
3805 guint *min_heights, *nat_heights;
3806 guint border_width;
3807 gint n_heights, i;
3808 gint min_height, single_height, nat_height;
3809
3810 border_width = ctk_container_get_border_width (CTK_CONTAINER (menu)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_container_get_type ()))))))
);
3811 get_menu_padding (widget, &padding);
3812
3813 min_height = nat_height = (2 * border_width) + padding.top + padding.bottom;
3814 single_height = 0;
3815
3816 n_heights =
3817 calculate_line_heights (menu, for_size, &min_heights, &nat_heights);
3818
3819 for (i = 0; i < n_heights; i++)
3820 {
3821 min_height += min_heights[i];
3822 single_height = MAX (single_height, min_heights[i])(((single_height) > (min_heights[i])) ? (single_height) : (
min_heights[i]))
;
3823 nat_height += nat_heights[i];
3824 }
3825
3826 get_arrows_border (menu, &arrow_border);
3827 single_height += (2 * border_width)
3828 + padding.top + padding.bottom
3829 + arrow_border.top + arrow_border.bottom;
3830 min_height = MIN (min_height, single_height)(((min_height) < (single_height)) ? (min_height) : (single_height
))
;
3831
3832 if (priv->have_position)
3833 {
3834 CdkDisplay *display;
3835 CdkMonitor *monitor;
3836 CdkRectangle workarea;
3837 CtkBorder border;
3838
3839 display = ctk_widget_get_display (priv->toplevel);
3840 monitor = cdk_display_get_monitor (display, priv->monitor_num);
3841 cdk_monitor_get_workarea (monitor, &workarea);
3842
3843 if (priv->position_y + min_height > workarea.y + workarea.height)
3844 min_height = workarea.y + workarea.height - priv->position_y;
3845
3846 if (priv->position_y + nat_height > workarea.y + workarea.height)
3847 nat_height = workarea.y + workarea.height - priv->position_y;
3848
3849 _ctk_window_get_shadow_width (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
, &border);
3850
3851 if (priv->position_y + border.top < workarea.y)
3852 {
3853 min_height -= workarea.y - (priv->position_y + border.top);
3854 nat_height -= workarea.y - (priv->position_y + border.top);
3855 }
3856 }
3857
3858 *minimum_size = min_height;
3859 *natural_size = nat_height;
3860
3861 g_free (min_heights);
3862 g_free (nat_heights);
3863}
3864
3865static gboolean
3866pointer_in_menu_window (CtkWidget *widget,
3867 gdouble x_root,
3868 gdouble y_root)
3869{
3870 CtkMenu *menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
3871 CtkMenuPrivate *priv = menu->priv;
3872 CtkAllocation allocation;
3873
3874 if (ctk_widget_get_mapped (priv->toplevel))
3875 {
3876 CtkMenuShell *menu_shell;
3877 gint window_x, window_y;
3878
3879 cdk_window_get_position (ctk_widget_get_window (priv->toplevel),
3880 &window_x, &window_y);
3881
3882 ctk_widget_get_allocation (widget, &allocation);
3883 if (x_root >= window_x && x_root < window_x + allocation.width &&
3884 y_root >= window_y && y_root < window_y + allocation.height)
3885 return TRUE(!(0));
3886
3887 menu_shell = CTK_MENU_SHELL (widget)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_shell_get_type ()))))))
;
3888
3889 if (CTK_IS_MENU (menu_shell->priv->parent_menu_shell)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(menu_shell->priv->parent_menu_shell)); GType __t = ((ctk_menu_get_type
())); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; }))))
)
3890 return pointer_in_menu_window (menu_shell->priv->parent_menu_shell,
3891 x_root, y_root);
3892 }
3893
3894 return FALSE(0);
3895}
3896
3897static gboolean
3898ctk_menu_button_press (CtkWidget *widget,
3899 CdkEventButton *event)
3900{
3901 CdkDevice *source_device;
3902 CtkWidget *event_widget;
3903 CtkMenu *menu;
3904
3905 if (event->type != CDK_BUTTON_PRESS)
3906 return FALSE(0);
3907
3908 source_device = cdk_event_get_source_device ((CdkEvent *) event);
3909 event_widget = ctk_get_event_widget ((CdkEvent *) event);
3910 menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
3911
3912 /* Don't pass down to menu shell if a non-menuitem part of the menu
3913 * was clicked. The check for the event_widget being a CtkMenuShell
3914 * works because we have the pointer grabbed on menu_shell->window
3915 * with owner_events=TRUE, so all events that are either outside
3916 * the menu or on its border are delivered relative to
3917 * menu_shell->window.
3918 */
3919 if (CTK_IS_MENU_SHELL (event_widget)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(event_widget)); GType __t = ((ctk_menu_shell_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
&&
3920 pointer_in_menu_window (widget, event->x_root, event->y_root))
3921 return TRUE(!(0));
3922
3923 if (CTK_IS_MENU_ITEM (event_widget)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(event_widget)); GType __t = ((ctk_menu_item_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
&&
3924 cdk_device_get_source (source_device) == CDK_SOURCE_TOUCHSCREEN &&
3925 CTK_MENU_ITEM (event_widget)((((CtkMenuItem*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((event_widget)), ((ctk_menu_item_get_type ()))))))
->priv->submenu != NULL((void*)0) &&
3926 !ctk_widget_is_drawable (CTK_MENU_ITEM (event_widget)((((CtkMenuItem*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((event_widget)), ((ctk_menu_item_get_type ()))))))
->priv->submenu))
3927 menu->priv->ignore_button_release = TRUE(!(0));
3928
3929 return CTK_WIDGET_CLASS (ctk_menu_parent_class)((((CtkWidgetClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), ((ctk_widget_get_type ()))))))
->button_press_event (widget, event);
3930}
3931
3932static gboolean
3933ctk_menu_button_release (CtkWidget *widget,
3934 CdkEventButton *event)
3935{
3936 CtkMenuPrivate *priv = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
->priv;
3937
3938 if (priv->ignore_button_release)
3939 {
3940 priv->ignore_button_release = FALSE(0);
3941 return FALSE(0);
3942 }
3943
3944 if (event->type != CDK_BUTTON_RELEASE)
3945 return FALSE(0);
3946
3947 /* Don't pass down to menu shell if a non-menuitem part of the menu
3948 * was clicked (see comment in button_press()).
3949 */
3950 if (CTK_IS_MENU_SHELL (ctk_get_event_widget ((CdkEvent *) event))(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(ctk_get_event_widget ((CdkEvent *) event))); GType __t = ((ctk_menu_shell_get_type
())); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; }))))
&&
3951 pointer_in_menu_window (widget, event->x_root, event->y_root))
3952 {
3953 /* Ugly: make sure menu_shell->button gets reset to 0 when we
3954 * bail out early here so it is in a consistent state for the
3955 * next button_press/button_release in CtkMenuShell.
3956 * See bug #449371.
3957 */
3958 if (CTK_MENU_SHELL (widget)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_shell_get_type ()))))))
->priv->active)
3959 CTK_MENU_SHELL (widget)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_shell_get_type ()))))))
->priv->button = 0;
3960
3961 return TRUE(!(0));
3962 }
3963
3964 return CTK_WIDGET_CLASS (ctk_menu_parent_class)((((CtkWidgetClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), ((ctk_widget_get_type ()))))))
->button_release_event (widget, event);
3965}
3966
3967static gboolean
3968ctk_menu_key_press (CtkWidget *widget,
3969 CdkEventKey *event)
3970{
3971 CtkMenu *menu;
3972
3973 g_return_val_if_fail (CTK_IS_MENU (widget), FALSE)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((widget)); GType __t = ((ctk_menu_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (widget)"); return ((0)); } } while (0)
;
3974 g_return_val_if_fail (event != NULL, FALSE)do { if ((event != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "event != NULL"); return
((0)); } } while (0)
;
3975
3976 menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
3977
3978 ctk_menu_stop_navigating_submenu (menu);
3979
3980 return CTK_WIDGET_CLASS (ctk_menu_parent_class)((((CtkWidgetClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), ((ctk_widget_get_type ()))))))
->key_press_event (widget, event);
3981}
3982
3983static gboolean
3984check_threshold (CtkWidget *widget G_GNUC_UNUSED__attribute__ ((__unused__)),
3985 gint start_x,
3986 gint start_y,
3987 gint x,
3988 gint y)
3989{
3990#define THRESHOLD8 8
3991
3992 return
3993 ABS (start_x - x)(((start_x - x) < 0) ? -(start_x - x) : (start_x - x)) > THRESHOLD8 ||
3994 ABS (start_y - y)(((start_y - y) < 0) ? -(start_y - y) : (start_y - y)) > THRESHOLD8;
3995}
3996
3997static gboolean
3998definitely_within_item (CtkWidget *widget,
3999 gint x,
4000 gint y)
4001{
4002 CdkWindow *window = CTK_MENU_ITEM (widget)((((CtkMenuItem*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_item_get_type ()))))))
->priv->event_window;
4003 int w, h;
4004
4005 w = cdk_window_get_width (window);
4006 h = cdk_window_get_height (window);
4007
4008 return
4009 check_threshold (widget, 0, 0, x, y) &&
4010 check_threshold (widget, w - 1, 0, x, y) &&
4011 check_threshold (widget, w - 1, h - 1, x, y) &&
4012 check_threshold (widget, 0, h - 1, x, y);
4013}
4014
4015static gboolean
4016ctk_menu_has_navigation_triangle (CtkMenu *menu)
4017{
4018 CtkMenuPrivate *priv = menu->priv;
4019
4020 return priv->navigation_height && priv->navigation_width;
4021}
4022
4023static gboolean
4024ctk_menu_motion_notify (CtkWidget *widget,
4025 CdkEventMotion *event)
4026{
4027 CtkWidget *menu_item;
4028 CtkMenu *menu;
4029 CtkMenuShell *menu_shell;
4030 CtkWidget *parent;
4031 CdkDevice *source_device;
4032
4033 gboolean need_enter;
4034
4035 source_device = cdk_event_get_source_device ((CdkEvent *) event);
4036
4037 if (CTK_IS_MENU (widget)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(widget)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
&&
4038 cdk_device_get_source (source_device) != CDK_SOURCE_TOUCHSCREEN)
4039 {
4040 CtkMenuPrivate *priv = CTK_MENU(widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
->priv;
4041
4042 if (priv->ignore_button_release)
4043 priv->ignore_button_release = FALSE(0);
4044
4045 ctk_menu_handle_scrolling (CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
, event->x_root, event->y_root,
4046 TRUE(!(0)), TRUE(!(0)));
4047 }
4048
4049 /* We received the event for one of two reasons:
4050 *
4051 * a) We are the active menu, and did ctk_grab_add()
4052 * b) The widget is a child of ours, and the event was propagated
4053 *
4054 * Since for computation of navigation regions, we want the menu which
4055 * is the parent of the menu item, for a), we need to find that menu,
4056 * which may be different from 'widget'.
4057 */
4058 menu_item = ctk_get_event_widget ((CdkEvent*) event);
4059 parent = ctk_widget_get_parent (menu_item);
4060 if (!CTK_IS_MENU_ITEM (menu_item)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(menu_item)); GType __t = ((ctk_menu_item_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
||
4061 !CTK_IS_MENU (parent)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(parent)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
)
4062 return FALSE(0);
4063
4064 menu_shell = CTK_MENU_SHELL (parent)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((parent)), ((ctk_menu_shell_get_type ()))))))
;
4065 menu = CTK_MENU (menu_shell)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu_shell)), ((ctk_menu_get_type ()))))))
;
4066
4067 if (definitely_within_item (menu_item, event->x, event->y))
4068 menu_shell->priv->activate_time = 0;
4069
4070 need_enter = (ctk_menu_has_navigation_triangle (menu) || menu_shell->priv->ignore_enter);
4071
4072 /* Check to see if we are within an active submenu's navigation region
4073 */
4074 if (ctk_menu_navigating_submenu (menu, event->x_root, event->y_root))
4075 return TRUE(!(0));
4076
4077 /* Make sure we pop down if we enter a non-selectable menu item, so we
4078 * don't show a submenu when the cursor is outside the stay-up triangle.
4079 */
4080 if (!_ctk_menu_item_is_selectable (menu_item))
4081 {
4082 /* We really want to deselect, but this gives the menushell code
4083 * a chance to do some bookkeeping about the menuitem.
4084 */
4085 ctk_menu_shell_select_item (menu_shell, menu_item);
4086 return FALSE(0);
4087 }
4088
4089 if (need_enter)
4090 {
4091 /* The menu is now sensitive to enter events on its items, but
4092 * was previously sensitive. So we fake an enter event.
4093 */
4094 menu_shell->priv->ignore_enter = FALSE(0);
4095
4096 if (event->x >= 0 && event->x < cdk_window_get_width (event->window) &&
4097 event->y >= 0 && event->y < cdk_window_get_height (event->window))
4098 {
4099 CdkEvent *send_event = cdk_event_new (CDK_ENTER_NOTIFY);
4100 gboolean result;
4101
4102 send_event->crossing.window = g_object_ref (event->window)((__typeof__ (event->window)) (g_object_ref) (event->window
))
;
4103 send_event->crossing.time = event->time;
4104 send_event->crossing.send_event = TRUE(!(0));
4105 send_event->crossing.x_root = event->x_root;
4106 send_event->crossing.y_root = event->y_root;
4107 send_event->crossing.x = event->x;
4108 send_event->crossing.y = event->y;
4109 send_event->crossing.state = event->state;
4110 cdk_event_set_device (send_event, cdk_event_get_device ((CdkEvent *) event));
4111
4112 /* We send the event to 'widget', the currently active menu,
4113 * instead of 'menu', the menu that the pointer is in. This
4114 * will ensure that the event will be ignored unless the
4115 * menuitem is a child of the active menu or some parent
4116 * menu of the active menu.
4117 */
4118 result = ctk_widget_event (widget, send_event);
4119 cdk_event_free (send_event);
4120
4121 return result;
4122 }
4123 }
4124
4125 return FALSE(0);
4126}
4127
4128static void
4129ctk_menu_scroll_by (CtkMenu *menu,
4130 gint step)
4131{
4132 CtkMenuPrivate *priv = menu->priv;
4133 CtkBorder arrow_border;
4134 CtkWidget *widget;
4135 gint offset;
4136 gint view_height;
4137
4138 widget = CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
;
4139 offset = priv->scroll_offset + step;
4140
4141 get_arrows_border (menu, &arrow_border);
4142
4143 /* Don't scroll over the top if we weren't before: */
4144 if ((priv->scroll_offset >= 0) && (offset < 0))
4145 offset = 0;
4146
4147 view_height = cdk_window_get_height (ctk_widget_get_window (widget));
4148
4149 if (priv->scroll_offset == 0 &&
4150 view_height >= priv->requested_height)
4151 return;
4152
4153 /* Don't scroll past the bottom if we weren't before: */
4154 if (priv->scroll_offset > 0)
4155 view_height -= arrow_border.top;
4156
4157 /* Since arrows are shown, reduce view height even more */
4158 view_height -= arrow_border.bottom;
4159
4160 if ((priv->scroll_offset + view_height <= priv->requested_height) &&
4161 (offset + view_height > priv->requested_height))
4162 offset = priv->requested_height - view_height;
4163
4164 if (offset != priv->scroll_offset)
4165 ctk_menu_scroll_to (menu, offset, CTK_MENU_SCROLL_FLAG_NONE);
4166}
4167
4168static gboolean
4169ctk_menu_scroll_timeout (gpointer data)
4170{
4171 CtkMenu *menu;
4172
4173 menu = CTK_MENU (data)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((data)), ((ctk_menu_get_type ()))))))
;
4174 ctk_menu_scroll_by (menu, menu->priv->scroll_step);
4175
4176 return TRUE(!(0));
4177}
4178
4179static gboolean
4180ctk_menu_scroll (CtkWidget *widget,
4181 CdkEventScroll *event)
4182{
4183 CtkMenu *menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
4184
4185 if (cdk_event_get_pointer_emulated ((CdkEvent *) event))
4186 return CDK_EVENT_PROPAGATE((0));
4187
4188 switch (event->direction)
4189 {
4190 case CDK_SCROLL_DOWN:
4191 ctk_menu_scroll_by (menu, MENU_SCROLL_STEP215);
4192 break;
4193 case CDK_SCROLL_UP:
4194 ctk_menu_scroll_by (menu, - MENU_SCROLL_STEP215);
4195 break;
4196 case CDK_SCROLL_SMOOTH:
4197 ctk_menu_scroll_by (menu, event->delta_y * MENU_SCROLL_STEP215);
4198 break;
4199 default:
4200 return CDK_EVENT_PROPAGATE((0));
4201 break;
4202 }
4203
4204 return CDK_EVENT_STOP((!(0)));
4205}
4206
4207static void
4208get_arrows_sensitive_area (CtkMenu *menu,
4209 CdkRectangle *upper,
4210 CdkRectangle *lower)
4211{
4212 CtkMenuPrivate *priv = menu->priv;
4213 CtkWidget *widget = CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
;
4214 CdkWindow *window;
4215 gint width, height;
4216 guint border;
4217 gint win_x, win_y;
4218 CtkBorder padding;
4219 gint top_arrow_height, bottom_arrow_height;
4220
4221 ctk_css_gadget_get_preferred_size (priv->top_arrow_gadget,
4222 CTK_ORIENTATION_VERTICAL,
4223 -1,
4224 &top_arrow_height, NULL((void*)0),
4225 NULL((void*)0), NULL((void*)0));
4226 ctk_css_gadget_get_preferred_size (priv->bottom_arrow_gadget,
4227 CTK_ORIENTATION_VERTICAL,
4228 -1,
4229 &bottom_arrow_height, NULL((void*)0),
4230 NULL((void*)0), NULL((void*)0));
4231
4232 window = ctk_widget_get_window (widget);
4233 width = cdk_window_get_width (window);
4234 height = cdk_window_get_height (window);
4235
4236 border = ctk_container_get_border_width (CTK_CONTAINER (menu)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_container_get_type ()))))))
);
4237 get_menu_padding (widget, &padding);
4238
4239 cdk_window_get_position (window, &win_x, &win_y);
4240
4241 if (upper)
4242 {
4243 upper->x = win_x;
4244 upper->y = win_y;
4245 upper->width = width;
4246 upper->height = top_arrow_height + border + padding.top;
4247 }
4248
4249 if (lower)
4250 {
4251 lower->x = win_x;
4252 lower->y = win_y + height - border - padding.bottom - bottom_arrow_height;
4253 lower->width = width;
4254 lower->height = bottom_arrow_height + border + padding.bottom;
4255 }
4256}
4257
4258static void
4259ctk_menu_handle_scrolling (CtkMenu *menu,
4260 gint x,
4261 gint y,
4262 gboolean enter,
4263 gboolean motion G_GNUC_UNUSED__attribute__ ((__unused__)))
4264{
4265 CtkMenuPrivate *priv = menu->priv;
4266 CtkMenuShell *menu_shell;
4267 CdkRectangle rect;
4268 gboolean in_arrow;
4269 gboolean scroll_fast = FALSE(0);
4270 gint top_x, top_y;
4271
4272 menu_shell = CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
;
4273
4274 cdk_window_get_position (ctk_widget_get_window (priv->toplevel),
4275 &top_x, &top_y);
4276 x -= top_x;
4277 y -= top_y;
4278
4279 /* upper arrow handling */
4280
4281 get_arrows_sensitive_area (menu, &rect, NULL((void*)0));
4282
4283 in_arrow = FALSE(0);
4284 if (priv->upper_arrow_visible && !priv->tearoff_active &&
4285 (x >= rect.x) && (x < rect.x + rect.width) &&
4286 (y >= rect.y) && (y < rect.y + rect.height))
4287 {
4288 in_arrow = TRUE(!(0));
4289 }
4290
4291 if ((priv->upper_arrow_state & CTK_STATE_FLAG_INSENSITIVE) == 0)
4292 {
4293 gboolean arrow_pressed = FALSE(0);
4294
4295 if (priv->upper_arrow_visible && !priv->tearoff_active)
4296 {
4297 scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE8);
4298
4299 if (enter && in_arrow &&
4300 (!priv->upper_arrow_prelight ||
4301 priv->scroll_fast != scroll_fast))
4302 {
4303 priv->upper_arrow_prelight = TRUE(!(0));
4304 priv->scroll_fast = scroll_fast;
4305
4306 /* Deselect the active item so that
4307 * any submenus are popped down
4308 */
4309 ctk_menu_shell_deselect (menu_shell);
4310
4311 ctk_menu_remove_scroll_timeout (menu);
4312 priv->scroll_step = scroll_fast
4313 ? -MENU_SCROLL_STEP215
4314 : -MENU_SCROLL_STEP18;
4315
4316 priv->scroll_timeout =
4317 cdk_threads_add_timeout (scroll_fast
4318 ? MENU_SCROLL_TIMEOUT220
4319 : MENU_SCROLL_TIMEOUT150,
4320 ctk_menu_scroll_timeout, menu);
4321 g_source_set_name_by_id (priv->scroll_timeout, "[ctk+] ctk_menu_scroll_timeout");
4322 }
4323 else if (!enter && !in_arrow && priv->upper_arrow_prelight)
4324 {
4325 ctk_menu_stop_scrolling (menu);
4326 }
4327 }
4328
4329 /* check if the button isn't insensitive before
4330 * changing it to something else.
4331 */
4332 if ((priv->upper_arrow_state & CTK_STATE_FLAG_INSENSITIVE) == 0)
4333 {
4334 CtkStateFlags arrow_state = 0;
4335
4336 if (arrow_pressed)
4337 arrow_state |= CTK_STATE_FLAG_ACTIVE;
4338
4339 if (priv->upper_arrow_prelight)
4340 arrow_state |= CTK_STATE_FLAG_PRELIGHT;
4341
4342 if (arrow_state != priv->upper_arrow_state)
4343 {
4344 priv->upper_arrow_state = arrow_state;
4345 ctk_css_gadget_set_state (priv->top_arrow_gadget, arrow_state);
4346
4347 cdk_window_invalidate_rect (ctk_widget_get_window (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
),
4348 &rect, FALSE(0));
4349 }
4350 }
4351 }
4352
4353 /* lower arrow handling */
4354
4355 get_arrows_sensitive_area (menu, NULL((void*)0), &rect);
4356
4357 in_arrow = FALSE(0);
4358 if (priv->lower_arrow_visible && !priv->tearoff_active &&
4359 (x >= rect.x) && (x < rect.x + rect.width) &&
4360 (y >= rect.y) && (y < rect.y + rect.height))
4361 {
4362 in_arrow = TRUE(!(0));
4363 }
4364
4365 if ((priv->lower_arrow_state & CTK_STATE_FLAG_INSENSITIVE) == 0)
4366 {
4367 gboolean arrow_pressed = FALSE(0);
4368
4369 if (priv->lower_arrow_visible && !priv->tearoff_active)
4370 {
4371 scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE8);
4372
4373 if (enter && in_arrow &&
4374 (!priv->lower_arrow_prelight ||
4375 priv->scroll_fast != scroll_fast))
4376 {
4377 priv->lower_arrow_prelight = TRUE(!(0));
4378 priv->scroll_fast = scroll_fast;
4379
4380 /* Deselect the active item so that
4381 * any submenus are popped down
4382 */
4383 ctk_menu_shell_deselect (menu_shell);
4384
4385 ctk_menu_remove_scroll_timeout (menu);
4386 priv->scroll_step = scroll_fast
4387 ? MENU_SCROLL_STEP215
4388 : MENU_SCROLL_STEP18;
4389
4390 priv->scroll_timeout =
4391 cdk_threads_add_timeout (scroll_fast
4392 ? MENU_SCROLL_TIMEOUT220
4393 : MENU_SCROLL_TIMEOUT150,
4394 ctk_menu_scroll_timeout, menu);
4395 g_source_set_name_by_id (priv->scroll_timeout, "[ctk+] ctk_menu_scroll_timeout");
4396 }
4397 else if (!enter && !in_arrow && priv->lower_arrow_prelight)
4398 {
4399 ctk_menu_stop_scrolling (menu);
4400 }
4401 }
4402
4403 /* check if the button isn't insensitive before
4404 * changing it to something else.
4405 */
4406 if ((priv->lower_arrow_state & CTK_STATE_FLAG_INSENSITIVE) == 0)
4407 {
4408 CtkStateFlags arrow_state = 0;
4409
4410 if (arrow_pressed)
4411 arrow_state |= CTK_STATE_FLAG_ACTIVE;
4412
4413 if (priv->lower_arrow_prelight)
4414 arrow_state |= CTK_STATE_FLAG_PRELIGHT;
4415
4416 if (arrow_state != priv->lower_arrow_state)
4417 {
4418 priv->lower_arrow_state = arrow_state;
4419 ctk_css_gadget_set_state (priv->bottom_arrow_gadget, arrow_state);
4420
4421 cdk_window_invalidate_rect (ctk_widget_get_window (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
),
4422 &rect, FALSE(0));
4423 }
4424 }
4425 }
4426}
4427
4428static gboolean
4429ctk_menu_enter_notify (CtkWidget *widget,
4430 CdkEventCrossing *event)
4431{
4432 CtkWidget *menu_item;
4433 CtkWidget *parent;
4434 CdkDevice *source_device;
4435
4436 if (event->mode == CDK_CROSSING_CTK_GRAB ||
4437 event->mode == CDK_CROSSING_CTK_UNGRAB ||
4438 event->mode == CDK_CROSSING_STATE_CHANGED)
4439 return TRUE(!(0));
4440
4441 source_device = cdk_event_get_source_device ((CdkEvent *) event);
4442 menu_item = ctk_get_event_widget ((CdkEvent*) event);
4443
4444 if (CTK_IS_MENU (widget)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(widget)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
&&
4445 cdk_device_get_source (source_device) != CDK_SOURCE_TOUCHSCREEN)
4446 {
4447 CtkMenuShell *menu_shell = CTK_MENU_SHELL (widget)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_shell_get_type ()))))))
;
4448
4449 if (!menu_shell->priv->ignore_enter)
4450 ctk_menu_handle_scrolling (CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
,
4451 event->x_root, event->y_root, TRUE(!(0)), TRUE(!(0)));
4452 }
4453
4454 if (cdk_device_get_source (source_device) != CDK_SOURCE_TOUCHSCREEN &&
4455 CTK_IS_MENU_ITEM (menu_item)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(menu_item)); GType __t = ((ctk_menu_item_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
)
4456 {
4457 CtkWidget *menu = ctk_widget_get_parent (menu_item);
4458
4459 if (CTK_IS_MENU (menu)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r; if
(!__inst) __r = (0); else if (__inst->g_class && __inst
->g_class->g_type == __t) __r = (!(0)); else __r = g_type_check_instance_is_a
(__inst, __t); __r; }))))
)
4460 {
4461 CtkMenuPrivate *priv = (CTK_MENU (menu)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_get_type ()))))))
)->priv;
4462 CtkMenuShell *menu_shell = CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
;
4463
4464 if (priv->seen_item_enter)
4465 {
4466 /* This is the second enter we see for an item
4467 * on this menu. This means a release should always
4468 * mean activate.
4469 */
4470 menu_shell->priv->activate_time = 0;
4471 }
4472 else if ((event->detail != CDK_NOTIFY_NONLINEAR &&
4473 event->detail != CDK_NOTIFY_NONLINEAR_VIRTUAL))
4474 {
4475 if (definitely_within_item (menu_item, event->x, event->y))
4476 {
4477 /* This is an actual user-enter (ie. not a pop-under)
4478 * In this case, the user must either have entered
4479 * sufficiently far enough into the item, or he must move
4480 * far enough away from the enter point. (see
4481 * ctk_menu_motion_notify())
4482 */
4483 menu_shell->priv->activate_time = 0;
4484 }
4485 }
4486
4487 priv->seen_item_enter = TRUE(!(0));
4488 }
4489 }
4490
4491 /* If this is a faked enter (see ctk_menu_motion_notify), 'widget'
4492 * will not correspond to the event widget's parent. Check to see
4493 * if we are in the parent's navigation region.
4494 */
4495 parent = ctk_widget_get_parent (menu_item);
4496 if (CTK_IS_MENU_ITEM (menu_item)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(menu_item)); GType __t = ((ctk_menu_item_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
&& CTK_IS_MENU (parent)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(parent)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
&&
4497 ctk_menu_navigating_submenu (CTK_MENU (parent)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((parent)), ((ctk_menu_get_type ()))))))
,
4498 event->x_root, event->y_root))
4499 return TRUE(!(0));
4500
4501 return CTK_WIDGET_CLASS (ctk_menu_parent_class)((((CtkWidgetClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), ((ctk_widget_get_type ()))))))
->enter_notify_event (widget, event);
4502}
4503
4504static gboolean
4505ctk_menu_leave_notify (CtkWidget *widget,
4506 CdkEventCrossing *event)
4507{
4508 CtkMenuShell *menu_shell;
4509 CtkMenu *menu;
4510 CtkMenuItem *menu_item;
4511 CtkWidget *event_widget;
4512 CdkDevice *source_device;
4513
4514 if (event->mode == CDK_CROSSING_CTK_GRAB ||
4515 event->mode == CDK_CROSSING_CTK_UNGRAB ||
4516 event->mode == CDK_CROSSING_STATE_CHANGED)
4517 return TRUE(!(0));
4518
4519 menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
4520 menu_shell = CTK_MENU_SHELL (widget)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_shell_get_type ()))))))
;
4521
4522 if (ctk_menu_navigating_submenu (menu, event->x_root, event->y_root))
4523 return TRUE(!(0));
4524
4525 source_device = cdk_event_get_source_device ((CdkEvent *) event);
4526
4527 if (cdk_device_get_source (source_device) != CDK_SOURCE_TOUCHSCREEN)
4528 ctk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE(0), TRUE(!(0)));
4529
4530 event_widget = ctk_get_event_widget ((CdkEvent*) event);
4531
4532 if (!CTK_IS_MENU_ITEM (event_widget)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(event_widget)); GType __t = ((ctk_menu_item_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
)
4533 return TRUE(!(0));
4534
4535 menu_item = CTK_MENU_ITEM (event_widget)((((CtkMenuItem*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((event_widget)), ((ctk_menu_item_get_type ()))))))
;
4536
4537 /* Here we check to see if we're leaving an active menu item
4538 * with a submenu, in which case we enter submenu navigation mode.
4539 */
4540 if (menu_shell->priv->active_menu_item != NULL((void*)0)
4541 && menu_item->priv->submenu != NULL((void*)0)
4542 && menu_item->priv->submenu_placement == CTK_LEFT_RIGHT)
4543 {
4544 if (CTK_MENU_SHELL (menu_item->priv->submenu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu_item->priv->submenu)), ((ctk_menu_shell_get_type
()))))))
->priv->active)
4545 {
4546 ctk_menu_set_submenu_navigation_region (menu, menu_item, event);
4547 return TRUE(!(0));
4548 }
4549 else if (menu_item == CTK_MENU_ITEM (menu_shell->priv->active_menu_item)((((CtkMenuItem*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu_shell->priv->active_menu_item)), ((ctk_menu_item_get_type
()))))))
)
4550 {
4551 /* We are leaving an active menu item with nonactive submenu.
4552 * Deselect it so we don't surprise the user with by popping
4553 * up a submenu _after_ he left the item.
4554 */
4555 ctk_menu_shell_deselect (menu_shell);
4556 return TRUE(!(0));
4557 }
4558 }
4559
4560 return CTK_WIDGET_CLASS (ctk_menu_parent_class)((((CtkWidgetClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), ((ctk_widget_get_type ()))))))
->leave_notify_event (widget, event);
4561}
4562
4563static gboolean
4564pointer_on_menu_widget (CtkMenu *menu,
4565 gdouble x_root,
4566 gdouble y_root)
4567{
4568 CtkMenuPrivate *priv = menu->priv;
4569 CtkAllocation allocation;
4570 gint window_x, window_y;
4571
4572 ctk_widget_get_allocation (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
, &allocation);
4573 cdk_window_get_position (ctk_widget_get_window (priv->toplevel),
4574 &window_x, &window_y);
4575
4576 if (x_root >= window_x && x_root < window_x + allocation.width &&
4577 y_root >= window_y && y_root < window_y + allocation.height)
4578 return TRUE(!(0));
4579
4580 return FALSE(0);
4581}
4582
4583static gboolean
4584ctk_menu_captured_event (CtkWidget *widget,
4585 CdkEvent *event)
4586{
4587 CdkDevice *source_device;
4588 gboolean retval = FALSE(0);
4589 CtkMenuPrivate *priv;
4590 CtkMenu *menu;
4591 gdouble x_root, y_root;
4592 guint button;
4593 CdkModifierType state;
4594
4595 menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
4596 priv = menu->priv;
4597
4598 if (!priv->upper_arrow_visible && !priv->lower_arrow_visible && priv->drag_start_y < 0)
4599 return retval;
4600
4601 source_device = cdk_event_get_source_device (event);
4602 cdk_event_get_root_coords (event, &x_root, &y_root);
4603
4604 switch (event->type)
4605 {
4606 case CDK_TOUCH_BEGIN:
4607 case CDK_BUTTON_PRESS:
4608 if ((!cdk_event_get_button (event, &button) || button == 1) &&
4609 cdk_device_get_source (source_device) == CDK_SOURCE_TOUCHSCREEN &&
4610 pointer_on_menu_widget (menu, x_root, y_root))
4611 {
4612 priv->drag_start_y = event->button.y_root;
4613 priv->initial_drag_offset = priv->scroll_offset;
4614 priv->drag_scroll_started = FALSE(0);
4615 }
4616 else
4617 priv->drag_start_y = -1;
4618
4619 priv->drag_already_pressed = TRUE(!(0));
4620 break;
4621 case CDK_TOUCH_END:
4622 case CDK_BUTTON_RELEASE:
4623 if (priv->drag_scroll_started)
4624 {
4625 priv->drag_scroll_started = FALSE(0);
4626 priv->drag_start_y = -1;
4627 priv->drag_already_pressed = FALSE(0);
4628 retval = TRUE(!(0));
4629 }
4630 break;
4631 case CDK_TOUCH_UPDATE:
4632 case CDK_MOTION_NOTIFY:
4633 if ((!cdk_event_get_state (event, &state) || (state & CDK_BUTTON1_MASK) != 0) &&
4634 cdk_device_get_source (source_device) == CDK_SOURCE_TOUCHSCREEN)
4635 {
4636 if (!priv->drag_already_pressed)
4637 {
4638 if (pointer_on_menu_widget (menu, x_root, y_root))
4639 {
4640 priv->drag_start_y = y_root;
4641 priv->initial_drag_offset = priv->scroll_offset;
4642 priv->drag_scroll_started = FALSE(0);
4643 }
4644 else
4645 priv->drag_start_y = -1;
4646
4647 priv->drag_already_pressed = TRUE(!(0));
4648 }
4649
4650 if (priv->drag_start_y < 0 && !priv->drag_scroll_started)
4651 break;
4652
4653 if (priv->drag_scroll_started)
4654 {
4655 gint offset, view_height;
4656 CtkBorder arrow_border;
4657 gdouble y_diff;
4658
4659 y_diff = y_root - priv->drag_start_y;
4660 offset = priv->initial_drag_offset - y_diff;
4661
4662 view_height = cdk_window_get_height (ctk_widget_get_window (widget));
4663 get_arrows_border (menu, &arrow_border);
4664
4665 if (priv->upper_arrow_visible)
4666 view_height -= arrow_border.top;
4667
4668 if (priv->lower_arrow_visible)
4669 view_height -= arrow_border.bottom;
4670
4671 offset = CLAMP (offset,(((offset) > ((((priv->scroll_offset) > (priv->requested_height
- view_height)) ? (priv->scroll_offset) : (priv->requested_height
- view_height)))) ? ((((priv->scroll_offset) > (priv->
requested_height - view_height)) ? (priv->scroll_offset) :
(priv->requested_height - view_height))) : (((offset) <
((((priv->scroll_offset) < (0)) ? (priv->scroll_offset
) : (0)))) ? ((((priv->scroll_offset) < (0)) ? (priv->
scroll_offset) : (0))) : (offset)))
4672 MIN (priv->scroll_offset, 0),(((offset) > ((((priv->scroll_offset) > (priv->requested_height
- view_height)) ? (priv->scroll_offset) : (priv->requested_height
- view_height)))) ? ((((priv->scroll_offset) > (priv->
requested_height - view_height)) ? (priv->scroll_offset) :
(priv->requested_height - view_height))) : (((offset) <
((((priv->scroll_offset) < (0)) ? (priv->scroll_offset
) : (0)))) ? ((((priv->scroll_offset) < (0)) ? (priv->
scroll_offset) : (0))) : (offset)))
4673 MAX (priv->scroll_offset, priv->requested_height - view_height))(((offset) > ((((priv->scroll_offset) > (priv->requested_height
- view_height)) ? (priv->scroll_offset) : (priv->requested_height
- view_height)))) ? ((((priv->scroll_offset) > (priv->
requested_height - view_height)) ? (priv->scroll_offset) :
(priv->requested_height - view_height))) : (((offset) <
((((priv->scroll_offset) < (0)) ? (priv->scroll_offset
) : (0)))) ? ((((priv->scroll_offset) < (0)) ? (priv->
scroll_offset) : (0))) : (offset)))
;
4674
4675 ctk_menu_scroll_to (menu, offset, CTK_MENU_SCROLL_FLAG_NONE);
4676
4677 retval = TRUE(!(0));
4678 }
4679 else if (ctk_drag_check_threshold (widget,
4680 0, priv->drag_start_y,
4681 0, y_root))
4682 {
4683 priv->drag_scroll_started = TRUE(!(0));
4684 ctk_menu_shell_deselect (CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
);
4685 retval = TRUE(!(0));
4686 }
4687 }
4688 break;
4689 case CDK_ENTER_NOTIFY:
4690 case CDK_LEAVE_NOTIFY:
4691 if (priv->drag_scroll_started)
4692 retval = TRUE(!(0));
4693 break;
4694 default:
4695 break;
4696 }
4697
4698 return retval;
4699}
4700
4701static void
4702ctk_menu_stop_navigating_submenu (CtkMenu *menu)
4703{
4704 CtkMenuPrivate *priv = menu->priv;
4705
4706 priv->navigation_x = 0;
4707 priv->navigation_y = 0;
4708 priv->navigation_width = 0;
4709 priv->navigation_height = 0;
4710
4711 if (priv->navigation_timeout)
4712 {
4713 g_source_remove (priv->navigation_timeout);
4714 priv->navigation_timeout = 0;
4715 }
4716}
4717
4718/* When the timeout is elapsed, the navigation region is destroyed
4719 * and the menuitem under the pointer (if any) is selected.
4720 */
4721static gboolean
4722ctk_menu_stop_navigating_submenu_cb (gpointer user_data)
4723{
4724 CtkMenuPopdownData *popdown_data = user_data;
4725 CtkMenu *menu = popdown_data->menu;
4726 CtkMenuPrivate *priv = menu->priv;
4727 CdkWindow *child_window;
4728
4729 ctk_menu_stop_navigating_submenu (menu);
4730
4731 if (ctk_widget_get_realized (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
))
4732 {
4733 child_window = cdk_window_get_device_position (priv->bin_window,
4734 popdown_data->device,
4735 NULL((void*)0), NULL((void*)0), NULL((void*)0));
4736
4737 if (child_window)
4738 {
4739 CdkEvent *send_event = cdk_event_new (CDK_ENTER_NOTIFY);
4740
4741 send_event->crossing.window = g_object_ref (child_window)((__typeof__ (child_window)) (g_object_ref) (child_window));
4742 send_event->crossing.time = CDK_CURRENT_TIME0L; /* Bogus */
4743 send_event->crossing.send_event = TRUE(!(0));
4744 cdk_event_set_device (send_event, popdown_data->device);
4745
4746 CTK_WIDGET_CLASS (ctk_menu_parent_class)((((CtkWidgetClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), ((ctk_widget_get_type ()))))))
->enter_notify_event (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
, (CdkEventCrossing *)send_event);
4747
4748 cdk_event_free (send_event);
4749 }
4750 }
4751
4752 return FALSE(0);
4753}
4754
4755static gboolean
4756ctk_menu_navigating_submenu (CtkMenu *menu,
4757 gint event_x,
4758 gint event_y)
4759{
4760 CtkMenuPrivate *priv = menu->priv;
4761 gint width, height;
4762
4763 if (!ctk_menu_has_navigation_triangle (menu))
4764 return FALSE(0);
4765
4766 width = priv->navigation_width;
4767 height = priv->navigation_height;
4768
4769 /* Check if x/y are in the triangle spanned by the navigation parameters */
4770
4771 /* 1) Move the coordinates so the triangle starts at 0,0 */
4772 event_x -= priv->navigation_x;
4773 event_y -= priv->navigation_y;
4774
4775 /* 2) Ensure both legs move along the positive axis */
4776 if (width < 0)
4777 {
4778 event_x = -event_x;
4779 width = -width;
4780 }
4781 if (height < 0)
4782 {
4783 event_y = -event_y;
4784 height = -height;
4785 }
4786
4787 /* 3) Check that the given coordinate is inside the triangle. The formula
4788 * is a transformed form of this formula: x/w + y/h <= 1
4789 */
4790 if (event_x >= 0 && event_y >= 0 &&
4791 event_x * height + event_y * width <= width * height)
4792 {
4793 return TRUE(!(0));
4794 }
4795 else
4796 {
4797 ctk_menu_stop_navigating_submenu (menu);
4798 return FALSE(0);
4799 }
4800}
4801
4802static void
4803ctk_menu_set_submenu_navigation_region (CtkMenu *menu,
4804 CtkMenuItem *menu_item,
4805 CdkEventCrossing *event)
4806{
4807 CtkMenuPrivate *priv = menu->priv;
4808 CtkWidget *event_widget;
4809 CdkWindow *window;
4810 int submenu_left;
4811 int submenu_right;
4812 int submenu_top;
4813 int submenu_bottom;
4814 int width;
4815
4816 g_return_if_fail (menu_item->priv->submenu != NULL)do { if ((menu_item->priv->submenu != ((void*)0))) { } else
{ g_return_if_fail_warning ("Ctk", ((const char*) (__func__)
), "menu_item->priv->submenu != NULL"); return; } } while
(0)
;
4817 g_return_if_fail (event != NULL)do { if ((event != ((void*)0))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "event != NULL"); return
; } } while (0)
;
4818
4819 event_widget = ctk_get_event_widget ((CdkEvent*) event);
4820
4821 window = ctk_widget_get_window (menu_item->priv->submenu);
4822 cdk_window_get_origin (window, &submenu_left, &submenu_top);
4823
4824 submenu_right = submenu_left + cdk_window_get_width (window);
4825 submenu_bottom = submenu_top + cdk_window_get_height (window);
4826
4827 width = cdk_window_get_width (ctk_widget_get_window (event_widget));
4828
4829 if (event->x >= 0 && event->x < width)
4830 {
4831 CtkMenuPopdownData *popdown_data;
4832 /* The calculations below assume floored coordinates */
4833 int x_root = floor (event->x_root);
4834 int y_root = floor (event->y_root);
4835
4836 ctk_menu_stop_navigating_submenu (menu);
4837
4838 /* The navigation region is the triangle closest to the x/y
4839 * location of the rectangle. This is why the width or height
4840 * can be negative.
4841 */
4842 if (menu_item->priv->submenu_direction == CTK_DIRECTION_RIGHT)
4843 {
4844 /* right */
4845 priv->navigation_x = submenu_left;
4846 priv->navigation_width = x_root - submenu_left;
4847 }
4848 else
4849 {
4850 /* left */
4851 priv->navigation_x = submenu_right;
4852 priv->navigation_width = x_root - submenu_right;
4853 }
4854
4855 if (event->y < 0)
4856 {
4857 /* top */
4858 priv->navigation_y = y_root;
4859 priv->navigation_height = submenu_top - y_root - NAVIGATION_REGION_OVERSHOOT50;
4860
4861 if (priv->navigation_height >= 0)
4862 return;
4863 }
4864 else
4865 {
4866 /* bottom */
4867 priv->navigation_y = y_root;
4868 priv->navigation_height = submenu_bottom - y_root + NAVIGATION_REGION_OVERSHOOT50;
4869
4870 if (priv->navigation_height <= 0)
4871 return;
4872 }
4873
4874 popdown_data = g_new (CtkMenuPopdownData, 1)((CtkMenuPopdownData *) g_malloc_n ((1), sizeof (CtkMenuPopdownData
)))
;
4875 popdown_data->menu = menu;
4876 popdown_data->device = cdk_event_get_device ((CdkEvent *) event);
4877
4878 priv->navigation_timeout = cdk_threads_add_timeout_full (G_PRIORITY_DEFAULT0,
4879 MENU_POPDOWN_DELAY1000,
4880 ctk_menu_stop_navigating_submenu_cb,
4881 popdown_data,
4882 (GDestroyNotify) g_free);
4883 g_source_set_name_by_id (priv->navigation_timeout, "[ctk+] ctk_menu_stop_navigating_submenu_cb");
4884 }
4885}
4886
4887static void
4888ctk_menu_deactivate (CtkMenuShell *menu_shell)
4889{
4890 CtkWidget *parent;
4891
4892 g_return_if_fail (CTK_IS_MENU (menu_shell))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu_shell)); GType __t = ((ctk_menu_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu_shell)"); return; } } while (0)
;
4893
4894 parent = menu_shell->priv->parent_menu_shell;
4895
4896 menu_shell->priv->activate_time = 0;
4897 ctk_menu_popdown (CTK_MENU (menu_shell)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu_shell)), ((ctk_menu_get_type ()))))))
);
4898
4899 if (parent)
4900 ctk_menu_shell_deactivate (CTK_MENU_SHELL (parent)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((parent)), ((ctk_menu_shell_get_type ()))))))
);
4901}
4902
4903static void
4904ctk_menu_position_legacy (CtkMenu *menu,
4905 gboolean set_scroll_offset)
4906{
4907 CtkMenuPrivate *priv = menu->priv;
4908 CtkWidget *widget;
4909 CtkRequisition requisition;
4910 gint x, y;
4911 gint scroll_offset;
4912 CdkDisplay *display;
4913 CdkMonitor *monitor;
4914 CdkRectangle workarea;
4915 gint monitor_num;
4916 CdkDevice *pointer;
4917 CtkBorder border;
4918 gint i;
4919
4920 widget = CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
;
4921
4922 display = ctk_widget_get_display (widget);
4923 pointer = _ctk_menu_shell_get_grab_device (CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
);
4924 cdk_device_get_position (pointer, NULL((void*)0), &x, &y);
4925
4926 /* Realize so we have the proper width and height to figure out
4927 * the right place to popup the menu.
4928 */
4929 ctk_widget_realize (priv->toplevel);
4930
4931 _ctk_window_get_shadow_width (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
, &border);
4932
4933 requisition.width = ctk_widget_get_allocated_width (widget);
4934 requisition.height = ctk_widget_get_allocated_height (widget);
4935
4936 monitor = cdk_display_get_monitor_at_point (display, x, y);
4937 monitor_num = 0;
4938 for (i = 0; ; i++)
4939 {
4940 CdkMonitor *m = cdk_display_get_monitor (display, i);
4941
4942 if (m == monitor)
4943 {
4944 monitor_num = i;
4945 break;
4946 }
4947 if (m == NULL((void*)0))
4948 break;
4949 }
4950
4951 priv->monitor_num = monitor_num;
4952 priv->initially_pushed_in = FALSE(0);
4953
4954 /* Set the type hint here to allow custom position functions
4955 * to set a different hint
4956 */
4957 if (!ctk_widget_get_visible (priv->toplevel))
4958 ctk_window_set_type_hint (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
, CDK_WINDOW_TYPE_HINT_POPUP_MENU);
4959
4960 if (priv->position_func)
4961 {
4962 (* priv->position_func) (menu, &x, &y, &priv->initially_pushed_in,
4963 priv->position_func_data);
4964
4965 if (priv->monitor_num < 0)
4966 priv->monitor_num = monitor_num;
4967
4968 monitor = cdk_display_get_monitor (display, priv->monitor_num);
4969 cdk_monitor_get_workarea (monitor, &workarea);
4970 }
4971 else
4972 {
4973 gint space_left, space_right, space_above, space_below;
4974 gint needed_width;
4975 gint needed_height;
4976 CtkBorder padding;
4977 CtkBorder margin;
4978 gboolean rtl = (ctk_widget_get_direction (widget) == CTK_TEXT_DIR_RTL);
4979
4980 get_menu_padding (widget, &padding);
4981 get_menu_margin (widget, &margin);
4982
4983 /* The placement of popup menus horizontally works like this (with
4984 * RTL in parentheses)
4985 *
4986 * - If there is enough room to the right (left) of the mouse cursor,
4987 * position the menu there.
4988 *
4989 * - Otherwise, if if there is enough room to the left (right) of the
4990 * mouse cursor, position the menu there.
4991 *
4992 * - Otherwise if the menu is smaller than the monitor, position it
4993 * on the side of the mouse cursor that has the most space available
4994 *
4995 * - Otherwise (if there is simply not enough room for the menu on the
4996 * monitor), position it as far left (right) as possible.
4997 *
4998 * Positioning in the vertical direction is similar: first try below
4999 * mouse cursor, then above.
5000 */
5001 monitor = cdk_display_get_monitor (display, priv->monitor_num);
5002 cdk_monitor_get_workarea (monitor, &workarea);
5003
5004 space_left = x - workarea.x;
5005 space_right = workarea.x + workarea.width - x - 1;
5006 space_above = y - workarea.y;
5007 space_below = workarea.y + workarea.height - y - 1;
5008
5009 /* Position horizontally. */
5010
5011 /* the amount of space we need to position the menu.
5012 * Note the menu is offset "thickness" pixels
5013 */
5014 needed_width = requisition.width - padding.left;
5015
5016 if (needed_width <= space_left ||
5017 needed_width <= space_right)
5018 {
5019 if ((rtl && needed_width <= space_left) ||
5020 (!rtl && needed_width > space_right))
5021 {
5022 /* position left */
5023 x = x - margin.left + padding.left - requisition.width + 1;
5024 }
5025 else
5026 {
5027 /* position right */
5028 x = x + margin.right - padding.right;
5029 }
5030
5031 /* x is clamped on-screen further down */
5032 }
5033 else if (requisition.width <= workarea.width)
5034 {
5035 /* the menu is too big to fit on either side of the mouse
5036 * cursor, but smaller than the monitor. Position it on
5037 * the side that has the most space
5038 */
5039 if (space_left > space_right)
5040 {
5041 /* left justify */
5042 x = workarea.x;
5043 }
5044 else
5045 {
5046 /* right justify */
5047 x = workarea.x + workarea.width - requisition.width;
5048 }
5049 }
5050 else /* menu is simply too big for the monitor */
5051 {
5052 if (rtl)
5053 {
5054 /* right justify */
5055 x = workarea.x + workarea.width - requisition.width;
5056 }
5057 else
5058 {
5059 /* left justify */
5060 x = workarea.x;
5061 }
5062 }
5063
5064 /* Position vertically.
5065 * The algorithm is the same as above, but simpler
5066 * because we don't have to take RTL into account.
5067 */
5068 needed_height = requisition.height - padding.top;
5069
5070 if (needed_height <= space_above ||
5071 needed_height <= space_below)
5072 {
5073 if (needed_height <= space_below)
5074 y = y + margin.top - padding.top;
5075 else
5076 y = y - margin.bottom + padding.bottom - requisition.height + 1;
5077
5078 y = CLAMP (y, workarea.y,(((y) > (workarea.y + workarea.height - requisition.height
)) ? (workarea.y + workarea.height - requisition.height) : ((
(y) < (workarea.y)) ? (workarea.y) : (y)))
5079 workarea.y + workarea.height - requisition.height)(((y) > (workarea.y + workarea.height - requisition.height
)) ? (workarea.y + workarea.height - requisition.height) : ((
(y) < (workarea.y)) ? (workarea.y) : (y)))
;
5080 }
5081 else if (needed_height > space_below && needed_height > space_above)
5082 {
5083 if (space_below >= space_above)
5084 y = workarea.y + workarea.height - requisition.height;
5085 else
5086 y = workarea.y;
5087 }
5088 else
5089 {
5090 y = workarea.y;
5091 }
5092 }
5093
5094 scroll_offset = 0;
5095
5096 if (y + requisition.height > workarea.y + workarea.height)
5097 {
5098 if (priv->initially_pushed_in)
5099 scroll_offset += (workarea.y + workarea.height) - requisition.height - y;
5100 y = (workarea.y + workarea.height) - requisition.height;
5101 }
5102
5103 if (y < workarea.y)
5104 {
5105 if (priv->initially_pushed_in)
5106 scroll_offset += workarea.y - y;
5107 y = workarea.y;
5108 }
5109
5110 x = CLAMP (x, workarea.x, MAX (workarea.x, workarea.x + workarea.width - requisition.width))(((x) > ((((workarea.x) > (workarea.x + workarea.width -
requisition.width)) ? (workarea.x) : (workarea.x + workarea.
width - requisition.width)))) ? ((((workarea.x) > (workarea
.x + workarea.width - requisition.width)) ? (workarea.x) : (workarea
.x + workarea.width - requisition.width))) : (((x) < (workarea
.x)) ? (workarea.x) : (x)))
;
5111
5112 x -= border.left;
5113 y -= border.top;
5114
5115 if (CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
->priv->active)
5116 {
5117 priv->have_position = TRUE(!(0));
5118 priv->position_x = x;
5119 priv->position_y = y;
5120 }
5121
5122 if (scroll_offset != 0)
5123 {
5124 CtkBorder arrow_border;
5125
5126 get_arrows_border (menu, &arrow_border);
5127 scroll_offset += arrow_border.top;
5128 }
5129
5130 ctk_window_move (CTK_WINDOW (CTK_MENU_SHELL (menu)->priv->active ? priv->toplevel : priv->tearoff_window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((((((CtkMenuShell*) (void *) g_type_check_instance_cast (
(GTypeInstance*) ((menu)), ((ctk_menu_shell_get_type ()))))))
->priv->active ? priv->toplevel : priv->tearoff_window
)), ((ctk_window_get_type ()))))))
,
5131 x, y);
5132
5133 if (!CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
->priv->active)
5134 {
5135 ctk_window_resize (CTK_WINDOW (priv->tearoff_window)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->tearoff_window)), ((ctk_window_get_type ())))))
)
,
5136 requisition.width, requisition.height);
5137 }
5138
5139 if (set_scroll_offset)
5140 priv->scroll_offset = scroll_offset;
5141}
5142
5143static CdkGravity
5144get_horizontally_flipped_anchor (CdkGravity anchor)
5145{
5146 switch (anchor)
5147 {
5148 case CDK_GRAVITY_STATIC:
5149 case CDK_GRAVITY_NORTH_WEST:
5150 return CDK_GRAVITY_NORTH_EAST;
5151 case CDK_GRAVITY_NORTH:
5152 return CDK_GRAVITY_NORTH;
5153 case CDK_GRAVITY_NORTH_EAST:
5154 return CDK_GRAVITY_NORTH_WEST;
5155 case CDK_GRAVITY_WEST:
5156 return CDK_GRAVITY_EAST;
5157 case CDK_GRAVITY_CENTER:
5158 return CDK_GRAVITY_CENTER;
5159 case CDK_GRAVITY_EAST:
5160 return CDK_GRAVITY_WEST;
5161 case CDK_GRAVITY_SOUTH_WEST:
5162 return CDK_GRAVITY_SOUTH_EAST;
5163 case CDK_GRAVITY_SOUTH:
5164 return CDK_GRAVITY_SOUTH;
5165 case CDK_GRAVITY_SOUTH_EAST:
5166 return CDK_GRAVITY_SOUTH_WEST;
5167 }
5168
5169 g_warning ("unknown CdkGravity: %d", anchor);
5170 return anchor;
5171}
5172
5173static void
5174ctk_menu_position (CtkMenu *menu,
5175 gboolean set_scroll_offset)
5176{
5177 CtkMenuPrivate *priv = menu->priv;
5178 CdkWindow *rect_window = NULL((void*)0);
5179 CdkRectangle rect;
5180 CtkTextDirection text_direction = CTK_TEXT_DIR_NONE;
5181 CdkGravity rect_anchor;
5182 CdkGravity menu_anchor;
5183 CdkAnchorHints anchor_hints;
5184 gint rect_anchor_dx, rect_anchor_dy;
5185 CdkWindow *toplevel;
5186 gboolean emulated_move_to_rect = FALSE(0);
5187
5188 rect_anchor = priv->rect_anchor;
5189 menu_anchor = priv->menu_anchor;
5190 anchor_hints = priv->anchor_hints;
5191 rect_anchor_dx = priv->rect_anchor_dx;
5192 rect_anchor_dy = priv->rect_anchor_dy;
5193
5194 if (priv->rect_window)
5195 {
5196 rect_window = priv->rect_window;
5197 rect = priv->rect;
5198 }
5199 else if (priv->widget)
5200 {
5201 rect_window = ctk_widget_get_window (priv->widget);
5202 ctk_widget_get_allocation (priv->widget, &rect);
5203 text_direction = ctk_widget_get_direction (priv->widget);
5204 }
5205 else if (!priv->position_func)
5206 {
5207 CtkWidget *attach_widget;
5208 CdkDevice *grab_device;
5209
5210 /*
5211 * One of the legacy ctk_menu_popup*() functions were used to popup but
5212 * without a custom positioning function, so make an attempt to let the
5213 * backend do the position constraining when required conditions are met.
5214 */
5215
5216 grab_device = _ctk_menu_shell_get_grab_device (CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
);
5217 attach_widget = ctk_menu_get_attach_widget (menu);
5218
5219 if (grab_device && attach_widget)
5220 {
5221 rect.x = 0;
5222 rect.y = 0;
5223 rect.width = 1;
5224 rect.height = 1;
5225
5226 rect_window = ctk_widget_get_window (attach_widget);
5227 cdk_window_get_device_position (rect_window, grab_device,
5228 &rect.x, &rect.y, NULL((void*)0));
5229 text_direction = ctk_widget_get_direction (attach_widget);
5230 rect_anchor = CDK_GRAVITY_SOUTH_EAST;
5231 menu_anchor = CDK_GRAVITY_NORTH_WEST;
5232 anchor_hints = CDK_ANCHOR_FLIP | CDK_ANCHOR_SLIDE | CDK_ANCHOR_RESIZE;
5233 rect_anchor_dx = 0;
5234 rect_anchor_dy = 0;
5235 emulated_move_to_rect = TRUE(!(0));
5236 }
5237 }
5238
5239 if (!rect_window)
5240 {
5241 ctk_window_set_unlimited_guessed_size (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
,
5242 FALSE(0), FALSE(0));
5243 ctk_menu_position_legacy (menu, set_scroll_offset);
5244 return;
5245 }
5246
5247 ctk_window_set_unlimited_guessed_size (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
,
5248 !!(anchor_hints & CDK_ANCHOR_RESIZE_X),
5249 !!(anchor_hints & CDK_ANCHOR_RESIZE_Y));
5250
5251 if (!ctk_widget_get_visible (priv->toplevel))
5252 ctk_window_set_type_hint (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
, priv->menu_type_hint);
5253
5254 /* Realize so we have the proper width and height to figure out
5255 * the right place to popup the menu.
5256 */
5257 ctk_widget_realize (priv->toplevel);
5258 ctk_window_move_resize (CTK_WINDOW (priv->toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((priv->toplevel)), ((ctk_window_get_type ()))))))
);
5259
5260 if (text_direction == CTK_TEXT_DIR_NONE)
5261 text_direction = ctk_widget_get_direction (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
5262
5263 if (text_direction == CTK_TEXT_DIR_RTL)
5264 {
5265 rect_anchor = get_horizontally_flipped_anchor (rect_anchor);
5266 menu_anchor = get_horizontally_flipped_anchor (menu_anchor);
5267 }
5268
5269 toplevel = ctk_widget_get_window (priv->toplevel);
5270
5271 cdk_window_set_transient_for (toplevel, rect_window);
5272
5273 g_signal_handlers_disconnect_by_func (toplevel, moved_to_rect_cb, menu)g_signal_handlers_disconnect_matched ((toplevel), (GSignalMatchType
) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA), 0, 0, ((void*)
0), (moved_to_rect_cb), (menu))
;
5274
5275 g_signal_connect (toplevel, "moved-to-rect", G_CALLBACK (moved_to_rect_cb),g_signal_connect_data ((toplevel), ("moved-to-rect"), (((GCallback
) (moved_to_rect_cb))), (menu), ((void*)0), (GConnectFlags) 0
)
5276 menu)g_signal_connect_data ((toplevel), ("moved-to-rect"), (((GCallback
) (moved_to_rect_cb))), (menu), ((void*)0), (GConnectFlags) 0
)
;
5277 priv->emulated_move_to_rect = emulated_move_to_rect;
5278
5279 cdk_window_move_to_rect (toplevel,
5280 &rect,
5281 rect_anchor,
5282 menu_anchor,
5283 anchor_hints,
5284 rect_anchor_dx,
5285 rect_anchor_dy);
5286}
5287
5288static void
5289ctk_menu_remove_scroll_timeout (CtkMenu *menu)
5290{
5291 CtkMenuPrivate *priv = menu->priv;
5292
5293 if (priv->scroll_timeout)
5294 {
5295 g_source_remove (priv->scroll_timeout);
5296 priv->scroll_timeout = 0;
5297 }
5298}
5299
5300static void
5301ctk_menu_stop_scrolling (CtkMenu *menu)
5302{
5303 CtkMenuPrivate *priv = menu->priv;
5304 CtkCssNode *top_arrow_node, *bottom_arrow_node;
5305 CtkStateFlags state;
5306
5307 ctk_menu_remove_scroll_timeout (menu);
5308 priv->upper_arrow_prelight = FALSE(0);
5309 priv->lower_arrow_prelight = FALSE(0);
5310
5311 top_arrow_node = ctk_css_gadget_get_node (priv->top_arrow_gadget);
5312 state = ctk_css_node_get_state (top_arrow_node);
5313 ctk_css_node_set_state (top_arrow_node, state & ~CTK_STATE_FLAG_PRELIGHT);
5314
5315 bottom_arrow_node = ctk_css_gadget_get_node (priv->bottom_arrow_gadget);
5316 state = ctk_css_node_get_state (bottom_arrow_node);
5317 ctk_css_node_set_state (bottom_arrow_node, state & ~CTK_STATE_FLAG_PRELIGHT);
5318}
5319
5320static void
5321sync_arrows_state (CtkMenu *menu)
5322{
5323 CtkMenuPrivate *priv = menu->priv;
5324 CtkCssNode *top_arrow_node, *bottom_arrow_node;
5325
5326 top_arrow_node = ctk_css_gadget_get_node (priv->top_arrow_gadget);
5327 ctk_css_node_set_visible (top_arrow_node, priv->upper_arrow_visible);
5328 ctk_css_node_set_state (top_arrow_node, priv->upper_arrow_state);
5329
5330 bottom_arrow_node = ctk_css_gadget_get_node (priv->bottom_arrow_gadget);
5331 ctk_css_node_set_visible (bottom_arrow_node, priv->lower_arrow_visible);
5332 ctk_css_node_set_state (bottom_arrow_node, priv->lower_arrow_state);
5333}
5334
5335static void
5336ctk_menu_scroll_to (CtkMenu *menu,
5337 gint offset,
5338 CtkMenuScrollFlag flags)
5339{
5340 CtkMenuPrivate *priv = menu->priv;
5341 CtkBorder arrow_border, padding;
5342 CtkWidget *widget;
5343 gint x, y;
5344 gint view_width, view_height;
5345 gint border_width;
5346 gint menu_height;
5347
5348 widget = CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
;
5349
5350 if (priv->tearoff_active && priv->tearoff_adjustment)
5351 ctk_adjustment_set_value (priv->tearoff_adjustment, offset);
5352
5353 /* Move/resize the viewport according to arrows: */
5354 view_width = ctk_widget_get_allocated_width (widget);
5355 view_height = ctk_widget_get_allocated_height (widget);
5356
5357 get_menu_padding (widget, &padding);
5358
5359 border_width = ctk_container_get_border_width (CTK_CONTAINER (menu)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_container_get_type ()))))))
);
5360
5361 view_width -= (2 * border_width) + padding.left + padding.right;
5362 view_height -= (2 * border_width) + padding.top + padding.bottom;
5363 menu_height = priv->requested_height - (2 * border_width) - padding.top - padding.bottom;
5364
5365 x = border_width + padding.left;
5366 y = border_width + padding.top;
5367
5368 if (!priv->tearoff_active)
5369 {
5370 if (view_height < menu_height ||
5371 (offset > 0 && priv->scroll_offset > 0) ||
5372 (offset < 0 && priv->scroll_offset < 0))
5373 {
5374 CtkStateFlags upper_arrow_previous_state = priv->upper_arrow_state;
5375 CtkStateFlags lower_arrow_previous_state = priv->lower_arrow_state;
5376 gboolean should_offset_by_arrow;
5377
5378 if (!priv->upper_arrow_visible || !priv->lower_arrow_visible)
5379 ctk_widget_queue_draw (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
5380
5381 if (!priv->upper_arrow_visible &&
5382 flags & CTK_MENU_SCROLL_FLAG_ADAPT)
5383 should_offset_by_arrow = TRUE(!(0));
5384 else
5385 should_offset_by_arrow = FALSE(0);
5386
5387 priv->upper_arrow_visible = priv->lower_arrow_visible = TRUE(!(0));
5388
5389 if (flags & CTK_MENU_SCROLL_FLAG_ADAPT)
5390 sync_arrows_state (menu);
5391
5392 get_arrows_border (menu, &arrow_border);
5393 if (should_offset_by_arrow)
5394 offset += arrow_border.top;
5395 y += arrow_border.top;
5396 view_height -= arrow_border.top;
5397 view_height -= arrow_border.bottom;
5398
5399 if (offset <= 0)
5400 priv->upper_arrow_state |= CTK_STATE_FLAG_INSENSITIVE;
5401 else
5402 {
5403 priv->upper_arrow_state &= ~(CTK_STATE_FLAG_INSENSITIVE);
5404
5405 if (priv->upper_arrow_prelight)
5406 priv->upper_arrow_state |= CTK_STATE_FLAG_PRELIGHT;
5407 else
5408 priv->upper_arrow_state &= ~(CTK_STATE_FLAG_PRELIGHT);
5409 }
5410
5411 if (offset >= menu_height - view_height)
5412 priv->lower_arrow_state |= CTK_STATE_FLAG_INSENSITIVE;
5413 else
5414 {
5415 priv->lower_arrow_state &= ~(CTK_STATE_FLAG_INSENSITIVE);
5416
5417 if (priv->lower_arrow_prelight)
5418 priv->lower_arrow_state |= CTK_STATE_FLAG_PRELIGHT;
5419 else
5420 priv->lower_arrow_state &= ~(CTK_STATE_FLAG_PRELIGHT);
5421 }
5422
5423 if ((priv->upper_arrow_state != upper_arrow_previous_state) ||
5424 (priv->lower_arrow_state != lower_arrow_previous_state))
5425 ctk_widget_queue_draw (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
5426
5427 if ((upper_arrow_previous_state & CTK_STATE_FLAG_INSENSITIVE) == 0 &&
5428 (priv->upper_arrow_state & CTK_STATE_FLAG_INSENSITIVE) != 0)
5429 {
5430 /* At the upper border, possibly remove timeout */
5431 if (priv->scroll_step < 0)
5432 {
5433 ctk_menu_stop_scrolling (menu);
5434 ctk_widget_queue_draw (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
5435 }
5436 }
5437
5438 if ((lower_arrow_previous_state & CTK_STATE_FLAG_INSENSITIVE) == 0 &&
5439 (priv->lower_arrow_state & CTK_STATE_FLAG_INSENSITIVE) != 0)
5440 {
5441 /* At the lower border, possibly remove timeout */
5442 if (priv->scroll_step > 0)
5443 {
5444 ctk_menu_stop_scrolling (menu);
5445 ctk_widget_queue_draw (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
5446 }
5447 }
5448 }
5449 else if (priv->upper_arrow_visible || priv->lower_arrow_visible)
5450 {
5451 offset = 0;
5452
5453 priv->upper_arrow_visible = priv->lower_arrow_visible = FALSE(0);
5454 priv->upper_arrow_prelight = priv->lower_arrow_prelight = FALSE(0);
5455
5456 ctk_menu_stop_scrolling (menu);
5457 ctk_widget_queue_draw (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
5458 }
5459 }
5460
5461 sync_arrows_state (menu);
5462
5463 /* Scroll the menu: */
5464 if (ctk_widget_get_realized (widget))
5465 {
5466 cdk_window_move (priv->bin_window, 0, -offset);
5467 cdk_window_move_resize (priv->view_window, x, y, view_width, view_height);
5468 }
5469
5470 priv->scroll_offset = offset;
5471}
5472
5473static gboolean
5474compute_child_offset (CtkMenu *menu,
5475 CtkWidget *menu_item,
5476 gint *offset,
5477 gint *height,
5478 gboolean *is_last_child)
5479{
5480 CtkMenuPrivate *priv = menu->priv;
5481 gint item_top_attach;
5482 gint item_bottom_attach;
5483 gint child_offset = 0;
5484 gint i;
5485
5486 get_effective_child_attach (menu_item, NULL((void*)0), NULL((void*)0),
6
Calling 'get_effective_child_attach'
5487 &item_top_attach, &item_bottom_attach);
5488
5489 /* there is a possibility that we get called before _size_request,
5490 * so check the height table for safety.
5491 */
5492 if (!priv->heights || priv->heights_length < ctk_menu_get_n_rows (menu))
5493 return FALSE(0);
5494
5495 /* when we have a row with only invisible children, its height will
5496 * be zero, so there's no need to check WIDGET_VISIBLE here
5497 */
5498 for (i = 0; i < item_top_attach; i++)
5499 child_offset += priv->heights[i];
5500
5501 if (is_last_child)
5502 *is_last_child = (item_bottom_attach == ctk_menu_get_n_rows (menu));
5503 if (offset)
5504 *offset = child_offset;
5505 if (height)
5506 *height = priv->heights[item_top_attach];
5507
5508 return TRUE(!(0));
5509}
5510
5511static void
5512ctk_menu_scroll_item_visible (CtkMenuShell *menu_shell,
5513 CtkWidget *menu_item)
5514{
5515 CtkMenu *menu = CTK_MENU (menu_shell)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu_shell)), ((ctk_menu_get_type ()))))))
;
5516 CtkMenuPrivate *priv = menu->priv;
5517 CtkWidget *widget = CTK_WIDGET (menu_shell)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu_shell)), ((ctk_widget_get_type ()))))))
;
5518 gint child_offset, child_height;
5519 gint height;
5520 gint y;
5521 gint arrow_height;
5522 gboolean last_child = 0;
5523
5524 /* We need to check if the selected item fully visible.
5525 * If not we need to scroll the menu so that it becomes fully
5526 * visible.
5527 */
5528 if (compute_child_offset (menu, menu_item,
5529 &child_offset, &child_height, &last_child))
5530 {
5531 CtkBorder padding;
5532
5533 y = priv->scroll_offset;
5534 height = cdk_window_get_height (ctk_widget_get_window (widget));
5535
5536 get_menu_padding (widget, &padding);
5537
5538 height -= 2 * ctk_container_get_border_width (CTK_CONTAINER (menu)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_container_get_type ()))))))
) +
5539 padding.top + padding.bottom;
5540
5541 if (child_offset < y)
5542 {
5543 /* Ignore the enter event we might get if the pointer
5544 * is on the menu
5545 */
5546 menu_shell->priv->ignore_enter = TRUE(!(0));
5547 ctk_menu_scroll_to (menu, child_offset, CTK_MENU_SCROLL_FLAG_NONE);
5548 }
5549 else
5550 {
5551 CtkBorder arrow_border;
5552
5553 arrow_height = 0;
5554
5555 get_arrows_border (menu, &arrow_border);
5556 if (!priv->tearoff_active)
5557 arrow_height = arrow_border.top + arrow_border.bottom;
5558
5559 if (child_offset + child_height > y + height - arrow_height)
5560 {
5561 arrow_height = arrow_border.bottom + arrow_border.top;
5562 y = child_offset + child_height - height + arrow_height;
5563
5564 /* Ignore the enter event we might get if the pointer
5565 * is on the menu
5566 */
5567 menu_shell->priv->ignore_enter = TRUE(!(0));
5568 ctk_menu_scroll_to (menu, y, CTK_MENU_SCROLL_FLAG_NONE);
5569 }
5570 }
5571 }
5572}
5573
5574static void
5575ctk_menu_select_item (CtkMenuShell *menu_shell,
5576 CtkWidget *menu_item)
5577{
5578 CtkMenu *menu = CTK_MENU (menu_shell)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu_shell)), ((ctk_menu_get_type ()))))))
;
5579
5580 if (ctk_widget_get_realized (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
))
5581 ctk_menu_scroll_item_visible (menu_shell, menu_item);
5582
5583 CTK_MENU_SHELL_CLASS (ctk_menu_parent_class)((((CtkMenuShellClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), ((ctk_menu_shell_get_type ())))
)))
->select_item (menu_shell, menu_item);
5584}
5585
5586
5587/* Reparent the menu, taking care of the refcounting
5588 *
5589 * If unrealize is true we force a unrealize while reparenting the parent.
5590 * This can help eliminate flicker in some cases.
5591 *
5592 * What happens is that when the menu is unrealized and then re-realized,
5593 * the allocations are as follows:
5594 *
5595 * parent - 1x1 at (0,0)
5596 * child1 - 100x20 at (0,0)
5597 * child2 - 100x20 at (0,20)
5598 * child3 - 100x20 at (0,40)
5599 *
5600 * That is, the parent is small but the children are full sized. Then,
5601 * when the queued_resize gets processed, the parent gets resized to
5602 * full size.
5603 *
5604 * But in order to eliminate flicker when scrolling, cdkgeometry-x11.c
5605 * contains the following logic:
5606 *
5607 * - if a move or resize operation on a window would change the clip
5608 * region on the children, then before the window is resized
5609 * the background for children is temporarily set to None, the
5610 * move/resize done, and the background for the children restored.
5611 *
5612 * So, at the point where the parent is resized to final size, the
5613 * background for the children is temporarily None, and thus they
5614 * are not cleared to the background color and the previous background
5615 * (the image of the menu) is left in place.
5616 */
5617static void
5618ctk_menu_reparent (CtkMenu *menu,
5619 CtkWidget *new_parent,
5620 gboolean unrealize)
5621{
5622 GObject *object = G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
;
5623 CtkWidget *widget = CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
;
5624 gboolean was_floating = g_object_is_floating (object);
5625
5626 g_object_ref_sink (object)((__typeof__ (object)) (g_object_ref_sink) (object));
5627
5628 if (unrealize)
5629 {
5630 g_object_ref (object)((__typeof__ (object)) (g_object_ref) (object));
5631 ctk_container_remove (CTK_CONTAINER (ctk_widget_get_parent (widget))((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((ctk_widget_get_parent (widget))), ((ctk_container_get_type
()))))))
, widget);
5632 ctk_container_add (CTK_CONTAINER (new_parent)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((new_parent)), ((ctk_container_get_type ()))))))
, widget);
5633 g_object_unref (object);
5634 }
5635 else
5636 {
5637 ctk_widget_reparent (widget, new_parent);
5638 }
5639
5640 if (was_floating)
5641 g_object_force_floating (object);
5642 else
5643 g_object_unref (object);
5644}
5645
5646static void
5647ctk_menu_show_all (CtkWidget *widget)
5648{
5649 /* Show children, but not self. */
5650 ctk_container_foreach (CTK_CONTAINER (widget)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_container_get_type ()))))))
, (CtkCallback) ctk_widget_show_all, NULL((void*)0));
5651}
5652
5653/**
5654 * ctk_menu_set_screen:
5655 * @menu: a #CtkMenu
5656 * @screen: (allow-none): a #CdkScreen, or %NULL if the screen should be
5657 * determined by the widget the menu is attached to
5658 *
5659 * Sets the #CdkScreen on which the menu will be displayed.
5660 *
5661 * Since: 2.2
5662 */
5663void
5664ctk_menu_set_screen (CtkMenu *menu,
5665 CdkScreen *screen)
5666{
5667 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
5668 g_return_if_fail (screen == NULL || CDK_IS_SCREEN (screen))do { if ((screen == ((void*)0) || (((__extension__ ({ GTypeInstance
*__inst = (GTypeInstance*) ((screen)); GType __t = ((cdk_screen_get_type
())); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("Ctk", ((const
char*) (__func__)), "screen == NULL || CDK_IS_SCREEN (screen)"
); return; } } while (0)
;
5669
5670 g_object_set_data (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, I_("ctk-menu-explicit-screen")g_intern_static_string ("ctk-menu-explicit-screen"), screen);
5671
5672 if (screen)
5673 {
5674 menu_change_screen (menu, screen);
5675 }
5676 else
5677 {
5678 CtkWidget *attach_widget = ctk_menu_get_attach_widget (menu);
5679 if (attach_widget)
5680 attach_widget_screen_changed (attach_widget, NULL((void*)0), menu);
5681 }
5682}
5683
5684/**
5685 * ctk_menu_attach:
5686 * @menu: a #CtkMenu
5687 * @child: a #CtkMenuItem
5688 * @left_attach: The column number to attach the left side of the item to
5689 * @right_attach: The column number to attach the right side of the item to
5690 * @top_attach: The row number to attach the top of the item to
5691 * @bottom_attach: The row number to attach the bottom of the item to
5692 *
5693 * Adds a new #CtkMenuItem to a (table) menu. The number of “cells” that
5694 * an item will occupy is specified by @left_attach, @right_attach,
5695 * @top_attach and @bottom_attach. These each represent the leftmost,
5696 * rightmost, uppermost and lower column and row numbers of the table.
5697 * (Columns and rows are indexed from zero).
5698 *
5699 * Note that this function is not related to ctk_menu_detach().
5700 *
5701 * Since: 2.4
5702 */
5703void
5704ctk_menu_attach (CtkMenu *menu,
5705 CtkWidget *child,
5706 guint left_attach,
5707 guint right_attach,
5708 guint top_attach,
5709 guint bottom_attach)
5710{
5711 CtkMenuShell *menu_shell;
5712 CtkWidget *parent;
5713 CtkCssNode *widget_node, *child_node;
5714
5715 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
5716 g_return_if_fail (CTK_IS_MENU_ITEM (child))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((child)); GType __t = ((ctk_menu_item_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU_ITEM (child)"); return; } } while (0)
;
5717 parent = ctk_widget_get_parent (child);
5718 g_return_if_fail (parent == NULL || parent == CTK_WIDGET (menu))do { if ((parent == ((void*)0) || parent == ((((CtkWidget*) (
void *) g_type_check_instance_cast ((GTypeInstance*) ((menu))
, ((ctk_widget_get_type ())))))))) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "parent == NULL || parent == CTK_WIDGET (menu)"
); return; } } while (0)
;
5719 g_return_if_fail (left_attach < right_attach)do { if ((left_attach < right_attach)) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "left_attach < right_attach"
); return; } } while (0)
;
5720 g_return_if_fail (top_attach < bottom_attach)do { if ((top_attach < bottom_attach)) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "top_attach < bottom_attach"
); return; } } while (0)
;
5721
5722 menu_shell = CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
;
5723
5724 if (!parent)
5725 {
5726 AttachInfo *ai = get_attach_info (child);
5727
5728 ai->left_attach = left_attach;
5729 ai->right_attach = right_attach;
5730 ai->top_attach = top_attach;
5731 ai->bottom_attach = bottom_attach;
5732
5733 menu_shell->priv->children = g_list_append (menu_shell->priv->children, child);
5734
5735 widget_node = ctk_widget_get_css_node (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
5736 child_node = ctk_widget_get_css_node (child);
5737 ctk_css_node_insert_before (widget_node, child_node,
5738 ctk_css_gadget_get_node (menu->priv->bottom_arrow_gadget));
5739
5740 ctk_widget_set_parent (child, CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
5741
5742 menu_queue_resize (menu);
5743 }
5744 else
5745 {
5746 ctk_container_child_set (CTK_CONTAINER (parent)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((parent)), ((ctk_container_get_type ()))))))
, child,
5747 "left-attach", left_attach,
5748 "right-attach", right_attach,
5749 "top-attach", top_attach,
5750 "bottom-attach", bottom_attach,
5751 NULL((void*)0));
5752 }
5753}
5754
5755static gint
5756ctk_menu_get_popup_delay (CtkMenuShell *menu_shell G_GNUC_UNUSED__attribute__ ((__unused__)))
5757{
5758 return MENU_POPUP_DELAY225;
5759}
5760
5761static CtkWidget *
5762find_child_containing (CtkMenuShell *menu_shell,
5763 int left,
5764 int right,
5765 int top,
5766 int bottom)
5767{
5768 GList *list;
5769
5770 /* find a child which includes the area given by
5771 * left, right, top, bottom.
5772 */
5773 for (list = menu_shell->priv->children; list; list = list->next)
5774 {
5775 gint l, r, t, b;
5776
5777 if (!_ctk_menu_item_is_selectable (list->data))
5778 continue;
5779
5780 get_effective_child_attach (list->data, &l, &r, &t, &b);
5781
5782 if (l <= left && right <= r && t <= top && bottom <= b)
5783 return CTK_WIDGET (list->data)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((list->data)), ((ctk_widget_get_type ()))))))
;
5784 }
5785
5786 return NULL((void*)0);
5787}
5788
5789static void
5790ctk_menu_move_current (CtkMenuShell *menu_shell,
5791 CtkMenuDirectionType direction)
5792{
5793 CtkMenu *menu = CTK_MENU (menu_shell)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu_shell)), ((ctk_menu_get_type ()))))))
;
5794 gint i;
5795 gint l, r, t, b;
5796 CtkWidget *match = NULL((void*)0);
5797
5798 if (ctk_widget_get_direction (CTK_WIDGET (menu_shell)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu_shell)), ((ctk_widget_get_type ()))))))
) == CTK_TEXT_DIR_RTL)
5799 {
5800 switch (direction)
5801 {
5802 case CTK_MENU_DIR_CHILD:
5803 direction = CTK_MENU_DIR_PARENT;
5804 break;
5805 case CTK_MENU_DIR_PARENT:
5806 direction = CTK_MENU_DIR_CHILD;
5807 break;
5808 default: ;
5809 }
5810 }
5811
5812 /* use special table menu key bindings */
5813 if (menu_shell->priv->active_menu_item && ctk_menu_get_n_columns (menu) > 1)
5814 {
5815 get_effective_child_attach (menu_shell->priv->active_menu_item, &l, &r, &t, &b);
5816
5817 if (direction == CTK_MENU_DIR_NEXT)
5818 {
5819 for (i = b; i < ctk_menu_get_n_rows (menu); i++)
5820 {
5821 match = find_child_containing (menu_shell, l, l + 1, i, i + 1);
5822 if (match)
5823 break;
5824 }
5825
5826 if (!match)
5827 {
5828 /* wrap around */
5829 for (i = 0; i < t; i++)
5830 {
5831 match = find_child_containing (menu_shell,
5832 l, l + 1, i, i + 1);
5833 if (match)
5834 break;
5835 }
5836 }
5837 }
5838 else if (direction == CTK_MENU_DIR_PREV)
5839 {
5840 for (i = t; i > 0; i--)
5841 {
5842 match = find_child_containing (menu_shell,
5843 l, l + 1, i - 1, i);
5844 if (match)
5845 break;
5846 }
5847
5848 if (!match)
5849 {
5850 /* wrap around */
5851 for (i = ctk_menu_get_n_rows (menu); i > b; i--)
5852 {
5853 match = find_child_containing (menu_shell,
5854 l, l + 1, i - 1, i);
5855 if (match)
5856 break;
5857 }
5858 }
5859 }
5860 else if (direction == CTK_MENU_DIR_PARENT)
5861 {
5862 /* we go one left if possible */
5863 if (l > 0)
5864 match = find_child_containing (menu_shell,
5865 l - 1, l, t, t + 1);
5866
5867 if (!match)
5868 {
5869 CtkWidget *parent = menu_shell->priv->parent_menu_shell;
5870
5871 if (!parent
5872 || g_list_length (CTK_MENU_SHELL (parent)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((parent)), ((ctk_menu_shell_get_type ()))))))
->priv->children) <= 1)
5873 match = menu_shell->priv->active_menu_item;
5874 }
5875 }
5876 else if (direction == CTK_MENU_DIR_CHILD)
5877 {
5878 /* we go one right if possible */
5879 if (r < ctk_menu_get_n_columns (menu))
5880 match = find_child_containing (menu_shell, r, r + 1, t, t + 1);
5881
5882 if (!match)
5883 {
5884 CtkWidget *parent = menu_shell->priv->parent_menu_shell;
5885
5886 if (! CTK_MENU_ITEM (menu_shell->priv->active_menu_item)((((CtkMenuItem*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu_shell->priv->active_menu_item)), ((ctk_menu_item_get_type
()))))))
->priv->submenu &&
5887 (!parent ||
5888 g_list_length (CTK_MENU_SHELL (parent)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((parent)), ((ctk_menu_shell_get_type ()))))))
->priv->children) <= 1))
5889 match = menu_shell->priv->active_menu_item;
5890 }
5891 }
5892
5893 if (match)
5894 {
5895 ctk_menu_shell_select_item (menu_shell, match);
5896 return;
5897 }
5898 }
5899
5900 CTK_MENU_SHELL_CLASS (ctk_menu_parent_class)((((CtkMenuShellClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((ctk_menu_parent_class)), ((ctk_menu_shell_get_type ())))
)))
->move_current (menu_shell, direction);
5901}
5902
5903static gint
5904get_visible_size (CtkMenu *menu)
5905{
5906 CtkMenuPrivate *priv = menu->priv;
5907 CtkAllocation allocation;
5908 CtkWidget *widget = CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
;
5909 CtkContainer *container = CTK_CONTAINER (menu)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_container_get_type ()))))))
;
5910 CtkBorder padding;
5911 gint menu_height;
5912
5913 ctk_widget_get_allocation (widget, &allocation);
5914 get_menu_padding (widget, &padding);
5915
5916 menu_height = (allocation.height -
5917 (2 * ctk_container_get_border_width (container)) -
5918 padding.top - padding.bottom);
5919
5920 if (!priv->tearoff_active)
5921 {
5922 CtkBorder arrow_border;
5923
5924 get_arrows_border (menu, &arrow_border);
5925 menu_height -= arrow_border.top;
5926 menu_height -= arrow_border.bottom;
5927 }
5928
5929 return menu_height;
5930}
5931
5932/* Find the sensitive on-screen child containing @y, or if none,
5933 * the nearest selectable onscreen child. (%NULL if none)
5934 */
5935static CtkWidget *
5936child_at (CtkMenu *menu,
5937 gint y)
5938{
5939 CtkMenuPrivate *priv = menu->priv;
5940 CtkMenuShell *menu_shell = CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
;
5941 CtkWidget *child = NULL((void*)0);
5942 gint child_offset = 0;
5943 GList *children;
5944 gint menu_height;
5945 gint lower, upper; /* Onscreen bounds */
5946
5947 menu_height = get_visible_size (menu);
5948 lower = priv->scroll_offset;
5949 upper = priv->scroll_offset + menu_height;
5950
5951 for (children = menu_shell->priv->children; children; children = children->next)
5952 {
5953 if (ctk_widget_get_visible (children->data))
5954 {
5955 CtkRequisition child_requisition;
5956
5957 ctk_widget_get_preferred_size (children->data,
5958 &child_requisition, NULL((void*)0));
5959
5960 if (_ctk_menu_item_is_selectable (children->data) &&
5961 child_offset >= lower &&
5962 child_offset + child_requisition.height <= upper)
5963 {
5964 child = children->data;
5965
5966 if (child_offset + child_requisition.height > y &&
5967 !CTK_IS_TEAROFF_MENU_ITEM (child)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(child)); GType __t = ((ctk_tearoff_menu_item_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
)
5968 return child;
5969 }
5970
5971 child_offset += child_requisition.height;
5972 }
5973 }
5974
5975 return child;
5976}
5977
5978static gint
5979get_menu_height (CtkMenu *menu)
5980{
5981 CtkMenuPrivate *priv = menu->priv;
5982 CtkWidget *widget = CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
;
5983 CtkBorder padding;
5984 gint height;
5985
5986 get_menu_padding (widget, &padding);
5987
5988 height = priv->requested_height;
5989 height -= (ctk_container_get_border_width (CTK_CONTAINER (widget)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_container_get_type ()))))))
) * 2) +
5990 padding.top + padding.bottom;
5991
5992 if (!priv->tearoff_active)
5993 {
5994 CtkBorder arrow_border;
5995
5996 get_arrows_border (menu, &arrow_border);
5997 height -= arrow_border.top;
5998 height -= arrow_border.bottom;
5999 }
6000
6001 return height;
6002}
6003
6004static void
6005ctk_menu_real_move_scroll (CtkMenu *menu,
6006 CtkScrollType type)
6007{
6008 CtkMenuPrivate *priv = menu->priv;
6009 gint page_size = get_visible_size (menu);
6010 gint end_position = get_menu_height (menu);
6011 CtkMenuShell *menu_shell = CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
;
6012
6013 switch (type)
1
Control jumps to 'case CTK_SCROLL_PAGE_UP:' at line 6015
6014 {
6015 case CTK_SCROLL_PAGE_UP:
6016 case CTK_SCROLL_PAGE_DOWN:
6017 {
6018 gint old_offset;
6019 gint new_offset;
6020 gint child_offset = 0;
6021 gboolean old_upper_arrow_visible;
6022 gint step;
6023
6024 if (type
1.1
'type' is equal to CTK_SCROLL_PAGE_UP
== CTK_SCROLL_PAGE_UP)
2
Taking true branch
6025 step = - page_size;
6026 else
6027 step = page_size;
6028
6029 if (menu_shell->priv->active_menu_item)
3
Assuming field 'active_menu_item' is non-null
4
Taking true branch
6030 {
6031 gint child_height;
6032
6033 if (compute_child_offset (menu, menu_shell->priv->active_menu_item,
5
Calling 'compute_child_offset'
6034 &child_offset, &child_height, NULL((void*)0)))
6035 child_offset += child_height / 2;
6036 }
6037
6038 menu_shell->priv->ignore_enter = TRUE(!(0));
6039 old_upper_arrow_visible = priv->upper_arrow_visible && !priv->tearoff_active;
6040 old_offset = priv->scroll_offset;
6041
6042 new_offset = priv->scroll_offset + step;
6043 new_offset = CLAMP (new_offset, 0, end_position - page_size)(((new_offset) > (end_position - page_size)) ? (end_position
- page_size) : (((new_offset) < (0)) ? (0) : (new_offset)
))
;
6044
6045 ctk_menu_scroll_to (menu, new_offset, CTK_MENU_SCROLL_FLAG_NONE);
6046
6047 if (menu_shell->priv->active_menu_item)
6048 {
6049 CtkWidget *new_child;
6050 gboolean new_upper_arrow_visible = priv->upper_arrow_visible && !priv->tearoff_active;
6051 CtkBorder arrow_border;
6052
6053 get_arrows_border (menu, &arrow_border);
6054
6055 if (priv->scroll_offset != old_offset)
6056 step = priv->scroll_offset - old_offset;
6057
6058 step -= (new_upper_arrow_visible - old_upper_arrow_visible) * arrow_border.top;
6059
6060 new_child = child_at (menu, child_offset + step);
6061 if (new_child)
6062 ctk_menu_shell_select_item (menu_shell, new_child);
6063 }
6064 }
6065 break;
6066 case CTK_SCROLL_START:
6067 /* Ignore the enter event we might get if the pointer is on the menu */
6068 menu_shell->priv->ignore_enter = TRUE(!(0));
6069 ctk_menu_shell_select_first (menu_shell, TRUE(!(0)));
6070 break;
6071 case CTK_SCROLL_END:
6072 /* Ignore the enter event we might get if the pointer is on the menu */
6073 menu_shell->priv->ignore_enter = TRUE(!(0));
6074 _ctk_menu_shell_select_last (menu_shell, TRUE(!(0)));
6075 break;
6076 default:
6077 break;
6078 }
6079}
6080
6081/**
6082 * ctk_menu_set_monitor:
6083 * @menu: a #CtkMenu
6084 * @monitor_num: the number of the monitor on which the menu should
6085 * be popped up
6086 *
6087 * Informs CTK+ on which monitor a menu should be popped up.
6088 * See cdk_monitor_get_geometry().
6089 *
6090 * This function should be called from a #CtkMenuPositionFunc
6091 * if the menu should not appear on the same monitor as the pointer.
6092 * This information can’t be reliably inferred from the coordinates
6093 * returned by a #CtkMenuPositionFunc, since, for very long menus,
6094 * these coordinates may extend beyond the monitor boundaries or even
6095 * the screen boundaries.
6096 *
6097 * Since: 2.4
6098 */
6099void
6100ctk_menu_set_monitor (CtkMenu *menu,
6101 gint monitor_num)
6102{
6103 CtkMenuPrivate *priv;
6104
6105 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
6106
6107 priv = menu->priv;
6108
6109 if (priv->monitor_num != monitor_num)
6110 {
6111 priv->monitor_num = monitor_num;
6112 g_object_notify (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, "monitor");
6113 }
6114}
6115
6116/**
6117 * ctk_menu_get_monitor:
6118 * @menu: a #CtkMenu
6119 *
6120 * Retrieves the number of the monitor on which to show the menu.
6121 *
6122 * Returns: the number of the monitor on which the menu should
6123 * be popped up or -1, if no monitor has been set
6124 *
6125 * Since: 2.14
6126 */
6127gint
6128ctk_menu_get_monitor (CtkMenu *menu)
6129{
6130 g_return_val_if_fail (CTK_IS_MENU (menu), -1)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return (-1); } } while (0)
;
6131
6132 return menu->priv->monitor_num;
6133}
6134
6135/**
6136 * ctk_menu_place_on_monitor:
6137 * @menu: a #CtkMenu
6138 * @monitor: the monitor to place the menu on
6139 *
6140 * Places @menu on the given monitor.
6141 *
6142 * Since: 3.22
6143 */
6144void
6145ctk_menu_place_on_monitor (CtkMenu *menu,
6146 CdkMonitor *monitor)
6147{
6148 CdkDisplay *display;
6149 gint i, monitor_num;
6150
6151 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
6152
6153 display = ctk_widget_get_display (CTK_WIDGET (menu)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_widget_get_type ()))))))
);
6154 monitor_num = 0;
6155 for (i = 0; ; i++)
6156 {
6157 CdkMonitor *m = cdk_display_get_monitor (display, i);
6158 if (m == monitor)
6159 {
6160 monitor_num = i;
6161 break;
6162 }
6163 if (m == NULL((void*)0))
6164 break;
6165 }
6166
6167 ctk_menu_set_monitor (menu, monitor_num);
6168}
6169
6170/**
6171 * ctk_menu_get_for_attach_widget:
6172 * @widget: a #CtkWidget
6173 *
6174 * Returns a list of the menus which are attached to this widget.
6175 * This list is owned by CTK+ and must not be modified.
6176 *
6177 * Returns: (element-type CtkWidget) (transfer none): the list
6178 * of menus attached to his widget.
6179 *
6180 * Since: 2.6
6181 */
6182GList*
6183ctk_menu_get_for_attach_widget (CtkWidget *widget)
6184{
6185 GList *list;
6186
6187 g_return_val_if_fail (CTK_IS_WIDGET (widget), NULL)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((widget)); GType __t = ((ctk_widget_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_WIDGET (widget)"); return (((void*)0)); } } while
(0)
;
6188
6189 list = g_object_get_data (G_OBJECT (widget)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), (((GType) ((20) << (2))))))))
, ATTACHED_MENUS"ctk-attached-menus");
6190
6191 return list;
6192}
6193
6194static void
6195ctk_menu_grab_notify (CtkWidget *widget,
6196 gboolean was_grabbed G_GNUC_UNUSED__attribute__ ((__unused__)))
6197{
6198 CtkMenu *menu;
6199 CtkWidget *toplevel;
6200 CtkWindowGroup *group;
6201 CtkWidget *grab;
6202 CdkDevice *pointer;
6203
6204 menu = CTK_MENU (widget)((((CtkMenu*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_get_type ()))))))
;
6205 pointer = _ctk_menu_shell_get_grab_device (CTK_MENU_SHELL (widget)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_shell_get_type ()))))))
);
6206
6207 if (!pointer ||
6208 !ctk_widget_device_is_shadowed (widget, pointer))
6209 return;
6210
6211 toplevel = ctk_widget_get_toplevel (widget);
6212
6213 if (!CTK_IS_WINDOW (toplevel)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(toplevel)); GType __t = ((ctk_window_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
)
6214 return;
6215
6216 group = ctk_window_get_group (CTK_WINDOW (toplevel)((((CtkWindow*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((toplevel)), ((ctk_window_get_type ()))))))
);
6217 grab = ctk_window_group_get_current_grab (group);
6218
6219 if (CTK_MENU_SHELL (widget)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_shell_get_type ()))))))
->priv->active && !CTK_IS_MENU_SHELL (grab)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(grab)); GType __t = ((ctk_menu_shell_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; }))))
&&
6220 !ctk_widget_is_ancestor (grab, widget))
6221 ctk_menu_shell_cancel (CTK_MENU_SHELL (widget)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((widget)), ((ctk_menu_shell_get_type ()))))))
);
6222
6223 menu->priv->drag_scroll_started = FALSE(0);
6224}
6225
6226/**
6227 * ctk_menu_set_reserve_toggle_size:
6228 * @menu: a #CtkMenu
6229 * @reserve_toggle_size: whether to reserve size for toggles
6230 *
6231 * Sets whether the menu should reserve space for drawing toggles
6232 * or icons, regardless of their actual presence.
6233 *
6234 * Since: 2.18
6235 */
6236void
6237ctk_menu_set_reserve_toggle_size (CtkMenu *menu,
6238 gboolean reserve_toggle_size)
6239{
6240 CtkMenuPrivate *priv;
6241 gboolean no_toggle_size;
6242
6243 g_return_if_fail (CTK_IS_MENU (menu))do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return; } } while (0)
;
6244
6245 priv = menu->priv;
6246
6247 no_toggle_size = !reserve_toggle_size;
6248 if (priv->no_toggle_size != no_toggle_size)
6249 {
6250 priv->no_toggle_size = no_toggle_size;
6251
6252 g_object_notify (G_OBJECT (menu)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), (((GType) ((20) << (2))))))))
, "reserve-toggle-size");
6253 }
6254}
6255
6256/**
6257 * ctk_menu_get_reserve_toggle_size:
6258 * @menu: a #CtkMenu
6259 *
6260 * Returns whether the menu reserves space for toggles and
6261 * icons, regardless of their actual presence.
6262 *
6263 * Returns: Whether the menu reserves toggle space
6264 *
6265 * Since: 2.18
6266 */
6267gboolean
6268ctk_menu_get_reserve_toggle_size (CtkMenu *menu)
6269{
6270 g_return_val_if_fail (CTK_IS_MENU (menu), FALSE)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((menu)); GType __t = ((ctk_menu_get_type ())); gboolean __r
; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "CTK_IS_MENU (menu)"); return ((0)); } } while (0)
;
6271
6272 return !menu->priv->no_toggle_size;
6273}
6274
6275/**
6276 * ctk_menu_new_from_model:
6277 * @model: a #GMenuModel
6278 *
6279 * Creates a #CtkMenu and populates it with menu items and
6280 * submenus according to @model.
6281 *
6282 * The created menu items are connected to actions found in the
6283 * #CtkApplicationWindow to which the menu belongs - typically
6284 * by means of being attached to a widget (see ctk_menu_attach_to_widget())
6285 * that is contained within the #CtkApplicationWindows widget hierarchy.
6286 *
6287 * Actions can also be added using ctk_widget_insert_action_group() on the menu's
6288 * attach widget or on any of its parent widgets.
6289 *
6290 * Returns: a new #CtkMenu
6291 *
6292 * Since: 3.4
6293 */
6294CtkWidget *
6295ctk_menu_new_from_model (GMenuModel *model)
6296{
6297 CtkWidget *menu;
6298
6299 g_return_val_if_fail (G_IS_MENU_MODEL (model), NULL)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((model)); GType __t = ((g_menu_model_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("Ctk", ((const char*) (__func__
)), "G_IS_MENU_MODEL (model)"); return (((void*)0)); } } while
(0)
;
6300
6301 menu = ctk_menu_new ();
6302 ctk_menu_shell_bind_model (CTK_MENU_SHELL (menu)((((CtkMenuShell*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((menu)), ((ctk_menu_shell_get_type ()))))))
, model, NULL((void*)0), TRUE(!(0)));
6303
6304 return menu;
6305}