1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2005-2008 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <gtk/gtk.h>
24 #include "glib-utils.h"
25 #include "gth-main.h"
26 #include "gth-window.h"
27 #include "gth-window-title.h"
28 #include "gtk-utils.h"
29 #include "main.h"
30
31
32 enum {
33 PROP_0,
34 PROP_N_PAGES,
35 PROP_USE_HEADER_BAR
36 };
37
38
39 struct _GthWindowPrivate {
40 int n_pages;
41 gboolean use_header_bar;
42 int current_page;
43 GtkWidget *overlay;
44 GtkWidget *grid;
45 GtkWidget *stack;
46 GtkWidget *headerbar_container;
47 GtkWidget *headerbar;
48 GtkWidget *title;
49 GtkWidget *menubar;
50 GtkWidget *toolbar;
51 GtkWidget *infobar;
52 GtkWidget *statusbar;
53 GtkWidget **toolbars;
54 GtkWidget **contents;
55 GtkWidget **pages;
56 GthWindowSize *window_size;
57 GtkWindowGroup *window_group;
58 GtkAccelGroup *accel_group;
59 GHashTable *shortcuts;
60 GPtrArray *shortcuts_v;
61 GHashTable *shortcut_groups;
62 };
63
64
G_DEFINE_TYPE_WITH_CODE(GthWindow,gth_window,GTK_TYPE_APPLICATION_WINDOW,G_ADD_PRIVATE (GthWindow))65 G_DEFINE_TYPE_WITH_CODE (GthWindow,
66 gth_window,
67 GTK_TYPE_APPLICATION_WINDOW,
68 G_ADD_PRIVATE (GthWindow))
69
70
71 static gboolean
72 overlay_get_child_position_cb (GtkOverlay *overlay,
73 GtkWidget *widget,
74 GdkRectangle *allocation,
75 gpointer user_data)
76 {
77 GtkAllocation main_alloc;
78
79 gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN (overlay)), &main_alloc);
80
81 allocation->x = 0;
82 allocation->y = 0;
83 allocation->width = main_alloc.width;
84 gtk_widget_get_preferred_height (widget, NULL, &allocation->height);
85
86 return TRUE;
87 }
88
89
90 static void
gth_window_set_n_pages(GthWindow * self,int n_pages)91 gth_window_set_n_pages (GthWindow *self,
92 int n_pages)
93 {
94 int i;
95
96 if (self->priv->n_pages != 0) {
97 g_critical ("The number of pages of a GthWindow can be set only once.");
98 return;
99 }
100
101 self->priv->n_pages = n_pages;
102
103 self->priv->overlay = gtk_overlay_new ();
104 gtk_style_context_add_class (gtk_widget_get_style_context (self->priv->overlay), "window-overlay");
105 gtk_widget_show (self->priv->overlay);
106 gtk_container_add (GTK_CONTAINER (self), self->priv->overlay);
107
108 g_signal_connect (self->priv->overlay,
109 "get-child-position",
110 G_CALLBACK (overlay_get_child_position_cb),
111 self);
112
113 self->priv->grid = gtk_grid_new ();
114 gtk_widget_show (self->priv->grid);
115 gtk_container_add (GTK_CONTAINER (self->priv->overlay), self->priv->grid);
116
117 self->priv->stack = gtk_stack_new ();
118 gtk_stack_set_transition_type (GTK_STACK (self->priv->stack), GTK_STACK_TRANSITION_TYPE_CROSSFADE);
119 gtk_widget_show (self->priv->stack);
120 gtk_grid_attach (GTK_GRID (self->priv->grid),
121 self->priv->stack,
122 0, 2,
123 1, 1);
124
125 self->priv->toolbars = g_new0 (GtkWidget *, n_pages);
126 self->priv->contents = g_new0 (GtkWidget *, n_pages);
127 self->priv->pages = g_new0 (GtkWidget *, n_pages);
128
129 for (i = 0; i < n_pages; i++) {
130 GtkWidget *page;
131
132 self->priv->pages[i] = page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
133 gtk_widget_show (page);
134 gtk_container_add (GTK_CONTAINER (self->priv->stack), page);
135
136 self->priv->toolbars[i] = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
137 gtk_widget_show (self->priv->toolbars[i]);
138 gtk_box_pack_start (GTK_BOX (page), self->priv->toolbars[i], FALSE, FALSE, 0);
139
140 self->priv->contents[i] = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
141 gtk_widget_hide (self->priv->contents[i]);
142 gtk_box_pack_start (GTK_BOX (page), self->priv->contents[i], TRUE, TRUE, 0);
143 }
144
145 self->priv->window_size = g_new0 (GthWindowSize, n_pages);
146 for (i = 0; i < n_pages; i++)
147 self->priv->window_size[i].saved = FALSE;
148 }
149
150
151 static void
_gth_window_add_header_bar(GthWindow * self)152 _gth_window_add_header_bar (GthWindow *self)
153 {
154 self->priv->headerbar = gtk_header_bar_new ();
155 gtk_widget_show (self->priv->headerbar);
156 gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (self->priv->headerbar), TRUE);
157
158 g_object_add_weak_pointer (G_OBJECT (self->priv->headerbar), (gpointer *) &self->priv->headerbar);
159
160 {
161 gboolean shell_shows_app_menu;
162 char *decoration_layout;
163
164 g_object_get (gtk_settings_get_default (),
165 "gtk-shell-shows-app-menu", &shell_shows_app_menu,
166 "gtk-decoration-layout", &decoration_layout,
167 NULL);
168 if (! shell_shows_app_menu && ((decoration_layout == NULL) || (strstr (decoration_layout, "menu") == NULL))) {
169 gboolean left_part_is_empty;
170 char *new_layout;
171
172 /* add 'menu' to the left */
173
174 left_part_is_empty = (decoration_layout == NULL) || (decoration_layout[0] == '\0') || (decoration_layout[0] == ':');
175 new_layout = g_strconcat ("menu", (left_part_is_empty ? "" : ","), decoration_layout, NULL);
176 gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (self->priv->headerbar), new_layout);
177
178 g_free (new_layout);
179 }
180
181 g_free (decoration_layout);
182 }
183
184 self->priv->title = gth_window_title_new ();
185 gtk_widget_show (self->priv->title);
186 gtk_header_bar_set_custom_title (GTK_HEADER_BAR (self->priv->headerbar), self->priv->title);
187
188 self->priv->headerbar_container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
189 gtk_widget_show (self->priv->headerbar_container);
190 gtk_box_pack_start (GTK_BOX (self->priv->headerbar_container), self->priv->headerbar, TRUE, TRUE, 0);
191
192 gtk_window_set_titlebar (GTK_WINDOW (self), self->priv->headerbar_container);
193 }
194
195
196 static void
gth_window_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)197 gth_window_set_property (GObject *object,
198 guint property_id,
199 const GValue *value,
200 GParamSpec *pspec)
201 {
202 GthWindow *self;
203
204 self = GTH_WINDOW (object);
205
206 switch (property_id) {
207 case PROP_N_PAGES:
208 gth_window_set_n_pages (self, g_value_get_int (value));
209 break;
210 case PROP_USE_HEADER_BAR:
211 self->priv->use_header_bar = g_value_get_boolean (value);
212 if (self->priv->use_header_bar)
213 _gth_window_add_header_bar (self);
214 break;
215 default:
216 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
217 break;
218 }
219 }
220
221
222 static void
gth_window_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)223 gth_window_get_property (GObject *object,
224 guint property_id,
225 GValue *value,
226 GParamSpec *pspec)
227 {
228 GthWindow *self;
229
230 self = GTH_WINDOW (object);
231
232 switch (property_id) {
233 case PROP_N_PAGES:
234 g_value_set_int (value, self->priv->n_pages);
235 break;
236 case PROP_USE_HEADER_BAR:
237 g_value_set_boolean (value, self->priv->use_header_bar);
238 break;
239 default:
240 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
241 break;
242 }
243 }
244
245
246 static void
gth_window_finalize(GObject * object)247 gth_window_finalize (GObject *object)
248 {
249 GthWindow *window = GTH_WINDOW (object);
250
251 g_free (window->priv->toolbars);
252 g_free (window->priv->contents);
253 g_free (window->priv->pages);
254 g_free (window->priv->window_size);
255 g_object_unref (window->priv->window_group);
256 g_object_unref (window->priv->accel_group);
257 g_hash_table_unref (window->priv->shortcuts);
258 g_ptr_array_unref (window->priv->shortcuts_v);
259 g_hash_table_unref (window->priv->shortcut_groups);
260
261 G_OBJECT_CLASS (gth_window_parent_class)->finalize (object);
262 }
263
264
265 static gboolean
gth_window_delete_event(GtkWidget * widget,GdkEventAny * event)266 gth_window_delete_event (GtkWidget *widget,
267 GdkEventAny *event)
268 {
269 gth_window_close ((GthWindow*) widget);
270 return TRUE;
271 }
272
273
274 static void
gth_window_real_close(GthWindow * window)275 gth_window_real_close (GthWindow *window)
276 {
277 /* virtual */
278 }
279
280
281 static void
gth_window_real_set_current_page(GthWindow * window,int page)282 gth_window_real_set_current_page (GthWindow *window,
283 int page)
284 {
285 int i;
286
287 if (window->priv->current_page == page)
288 return;
289
290 window->priv->current_page = page;
291 gtk_stack_set_visible_child (GTK_STACK (window->priv->stack), window->priv->pages[page]);
292
293 for (i = 0; i < window->priv->n_pages; i++)
294 if (i == page)
295 gtk_widget_show (window->priv->contents[i]);
296 else
297 gtk_widget_hide (window->priv->contents[i]);
298 }
299
300
301 static void
_gth_window_add_css_provider(GtkWidget * widget,const char * path)302 _gth_window_add_css_provider (GtkWidget *widget,
303 const char *path)
304 {
305 GBytes *bytes;
306 gconstpointer css_data;
307 gsize css_data_size;
308 GtkCssProvider *css_provider;
309 GError *error = NULL;
310
311
312 bytes = g_resources_lookup_data (path, 0, &error);
313 if (bytes == NULL) {
314 g_warning ("%s", error->message);
315 g_error_free (error);
316 return;
317 }
318
319 css_data = g_bytes_get_data (bytes, &css_data_size);
320 css_provider = gtk_css_provider_new ();
321 if (! gtk_css_provider_load_from_data (css_provider, css_data, css_data_size, &error)) {
322 g_warning ("%s", error->message);
323 g_error_free (error);
324 }
325 gtk_style_context_add_provider_for_screen (gtk_widget_get_screen (widget),
326 GTK_STYLE_PROVIDER (css_provider),
327 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
328
329 g_object_unref (css_provider);
330 g_bytes_unref (bytes);
331 }
332
333
334 static void
gth_window_realize(GtkWidget * widget)335 gth_window_realize (GtkWidget *widget)
336 {
337 GTK_WIDGET_CLASS (gth_window_parent_class)->realize (widget);
338
339 gtk_icon_theme_append_search_path (gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget)), GTHUMB_ICON_DIR);
340
341 _gth_window_add_css_provider (widget, "/org/gnome/gThumb/resources/gthumb.css");
342 if ((gtk_major_version >= 3) && (gtk_minor_version >= 20))
343 _gth_window_add_css_provider (widget, "/org/gnome/gThumb/resources/gthumb-gtk320.css");
344 else if ((gtk_major_version >= 3) && (gtk_minor_version >= 14))
345 _gth_window_add_css_provider (widget, "/org/gnome/gThumb/resources/gthumb-gtk314.css");
346 else if ((gtk_major_version >= 3) && (gtk_minor_version >= 10))
347 _gth_window_add_css_provider (widget, "/org/gnome/gThumb/resources/gthumb-gtk312.css");
348 }
349
350
351 static void
gth_window_class_init(GthWindowClass * klass)352 gth_window_class_init (GthWindowClass *klass)
353 {
354 GObjectClass *gobject_class;
355 GtkWidgetClass *widget_class;
356
357 gobject_class = (GObjectClass*) klass;
358 gobject_class->set_property = gth_window_set_property;
359 gobject_class->get_property = gth_window_get_property;
360 gobject_class->finalize = gth_window_finalize;
361
362 widget_class = (GtkWidgetClass*) klass;
363 widget_class->delete_event = gth_window_delete_event;
364 widget_class->realize = gth_window_realize;
365
366 klass->close = gth_window_real_close;
367 klass->set_current_page = gth_window_real_set_current_page;
368
369 g_object_class_install_property (G_OBJECT_CLASS (klass),
370 PROP_N_PAGES,
371 g_param_spec_int ("n-pages",
372 "n-pages",
373 "n-pages",
374 0,
375 G_MAXINT,
376 1,
377 G_PARAM_READWRITE));
378 g_object_class_install_property (G_OBJECT_CLASS (klass),
379 PROP_USE_HEADER_BAR,
380 g_param_spec_boolean ("use-header-bar",
381 "use-header-bar",
382 "use-header-bar",
383 FALSE,
384 G_PARAM_READWRITE));
385 }
386
387
388 static void
gth_window_init(GthWindow * window)389 gth_window_init (GthWindow *window)
390 {
391 window->priv = gth_window_get_instance_private (window);
392 window->priv->grid = NULL;
393 window->priv->contents = NULL;
394 window->priv->pages = NULL;
395 window->priv->n_pages = 0;
396 window->priv->current_page = GTH_WINDOW_PAGE_UNDEFINED;
397 window->priv->menubar = NULL;
398 window->priv->toolbar = NULL;
399 window->priv->infobar = NULL;
400 window->priv->statusbar = NULL;
401 window->priv->headerbar = NULL;
402 window->priv->use_header_bar = FALSE;
403
404 window->priv->window_group = gtk_window_group_new ();
405 gtk_window_group_add_window (window->priv->window_group, GTK_WINDOW (window));
406
407 window->priv->accel_group = gtk_accel_group_new ();
408 gtk_window_add_accel_group (GTK_WINDOW (window), window->priv->accel_group);
409
410 window->priv->shortcuts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
411 window->priv->shortcuts_v = g_ptr_array_new_with_free_func ((GDestroyNotify) gth_shortcut_free);
412
413 window->priv->shortcut_groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_ptr_array_unref);
414
415 gtk_window_set_application (GTK_WINDOW (window), Main_Application);
416 }
417
418
419 void
gth_window_close(GthWindow * window)420 gth_window_close (GthWindow *window)
421 {
422 GTH_WINDOW_GET_CLASS (window)->close (window);
423 }
424
425
426 void
gth_window_attach(GthWindow * window,GtkWidget * child,GthWindowArea area)427 gth_window_attach (GthWindow *window,
428 GtkWidget *child,
429 GthWindowArea area)
430 {
431 int position;
432
433 g_return_if_fail (window != NULL);
434 g_return_if_fail (GTH_IS_WINDOW (window));
435 g_return_if_fail (child != NULL);
436 g_return_if_fail (GTK_IS_WIDGET (child));
437
438 switch (area) {
439 case GTH_WINDOW_MENUBAR:
440 window->priv->menubar = child;
441 position = 0;
442 break;
443 case GTH_WINDOW_TOOLBAR:
444 window->priv->toolbar = child;
445 position = 1;
446 break;
447 case GTH_WINDOW_INFOBAR:
448 window->priv->infobar = child;
449 position = 4;
450 break;
451 case GTH_WINDOW_STATUSBAR:
452 window->priv->statusbar = child;
453 position = 3;
454 break;
455 default:
456 return;
457 }
458
459 gtk_widget_set_vexpand (child, FALSE);
460 gtk_grid_attach (GTK_GRID (window->priv->grid),
461 child,
462 0, position,
463 1, 1);
464 }
465
466
467 void
gth_window_attach_toolbar(GthWindow * window,int page,GtkWidget * child)468 gth_window_attach_toolbar (GthWindow *window,
469 int page,
470 GtkWidget *child)
471 {
472 g_return_if_fail (window != NULL);
473 g_return_if_fail (GTH_IS_WINDOW (window));
474 g_return_if_fail (page >= 0 && page < window->priv->n_pages);
475 g_return_if_fail (child != NULL);
476 g_return_if_fail (GTK_IS_WIDGET (child));
477
478 _gtk_container_remove_children (GTK_CONTAINER (window->priv->toolbars[page]), NULL, NULL);
479 gtk_style_context_add_class (gtk_widget_get_style_context (child), GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
480 gtk_widget_set_hexpand (child, TRUE);
481 gtk_container_add (GTK_CONTAINER (window->priv->toolbars[page]), child);
482 }
483
484
485 void
gth_window_attach_content(GthWindow * window,int page,GtkWidget * child)486 gth_window_attach_content (GthWindow *window,
487 int page,
488 GtkWidget *child)
489 {
490 g_return_if_fail (window != NULL);
491 g_return_if_fail (GTH_IS_WINDOW (window));
492 g_return_if_fail (page >= 0 && page < window->priv->n_pages);
493 g_return_if_fail (child != NULL);
494 g_return_if_fail (GTK_IS_WIDGET (child));
495
496 _gtk_container_remove_children (GTK_CONTAINER (window->priv->contents[page]), NULL, NULL);
497 gtk_widget_set_hexpand (child, TRUE);
498 gtk_widget_set_vexpand (child, TRUE);
499 gtk_container_add (GTK_CONTAINER (window->priv->contents[page]), child);
500 }
501
502
503 void
gth_window_set_current_page(GthWindow * window,int page)504 gth_window_set_current_page (GthWindow *window,
505 int page)
506 {
507 g_return_if_fail (window != NULL);
508 g_return_if_fail (GTH_IS_WINDOW (window));
509 g_return_if_fail (page >= 0 && page < window->priv->n_pages);
510
511 GTH_WINDOW_GET_CLASS (window)->set_current_page (window, page);
512 }
513
514
515 int
gth_window_get_current_page(GthWindow * window)516 gth_window_get_current_page (GthWindow *window)
517 {
518 return window->priv->current_page;
519 }
520
521
522 static void
hide_widget(GtkWidget * widget)523 hide_widget (GtkWidget *widget)
524 {
525 if (widget != NULL)
526 gtk_widget_hide (widget);
527 }
528
529
530 static void
show_widget(GtkWidget * widget)531 show_widget (GtkWidget *widget)
532 {
533 if (widget != NULL)
534 gtk_widget_show (widget);
535 }
536
537
538 void
gth_window_show_only_content(GthWindow * window,gboolean only_content)539 gth_window_show_only_content (GthWindow *window,
540 gboolean only_content)
541 {
542 int i;
543
544 if (only_content) {
545 for (i = 0; i < window->priv->n_pages; i++)
546 hide_widget (window->priv->toolbars[i]);
547 hide_widget (window->priv->menubar);
548 hide_widget (window->priv->toolbar);
549 hide_widget (window->priv->statusbar);
550 }
551 else {
552 for (i = 0; i < window->priv->n_pages; i++)
553 show_widget (window->priv->toolbars[i]);
554 show_widget (window->priv->menubar);
555 show_widget (window->priv->toolbar);
556 show_widget (window->priv->statusbar);
557 }
558 }
559
560
561 GtkWidget *
gth_window_get_area(GthWindow * window,GthWindowArea area)562 gth_window_get_area (GthWindow *window,
563 GthWindowArea area)
564 {
565 switch (area) {
566 case GTH_WINDOW_MENUBAR:
567 return window->priv->menubar;
568 break;
569 case GTH_WINDOW_TOOLBAR:
570 return window->priv->toolbar;
571 break;
572 case GTH_WINDOW_INFOBAR:
573 return window->priv->infobar;
574 break;
575 case GTH_WINDOW_STATUSBAR:
576 return window->priv->statusbar;
577 break;
578 default:
579 break;
580 }
581
582 return NULL;
583 }
584
585
586 void
gth_window_add_overlay(GthWindow * window,GtkWidget * widget)587 gth_window_add_overlay (GthWindow *window,
588 GtkWidget *widget)
589 {
590 gtk_overlay_add_overlay (GTK_OVERLAY (window->priv->overlay), widget);
591 }
592
593
594 void
gth_window_set_header_bar(GthWindow * window,GtkWidget * header_bar)595 gth_window_set_header_bar (GthWindow *window,
596 GtkWidget *header_bar)
597 {
598 if (window->priv->headerbar != header_bar) {
599 if (window->priv->headerbar != NULL)
600 gtk_widget_destroy (window->priv->headerbar);
601 window->priv->headerbar = header_bar;
602 }
603 gtk_widget_show (window->priv->headerbar);
604 gtk_box_pack_start (GTK_BOX (window->priv->headerbar_container), window->priv->headerbar, TRUE, TRUE, 0);
605 }
606
607
608 GtkWidget *
gth_window_get_header_bar(GthWindow * window)609 gth_window_get_header_bar (GthWindow *window)
610 {
611 return window->priv->headerbar;
612 }
613
614
615 void
gth_window_save_page_size(GthWindow * window,int page,int width,int height)616 gth_window_save_page_size (GthWindow *window,
617 int page,
618 int width,
619 int height)
620 {
621 g_return_if_fail (window != NULL);
622 g_return_if_fail (GTH_IS_WINDOW (window));
623 g_return_if_fail (page >= 0 && page < window->priv->n_pages);
624
625 window->priv->window_size[page].width = width;
626 window->priv->window_size[page].height = height;
627 window->priv->window_size[page].saved = TRUE;
628 }
629
630
631 void
gth_window_apply_saved_size(GthWindow * window,int page)632 gth_window_apply_saved_size (GthWindow *window,
633 int page)
634 {
635 g_return_if_fail (window != NULL);
636 g_return_if_fail (GTH_IS_WINDOW (window));
637 g_return_if_fail (page >= 0 && page < window->priv->n_pages);
638
639 if (! window->priv->window_size[page].saved)
640 return;
641
642 gtk_window_resize (GTK_WINDOW (window),
643 window->priv->window_size[page].width,
644 window->priv->window_size[page].height);
645 }
646
647
648 void
gth_window_clear_saved_size(GthWindow * window,int page)649 gth_window_clear_saved_size (GthWindow *window,
650 int page)
651 {
652 g_return_if_fail (window != NULL);
653 g_return_if_fail (GTH_IS_WINDOW (window));
654 g_return_if_fail (page >= 0 && page < window->priv->n_pages);
655
656 window->priv->window_size[page].saved = FALSE;
657 }
658
659
660 gboolean
gth_window_get_page_size(GthWindow * window,int page,int * width,int * height)661 gth_window_get_page_size (GthWindow *window,
662 int page,
663 int *width,
664 int *height)
665 {
666 g_return_val_if_fail (window != NULL, FALSE);
667 g_return_val_if_fail (GTH_IS_WINDOW (window), FALSE);
668 g_return_val_if_fail (page >= 0 && page < window->priv->n_pages, FALSE);
669
670 if (! window->priv->window_size[page].saved)
671 return FALSE;
672
673 if (width != NULL)
674 *width = window->priv->window_size[page].width;
675 if (height != NULL)
676 *height = window->priv->window_size[page].height;
677
678 return TRUE;
679 }
680
681
682 void
gth_window_set_title(GthWindow * window,const char * title,GList * emblems)683 gth_window_set_title (GthWindow *window,
684 const char *title,
685 GList *emblems)
686 {
687 if (window->priv->use_header_bar) {
688 gth_window_title_set_title (GTH_WINDOW_TITLE (window->priv->title), title);
689 gth_window_title_set_emblems (GTH_WINDOW_TITLE (window->priv->title), emblems);
690 }
691 else
692 gtk_window_set_title (GTK_WINDOW (window), title);
693 }
694
695
696 GtkAccelGroup *
gth_window_get_accel_group(GthWindow * window)697 gth_window_get_accel_group (GthWindow *window)
698 {
699 if (window->priv->accel_group == NULL) {
700 window->priv->accel_group = gtk_accel_group_new ();
701 gtk_window_add_accel_group (GTK_WINDOW (window), window->priv->accel_group);
702 }
703
704 return window->priv->accel_group;
705 }
706
707
708 static void
_gth_window_add_shortcut(GthWindow * window,GthShortcut * shortcut)709 _gth_window_add_shortcut (GthWindow *window,
710 GthShortcut *shortcut)
711 {
712 g_ptr_array_add (window->priv->shortcuts_v, shortcut);
713 g_hash_table_insert (window->priv->shortcuts,
714 g_strdup (shortcut->detailed_action),
715 shortcut);
716 }
717
718
719 static void
_gth_window_remove_shortcut(GthWindow * window,GthShortcut * shortcut)720 _gth_window_remove_shortcut (GthWindow *window,
721 GthShortcut *shortcut)
722 {
723 g_hash_table_remove (window->priv->shortcuts, shortcut->detailed_action);
724 g_ptr_array_remove (window->priv->shortcuts_v, shortcut);
725 }
726
727
728 void
gth_window_add_accelerators(GthWindow * window,const GthAccelerator * accelerators,int n_accelerators)729 gth_window_add_accelerators (GthWindow *window,
730 const GthAccelerator *accelerators,
731 int n_accelerators)
732 {
733 GtkAccelGroup *accel_group;
734 int i;
735
736 accel_group = gth_window_get_accel_group (window);
737 for (i = 0; i < n_accelerators; i++) {
738 const GthAccelerator *acc = accelerators + i;
739 GthShortcut *shortcut;
740
741 _gtk_window_add_accelerator_for_action (GTK_WINDOW (window),
742 accel_group,
743 acc->action_name,
744 acc->accelerator,
745 NULL);
746
747 shortcut = gth_shortcut_new (acc->action_name, NULL);
748 shortcut->context = GTH_SHORTCUT_CONTEXT_INTERNAL | GTH_SHORTCUT_CONTEXT_BROWSER_VIEWER;
749 shortcut->category = GTH_SHORTCUT_CATEGORY_HIDDEN;
750 gth_shortcut_set_accelerator (shortcut, acc->accelerator);
751 _gth_window_add_shortcut (window, shortcut);
752 }
753 }
754
755
756 void
gth_window_enable_action(GthWindow * window,const char * action_name,gboolean enabled)757 gth_window_enable_action (GthWindow *window,
758 const char *action_name,
759 gboolean enabled)
760 {
761 GAction *action;
762
763 action = g_action_map_lookup_action (G_ACTION_MAP (window), action_name);
764 g_object_set (action, "enabled", enabled, NULL);
765 }
766
767
768 gboolean
gth_window_get_action_state(GthWindow * window,const char * action_name)769 gth_window_get_action_state (GthWindow *window,
770 const char *action_name)
771 {
772 GAction *action;
773 GVariant *state;
774 gboolean value;
775
776 action = g_action_map_lookup_action (G_ACTION_MAP (window), action_name);
777 g_return_val_if_fail (action != NULL, FALSE);
778 state = g_action_get_state (action);
779 value = g_variant_get_boolean (state);
780
781 g_variant_unref (state);
782
783 return value;
784 }
785
786
787 void
gth_window_change_action_state(GthWindow * window,const char * action_name,gboolean value)788 gth_window_change_action_state (GthWindow *window,
789 const char *action_name,
790 gboolean value)
791 {
792 GAction *action;
793 GVariant *old_state;
794 GVariant *new_state;
795
796 action = g_action_map_lookup_action (G_ACTION_MAP (window), action_name);
797 g_return_if_fail (action != NULL);
798
799 old_state = g_action_get_state (action);
800 new_state = g_variant_ref_sink (g_variant_new_boolean (value));
801 if ((old_state == NULL) || ! g_variant_equal (old_state, new_state))
802 g_action_change_state (action, new_state);
803
804 if (old_state != NULL)
805 g_variant_unref (old_state);
806 g_variant_unref (new_state);
807 }
808
809
810 void
gth_window_add_shortcuts(GthWindow * window,const GthShortcut * shortcuts,int n_shortcuts)811 gth_window_add_shortcuts (GthWindow *window,
812 const GthShortcut *shortcuts,
813 int n_shortcuts)
814 {
815 int i;
816
817 for (i = 0; i < n_shortcuts; i++) {
818 const GthShortcut *shortcut = shortcuts + i;
819 GthShortcut *new_shortcut;
820
821 new_shortcut = gth_shortcut_dup (shortcut);
822 gth_shortcut_set_accelerator (new_shortcut, shortcut->default_accelerator);
823
824 _gth_window_add_shortcut (window, new_shortcut);
825 }
826 }
827
828
829 GPtrArray *
gth_window_get_shortcuts(GthWindow * window)830 gth_window_get_shortcuts (GthWindow *window)
831 {
832 g_return_val_if_fail (GTH_IS_WINDOW (window), NULL);
833
834 return window->priv->shortcuts_v;
835 }
836
837
838 GthShortcut *
gth_window_get_shortcut(GthWindow * window,const char * detailed_action)839 gth_window_get_shortcut (GthWindow *window,
840 const char *detailed_action)
841 {
842 g_return_val_if_fail (GTH_IS_WINDOW (window), NULL);
843
844 return g_hash_table_lookup (window->priv->shortcuts, detailed_action);
845 }
846
847
848 static int
sort_shortcuts_by_category(gconstpointer a,gconstpointer b)849 sort_shortcuts_by_category (gconstpointer a,
850 gconstpointer b)
851 {
852 const GthShortcut *sa = * (GthShortcut **) a;
853 const GthShortcut *sb = * (GthShortcut **) b;
854 int result;
855 GthShortcutCategory *cat_a;
856 GthShortcutCategory *cat_b;
857
858 result = 0;
859 cat_a = gth_main_get_shortcut_category (sa->category);
860 cat_b = gth_main_get_shortcut_category (sb->category);
861 if ((cat_a != NULL) && (cat_b != NULL)) {
862 if (cat_a->sort_order < cat_b->sort_order)
863 result = -1;
864 else if (cat_a->sort_order > cat_b->sort_order)
865 result = 1;
866 }
867 if (result == 0)
868 result = g_strcmp0 (sa->description, sb->description);
869
870 return result;
871 }
872
873
874 GPtrArray *
gth_window_get_shortcuts_by_category(GthWindow * window)875 gth_window_get_shortcuts_by_category (GthWindow *window)
876 {
877 g_return_val_if_fail (GTH_IS_WINDOW (window), NULL);
878
879 g_ptr_array_sort (window->priv->shortcuts_v, sort_shortcuts_by_category);
880 return window->priv->shortcuts_v;
881 }
882
883
884 gboolean
gth_window_activate_shortcut(GthWindow * window,int context,guint keycode,GdkModifierType modifiers)885 gth_window_activate_shortcut (GthWindow *window,
886 int context,
887 guint keycode,
888 GdkModifierType modifiers)
889 {
890 gboolean activated = FALSE;
891 GthShortcut *shortcut;
892
893 modifiers = modifiers & gtk_accelerator_get_default_mod_mask ();
894 shortcut = gth_shortcut_array_find (window->priv->shortcuts_v, context, keycode, modifiers);
895 if (shortcut != NULL) {
896 GAction *action;
897
898 if ((shortcut->context & GTH_SHORTCUT_CONTEXT_INTERNAL) != 0)
899 return FALSE;
900
901 if ((shortcut->context & GTH_SHORTCUT_CONTEXT_DOC) != 0)
902 return FALSE;
903
904 action = g_action_map_lookup_action (G_ACTION_MAP (window), shortcut->action_name);
905 if (action != NULL) {
906 g_action_activate (action, shortcut->action_parameter);
907 activated = TRUE;
908 }
909 }
910
911 return activated;
912 }
913
914
915 void
gth_window_load_shortcuts(GthWindow * window)916 gth_window_load_shortcuts (GthWindow *window)
917 {
918 g_return_if_fail (GTH_IS_WINDOW (window));
919
920 gth_shortcuts_load_from_file (window->priv->shortcuts_v,
921 window->priv->shortcuts,
922 NULL);
923 }
924
925
926 void
gth_window_add_removable_shortcut(GthWindow * window,const char * group_name,GthShortcut * shortcut)927 gth_window_add_removable_shortcut (GthWindow *window,
928 const char *group_name,
929 GthShortcut *shortcut)
930 {
931 GPtrArray *shortcuts_v;
932 GthShortcut *old_shortcut;
933 GthShortcut *new_shortcut;
934
935 g_return_if_fail (GTH_IS_WINDOW (window));
936 g_return_if_fail (group_name != NULL);
937 g_return_if_fail (shortcut != NULL);
938 g_return_if_fail (shortcut->detailed_action != NULL);
939
940 /* create the group if it doesn't exist. */
941
942 shortcuts_v = g_hash_table_lookup (window->priv->shortcut_groups, group_name);
943 if (shortcuts_v == NULL) {
944 shortcuts_v = g_ptr_array_new ();
945 g_hash_table_insert (window->priv->shortcut_groups,
946 g_strdup (group_name),
947 shortcuts_v);
948 }
949
950 /* remove the old shortcut */
951
952 old_shortcut = g_hash_table_lookup (window->priv->shortcuts, shortcut->detailed_action);
953 if (old_shortcut != NULL) {
954 g_ptr_array_remove (shortcuts_v, old_shortcut);
955 _gth_window_remove_shortcut (window, old_shortcut);
956 }
957
958 /* add the new shortcut */
959
960 new_shortcut = gth_shortcut_dup (shortcut);
961 _gth_window_add_shortcut (window, new_shortcut);
962 g_ptr_array_add (shortcuts_v, new_shortcut);
963 }
964
965
966 void
gth_window_remove_shortcuts(GthWindow * window,const char * group_name)967 gth_window_remove_shortcuts (GthWindow *window,
968 const char *group_name)
969 {
970 GPtrArray *shortcuts_v;
971 int i;
972
973 g_return_if_fail (GTH_IS_WINDOW (window));
974 g_return_if_fail (group_name != NULL);
975
976 shortcuts_v = g_hash_table_lookup (window->priv->shortcut_groups, group_name);
977 if (shortcuts_v == NULL)
978 return;
979
980 for (i = 0; i < shortcuts_v->len; i++) {
981 GthShortcut *shortcut = g_ptr_array_index (shortcuts_v, i);
982 _gth_window_remove_shortcut (window, shortcut);
983 }
984
985 g_hash_table_remove (window->priv->shortcut_groups, group_name);
986 }
987
988
989 gboolean
gth_window_can_change_shortcut(GthWindow * window,const char * detailed_action,int context,guint keycode,GdkModifierType modifiers,GtkWindow * parent)990 gth_window_can_change_shortcut (GthWindow *window,
991 const char *detailed_action,
992 int context,
993 guint keycode,
994 GdkModifierType modifiers,
995 GtkWindow *parent)
996 {
997 GthShortcut *shortcut;
998
999 if (window == NULL)
1000 return TRUE;
1001
1002 shortcut = gth_shortcut_array_find (gth_window_get_shortcuts (window ),
1003 context,
1004 keycode,
1005 modifiers);
1006
1007 if (shortcut == NULL)
1008 return TRUE;
1009
1010 if (g_strcmp0 (shortcut->detailed_action, detailed_action) == 0)
1011 return FALSE;
1012
1013 if (gth_shortcut_customizable (shortcut)) {
1014 char *label;
1015 char *msg;
1016 GtkWidget *dialog;
1017 gboolean reassign;
1018
1019 label = gtk_accelerator_get_label (keycode, modifiers);
1020 msg = g_strdup_printf (_("The key combination «%s» is already assigned to the action «%s». Do you want to reassign it to this action instead?"),
1021 label,
1022 shortcut->description);
1023
1024 dialog = _gtk_yesno_dialog_new (parent,
1025 GTK_DIALOG_MODAL,
1026 msg,
1027 _GTK_LABEL_CANCEL,
1028 _("Reassign"));
1029
1030 reassign = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES;
1031 gtk_widget_destroy (GTK_WIDGET (dialog));
1032
1033 g_free (msg);
1034 g_free (label);
1035
1036 if (! reassign)
1037 return FALSE;
1038 }
1039 else {
1040 char *label;
1041 char *msg;
1042 GtkWidget *dialog;
1043
1044 label = gtk_accelerator_get_label (keycode, modifiers);
1045 if (shortcut->description != NULL)
1046 msg = g_strdup_printf (_("The key combination «%s» is already assigned to the action «%s» and cannot be changed."),
1047 label,
1048 shortcut->description);
1049 else
1050 msg = g_strdup_printf (_("The key combination «%s» is already assigned and cannot be changed."),
1051 label);
1052
1053 dialog = _gtk_message_dialog_new (parent,
1054 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1055 _GTK_ICON_NAME_DIALOG_ERROR,
1056 NULL,
1057 msg,
1058 _GTK_LABEL_OK, GTK_RESPONSE_OK,
1059 NULL);
1060 gtk_dialog_run (GTK_DIALOG (dialog));
1061 gtk_widget_destroy (GTK_WIDGET (dialog));
1062
1063 g_free (msg);
1064 g_free (label);
1065
1066 return FALSE;
1067 }
1068
1069 return TRUE;
1070 }
1071
1072
1073 void
gth_window_copy_shortcuts(GthWindow * to_window,GthWindow * from_window,int context)1074 gth_window_copy_shortcuts (GthWindow *to_window,
1075 GthWindow *from_window,
1076 int context)
1077 {
1078 int i;
1079
1080 for (i = 0; i < from_window->priv->shortcuts_v->len; i++) {
1081 const GthShortcut *shortcut = g_ptr_array_index (from_window->priv->shortcuts_v, i);
1082 GthShortcut *new_shortcut;
1083
1084 if ((shortcut->context & context) == 0)
1085 continue;
1086
1087 new_shortcut = gth_shortcut_dup (shortcut);
1088 _gth_window_add_shortcut (to_window, new_shortcut);
1089 }
1090 }
1091