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