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