1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program 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
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <gegl.h>
21 #include <gtk/gtk.h>
22 #include <gdk/gdkkeysyms.h>
23 
24 #include "libgimpbase/gimpbase.h"
25 #include "libgimpwidgets/gimpwidgets.h"
26 
27 #include "actions-types.h"
28 
29 #include "config/gimpdisplayconfig.h"
30 #include "config/gimpguiconfig.h"
31 
32 #include "core/gimp.h"
33 #include "core/gimpimage.h"
34 #include "core/gimplist.h"
35 
36 #include "widgets/gimpaction.h"
37 #include "widgets/gimpactiongroup.h"
38 #include "widgets/gimpdialogfactory.h"
39 #include "widgets/gimpdock.h"
40 #include "widgets/gimpdockwindow.h"
41 #include "widgets/gimphelp-ids.h"
42 
43 #include "display/gimpdisplay.h"
44 #include "display/gimpdisplayshell.h"
45 
46 #include "dialogs/dialogs.h"
47 
48 #include "windows-actions.h"
49 #include "windows-commands.h"
50 
51 #include "gimp-intl.h"
52 
53 
54 static void  windows_actions_display_add               (GimpContainer     *container,
55                                                         GimpDisplay       *display,
56                                                         GimpActionGroup   *group);
57 static void  windows_actions_display_remove            (GimpContainer     *container,
58                                                         GimpDisplay       *display,
59                                                         GimpActionGroup   *group);
60 static void  windows_actions_display_reorder           (GimpContainer     *container,
61                                                         GimpDisplay       *display,
62                                                         gint               position,
63                                                         GimpActionGroup   *group);
64 static void  windows_actions_image_notify              (GimpDisplay       *display,
65                                                         const GParamSpec  *unused,
66                                                         GimpActionGroup   *group);
67 static void  windows_actions_title_notify              (GimpDisplayShell  *shell,
68                                                         const GParamSpec  *unused,
69                                                         GimpActionGroup   *group);
70 static void  windows_actions_update_display_accels     (GimpActionGroup   *group);
71 
72 static void  windows_actions_dock_window_added         (GimpDialogFactory *factory,
73                                                         GimpDockWindow    *dock_window,
74                                                         GimpActionGroup   *group);
75 static void  windows_actions_dock_window_removed       (GimpDialogFactory *factory,
76                                                         GimpDockWindow    *dock_window,
77                                                         GimpActionGroup   *group);
78 static void  windows_actions_dock_window_notify        (GimpDockWindow    *dock,
79                                                         const GParamSpec  *pspec,
80                                                         GimpActionGroup   *group);
81 static void  windows_actions_recent_add                (GimpContainer     *container,
82                                                         GimpSessionInfo   *info,
83                                                         GimpActionGroup   *group);
84 static void  windows_actions_recent_remove             (GimpContainer     *container,
85                                                         GimpSessionInfo   *info,
86                                                         GimpActionGroup   *group);
87 static void  windows_actions_single_window_mode_notify (GimpDisplayConfig *config,
88                                                         GParamSpec        *pspec,
89                                                         GimpActionGroup   *group);
90 
91 
92 /* The only reason we have "Tab" in the action entries below is to
93  * give away the hardcoded keyboard shortcut. If the user changes the
94  * shortcut to something else, both that shortcut and Tab will
95  * work. The reason we have the shortcut hardcoded is because
96  * gtk_accelerator_valid() returns FALSE for GDK_tab.
97  */
98 
99 static const GimpActionEntry windows_actions[] =
100 {
101   { "windows-menu",         NULL, NC_("windows-action",
102                                       "_Windows")               },
103   { "windows-docks-menu",   NULL, NC_("windows-action",
104                                       "_Recently Closed Docks") },
105   { "windows-dialogs-menu", NULL, NC_("windows-action",
106                                       "_Dockable Dialogs")      },
107 
108   { "windows-show-display-next", NULL,
109     NC_("windows-action", "Next Image"), "<alt>Tab",
110     NC_("windows-action", "Switch to the next image"),
111     windows_show_display_next_cmd_callback,
112     NULL },
113 
114   { "windows-show-display-previous", NULL,
115     NC_("windows-action", "Previous Image"), "<alt><shift>Tab",
116     NC_("windows-action", "Switch to the previous image"),
117     windows_show_display_previous_cmd_callback,
118     NULL },
119 
120   { "windows-tab-position",        NULL, NC_("windows-action",
121                                              "_Tabs Position")   },
122 };
123 
124 static const GimpToggleActionEntry windows_toggle_actions[] =
125 {
126   { "windows-hide-docks", NULL,
127     NC_("windows-action", "_Hide Docks"), "Tab",
128     NC_("windows-action", "When enabled, docks and other dialogs are hidden, leaving only image windows."),
129     windows_hide_docks_cmd_callback,
130     FALSE,
131     GIMP_HELP_WINDOWS_HIDE_DOCKS },
132 
133   { "windows-show-tabs", NULL,
134     NC_("windows-action", "_Show Tabs"), NULL,
135     NC_("windows-action", "When enabled, the image tabs bar is shown."),
136     windows_show_tabs_cmd_callback,
137     FALSE,
138     GIMP_HELP_WINDOWS_SHOW_TABS },
139 
140   { "windows-use-single-window-mode", NULL,
141     NC_("windows-action", "Single-Window _Mode"), NULL,
142     NC_("windows-action", "When enabled, GIMP is in a single-window mode."),
143     windows_use_single_window_mode_cmd_callback,
144     FALSE,
145     GIMP_HELP_WINDOWS_USE_SINGLE_WINDOW_MODE }
146 };
147 
148 static const GimpRadioActionEntry windows_tabs_position_actions[] =
149 {
150   { "windows-tabs-position-top", GIMP_ICON_GO_TOP,
151     NC_("windows-tabs-position-action", "_Top"), NULL,
152     NC_("windows-tabs-position-action", "Position the tabs on the top"),
153     GIMP_POSITION_TOP, GIMP_HELP_WINDOWS_TABS_POSITION },
154 
155   { "windows-tabs-position-bottom", GIMP_ICON_GO_BOTTOM,
156     NC_("windows-tabs-position-action", "_Bottom"), NULL,
157     NC_("windows-tabs-position-action", "Position the tabs on the bottom"),
158     GIMP_POSITION_BOTTOM, GIMP_HELP_WINDOWS_TABS_POSITION },
159 
160   { "windows-tabs-position-left", GIMP_ICON_GO_FIRST,
161     NC_("windows-tabs-position-action", "_Left"), NULL,
162     NC_("windows-tabs-position-action", "Position the tabs on the left"),
163     GIMP_POSITION_LEFT, GIMP_HELP_WINDOWS_TABS_POSITION },
164 
165   { "windows-tabs-position-right", GIMP_ICON_GO_LAST,
166     NC_("windows-tabs-position-action", "_Right"), NULL,
167     NC_("windows-tabs-position-action", "Position the tabs on the right"),
168     GIMP_POSITION_RIGHT, GIMP_HELP_WINDOWS_TABS_POSITION },
169 };
170 
171 void
windows_actions_setup(GimpActionGroup * group)172 windows_actions_setup (GimpActionGroup *group)
173 {
174   GList *list;
175 
176   gimp_action_group_add_actions (group, "windows-action",
177                                  windows_actions,
178                                  G_N_ELEMENTS (windows_actions));
179 
180   gimp_action_group_add_toggle_actions (group, "windows-action",
181                                         windows_toggle_actions,
182                                         G_N_ELEMENTS (windows_toggle_actions));
183 
184   gimp_action_group_add_radio_actions (group, "windows-tabs-position-action",
185                                        windows_tabs_position_actions,
186                                        G_N_ELEMENTS (windows_tabs_position_actions),
187                                        NULL, 0,
188                                        windows_set_tabs_position_cmd_callback);
189 
190   gimp_action_group_set_action_hide_empty (group, "windows-docks-menu", FALSE);
191 
192   g_signal_connect_object (group->gimp->displays, "add",
193                            G_CALLBACK (windows_actions_display_add),
194                            group, 0);
195   g_signal_connect_object (group->gimp->displays, "remove",
196                            G_CALLBACK (windows_actions_display_remove),
197                            group, 0);
198   g_signal_connect_object (group->gimp->displays, "reorder",
199                            G_CALLBACK (windows_actions_display_reorder),
200                            group, 0);
201 
202   for (list = gimp_get_display_iter (group->gimp);
203        list;
204        list = g_list_next (list))
205     {
206       GimpDisplay *display = list->data;
207 
208       windows_actions_display_add (group->gimp->displays, display, group);
209     }
210 
211   g_signal_connect_object (gimp_dialog_factory_get_singleton (), "dock-window-added",
212                            G_CALLBACK (windows_actions_dock_window_added),
213                            group, 0);
214   g_signal_connect_object (gimp_dialog_factory_get_singleton (), "dock-window-removed",
215                            G_CALLBACK (windows_actions_dock_window_removed),
216                            group, 0);
217 
218   for (list = gimp_dialog_factory_get_open_dialogs (gimp_dialog_factory_get_singleton ());
219        list;
220        list = g_list_next (list))
221     {
222       GimpDockWindow *dock_window = list->data;
223 
224       if (GIMP_IS_DOCK_WINDOW (dock_window))
225         windows_actions_dock_window_added (gimp_dialog_factory_get_singleton (),
226                                            dock_window,
227                                            group);
228     }
229 
230   g_signal_connect_object (global_recent_docks, "add",
231                            G_CALLBACK (windows_actions_recent_add),
232                            group, 0);
233   g_signal_connect_object (global_recent_docks, "remove",
234                            G_CALLBACK (windows_actions_recent_remove),
235                            group, 0);
236 
237   for (list = GIMP_LIST (global_recent_docks)->queue->head;
238        list;
239        list = g_list_next (list))
240     {
241       GimpSessionInfo *info = list->data;
242 
243       windows_actions_recent_add (global_recent_docks, info, group);
244     }
245 
246   g_signal_connect_object (group->gimp->config, "notify::single-window-mode",
247                            G_CALLBACK (windows_actions_single_window_mode_notify),
248                            group, 0);
249 }
250 
251 void
windows_actions_update(GimpActionGroup * group,gpointer data)252 windows_actions_update (GimpActionGroup *group,
253                         gpointer         data)
254 {
255   GimpGuiConfig *config = GIMP_GUI_CONFIG (group->gimp->config);
256   const gchar   *action = NULL;
257 
258 #define SET_ACTIVE(action,condition) \
259         gimp_action_group_set_action_active (group, action, (condition) != 0)
260 
261   SET_ACTIVE ("windows-use-single-window-mode", config->single_window_mode);
262   SET_ACTIVE ("windows-hide-docks", config->hide_docks);
263   SET_ACTIVE ("windows-show-tabs", config->show_tabs);
264 
265   switch (config->tabs_position)
266     {
267     case GIMP_POSITION_TOP:
268       action = "windows-tabs-position-top";
269       break;
270     case GIMP_POSITION_BOTTOM:
271       action = "windows-tabs-position-bottom";
272       break;
273     case GIMP_POSITION_LEFT:
274       action = "windows-tabs-position-left";
275       break;
276     case GIMP_POSITION_RIGHT:
277       action = "windows-tabs-position-right";
278       break;
279     default:
280       action = "windows-tabs-position-top";
281       break;
282     }
283 
284   gimp_action_group_set_action_active (group, action, TRUE);
285   gimp_action_group_set_action_sensitive (group, "windows-tab-position", config->single_window_mode);
286   gimp_action_group_set_action_sensitive (group, "windows-show-tabs", config->single_window_mode);
287 
288 #undef SET_ACTIVE
289 }
290 
291 gchar *
windows_actions_dock_window_to_action_name(GimpDockWindow * dock_window)292 windows_actions_dock_window_to_action_name (GimpDockWindow *dock_window)
293 {
294   return g_strdup_printf ("windows-dock-%04d",
295                           gimp_dock_window_get_id (dock_window));
296 }
297 
298 
299 /*  private functions  */
300 
301 static void
windows_actions_display_add(GimpContainer * container,GimpDisplay * display,GimpActionGroup * group)302 windows_actions_display_add (GimpContainer   *container,
303                              GimpDisplay     *display,
304                              GimpActionGroup *group)
305 {
306   GimpDisplayShell *shell = gimp_display_get_shell (display);
307 
308   g_signal_connect_object (display, "notify::image",
309                            G_CALLBACK (windows_actions_image_notify),
310                            group, 0);
311 
312   g_signal_connect_object (shell, "notify::title",
313                            G_CALLBACK (windows_actions_title_notify),
314                            group, 0);
315 
316   windows_actions_image_notify (display, NULL, group);
317 }
318 
319 static void
windows_actions_display_remove(GimpContainer * container,GimpDisplay * display,GimpActionGroup * group)320 windows_actions_display_remove (GimpContainer   *container,
321                                 GimpDisplay     *display,
322                                 GimpActionGroup *group)
323 {
324   GimpDisplayShell *shell = gimp_display_get_shell (display);
325   GimpAction       *action;
326   gchar            *action_name;
327 
328   if (shell)
329     g_signal_handlers_disconnect_by_func (shell,
330                                           windows_actions_title_notify,
331                                           group);
332 
333   action_name = gimp_display_get_action_name (display);
334   action = gimp_action_group_get_action (group, action_name);
335   g_free (action_name);
336 
337   if (action)
338     gimp_action_group_remove_action_and_accel (group, action);
339 
340   windows_actions_update_display_accels (group);
341 }
342 
343 static void
windows_actions_display_reorder(GimpContainer * container,GimpDisplay * display,gint new_index,GimpActionGroup * group)344 windows_actions_display_reorder (GimpContainer   *container,
345                                  GimpDisplay     *display,
346                                  gint             new_index,
347                                  GimpActionGroup *group)
348 {
349   windows_actions_update_display_accels (group);
350 }
351 
352 static void
windows_actions_image_notify(GimpDisplay * display,const GParamSpec * unused,GimpActionGroup * group)353 windows_actions_image_notify (GimpDisplay      *display,
354                               const GParamSpec *unused,
355                               GimpActionGroup  *group)
356 {
357   GimpImage  *image = gimp_display_get_image (display);
358   GimpAction *action;
359   gchar      *action_name;
360 
361   action_name = gimp_display_get_action_name (display);
362 
363   action = gimp_action_group_get_action (group, action_name);
364 
365   if (! action)
366     {
367       GimpActionEntry entry;
368 
369       entry.name        = action_name;
370       entry.icon_name   = GIMP_ICON_IMAGE;
371       entry.label       = "";
372       entry.accelerator = NULL;
373       entry.tooltip     = NULL;
374       entry.callback    = windows_show_display_cmd_callback;
375       entry.help_id     = NULL;
376 
377       gimp_action_group_add_actions (group, NULL, &entry, 1);
378 
379       gimp_action_group_set_action_always_show_image (group, action_name,
380                                                       TRUE);
381       action = gimp_action_group_get_action (group, action_name);
382 
383       g_object_set_data (G_OBJECT (action), "display", display);
384     }
385 
386   g_free (action_name);
387 
388   if (image)
389     {
390       const gchar *display_name;
391       gchar       *escaped;
392       gchar       *title;
393 
394       display_name = gimp_image_get_display_name (image);
395       escaped = gimp_escape_uline (display_name);
396 
397       title = g_strdup_printf ("%s-%d.%d", escaped,
398                                gimp_image_get_ID (image),
399                                gimp_display_get_instance (display));
400       g_free (escaped);
401 
402       g_object_set (action,
403                     "visible",  TRUE,
404                     "label",    title,
405                     "tooltip",  gimp_image_get_display_path (image),
406                     "viewable", image,
407                     "context",  gimp_get_user_context (group->gimp),
408                     NULL);
409 
410       g_free (title);
411 
412       windows_actions_update_display_accels (group);
413     }
414   else
415     {
416       g_object_set (action,
417                     "visible",  FALSE,
418                     "viewable", NULL,
419                     NULL);
420     }
421 }
422 
423 static void
windows_actions_title_notify(GimpDisplayShell * shell,const GParamSpec * unused,GimpActionGroup * group)424 windows_actions_title_notify (GimpDisplayShell *shell,
425                               const GParamSpec *unused,
426                               GimpActionGroup  *group)
427 {
428   windows_actions_image_notify (shell->display, NULL, group);
429 }
430 
431 static void
windows_actions_update_display_accels(GimpActionGroup * group)432 windows_actions_update_display_accels (GimpActionGroup *group)
433 {
434   GList *list;
435   gint   i;
436 
437   for (list = gimp_get_display_iter (group->gimp), i = 0;
438        list && i < 10;
439        list = g_list_next (list), i++)
440     {
441       GimpDisplay *display = list->data;
442       GimpAction  *action;
443       gchar       *action_name;
444 
445       if (! gimp_display_get_image (display))
446         break;
447 
448       action_name = gimp_display_get_action_name (display);
449 
450       action = gimp_action_group_get_action (group, action_name);
451       g_free (action_name);
452 
453       if (action)
454         {
455           const gchar *accel_path;
456           guint        accel_key;
457 
458           accel_path = gimp_action_get_accel_path (action);
459 
460           if (i < 9)
461             accel_key = GDK_KEY_1 + i;
462           else
463             accel_key = GDK_KEY_0;
464 
465           gtk_accel_map_change_entry (accel_path,
466                                       accel_key, GDK_MOD1_MASK,
467                                       TRUE);
468         }
469     }
470 }
471 
472 static void
windows_actions_dock_window_added(GimpDialogFactory * factory,GimpDockWindow * dock_window,GimpActionGroup * group)473 windows_actions_dock_window_added (GimpDialogFactory *factory,
474                                    GimpDockWindow    *dock_window,
475                                    GimpActionGroup   *group)
476 {
477   GimpAction      *action;
478   GimpActionEntry  entry;
479   gchar           *action_name = windows_actions_dock_window_to_action_name (dock_window);
480 
481   entry.name        = action_name;
482   entry.icon_name   = NULL;
483   entry.label       = "";
484   entry.accelerator = NULL;
485   entry.tooltip     = NULL;
486   entry.callback    = windows_show_dock_cmd_callback;
487   entry.help_id     = GIMP_HELP_WINDOWS_SHOW_DOCK;
488 
489   gimp_action_group_add_actions (group, NULL, &entry, 1);
490 
491   action = gimp_action_group_get_action (group, action_name);
492 
493   g_object_set (action,
494                 "ellipsize", PANGO_ELLIPSIZE_END,
495                 NULL);
496 
497   g_object_set_data (G_OBJECT (action), "dock-window", dock_window);
498 
499   g_free (action_name);
500 
501   g_signal_connect_object (dock_window, "notify::title",
502                            G_CALLBACK (windows_actions_dock_window_notify),
503                            group, 0);
504 
505   if (gtk_window_get_title (GTK_WINDOW (dock_window)))
506     windows_actions_dock_window_notify (dock_window, NULL, group);
507 }
508 
509 static void
windows_actions_dock_window_removed(GimpDialogFactory * factory,GimpDockWindow * dock_window,GimpActionGroup * group)510 windows_actions_dock_window_removed (GimpDialogFactory *factory,
511                                      GimpDockWindow    *dock_window,
512                                      GimpActionGroup   *group)
513 {
514   GimpAction *action;
515   gchar      *action_name;
516 
517   action_name = windows_actions_dock_window_to_action_name (dock_window);
518   action = gimp_action_group_get_action (group, action_name);
519   g_free (action_name);
520 
521   if (action)
522     gimp_action_group_remove_action_and_accel (group, action);
523 }
524 
525 static void
windows_actions_dock_window_notify(GimpDockWindow * dock_window,const GParamSpec * pspec,GimpActionGroup * group)526 windows_actions_dock_window_notify (GimpDockWindow   *dock_window,
527                                     const GParamSpec *pspec,
528                                     GimpActionGroup  *group)
529 {
530   GimpAction *action;
531   gchar      *action_name;
532 
533   action_name = windows_actions_dock_window_to_action_name (dock_window);
534   action = gimp_action_group_get_action (group, action_name);
535   g_free (action_name);
536 
537   if (action)
538     g_object_set (action,
539                   "label",   gtk_window_get_title (GTK_WINDOW (dock_window)),
540                   "tooltip", gtk_window_get_title (GTK_WINDOW (dock_window)),
541                   NULL);
542 }
543 
544 static void
windows_actions_recent_add(GimpContainer * container,GimpSessionInfo * info,GimpActionGroup * group)545 windows_actions_recent_add (GimpContainer   *container,
546                             GimpSessionInfo *info,
547                             GimpActionGroup *group)
548 {
549   GimpAction      *action;
550   GimpActionEntry  entry;
551   gint             info_id;
552   static gint      info_id_counter = 1;
553   gchar           *action_name;
554 
555   info_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (info),
556                                                 "recent-action-id"));
557 
558   if (! info_id)
559     {
560       info_id = info_id_counter++;
561 
562       g_object_set_data (G_OBJECT (info), "recent-action-id",
563                          GINT_TO_POINTER (info_id));
564     }
565 
566   action_name = g_strdup_printf ("windows-recent-%04d", info_id);
567 
568   entry.name        = action_name;
569   entry.icon_name   = NULL;
570   entry.label       = gimp_object_get_name (info);
571   entry.accelerator = NULL;
572   entry.tooltip     = gimp_object_get_name (info);
573   entry.callback    = windows_open_recent_cmd_callback;
574   entry.help_id     = GIMP_HELP_WINDOWS_OPEN_RECENT_DOCK;
575 
576   gimp_action_group_add_actions (group, NULL, &entry, 1);
577 
578   action = gimp_action_group_get_action (group, action_name);
579 
580   g_object_set (action,
581                 "ellipsize",       PANGO_ELLIPSIZE_END,
582                 "max-width-chars", 30,
583                 NULL);
584 
585   g_object_set_data (G_OBJECT (action), "info", info);
586 
587   g_free (action_name);
588 }
589 
590 static void
windows_actions_recent_remove(GimpContainer * container,GimpSessionInfo * info,GimpActionGroup * group)591 windows_actions_recent_remove (GimpContainer   *container,
592                                GimpSessionInfo *info,
593                                GimpActionGroup *group)
594 {
595   GimpAction *action;
596   gint        info_id;
597   gchar      *action_name;
598 
599   info_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (info),
600                                                 "recent-action-id"));
601 
602   action_name = g_strdup_printf ("windows-recent-%04d", info_id);
603   action = gimp_action_group_get_action (group, action_name);
604   g_free (action_name);
605 
606   if (action)
607     gimp_action_group_remove_action_and_accel (group, action);
608 }
609 
610 static void
windows_actions_single_window_mode_notify(GimpDisplayConfig * config,GParamSpec * pspec,GimpActionGroup * group)611 windows_actions_single_window_mode_notify (GimpDisplayConfig *config,
612                                            GParamSpec        *pspec,
613                                            GimpActionGroup   *group)
614 {
615   gimp_action_group_set_action_active (group,
616                                        "windows-use-single-window-mode",
617                                        GIMP_GUI_CONFIG (config)->single_window_mode);
618 }
619