1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpuimanager.c
5 * Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <string.h>
24
25 #include <gegl.h>
26 #undef GSEAL_ENABLE
27 #include <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29
30 #include "libgimpbase/gimpbase.h"
31 #include "libgimpwidgets/gimpwidgets.h"
32
33 #include "widgets-types.h"
34
35 #include "core/gimp.h"
36 #include "core/gimpmarshal.h"
37
38 #include "gimpaction.h"
39 #include "gimpactiongroup.h"
40 #include "gimphelp.h"
41 #include "gimphelp-ids.h"
42 #include "gimptoggleaction.h"
43 #include "gimpuimanager.h"
44
45 #include "gimp-intl.h"
46
47
48 enum
49 {
50 PROP_0,
51 PROP_NAME,
52 PROP_GIMP
53 };
54
55 enum
56 {
57 UPDATE,
58 SHOW_TOOLTIP,
59 HIDE_TOOLTIP,
60 LAST_SIGNAL
61 };
62
63
64 static void gimp_ui_manager_constructed (GObject *object);
65 static void gimp_ui_manager_dispose (GObject *object);
66 static void gimp_ui_manager_finalize (GObject *object);
67 static void gimp_ui_manager_set_property (GObject *object,
68 guint prop_id,
69 const GValue *value,
70 GParamSpec *pspec);
71 static void gimp_ui_manager_get_property (GObject *object,
72 guint prop_id,
73 GValue *value,
74 GParamSpec *pspec);
75 static void gimp_ui_manager_connect_proxy (GtkUIManager *manager,
76 GtkAction *action,
77 GtkWidget *proxy);
78 static GtkWidget *gimp_ui_manager_get_widget_impl (GtkUIManager *manager,
79 const gchar *path);
80 static GtkAction *gimp_ui_manager_get_action_impl (GtkUIManager *manager,
81 const gchar *path);
82 static void gimp_ui_manager_real_update (GimpUIManager *manager,
83 gpointer update_data);
84 static GimpUIManagerUIEntry *
85 gimp_ui_manager_entry_get (GimpUIManager *manager,
86 const gchar *ui_path);
87 static gboolean gimp_ui_manager_entry_load (GimpUIManager *manager,
88 GimpUIManagerUIEntry *entry,
89 GError **error);
90 static GimpUIManagerUIEntry *
91 gimp_ui_manager_entry_ensure (GimpUIManager *manager,
92 const gchar *path);
93 static void gimp_ui_manager_menu_position (GtkMenu *menu,
94 gint *x,
95 gint *y,
96 gpointer data);
97 static void gimp_ui_manager_menu_pos (GtkMenu *menu,
98 gint *x,
99 gint *y,
100 gboolean *push_in,
101 gpointer data);
102 static void gimp_ui_manager_delete_popdown_data (GtkWidget *widget,
103 GimpUIManager *manager);
104 static void gimp_ui_manager_item_realize (GtkWidget *widget,
105 GimpUIManager *manager);
106 static void gimp_ui_manager_menu_item_select (GtkWidget *widget,
107 GimpUIManager *manager);
108 static void gimp_ui_manager_menu_item_deselect (GtkWidget *widget,
109 GimpUIManager *manager);
110 static gboolean gimp_ui_manager_item_key_press (GtkWidget *widget,
111 GdkEventKey *kevent,
112 GimpUIManager *manager);
113 static GtkWidget *find_widget_under_pointer (GdkWindow *window,
114 gint *x,
115 gint *y);
116
117
118 G_DEFINE_TYPE (GimpUIManager, gimp_ui_manager, GTK_TYPE_UI_MANAGER)
119
120 #define parent_class gimp_ui_manager_parent_class
121
122 static guint manager_signals[LAST_SIGNAL] = { 0 };
123
124
125 static void
gimp_ui_manager_class_init(GimpUIManagerClass * klass)126 gimp_ui_manager_class_init (GimpUIManagerClass *klass)
127 {
128 GObjectClass *object_class = G_OBJECT_CLASS (klass);
129 GtkUIManagerClass *manager_class = GTK_UI_MANAGER_CLASS (klass);
130
131 object_class->constructed = gimp_ui_manager_constructed;
132 object_class->dispose = gimp_ui_manager_dispose;
133 object_class->finalize = gimp_ui_manager_finalize;
134 object_class->set_property = gimp_ui_manager_set_property;
135 object_class->get_property = gimp_ui_manager_get_property;
136
137 manager_class->connect_proxy = gimp_ui_manager_connect_proxy;
138 manager_class->get_widget = gimp_ui_manager_get_widget_impl;
139 manager_class->get_action = gimp_ui_manager_get_action_impl;
140
141 klass->update = gimp_ui_manager_real_update;
142
143 manager_signals[UPDATE] =
144 g_signal_new ("update",
145 G_TYPE_FROM_CLASS (klass),
146 G_SIGNAL_RUN_LAST,
147 G_STRUCT_OFFSET (GimpUIManagerClass, update),
148 NULL, NULL,
149 gimp_marshal_VOID__POINTER,
150 G_TYPE_NONE, 1,
151 G_TYPE_POINTER);
152
153 manager_signals[SHOW_TOOLTIP] =
154 g_signal_new ("show-tooltip",
155 G_TYPE_FROM_CLASS (klass),
156 G_SIGNAL_RUN_LAST,
157 G_STRUCT_OFFSET (GimpUIManagerClass, show_tooltip),
158 NULL, NULL,
159 gimp_marshal_VOID__STRING,
160 G_TYPE_NONE, 1,
161 G_TYPE_STRING);
162
163 manager_signals[HIDE_TOOLTIP] =
164 g_signal_new ("hide-tooltip",
165 G_TYPE_FROM_CLASS (klass),
166 G_SIGNAL_RUN_LAST,
167 G_STRUCT_OFFSET (GimpUIManagerClass, hide_tooltip),
168 NULL, NULL,
169 gimp_marshal_VOID__VOID,
170 G_TYPE_NONE, 0,
171 G_TYPE_NONE);
172
173 g_object_class_install_property (object_class, PROP_NAME,
174 g_param_spec_string ("name",
175 NULL, NULL,
176 NULL,
177 GIMP_PARAM_READWRITE |
178 G_PARAM_CONSTRUCT_ONLY));
179
180 g_object_class_install_property (object_class, PROP_GIMP,
181 g_param_spec_object ("gimp",
182 NULL, NULL,
183 GIMP_TYPE_GIMP,
184 GIMP_PARAM_READWRITE |
185 G_PARAM_CONSTRUCT_ONLY));
186
187 klass->managers = g_hash_table_new_full (g_str_hash, g_str_equal,
188 g_free, NULL);
189 }
190
191 static void
gimp_ui_manager_init(GimpUIManager * manager)192 gimp_ui_manager_init (GimpUIManager *manager)
193 {
194 manager->name = NULL;
195 manager->gimp = NULL;
196 }
197
198 static void
gimp_ui_manager_constructed(GObject * object)199 gimp_ui_manager_constructed (GObject *object)
200 {
201 GimpUIManager *manager = GIMP_UI_MANAGER (object);
202
203 G_OBJECT_CLASS (parent_class)->constructed (object);
204
205 if (manager->name)
206 {
207 GimpUIManagerClass *manager_class;
208 GList *list;
209
210 manager_class = GIMP_UI_MANAGER_GET_CLASS (object);
211
212 list = g_hash_table_lookup (manager_class->managers, manager->name);
213
214 list = g_list_append (list, manager);
215
216 g_hash_table_replace (manager_class->managers,
217 g_strdup (manager->name), list);
218 }
219 }
220
221 static void
gimp_ui_manager_dispose(GObject * object)222 gimp_ui_manager_dispose (GObject *object)
223 {
224 GimpUIManager *manager = GIMP_UI_MANAGER (object);
225
226 if (manager->name)
227 {
228 GimpUIManagerClass *manager_class;
229 GList *list;
230
231 manager_class = GIMP_UI_MANAGER_GET_CLASS (object);
232
233 list = g_hash_table_lookup (manager_class->managers, manager->name);
234
235 if (list)
236 {
237 list = g_list_remove (list, manager);
238
239 if (list)
240 g_hash_table_replace (manager_class->managers,
241 g_strdup (manager->name), list);
242 else
243 g_hash_table_remove (manager_class->managers, manager->name);
244 }
245 }
246
247 G_OBJECT_CLASS (parent_class)->dispose (object);
248 }
249
250 static void
gimp_ui_manager_finalize(GObject * object)251 gimp_ui_manager_finalize (GObject *object)
252 {
253 GimpUIManager *manager = GIMP_UI_MANAGER (object);
254 GList *list;
255
256 for (list = manager->registered_uis; list; list = g_list_next (list))
257 {
258 GimpUIManagerUIEntry *entry = list->data;
259
260 g_free (entry->ui_path);
261 g_free (entry->basename);
262
263 if (entry->widget)
264 g_object_unref (entry->widget);
265
266 g_slice_free (GimpUIManagerUIEntry, entry);
267 }
268
269 g_clear_pointer (&manager->registered_uis, g_list_free);
270 g_clear_pointer (&manager->name, g_free);
271
272 G_OBJECT_CLASS (parent_class)->finalize (object);
273 }
274
275 static void
gimp_ui_manager_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)276 gimp_ui_manager_set_property (GObject *object,
277 guint prop_id,
278 const GValue *value,
279 GParamSpec *pspec)
280 {
281 GimpUIManager *manager = GIMP_UI_MANAGER (object);
282
283 switch (prop_id)
284 {
285 case PROP_NAME:
286 g_free (manager->name);
287 manager->name = g_value_dup_string (value);
288 break;
289
290 case PROP_GIMP:
291 manager->gimp = g_value_get_object (value);
292 break;
293
294 default:
295 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
296 break;
297 }
298 }
299
300 static void
gimp_ui_manager_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)301 gimp_ui_manager_get_property (GObject *object,
302 guint prop_id,
303 GValue *value,
304 GParamSpec *pspec)
305 {
306 GimpUIManager *manager = GIMP_UI_MANAGER (object);
307
308 switch (prop_id)
309 {
310 case PROP_NAME:
311 g_value_set_string (value, manager->name);
312 break;
313
314 case PROP_GIMP:
315 g_value_set_object (value, manager->gimp);
316 break;
317
318 default:
319 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
320 break;
321 }
322 }
323
324 static void
gimp_ui_manager_connect_proxy(GtkUIManager * manager,GtkAction * action,GtkWidget * proxy)325 gimp_ui_manager_connect_proxy (GtkUIManager *manager,
326 GtkAction *action,
327 GtkWidget *proxy)
328 {
329 g_object_set_qdata (G_OBJECT (proxy), GIMP_HELP_ID,
330 g_object_get_qdata (G_OBJECT (action), GIMP_HELP_ID));
331
332 if (GTK_IS_MENU_ITEM (proxy))
333 {
334 g_signal_connect (proxy, "select",
335 G_CALLBACK (gimp_ui_manager_menu_item_select),
336 manager);
337 g_signal_connect (proxy, "deselect",
338 G_CALLBACK (gimp_ui_manager_menu_item_deselect),
339 manager);
340
341 g_signal_connect_after (proxy, "realize",
342 G_CALLBACK (gimp_ui_manager_item_realize),
343 manager);
344 }
345 }
346
347 static GtkWidget *
gimp_ui_manager_get_widget_impl(GtkUIManager * manager,const gchar * path)348 gimp_ui_manager_get_widget_impl (GtkUIManager *manager,
349 const gchar *path)
350 {
351 GimpUIManagerUIEntry *entry;
352
353 entry = gimp_ui_manager_entry_ensure (GIMP_UI_MANAGER (manager), path);
354
355 if (entry)
356 {
357 if (! strcmp (entry->ui_path, path))
358 return entry->widget;
359
360 return GTK_UI_MANAGER_CLASS (parent_class)->get_widget (manager, path);
361 }
362
363 return NULL;
364 }
365
366 static GtkAction *
gimp_ui_manager_get_action_impl(GtkUIManager * manager,const gchar * path)367 gimp_ui_manager_get_action_impl (GtkUIManager *manager,
368 const gchar *path)
369 {
370 if (gimp_ui_manager_entry_ensure (GIMP_UI_MANAGER (manager), path))
371 return GTK_UI_MANAGER_CLASS (parent_class)->get_action (manager, path);
372
373 return NULL;
374 }
375
376 static void
gimp_ui_manager_real_update(GimpUIManager * manager,gpointer update_data)377 gimp_ui_manager_real_update (GimpUIManager *manager,
378 gpointer update_data)
379 {
380 GList *list;
381
382 for (list = gimp_ui_manager_get_action_groups (manager);
383 list;
384 list = g_list_next (list))
385 {
386 gimp_action_group_update (list->data, update_data);
387 }
388 }
389
390 /**
391 * gimp_ui_manager_new:
392 * @gimp: the @Gimp instance this ui manager belongs to
393 * @name: the UI manager's name.
394 *
395 * Creates a new #GimpUIManager object.
396 *
397 * Returns: the new #GimpUIManager
398 */
399 GimpUIManager *
gimp_ui_manager_new(Gimp * gimp,const gchar * name)400 gimp_ui_manager_new (Gimp *gimp,
401 const gchar *name)
402 {
403 GimpUIManager *manager;
404
405 g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
406
407 manager = g_object_new (GIMP_TYPE_UI_MANAGER,
408 "name", name,
409 "gimp", gimp,
410 NULL);
411
412 return manager;
413 }
414
415 GList *
gimp_ui_managers_from_name(const gchar * name)416 gimp_ui_managers_from_name (const gchar *name)
417 {
418 GimpUIManagerClass *manager_class;
419 GList *list;
420
421 g_return_val_if_fail (name != NULL, NULL);
422
423 manager_class = g_type_class_ref (GIMP_TYPE_UI_MANAGER);
424
425 list = g_hash_table_lookup (manager_class->managers, name);
426
427 g_type_class_unref (manager_class);
428
429 return list;
430 }
431
432 void
gimp_ui_manager_update(GimpUIManager * manager,gpointer update_data)433 gimp_ui_manager_update (GimpUIManager *manager,
434 gpointer update_data)
435 {
436 g_return_if_fail (GIMP_IS_UI_MANAGER (manager));
437
438 g_signal_emit (manager, manager_signals[UPDATE], 0, update_data);
439 }
440
441 void
gimp_ui_manager_insert_action_group(GimpUIManager * manager,GimpActionGroup * group,gint pos)442 gimp_ui_manager_insert_action_group (GimpUIManager *manager,
443 GimpActionGroup *group,
444 gint pos)
445 {
446 gtk_ui_manager_insert_action_group ((GtkUIManager *) manager,
447 (GtkActionGroup *) group,
448 pos);
449 }
450
451 GimpActionGroup *
gimp_ui_manager_get_action_group(GimpUIManager * manager,const gchar * name)452 gimp_ui_manager_get_action_group (GimpUIManager *manager,
453 const gchar *name)
454 {
455 GList *list;
456
457 g_return_val_if_fail (GIMP_IS_UI_MANAGER (manager), NULL);
458 g_return_val_if_fail (name != NULL, NULL);
459
460 for (list = gimp_ui_manager_get_action_groups (manager);
461 list;
462 list = g_list_next (list))
463 {
464 GimpActionGroup *group = list->data;
465
466 if (! strcmp (name, gimp_action_group_get_name (group)))
467 return group;
468 }
469
470 return NULL;
471 }
472
473 GList *
gimp_ui_manager_get_action_groups(GimpUIManager * manager)474 gimp_ui_manager_get_action_groups (GimpUIManager *manager)
475 {
476 return gtk_ui_manager_get_action_groups ((GtkUIManager *) manager);
477 }
478
479 GtkAccelGroup *
gimp_ui_manager_get_accel_group(GimpUIManager * manager)480 gimp_ui_manager_get_accel_group (GimpUIManager *manager)
481 {
482 return gtk_ui_manager_get_accel_group ((GtkUIManager *) manager);
483 }
484
485 GtkWidget *
gimp_ui_manager_get_widget(GimpUIManager * manager,const gchar * path)486 gimp_ui_manager_get_widget (GimpUIManager *manager,
487 const gchar *path)
488 {
489 return gtk_ui_manager_get_widget ((GtkUIManager *) manager, path);
490 }
491
492 gchar *
gimp_ui_manager_get_ui(GimpUIManager * manager)493 gimp_ui_manager_get_ui (GimpUIManager *manager)
494 {
495 return gtk_ui_manager_get_ui ((GtkUIManager *) manager);
496 }
497
498 guint
gimp_ui_manager_new_merge_id(GimpUIManager * manager)499 gimp_ui_manager_new_merge_id (GimpUIManager *manager)
500 {
501 return gtk_ui_manager_new_merge_id ((GtkUIManager *) manager);
502 }
503
504 void
gimp_ui_manager_add_ui(GimpUIManager * manager,guint merge_id,const gchar * path,const gchar * name,const gchar * action,GtkUIManagerItemType type,gboolean top)505 gimp_ui_manager_add_ui (GimpUIManager *manager,
506 guint merge_id,
507 const gchar *path,
508 const gchar *name,
509 const gchar *action,
510 GtkUIManagerItemType type,
511 gboolean top)
512 {
513 gtk_ui_manager_add_ui ((GtkUIManager *) manager, merge_id,
514 path, name, action, type, top);
515 }
516
517 void
gimp_ui_manager_remove_ui(GimpUIManager * manager,guint merge_id)518 gimp_ui_manager_remove_ui (GimpUIManager *manager,
519 guint merge_id)
520 {
521 gtk_ui_manager_remove_ui ((GtkUIManager *) manager, merge_id);
522 }
523
524 void
gimp_ui_manager_ensure_update(GimpUIManager * manager)525 gimp_ui_manager_ensure_update (GimpUIManager *manager)
526 {
527 gtk_ui_manager_ensure_update ((GtkUIManager *) manager);
528 }
529
530 GimpAction *
gimp_ui_manager_get_action(GimpUIManager * manager,const gchar * path)531 gimp_ui_manager_get_action (GimpUIManager *manager,
532 const gchar *path)
533 {
534 return (GimpAction *) gtk_ui_manager_get_action ((GtkUIManager *) manager,
535 path);
536 }
537
538 GimpAction *
gimp_ui_manager_find_action(GimpUIManager * manager,const gchar * group_name,const gchar * action_name)539 gimp_ui_manager_find_action (GimpUIManager *manager,
540 const gchar *group_name,
541 const gchar *action_name)
542 {
543 GimpActionGroup *group;
544 GimpAction *action = NULL;
545
546 g_return_val_if_fail (GIMP_IS_UI_MANAGER (manager), NULL);
547 g_return_val_if_fail (action_name != NULL, NULL);
548
549 if (group_name)
550 {
551 group = gimp_ui_manager_get_action_group (manager, group_name);
552
553 if (group)
554 action = gimp_action_group_get_action (group, action_name);
555 }
556 else
557 {
558 GList *list;
559
560 for (list = gimp_ui_manager_get_action_groups (manager);
561 list;
562 list = g_list_next (list))
563 {
564 group = list->data;
565
566 action = gimp_action_group_get_action (group, action_name);
567
568 if (action)
569 break;
570 }
571 }
572
573 return action;
574 }
575
576 gboolean
gimp_ui_manager_activate_action(GimpUIManager * manager,const gchar * group_name,const gchar * action_name)577 gimp_ui_manager_activate_action (GimpUIManager *manager,
578 const gchar *group_name,
579 const gchar *action_name)
580 {
581 GimpAction *action;
582
583 g_return_val_if_fail (GIMP_IS_UI_MANAGER (manager), FALSE);
584 g_return_val_if_fail (action_name != NULL, FALSE);
585
586 action = gimp_ui_manager_find_action (manager, group_name, action_name);
587
588 if (action)
589 gimp_action_activate (action);
590
591 return (action != NULL);
592 }
593
594 gboolean
gimp_ui_manager_toggle_action(GimpUIManager * manager,const gchar * group_name,const gchar * action_name,gboolean active)595 gimp_ui_manager_toggle_action (GimpUIManager *manager,
596 const gchar *group_name,
597 const gchar *action_name,
598 gboolean active)
599 {
600 GimpAction *action;
601
602 g_return_val_if_fail (GIMP_IS_UI_MANAGER (manager), FALSE);
603 g_return_val_if_fail (action_name != NULL, FALSE);
604
605 action = gimp_ui_manager_find_action (manager, group_name, action_name);
606
607 if (GIMP_IS_TOGGLE_ACTION (action))
608 gimp_toggle_action_set_active (GIMP_TOGGLE_ACTION (action),
609 active ? TRUE : FALSE);
610
611 return GIMP_IS_TOGGLE_ACTION (action);
612 }
613
614 void
gimp_ui_manager_ui_register(GimpUIManager * manager,const gchar * ui_path,const gchar * basename,GimpUIManagerSetupFunc setup_func)615 gimp_ui_manager_ui_register (GimpUIManager *manager,
616 const gchar *ui_path,
617 const gchar *basename,
618 GimpUIManagerSetupFunc setup_func)
619 {
620 GimpUIManagerUIEntry *entry;
621
622 g_return_if_fail (GIMP_IS_UI_MANAGER (manager));
623 g_return_if_fail (ui_path != NULL);
624 g_return_if_fail (basename != NULL);
625 g_return_if_fail (gimp_ui_manager_entry_get (manager, ui_path) == NULL);
626
627 entry = g_slice_new0 (GimpUIManagerUIEntry);
628
629 entry->ui_path = g_strdup (ui_path);
630 entry->basename = g_strdup (basename);
631 entry->setup_func = setup_func;
632 entry->merge_id = 0;
633 entry->widget = NULL;
634
635 manager->registered_uis = g_list_prepend (manager->registered_uis, entry);
636 }
637
638
639 typedef struct
640 {
641 guint x;
642 guint y;
643 } MenuPos;
644
645 static void
menu_pos_free(MenuPos * pos)646 menu_pos_free (MenuPos *pos)
647 {
648 g_slice_free (MenuPos, pos);
649 }
650
651 void
gimp_ui_manager_ui_popup(GimpUIManager * manager,const gchar * ui_path,GtkWidget * parent,GimpMenuPositionFunc position_func,gpointer position_data,GDestroyNotify popdown_func,gpointer popdown_data)652 gimp_ui_manager_ui_popup (GimpUIManager *manager,
653 const gchar *ui_path,
654 GtkWidget *parent,
655 GimpMenuPositionFunc position_func,
656 gpointer position_data,
657 GDestroyNotify popdown_func,
658 gpointer popdown_data)
659 {
660 GtkWidget *widget;
661 GdkEvent *current_event;
662 gint x, y;
663 guint button;
664 guint32 activate_time;
665 MenuPos *menu_pos;
666
667 g_return_if_fail (GIMP_IS_UI_MANAGER (manager));
668 g_return_if_fail (ui_path != NULL);
669 g_return_if_fail (parent == NULL || GTK_IS_WIDGET (parent));
670
671 widget = gimp_ui_manager_get_widget (manager, ui_path);
672
673 if (GTK_IS_MENU_ITEM (widget))
674 widget = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
675
676 if (! widget)
677 return;
678
679 g_return_if_fail (GTK_IS_MENU (widget));
680
681 if (! position_func)
682 {
683 position_func = gimp_ui_manager_menu_position;
684 position_data = parent;
685 }
686
687 (* position_func) (GTK_MENU (widget), &x, &y, position_data);
688
689 current_event = gtk_get_current_event ();
690
691 if (current_event && current_event->type == GDK_BUTTON_PRESS)
692 {
693 GdkEventButton *bevent = (GdkEventButton *) current_event;
694
695 button = bevent->button;
696 activate_time = bevent->time;
697 }
698 else
699 {
700 button = 0;
701 activate_time = 0;
702 }
703
704 if (current_event)
705 gdk_event_free (current_event);
706
707 menu_pos = g_object_get_data (G_OBJECT (widget), "menu-pos");
708
709 if (! menu_pos)
710 {
711 menu_pos = g_slice_new0 (MenuPos);
712 g_object_set_data_full (G_OBJECT (widget), "menu-pos", menu_pos,
713 (GDestroyNotify) menu_pos_free);
714 }
715
716 menu_pos->x = x;
717 menu_pos->y = y;
718
719 if (popdown_func && popdown_data)
720 {
721 g_object_set_data_full (G_OBJECT (manager), "popdown-data",
722 popdown_data, popdown_func);
723 g_signal_connect (widget, "selection-done",
724 G_CALLBACK (gimp_ui_manager_delete_popdown_data),
725 manager);
726 }
727
728 gtk_menu_popup (GTK_MENU (widget),
729 NULL, NULL,
730 gimp_ui_manager_menu_pos, menu_pos,
731 button, activate_time);
732 }
733
734
735 /* private functions */
736
737 static GimpUIManagerUIEntry *
gimp_ui_manager_entry_get(GimpUIManager * manager,const gchar * ui_path)738 gimp_ui_manager_entry_get (GimpUIManager *manager,
739 const gchar *ui_path)
740 {
741 GList *list;
742 gchar *path;
743
744 path = g_strdup (ui_path);
745
746 if (strlen (path) > 1)
747 {
748 gchar *p = strchr (path + 1, '/');
749
750 if (p)
751 *p = '\0';
752 }
753
754 for (list = manager->registered_uis; list; list = g_list_next (list))
755 {
756 GimpUIManagerUIEntry *entry = list->data;
757
758 if (! strcmp (entry->ui_path, path))
759 {
760 g_free (path);
761
762 return entry;
763 }
764 }
765
766 g_free (path);
767
768 return NULL;
769 }
770
771 static gboolean
gimp_ui_manager_entry_load(GimpUIManager * manager,GimpUIManagerUIEntry * entry,GError ** error)772 gimp_ui_manager_entry_load (GimpUIManager *manager,
773 GimpUIManagerUIEntry *entry,
774 GError **error)
775 {
776 gchar *filename = NULL;
777 const gchar *menus_path_override = g_getenv ("GIMP_TESTING_MENUS_PATH");
778
779 /* In order for test cases to be able to run without GIMP being
780 * installed yet, allow them to override the menus directory to the
781 * menus dir in the source root
782 */
783 if (menus_path_override)
784 {
785 GList *path = gimp_path_parse (menus_path_override, 2, FALSE, NULL);
786 GList *list;
787
788 for (list = path; list; list = g_list_next (list))
789 {
790 filename = g_build_filename (list->data, entry->basename, NULL);
791
792 if (! list->next ||
793 g_file_test (filename, G_FILE_TEST_EXISTS))
794 break;
795
796 g_free (filename);
797 }
798
799 g_list_free_full (path, g_free);
800 }
801 else
802 {
803 filename = g_build_filename (gimp_data_directory (), "menus",
804 entry->basename, NULL);
805 }
806
807 if (manager->gimp->be_verbose)
808 g_print ("loading menu '%s' for %s\n",
809 gimp_filename_to_utf8 (filename), entry->ui_path);
810
811 entry->merge_id = gtk_ui_manager_add_ui_from_file (GTK_UI_MANAGER (manager),
812 filename, error);
813
814 g_free (filename);
815
816 if (! entry->merge_id)
817 return FALSE;
818
819 return TRUE;
820 }
821
822 static GimpUIManagerUIEntry *
gimp_ui_manager_entry_ensure(GimpUIManager * manager,const gchar * path)823 gimp_ui_manager_entry_ensure (GimpUIManager *manager,
824 const gchar *path)
825 {
826 GimpUIManagerUIEntry *entry;
827
828 entry = gimp_ui_manager_entry_get (manager, path);
829
830 if (! entry)
831 {
832 g_warning ("%s: no entry registered for \"%s\"", G_STRFUNC, path);
833 return NULL;
834 }
835
836 if (! entry->merge_id)
837 {
838 GError *error = NULL;
839
840 if (! gimp_ui_manager_entry_load (manager, entry, &error))
841 {
842 if (error->domain == G_FILE_ERROR &&
843 error->code == G_FILE_ERROR_EXIST)
844 {
845 gimp_message (manager->gimp, NULL, GIMP_MESSAGE_ERROR,
846 "%s\n\n%s\n\n%s",
847 _("Your GIMP installation is incomplete:"),
848 error->message,
849 _("Please make sure the menu XML files are "
850 "correctly installed."));
851 }
852 else
853 {
854 gimp_message (manager->gimp, NULL, GIMP_MESSAGE_ERROR,
855 _("There was an error parsing the menu definition "
856 "from %s: %s"),
857 gimp_filename_to_utf8 (entry->basename),
858 error->message);
859 }
860
861 g_clear_error (&error);
862 return NULL;
863 }
864 }
865
866 if (! entry->widget)
867 {
868 GtkUIManager *gtk_manager = GTK_UI_MANAGER (manager);
869
870 entry->widget =
871 GTK_UI_MANAGER_CLASS (parent_class)->get_widget (gtk_manager,
872 entry->ui_path);
873
874 if (entry->widget)
875 {
876 g_object_ref (entry->widget);
877
878 /* take ownership of popup menus */
879 if (GTK_IS_MENU (entry->widget))
880 {
881 g_object_ref_sink (entry->widget);
882 g_object_unref (entry->widget);
883 }
884
885 if (entry->setup_func)
886 entry->setup_func (manager, entry->ui_path);
887 }
888 else
889 {
890 g_warning ("%s: \"%s\" does not contain registered toplevel "
891 "widget \"%s\"",
892 G_STRFUNC, entry->basename, entry->ui_path);
893 return NULL;
894 }
895 }
896
897 return entry;
898 }
899
900 static void
gimp_ui_manager_menu_position(GtkMenu * menu,gint * x,gint * y,gpointer data)901 gimp_ui_manager_menu_position (GtkMenu *menu,
902 gint *x,
903 gint *y,
904 gpointer data)
905 {
906 GdkScreen *screen;
907 GtkRequisition requisition;
908 GdkRectangle rect;
909 gint monitor;
910 gint pointer_x;
911 gint pointer_y;
912
913 g_return_if_fail (GTK_IS_MENU (menu));
914 g_return_if_fail (x != NULL);
915 g_return_if_fail (y != NULL);
916 g_return_if_fail (GTK_IS_WIDGET (data));
917
918 gdk_display_get_pointer (gtk_widget_get_display (GTK_WIDGET (data)),
919 &screen, &pointer_x, &pointer_y, NULL);
920
921 monitor = gdk_screen_get_monitor_at_point (screen, pointer_x, pointer_y);
922 gdk_screen_get_monitor_workarea (screen, monitor, &rect);
923
924 gtk_menu_set_screen (menu, screen);
925
926 gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
927
928 if (gtk_widget_get_direction (GTK_WIDGET (menu)) == GTK_TEXT_DIR_RTL)
929 {
930 *x = pointer_x - 2 - requisition.width;
931
932 if (*x < rect.x)
933 *x = pointer_x + 2;
934 }
935 else
936 {
937 *x = pointer_x + 2;
938
939 if (*x + requisition.width > rect.x + rect.width)
940 *x = pointer_x - 2 - requisition.width;
941 }
942
943 *y = pointer_y + 2;
944
945 if (*y + requisition.height > rect.y + rect.height)
946 *y = pointer_y - 2 - requisition.height;
947
948 if (*x < rect.x) *x = rect.x;
949 if (*y < rect.y) *y = rect.y;
950 }
951
952 static void
gimp_ui_manager_menu_pos(GtkMenu * menu,gint * x,gint * y,gboolean * push_in,gpointer data)953 gimp_ui_manager_menu_pos (GtkMenu *menu,
954 gint *x,
955 gint *y,
956 gboolean *push_in,
957 gpointer data)
958 {
959 MenuPos *menu_pos = data;
960
961 *x = menu_pos->x;
962 *y = menu_pos->y;
963 }
964
965 static void
gimp_ui_manager_delete_popdown_data(GtkWidget * widget,GimpUIManager * manager)966 gimp_ui_manager_delete_popdown_data (GtkWidget *widget,
967 GimpUIManager *manager)
968 {
969 g_signal_handlers_disconnect_by_func (widget,
970 gimp_ui_manager_delete_popdown_data,
971 manager);
972 g_object_set_data (G_OBJECT (manager), "popdown-data", NULL);
973 }
974
975 static void
gimp_ui_manager_item_realize(GtkWidget * widget,GimpUIManager * manager)976 gimp_ui_manager_item_realize (GtkWidget *widget,
977 GimpUIManager *manager)
978 {
979 GtkWidget *menu;
980 GtkWidget *submenu;
981
982 g_signal_handlers_disconnect_by_func (widget,
983 gimp_ui_manager_item_realize,
984 manager);
985
986 menu = gtk_widget_get_parent (widget);
987
988 if (GTK_IS_MENU_SHELL (menu))
989 {
990 static GQuark quark_key_press_connected = 0;
991
992 if (! quark_key_press_connected)
993 quark_key_press_connected =
994 g_quark_from_static_string ("gimp-menu-item-key-press-connected");
995
996 if (! GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (menu),
997 quark_key_press_connected)))
998 {
999 g_signal_connect (menu, "key-press-event",
1000 G_CALLBACK (gimp_ui_manager_item_key_press),
1001 manager);
1002
1003 g_object_set_qdata (G_OBJECT (menu),
1004 quark_key_press_connected,
1005 GINT_TO_POINTER (TRUE));
1006 }
1007 }
1008
1009 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget));
1010
1011 if (submenu)
1012 g_object_set_qdata (G_OBJECT (submenu), GIMP_HELP_ID,
1013 g_object_get_qdata (G_OBJECT (widget),
1014 GIMP_HELP_ID));
1015 }
1016
1017 static void
gimp_ui_manager_menu_item_select(GtkWidget * widget,GimpUIManager * manager)1018 gimp_ui_manager_menu_item_select (GtkWidget *widget,
1019 GimpUIManager *manager)
1020 {
1021 GtkAction *action =
1022 gtk_activatable_get_related_action (GTK_ACTIVATABLE (widget));
1023
1024 if (action)
1025 {
1026 const gchar *tooltip = gimp_action_get_tooltip (GIMP_ACTION (action));
1027
1028 if (tooltip)
1029 g_signal_emit (manager, manager_signals[SHOW_TOOLTIP], 0, tooltip);
1030 }
1031 }
1032
1033 static void
gimp_ui_manager_menu_item_deselect(GtkWidget * widget,GimpUIManager * manager)1034 gimp_ui_manager_menu_item_deselect (GtkWidget *widget,
1035 GimpUIManager *manager)
1036 {
1037 g_signal_emit (manager, manager_signals[HIDE_TOOLTIP], 0);
1038 }
1039
1040 static gboolean
gimp_ui_manager_item_key_press(GtkWidget * widget,GdkEventKey * kevent,GimpUIManager * manager)1041 gimp_ui_manager_item_key_press (GtkWidget *widget,
1042 GdkEventKey *kevent,
1043 GimpUIManager *manager)
1044 {
1045 gchar *help_id = NULL;
1046
1047 while (! help_id && GTK_IS_MENU_SHELL (widget))
1048 {
1049 GtkWidget *menu_item = GTK_MENU_SHELL (widget)->active_menu_item;
1050
1051 if (! menu_item && GTK_IS_MENU (widget))
1052 {
1053 GtkWidget *parent = gtk_widget_get_parent (widget);
1054 GdkWindow *window = gtk_widget_get_window (parent);
1055
1056 if (window)
1057 {
1058 gint x, y;
1059
1060 gdk_window_get_pointer (window, &x, &y, NULL);
1061 menu_item = find_widget_under_pointer (window, &x, &y);
1062
1063 if (menu_item && ! GTK_IS_MENU_ITEM (menu_item))
1064 {
1065 menu_item = gtk_widget_get_ancestor (menu_item,
1066 GTK_TYPE_MENU_ITEM);
1067
1068 if (! GTK_IS_MENU_ITEM (menu_item))
1069 menu_item = NULL;
1070 }
1071 }
1072 }
1073
1074 /* first, get the help page from the item...
1075 */
1076 if (menu_item)
1077 {
1078 help_id = g_object_get_qdata (G_OBJECT (menu_item), GIMP_HELP_ID);
1079
1080 if (help_id && ! strlen (help_id))
1081 help_id = NULL;
1082 }
1083
1084 /* ...then try the parent menu...
1085 */
1086 if (! help_id)
1087 {
1088 help_id = g_object_get_qdata (G_OBJECT (widget), GIMP_HELP_ID);
1089
1090 if (help_id && ! strlen (help_id))
1091 help_id = NULL;
1092 }
1093
1094 /* ...finally try the menu's parent (if any)
1095 */
1096 if (! help_id)
1097 {
1098 menu_item = NULL;
1099
1100 if (GTK_IS_MENU (widget))
1101 menu_item = gtk_menu_get_attach_widget (GTK_MENU (widget));
1102
1103 if (! menu_item)
1104 break;
1105
1106 widget = gtk_widget_get_parent (menu_item);
1107
1108 if (! widget)
1109 break;
1110 }
1111 }
1112
1113 /* For any valid accelerator key except F1, continue with the
1114 * standard GtkMenuShell callback and assign a new shortcut, but
1115 * don't assign a shortcut to the help menu entries ...
1116 */
1117 if (kevent->keyval != GDK_KEY_F1)
1118 {
1119 if (help_id &&
1120 gtk_accelerator_valid (kevent->keyval, 0) &&
1121 (strcmp (help_id, GIMP_HELP_HELP) == 0 ||
1122 strcmp (help_id, GIMP_HELP_HELP_CONTEXT) == 0))
1123 {
1124 return TRUE;
1125 }
1126
1127 return FALSE;
1128 }
1129
1130 /* ...finally, if F1 was pressed over any menu, show its help page... */
1131
1132 if (help_id)
1133 {
1134 gchar *help_domain = NULL;
1135 gchar *help_string = NULL;
1136 gchar *domain_separator;
1137
1138 help_id = g_strdup (help_id);
1139
1140 domain_separator = strchr (help_id, '?');
1141
1142 if (domain_separator)
1143 {
1144 *domain_separator = '\0';
1145
1146 help_domain = g_strdup (help_id);
1147 help_string = g_strdup (domain_separator + 1);
1148 }
1149 else
1150 {
1151 help_string = g_strdup (help_id);
1152 }
1153
1154 gimp_help (manager->gimp, NULL, help_domain, help_string);
1155
1156 g_free (help_domain);
1157 g_free (help_string);
1158 g_free (help_id);
1159 }
1160
1161 return TRUE;
1162 }
1163
1164
1165 /* Stuff below taken from gtktooltip.c
1166 */
1167
1168 /* FIXME: remove this crack as soon as a GTK+ widget_under_pointer() is available */
1169
1170 struct ChildLocation
1171 {
1172 GtkWidget *child;
1173 GtkWidget *container;
1174
1175 gint x;
1176 gint y;
1177 };
1178
1179 static void
child_location_foreach(GtkWidget * child,gpointer data)1180 child_location_foreach (GtkWidget *child,
1181 gpointer data)
1182 {
1183 gint x, y;
1184 struct ChildLocation *child_loc = data;
1185
1186 /* Ignore invisible widgets */
1187 if (! gtk_widget_is_drawable (child))
1188 return;
1189
1190 /* (child_loc->x, child_loc->y) are relative to
1191 * child_loc->container's allocation.
1192 */
1193
1194 if (! child_loc->child &&
1195 gtk_widget_translate_coordinates (child_loc->container, child,
1196 child_loc->x, child_loc->y,
1197 &x, &y))
1198 {
1199 GtkAllocation child_allocation;
1200
1201 gtk_widget_get_allocation (child, &child_allocation);
1202
1203 #ifdef DEBUG_TOOLTIP
1204 g_print ("candidate: %s alloc=[(%d,%d) %dx%d] (%d, %d)->(%d, %d)\n",
1205 gtk_widget_get_name (child),
1206 child_allocation.x,
1207 child_allocation.y,
1208 child_allocation.width,
1209 child_allocation.height,
1210 child_loc->x, child_loc->y,
1211 x, y);
1212 #endif /* DEBUG_TOOLTIP */
1213
1214 /* (x, y) relative to child's allocation. */
1215 if (x >= 0 && x < child_allocation.width
1216 && y >= 0 && y < child_allocation.height)
1217 {
1218 if (GTK_IS_CONTAINER (child))
1219 {
1220 struct ChildLocation tmp = { NULL, NULL, 0, 0 };
1221
1222 /* Take (x, y) relative the child's allocation and
1223 * recurse.
1224 */
1225 tmp.x = x;
1226 tmp.y = y;
1227 tmp.container = child;
1228
1229 gtk_container_forall (GTK_CONTAINER (child),
1230 child_location_foreach, &tmp);
1231
1232 if (tmp.child)
1233 child_loc->child = tmp.child;
1234 else
1235 child_loc->child = child;
1236 }
1237 else
1238 {
1239 child_loc->child = child;
1240 }
1241 }
1242 }
1243 }
1244
1245 /* Translates coordinates from dest_widget->window relative (src_x, src_y),
1246 * to allocation relative (dest_x, dest_y) of dest_widget.
1247 */
1248 static void
window_to_alloc(GtkWidget * dest_widget,gint src_x,gint src_y,gint * dest_x,gint * dest_y)1249 window_to_alloc (GtkWidget *dest_widget,
1250 gint src_x,
1251 gint src_y,
1252 gint *dest_x,
1253 gint *dest_y)
1254 {
1255 GtkAllocation dest_allocation;
1256
1257 gtk_widget_get_allocation (dest_widget, &dest_allocation);
1258
1259 /* Translate from window relative to allocation relative */
1260 if (gtk_widget_get_has_window (dest_widget) &&
1261 gtk_widget_get_parent (dest_widget))
1262 {
1263 gint wx, wy;
1264
1265 gdk_window_get_position (gtk_widget_get_window (dest_widget), &wx, &wy);
1266
1267 /* Offset coordinates if widget->window is smaller than
1268 * widget->allocation.
1269 */
1270 src_x += wx - dest_allocation.x;
1271 src_y += wy - dest_allocation.y;
1272 }
1273 else
1274 {
1275 src_x -= dest_allocation.x;
1276 src_y -= dest_allocation.y;
1277 }
1278
1279 if (dest_x)
1280 *dest_x = src_x;
1281 if (dest_y)
1282 *dest_y = src_y;
1283 }
1284
1285 static GtkWidget *
find_widget_under_pointer(GdkWindow * window,gint * x,gint * y)1286 find_widget_under_pointer (GdkWindow *window,
1287 gint *x,
1288 gint *y)
1289 {
1290 GtkWidget *event_widget;
1291 struct ChildLocation child_loc = { NULL, NULL, 0, 0 };
1292
1293 gdk_window_get_user_data (window, (void **)&event_widget);
1294
1295 if (! event_widget)
1296 return NULL;
1297
1298 #ifdef DEBUG_TOOLTIP
1299 g_print ("event window %p (belonging to %p (%s)) (%d, %d)\n",
1300 window, event_widget, gtk_widget_get_name (event_widget),
1301 *x, *y);
1302 #endif
1303
1304 /* Coordinates are relative to event window */
1305 child_loc.x = *x;
1306 child_loc.y = *y;
1307
1308 /* We go down the window hierarchy to the widget->window,
1309 * coordinates stay relative to the current window.
1310 * We end up with window == widget->window, coordinates relative to that.
1311 */
1312 while (window && window != gtk_widget_get_window (event_widget))
1313 {
1314 gint px, py;
1315
1316 gdk_window_get_position (window, &px, &py);
1317 child_loc.x += px;
1318 child_loc.y += py;
1319
1320 window = gdk_window_get_parent (window);
1321 }
1322
1323 /* Failing to find widget->window can happen for e.g. a detached handle box;
1324 * chaining ::query-tooltip up to its parent probably makes little sense,
1325 * and users better implement tooltips on handle_box->child.
1326 * so we simply ignore the event for tooltips here.
1327 */
1328 if (!window)
1329 return NULL;
1330
1331 /* Convert the window relative coordinates to allocation
1332 * relative coordinates.
1333 */
1334 window_to_alloc (event_widget,
1335 child_loc.x, child_loc.y,
1336 &child_loc.x, &child_loc.y);
1337
1338 if (GTK_IS_CONTAINER (event_widget))
1339 {
1340 GtkWidget *container = event_widget;
1341
1342 child_loc.container = event_widget;
1343 child_loc.child = NULL;
1344
1345 gtk_container_forall (GTK_CONTAINER (event_widget),
1346 child_location_foreach, &child_loc);
1347
1348 /* Here we have a widget, with coordinates relative to
1349 * child_loc.container's allocation.
1350 */
1351
1352 if (child_loc.child)
1353 event_widget = child_loc.child;
1354 else if (child_loc.container)
1355 event_widget = child_loc.container;
1356
1357 /* Translate to event_widget's allocation */
1358 gtk_widget_translate_coordinates (container, event_widget,
1359 child_loc.x, child_loc.y,
1360 &child_loc.x, &child_loc.y);
1361
1362 }
1363
1364 /* We return (x, y) relative to the allocation of event_widget. */
1365 *x = child_loc.x;
1366 *y = child_loc.y;
1367
1368 return event_widget;
1369 }
1370