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