1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpsessioninfo.c
5 * Copyright (C) 2001-2008 Michael Natterer <mitch@gimp.org>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <string.h>
24
25 #include <gegl.h>
26 #include <gtk/gtk.h>
27
28 #include "libgimpconfig/gimpconfig.h"
29 #include "libgimpwidgets/gimpwidgets.h"
30
31 #include "widgets-types.h"
32
33 #include "config/gimpguiconfig.h"
34
35 #include "widgets/gimpdockcontainer.h"
36
37 #include "core/gimp.h"
38 #include "core/gimpcontext.h"
39
40 #include "gimpdialogfactory.h"
41 #include "gimpdock.h"
42 #include "gimpdockwindow.h"
43 #include "gimpsessioninfo.h"
44 #include "gimpsessioninfo-aux.h"
45 #include "gimpsessioninfo-book.h"
46 #include "gimpsessioninfo-dock.h"
47 #include "gimpsessioninfo-private.h"
48 #include "gimpsessionmanaged.h"
49
50 #include "gimp-log.h"
51
52
53 enum
54 {
55 SESSION_INFO_FACTORY_ENTRY,
56 SESSION_INFO_POSITION,
57 SESSION_INFO_SIZE,
58 SESSION_INFO_MONITOR,
59 SESSION_INFO_OPEN,
60 SESSION_INFO_AUX,
61 SESSION_INFO_DOCK,
62 SESSION_INFO_GIMP_DOCK,
63 SESSION_INFO_GIMP_TOOLBOX
64 };
65
66 #define DEFAULT_SCREEN -1
67 #define DEFAULT_MONITOR -1
68
69
70 typedef struct
71 {
72 GimpSessionInfo *info;
73 GimpDialogFactory *factory;
74 GdkScreen *screen;
75 gint monitor;
76 GtkWidget *dialog;
77 } GimpRestoreDocksData;
78
79
80 static void gimp_session_info_config_iface_init (GimpConfigInterface *iface);
81 static void gimp_session_info_finalize (GObject *object);
82 static gint64 gimp_session_info_get_memsize (GimpObject *object,
83 gint64 *gui_size);
84 static gboolean gimp_session_info_serialize (GimpConfig *config,
85 GimpConfigWriter *writer,
86 gpointer data);
87 static gboolean gimp_session_info_deserialize (GimpConfig *config,
88 GScanner *scanner,
89 gint nest_level,
90 gpointer data);
91 static gboolean gimp_session_info_is_for_dock_window (GimpSessionInfo *info);
92 static void gimp_session_info_dialog_show (GtkWidget *widget,
93 GimpSessionInfo *info);
94 static gboolean gimp_session_info_restore_docks (GimpRestoreDocksData *data);
95
96
G_DEFINE_TYPE_WITH_CODE(GimpSessionInfo,gimp_session_info,GIMP_TYPE_OBJECT,G_ADD_PRIVATE (GimpSessionInfo)G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,gimp_session_info_config_iface_init))97 G_DEFINE_TYPE_WITH_CODE (GimpSessionInfo, gimp_session_info, GIMP_TYPE_OBJECT,
98 G_ADD_PRIVATE (GimpSessionInfo)
99 G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
100 gimp_session_info_config_iface_init))
101
102 #define parent_class gimp_session_info_parent_class
103
104
105 static void
106 gimp_session_info_class_init (GimpSessionInfoClass *klass)
107 {
108 GObjectClass *object_class = G_OBJECT_CLASS (klass);
109 GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
110
111 object_class->finalize = gimp_session_info_finalize;
112
113 gimp_object_class->get_memsize = gimp_session_info_get_memsize;
114 }
115
116 static void
gimp_session_info_init(GimpSessionInfo * info)117 gimp_session_info_init (GimpSessionInfo *info)
118 {
119 info->p = gimp_session_info_get_instance_private (info);
120
121 info->p->monitor = DEFAULT_MONITOR;
122 info->p->screen = DEFAULT_SCREEN;
123 }
124
125 static void
gimp_session_info_config_iface_init(GimpConfigInterface * iface)126 gimp_session_info_config_iface_init (GimpConfigInterface *iface)
127 {
128 iface->serialize = gimp_session_info_serialize;
129 iface->deserialize = gimp_session_info_deserialize;
130 }
131
132 static void
gimp_session_info_finalize(GObject * object)133 gimp_session_info_finalize (GObject *object)
134 {
135 GimpSessionInfo *info = GIMP_SESSION_INFO (object);
136
137 gimp_session_info_clear_info (info);
138
139 gimp_session_info_set_widget (info, NULL);
140
141 G_OBJECT_CLASS (parent_class)->finalize (object);
142 }
143
144 static gint64
gimp_session_info_get_memsize(GimpObject * object,gint64 * gui_size)145 gimp_session_info_get_memsize (GimpObject *object,
146 gint64 *gui_size)
147 {
148 #if 0
149 GimpSessionInfo *info = GIMP_SESSION_INFO (object);
150 #endif
151 gint64 memsize = 0;
152
153 return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
154 gui_size);
155 }
156
157 static gboolean
gimp_session_info_serialize(GimpConfig * config,GimpConfigWriter * writer,gpointer data)158 gimp_session_info_serialize (GimpConfig *config,
159 GimpConfigWriter *writer,
160 gpointer data)
161 {
162 GimpSessionInfo *info = GIMP_SESSION_INFO (config);
163 GList *iter = NULL;
164 gint x;
165 gint y;
166 gint width;
167 gint height;
168
169 if (info->p->factory_entry && info->p->factory_entry->identifier)
170 {
171 gimp_config_writer_open (writer, "factory-entry");
172 gimp_config_writer_string (writer, info->p->factory_entry->identifier);
173 gimp_config_writer_close (writer);
174 }
175
176 x = gimp_session_info_apply_position_accuracy (info->p->x);
177 y = gimp_session_info_apply_position_accuracy (info->p->y);
178 width = gimp_session_info_apply_position_accuracy (info->p->width);
179 height = gimp_session_info_apply_position_accuracy (info->p->height);
180
181 gimp_config_writer_open (writer, "position");
182 gimp_config_writer_printf (writer, "%d %d", x, y);
183 gimp_config_writer_close (writer);
184
185 if (info->p->width > 0 && info->p->height > 0)
186 {
187 gimp_config_writer_open (writer, "size");
188 gimp_config_writer_printf (writer, "%d %d", width, height);
189 gimp_config_writer_close (writer);
190 }
191
192 if (info->p->monitor != DEFAULT_MONITOR)
193 {
194 gimp_config_writer_open (writer, "monitor");
195 gimp_config_writer_printf (writer, "%d", info->p->monitor);
196 gimp_config_writer_close (writer);
197 }
198
199 if (info->p->open)
200 {
201 gimp_config_writer_open (writer, "open-on-exit");
202
203 if (info->p->screen != DEFAULT_SCREEN)
204 gimp_config_writer_printf (writer, "%d", info->p->screen);
205
206 gimp_config_writer_close (writer);
207 }
208
209 if (info->p->aux_info)
210 gimp_session_info_aux_serialize (writer, info->p->aux_info);
211
212 for (iter = info->p->docks; iter; iter = g_list_next (iter))
213 gimp_session_info_dock_serialize (writer, iter->data);
214
215 return TRUE;
216 }
217
218 /*
219 * This function is just like gimp_scanner_parse_int(), but it is allows
220 * to detect the special value '-0'. This is used as in X geometry strings.
221 */
222 static gboolean
gimp_session_info_parse_offset(GScanner * scanner,gint * dest,gboolean * negative)223 gimp_session_info_parse_offset (GScanner *scanner,
224 gint *dest,
225 gboolean *negative)
226 {
227 if (g_scanner_peek_next_token (scanner) == '-')
228 {
229 *negative = TRUE;
230 g_scanner_get_next_token (scanner);
231 }
232 else
233 {
234 *negative = FALSE;
235 }
236
237 if (g_scanner_peek_next_token (scanner) != G_TOKEN_INT)
238 return FALSE;
239
240 g_scanner_get_next_token (scanner);
241
242 if (*negative)
243 *dest = -scanner->value.v_int64;
244 else
245 *dest = scanner->value.v_int64;
246
247 return TRUE;
248 }
249
250 static gboolean
gimp_session_info_deserialize(GimpConfig * config,GScanner * scanner,gint nest_level,gpointer data)251 gimp_session_info_deserialize (GimpConfig *config,
252 GScanner *scanner,
253 gint nest_level,
254 gpointer data)
255 {
256 GimpSessionInfo *info = GIMP_SESSION_INFO (config);
257 GTokenType token;
258 guint scope_id;
259 guint old_scope_id;
260
261 scope_id = g_type_qname (G_TYPE_FROM_INSTANCE (config));
262 old_scope_id = g_scanner_set_scope (scanner, scope_id);
263
264 g_scanner_scope_add_symbol (scanner, scope_id, "factory-entry",
265 GINT_TO_POINTER (SESSION_INFO_FACTORY_ENTRY));
266 g_scanner_scope_add_symbol (scanner, scope_id, "position",
267 GINT_TO_POINTER (SESSION_INFO_POSITION));
268 g_scanner_scope_add_symbol (scanner, scope_id, "size",
269 GINT_TO_POINTER (SESSION_INFO_SIZE));
270 g_scanner_scope_add_symbol (scanner, scope_id, "monitor",
271 GINT_TO_POINTER (SESSION_INFO_MONITOR));
272 g_scanner_scope_add_symbol (scanner, scope_id, "open-on-exit",
273 GINT_TO_POINTER (SESSION_INFO_OPEN));
274 g_scanner_scope_add_symbol (scanner, scope_id, "aux-info",
275 GINT_TO_POINTER (SESSION_INFO_AUX));
276 g_scanner_scope_add_symbol (scanner, scope_id, "gimp-dock",
277 GINT_TO_POINTER (SESSION_INFO_GIMP_DOCK));
278 g_scanner_scope_add_symbol (scanner, scope_id, "gimp-toolbox",
279 GINT_TO_POINTER (SESSION_INFO_GIMP_TOOLBOX));
280
281 /* For sessionrc files from version <= GIMP 2.6 */
282 g_scanner_scope_add_symbol (scanner, scope_id, "dock",
283 GINT_TO_POINTER (SESSION_INFO_DOCK));
284
285 token = G_TOKEN_LEFT_PAREN;
286
287 while (g_scanner_peek_next_token (scanner) == token)
288 {
289 token = g_scanner_get_next_token (scanner);
290
291 switch (token)
292 {
293 case G_TOKEN_LEFT_PAREN:
294 token = G_TOKEN_SYMBOL;
295 break;
296
297 case G_TOKEN_SYMBOL:
298 switch (GPOINTER_TO_INT (scanner->value.v_symbol))
299 {
300 case SESSION_INFO_FACTORY_ENTRY:
301 {
302 gchar *identifier = NULL;
303 GimpDialogFactoryEntry *entry = NULL;
304
305 token = G_TOKEN_STRING;
306 if (! gimp_scanner_parse_string (scanner, &identifier))
307 goto error;
308
309 entry = gimp_dialog_factory_find_entry (gimp_dialog_factory_get_singleton (),
310 identifier);
311 if (! entry)
312 goto error;
313
314 gimp_session_info_set_factory_entry (info, entry);
315
316 g_free (identifier);
317 }
318 break;
319
320 case SESSION_INFO_POSITION:
321 token = G_TOKEN_INT;
322 if (! gimp_session_info_parse_offset (scanner,
323 &info->p->x,
324 &info->p->right_align))
325 goto error;
326 if (! gimp_session_info_parse_offset (scanner,
327 &info->p->y,
328 &info->p->bottom_align))
329 goto error;
330 break;
331
332 case SESSION_INFO_SIZE:
333 token = G_TOKEN_INT;
334 if (! gimp_scanner_parse_int (scanner, &info->p->width))
335 goto error;
336 if (! gimp_scanner_parse_int (scanner, &info->p->height))
337 goto error;
338 break;
339
340 case SESSION_INFO_MONITOR:
341 token = G_TOKEN_INT;
342 if (! gimp_scanner_parse_int (scanner, &info->p->monitor))
343 goto error;
344 break;
345
346 case SESSION_INFO_OPEN:
347 info->p->open = TRUE;
348
349 /* the screen number is optional */
350 if (g_scanner_peek_next_token (scanner) == G_TOKEN_RIGHT_PAREN)
351 break;
352
353 token = G_TOKEN_INT;
354 if (! gimp_scanner_parse_int (scanner, &info->p->screen))
355 goto error;
356 break;
357
358 case SESSION_INFO_AUX:
359 token = gimp_session_info_aux_deserialize (scanner,
360 &info->p->aux_info);
361 if (token != G_TOKEN_LEFT_PAREN)
362 goto error;
363 break;
364
365 case SESSION_INFO_GIMP_TOOLBOX:
366 case SESSION_INFO_GIMP_DOCK:
367 case SESSION_INFO_DOCK:
368 {
369 GimpSessionInfoDock *dock_info = NULL;
370 const gchar *dock_type = NULL;
371
372 /* Handle old sessionrc:s from versions <= GIMP 2.6 */
373 if (GPOINTER_TO_INT (scanner->value.v_symbol) == SESSION_INFO_DOCK &&
374 info->p->factory_entry &&
375 info->p->factory_entry->identifier &&
376 strcmp ("gimp-toolbox-window",
377 info->p->factory_entry->identifier) == 0)
378 {
379 dock_type = "gimp-toolbox";
380 }
381 else
382 {
383 dock_type = ((GPOINTER_TO_INT (scanner->value.v_symbol) ==
384 SESSION_INFO_GIMP_TOOLBOX) ?
385 "gimp-toolbox" :
386 "gimp-dock");
387 }
388
389 g_scanner_set_scope (scanner, scope_id + 1);
390 token = gimp_session_info_dock_deserialize (scanner, scope_id + 1,
391 &dock_info,
392 dock_type);
393
394 if (token == G_TOKEN_LEFT_PAREN)
395 {
396 g_scanner_set_scope (scanner, scope_id);
397 info->p->docks = g_list_append (info->p->docks, dock_info);
398 }
399 else
400 goto error;
401 }
402 break;
403
404 default:
405 break;
406 }
407 token = G_TOKEN_RIGHT_PAREN;
408 break;
409
410 case G_TOKEN_RIGHT_PAREN:
411 token = G_TOKEN_LEFT_PAREN;
412 break;
413
414 default:
415 break;
416 }
417 }
418
419 error:
420
421 /* If we don't have docks, assume it is a toolbox dock window from a
422 * sessionrc file from GIMP <= 2.6 and add a toolbox dock manually
423 */
424 if (! info->p->docks &&
425 info->p->factory_entry &&
426 strcmp ("gimp-toolbox-window",
427 info->p->factory_entry->identifier) == 0)
428 {
429 info->p->docks =
430 g_list_append (info->p->docks,
431 gimp_session_info_dock_new ("gimp-toolbox"));
432 }
433
434 g_scanner_scope_remove_symbol (scanner, scope_id, "factory-entry");
435 g_scanner_scope_remove_symbol (scanner, scope_id, "position");
436 g_scanner_scope_remove_symbol (scanner, scope_id, "size");
437 g_scanner_scope_remove_symbol (scanner, scope_id, "open-on-exit");
438 g_scanner_scope_remove_symbol (scanner, scope_id, "aux-info");
439 g_scanner_scope_remove_symbol (scanner, scope_id, "gimp-dock");
440 g_scanner_scope_remove_symbol (scanner, scope_id, "gimp-toolbox");
441 g_scanner_scope_remove_symbol (scanner, scope_id, "dock");
442
443 g_scanner_set_scope (scanner, old_scope_id);
444
445 return gimp_config_deserialize_return (scanner, token, nest_level);
446 }
447
448 /**
449 * gimp_session_info_is_for_dock_window:
450 * @info:
451 *
452 * Helper function to determine if the session info is for a dock. It
453 * uses the dialog factory entry state and the associated widget state
454 * if any to determine that.
455 *
456 * Returns: %TRUE if session info is for a dock, %FALSE otherwise.
457 **/
458 static gboolean
gimp_session_info_is_for_dock_window(GimpSessionInfo * info)459 gimp_session_info_is_for_dock_window (GimpSessionInfo *info)
460 {
461 gboolean entry_state_for_dock = info->p->factory_entry == NULL;
462 gboolean widget_state_for_dock = (info->p->widget == NULL ||
463 GIMP_IS_DOCK_WINDOW (info->p->widget));
464
465 return entry_state_for_dock && widget_state_for_dock;
466 }
467
468 static void
gimp_session_info_dialog_show(GtkWidget * widget,GimpSessionInfo * info)469 gimp_session_info_dialog_show (GtkWidget *widget,
470 GimpSessionInfo *info)
471 {
472 gtk_window_move (GTK_WINDOW (widget),
473 info->p->x, info->p->y);
474 }
475
476 static gboolean
gimp_session_info_restore_docks(GimpRestoreDocksData * data)477 gimp_session_info_restore_docks (GimpRestoreDocksData *data)
478 {
479 GimpSessionInfo *info = data->info;
480 GimpDialogFactory *factory = data->factory;
481 GdkScreen *screen = data->screen;
482 gint monitor = data->monitor;
483 GtkWidget *dialog = data->dialog;
484 GList *iter;
485
486 if (GIMP_IS_DOCK_CONTAINER (dialog))
487 {
488 /* We expect expect there to always be docks. In sessionrc files
489 * from <= 2.6 not all dock window entries had dock entries, but we
490 * take care of that during sessionrc parsing
491 */
492 for (iter = info->p->docks; iter; iter = g_list_next (iter))
493 {
494 GimpSessionInfoDock *dock_info = (GimpSessionInfoDock *) iter->data;
495 GtkWidget *dock;
496
497 dock =
498 GTK_WIDGET (gimp_session_info_dock_restore (dock_info,
499 factory,
500 screen,
501 monitor,
502 GIMP_DOCK_CONTAINER (dialog)));
503
504 if (dock && dock_info->position != 0)
505 {
506 GtkWidget *parent = gtk_widget_get_parent (dock);
507
508 if (GTK_IS_PANED (parent))
509 {
510 GtkPaned *paned = GTK_PANED (parent);
511
512 if (dock == gtk_paned_get_child2 (paned))
513 gtk_paned_set_position (paned, dock_info->position);
514 }
515 }
516 }
517 }
518
519 gimp_session_info_clear_info (info);
520
521 g_object_unref (dialog);
522 g_object_unref (screen);
523 g_object_unref (factory);
524 g_object_unref (info);
525
526 g_slice_free (GimpRestoreDocksData, data);
527
528 return FALSE;
529 }
530
531
532 /* public functions */
533
534 GimpSessionInfo *
gimp_session_info_new(void)535 gimp_session_info_new (void)
536 {
537 return g_object_new (GIMP_TYPE_SESSION_INFO, NULL);
538 }
539
540 void
gimp_session_info_restore(GimpSessionInfo * info,GimpDialogFactory * factory,GdkScreen * screen,gint monitor)541 gimp_session_info_restore (GimpSessionInfo *info,
542 GimpDialogFactory *factory,
543 GdkScreen *screen,
544 gint monitor)
545 {
546 GtkWidget *dialog = NULL;
547 GimpRestoreDocksData *data;
548
549 g_return_if_fail (GIMP_IS_SESSION_INFO (info));
550 g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
551 g_return_if_fail (GDK_IS_SCREEN (screen));
552
553 g_object_ref (info);
554
555 if (info->p->screen != DEFAULT_SCREEN)
556 {
557 GdkDisplay *display;
558 GdkScreen *info_screen;
559
560 display = gdk_display_get_default ();
561 info_screen = gdk_display_get_screen (display, info->p->screen);
562
563 if (info_screen)
564 screen = info_screen;
565 }
566
567 info->p->open = FALSE;
568 info->p->screen = DEFAULT_SCREEN;
569
570 if (info->p->factory_entry &&
571 info->p->factory_entry->restore_func)
572 {
573 dialog = info->p->factory_entry->restore_func (factory,
574 screen,
575 monitor,
576 info);
577 }
578 else
579 g_printerr ("EEEEK\n");
580
581 if (GIMP_IS_SESSION_MANAGED (dialog) && info->p->aux_info)
582 gimp_session_managed_set_aux_info (GIMP_SESSION_MANAGED (dialog),
583 info->p->aux_info);
584
585 /* In single-window mode, gimp_session_managed_set_aux_info()
586 * will set the size of the dock areas at the sides. If we don't
587 * wait for those areas to get their size-allocation, we can't
588 * properly restore the docks inside them, so do that in an idle
589 * callback.
590 */
591
592 /* Objects are unreffed again in the callback */
593 data = g_slice_new0 (GimpRestoreDocksData);
594 data->info = g_object_ref (info);
595 data->factory = g_object_ref (factory);
596 data->screen = g_object_ref (screen);
597 data->monitor = monitor;
598 data->dialog = dialog ? g_object_ref (dialog) : NULL;
599
600 g_idle_add ((GSourceFunc) gimp_session_info_restore_docks, data);
601
602 g_object_unref (info);
603 }
604
605 /**
606 * gimp_session_info_apply_geometry:
607 * @info:
608 * @screen:
609 * @current_monitor:
610 *
611 * Apply the geometry stored in the session info object to the
612 * associated widget.
613 **/
614 void
gimp_session_info_apply_geometry(GimpSessionInfo * info,GdkScreen * screen,gint current_monitor,gboolean apply_stored_monitor)615 gimp_session_info_apply_geometry (GimpSessionInfo *info,
616 GdkScreen *screen,
617 gint current_monitor,
618 gboolean apply_stored_monitor)
619 {
620 GdkRectangle rect;
621 GdkRectangle work_rect;
622 gchar geom[32];
623 gint monitor;
624 gint width;
625 gint height;
626
627 g_return_if_fail (GIMP_IS_SESSION_INFO (info));
628 g_return_if_fail (GTK_IS_WINDOW (info->p->widget));
629 g_return_if_fail (GDK_IS_SCREEN (screen));
630
631 monitor = current_monitor;
632
633 if (apply_stored_monitor)
634 {
635 gint n_monitors;
636
637 n_monitors = gdk_screen_get_n_monitors (screen);
638
639 if (info->p->monitor != DEFAULT_MONITOR &&
640 info->p->monitor < n_monitors)
641 {
642 monitor = info->p->monitor;
643 }
644 else
645 {
646 monitor = gdk_screen_get_primary_monitor (screen);
647 }
648 }
649
650 gdk_screen_get_monitor_geometry (screen, monitor, &rect);
651 gdk_screen_get_monitor_workarea (screen, monitor, &work_rect);
652
653 info->p->x += rect.x;
654 info->p->y += rect.y;
655
656 if (gimp_session_info_get_remember_size (info) &&
657 info->p->width > 0 &&
658 info->p->height > 0)
659 {
660 width = info->p->width;
661 height = info->p->height;
662 }
663 else
664 {
665 GtkRequisition requisition;
666
667 gtk_widget_size_request (info->p->widget, &requisition);
668
669 width = requisition.width;
670 height = requisition.height;
671 }
672
673 info->p->x = CLAMP (info->p->x,
674 work_rect.x,
675 work_rect.x + work_rect.width - width);
676 info->p->y = CLAMP (info->p->y,
677 work_rect.y,
678 work_rect.y + work_rect.height - height);
679
680 if (info->p->right_align && info->p->bottom_align)
681 {
682 g_strlcpy (geom, "-0-0", sizeof (geom));
683 }
684 else if (info->p->right_align)
685 {
686 g_snprintf (geom, sizeof (geom), "-0%+d", info->p->y);
687 }
688 else if (info->p->bottom_align)
689 {
690 g_snprintf (geom, sizeof (geom), "%+d-0", info->p->x);
691 }
692 else
693 {
694 g_snprintf (geom, sizeof (geom), "%+d%+d", info->p->x, info->p->y);
695 }
696
697 gtk_window_parse_geometry (GTK_WINDOW (info->p->widget), geom);
698
699 if (gimp_session_info_get_remember_size (info) &&
700 info->p->width > 0 &&
701 info->p->height > 0)
702 {
703 gtk_window_set_default_size (GTK_WINDOW (info->p->widget),
704 info->p->width, info->p->height);
705 }
706
707 /* Window managers and windowing systems suck. They have their own
708 * ideas about WM standards and when it's appropriate to honor
709 * user/application-set window positions and when not. Therefore,
710 * use brute force and "manually" position dialogs whenever they
711 * are shown. This is important especially for transient dialogs,
712 * because window managers behave even "smarter" then...
713 */
714 if (GTK_IS_DIALOG (info->p->widget))
715 g_signal_connect (info->p->widget, "show",
716 G_CALLBACK (gimp_session_info_dialog_show),
717 info);
718 }
719
720 /**
721 * gimp_session_info_read_geometry:
722 * @info: A #GimpSessionInfo
723 * @cevent A #GdkEventConfigure. If set, use the size from here
724 * instead of from the window allocation.
725 *
726 * Read geometry related information from the associated widget.
727 **/
728 void
gimp_session_info_read_geometry(GimpSessionInfo * info,GdkEventConfigure * cevent)729 gimp_session_info_read_geometry (GimpSessionInfo *info,
730 GdkEventConfigure *cevent)
731 {
732 GdkWindow *window;
733 GdkScreen *screen;
734
735 g_return_if_fail (GIMP_IS_SESSION_INFO (info));
736 g_return_if_fail (GTK_IS_WINDOW (info->p->widget));
737
738 window = gtk_widget_get_window (info->p->widget);
739 screen = gtk_widget_get_screen (info->p->widget);
740
741 if (window)
742 {
743 gint x, y;
744 gint monitor;
745 GdkRectangle geometry;
746
747 gdk_window_get_root_origin (window, &x, &y);
748
749 /* Don't write negative values to the sessionrc, they are
750 * interpreted as relative to the right, respective bottom edge
751 * of the screen.
752 */
753 info->p->x = MAX (0, x);
754 info->p->y = MAX (0, y);
755
756 monitor = gdk_screen_get_monitor_at_point (screen,
757 info->p->x, info->p->y);
758 gdk_screen_get_monitor_geometry (screen, monitor, &geometry);
759
760 /* Always store window coordinates relative to the monitor */
761 info->p->x -= geometry.x;
762 info->p->y -= geometry.y;
763
764 if (gimp_session_info_get_remember_size (info))
765 {
766 int width;
767 int height;
768
769 if (cevent)
770 {
771 width = cevent->width;
772 height = cevent->height;
773 }
774 else
775 {
776 GtkAllocation allocation;
777
778 gtk_widget_get_allocation (info->p->widget, &allocation);
779
780 width = allocation.width;
781 height = allocation.height;
782 }
783
784 info->p->width = width;
785 info->p->height = height;
786 }
787 else
788 {
789 info->p->width = 0;
790 info->p->height = 0;
791 }
792
793 info->p->monitor = DEFAULT_MONITOR;
794
795 if (monitor != gdk_screen_get_primary_monitor (screen))
796 info->p->monitor = monitor;
797 }
798
799 info->p->open = FALSE;
800
801 if (gimp_session_info_get_remember_if_open (info))
802 {
803 GimpDialogVisibilityState visibility;
804
805 visibility =
806 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (info->p->widget),
807 GIMP_DIALOG_VISIBILITY_KEY));
808
809 switch (visibility)
810 {
811 case GIMP_DIALOG_VISIBILITY_UNKNOWN:
812 info->p->open = gtk_widget_get_visible (info->p->widget);
813 break;
814
815 case GIMP_DIALOG_VISIBILITY_INVISIBLE:
816 info->p->open = FALSE;
817 break;
818
819 case GIMP_DIALOG_VISIBILITY_HIDDEN:
820 case GIMP_DIALOG_VISIBILITY_VISIBLE:
821 /* Even if a dialog is hidden (with Windows->Hide docks) it
822 * is still considered open. It will be restored the next
823 * time GIMP starts
824 */
825 info->p->open = TRUE;
826 break;
827 }
828 }
829
830 info->p->screen = DEFAULT_SCREEN;
831
832 if (info->p->open)
833 {
834 GdkDisplay *display = gtk_widget_get_display (info->p->widget);
835
836 if (screen != gdk_display_get_default_screen (display))
837 info->p->screen = gdk_screen_get_number (screen);
838 }
839 }
840
841 void
gimp_session_info_get_info(GimpSessionInfo * info)842 gimp_session_info_get_info (GimpSessionInfo *info)
843 {
844 g_return_if_fail (GIMP_IS_SESSION_INFO (info));
845 g_return_if_fail (GTK_IS_WIDGET (info->p->widget));
846
847 gimp_session_info_read_geometry (info, NULL /*cevent*/);
848
849 if (GIMP_IS_SESSION_MANAGED (info->p->widget))
850 info->p->aux_info =
851 gimp_session_managed_get_aux_info (GIMP_SESSION_MANAGED (info->p->widget));
852
853 if (GIMP_IS_DOCK_CONTAINER (info->p->widget))
854 {
855 GimpDockContainer *dock_container = GIMP_DOCK_CONTAINER (info->p->widget);
856 GList *iter = NULL;
857 GList *docks;
858
859 docks = gimp_dock_container_get_docks (dock_container);
860
861 for (iter = docks;
862 iter;
863 iter = g_list_next (iter))
864 {
865 GimpDock *dock = GIMP_DOCK (iter->data);
866
867 info->p->docks =
868 g_list_append (info->p->docks,
869 gimp_session_info_dock_from_widget (dock));
870 }
871
872 g_list_free (docks);
873 }
874 }
875
876 /**
877 * gimp_session_info_get_info_with_widget:
878 * @info:
879 * @widget: #GtkWidget to use
880 *
881 * Temporarily sets @widget on @info and calls
882 * gimp_session_info_get_info(), then restores the old widget that was
883 * set.
884 **/
885 void
gimp_session_info_get_info_with_widget(GimpSessionInfo * info,GtkWidget * widget)886 gimp_session_info_get_info_with_widget (GimpSessionInfo *info,
887 GtkWidget *widget)
888 {
889 GtkWidget *old_widget;
890
891 g_return_if_fail (GIMP_IS_SESSION_INFO (info));
892 g_return_if_fail (GTK_IS_WIDGET (widget));
893
894 old_widget = gimp_session_info_get_widget (info);
895
896 gimp_session_info_set_widget (info, widget);
897 gimp_session_info_get_info (info);
898 gimp_session_info_set_widget (info, old_widget);
899 }
900
901 void
gimp_session_info_clear_info(GimpSessionInfo * info)902 gimp_session_info_clear_info (GimpSessionInfo *info)
903 {
904 g_return_if_fail (GIMP_IS_SESSION_INFO (info));
905
906 if (info->p->aux_info)
907 {
908 g_list_free_full (info->p->aux_info,
909 (GDestroyNotify) gimp_session_info_aux_free);
910 info->p->aux_info = NULL;
911 }
912
913 if (info->p->docks)
914 {
915 g_list_free_full (info->p->docks,
916 (GDestroyNotify) gimp_session_info_dock_free);
917 info->p->docks = NULL;
918 }
919 }
920
921 gboolean
gimp_session_info_is_singleton(GimpSessionInfo * info)922 gimp_session_info_is_singleton (GimpSessionInfo *info)
923 {
924 g_return_val_if_fail (GIMP_IS_SESSION_INFO (info), FALSE);
925
926 return (! gimp_session_info_is_for_dock_window (info) &&
927 info->p->factory_entry &&
928 info->p->factory_entry->singleton);
929 }
930
931 gboolean
gimp_session_info_is_session_managed(GimpSessionInfo * info)932 gimp_session_info_is_session_managed (GimpSessionInfo *info)
933 {
934 g_return_val_if_fail (GIMP_IS_SESSION_INFO (info), FALSE);
935
936 return (gimp_session_info_is_for_dock_window (info) ||
937 (info->p->factory_entry &&
938 info->p->factory_entry->session_managed));
939 }
940
941
942 gboolean
gimp_session_info_get_remember_size(GimpSessionInfo * info)943 gimp_session_info_get_remember_size (GimpSessionInfo *info)
944 {
945 g_return_val_if_fail (GIMP_IS_SESSION_INFO (info), FALSE);
946
947 return (gimp_session_info_is_for_dock_window (info) ||
948 (info->p->factory_entry &&
949 info->p->factory_entry->remember_size));
950 }
951
952 gboolean
gimp_session_info_get_remember_if_open(GimpSessionInfo * info)953 gimp_session_info_get_remember_if_open (GimpSessionInfo *info)
954 {
955 g_return_val_if_fail (GIMP_IS_SESSION_INFO (info), FALSE);
956
957 return (gimp_session_info_is_for_dock_window (info) ||
958 (info->p->factory_entry &&
959 info->p->factory_entry->remember_if_open));
960 }
961
962 GtkWidget *
gimp_session_info_get_widget(GimpSessionInfo * info)963 gimp_session_info_get_widget (GimpSessionInfo *info)
964 {
965 g_return_val_if_fail (GIMP_IS_SESSION_INFO (info), FALSE);
966
967 return info->p->widget;
968 }
969
970 void
gimp_session_info_set_widget(GimpSessionInfo * info,GtkWidget * widget)971 gimp_session_info_set_widget (GimpSessionInfo *info,
972 GtkWidget *widget)
973 {
974 g_return_if_fail (GIMP_IS_SESSION_INFO (info));
975
976 if (GTK_IS_DIALOG (info->p->widget))
977 g_signal_handlers_disconnect_by_func (info->p->widget,
978 gimp_session_info_dialog_show,
979 info);
980
981 info->p->widget = widget;
982 }
983
984 GimpDialogFactoryEntry *
gimp_session_info_get_factory_entry(GimpSessionInfo * info)985 gimp_session_info_get_factory_entry (GimpSessionInfo *info)
986 {
987 g_return_val_if_fail (GIMP_IS_SESSION_INFO (info), FALSE);
988
989 return info->p->factory_entry;
990 }
991
992 void
gimp_session_info_set_factory_entry(GimpSessionInfo * info,GimpDialogFactoryEntry * entry)993 gimp_session_info_set_factory_entry (GimpSessionInfo *info,
994 GimpDialogFactoryEntry *entry)
995 {
996 g_return_if_fail (GIMP_IS_SESSION_INFO (info));
997
998 info->p->factory_entry = entry;
999 }
1000
1001 gboolean
gimp_session_info_get_open(GimpSessionInfo * info)1002 gimp_session_info_get_open (GimpSessionInfo *info)
1003 {
1004 g_return_val_if_fail (GIMP_IS_SESSION_INFO (info), FALSE);
1005
1006 return info->p->open;
1007 }
1008
1009 gint
gimp_session_info_get_x(GimpSessionInfo * info)1010 gimp_session_info_get_x (GimpSessionInfo *info)
1011 {
1012 g_return_val_if_fail (GIMP_IS_SESSION_INFO (info), 0);
1013
1014 return info->p->x;
1015 }
1016
1017 gint
gimp_session_info_get_y(GimpSessionInfo * info)1018 gimp_session_info_get_y (GimpSessionInfo *info)
1019 {
1020 g_return_val_if_fail (GIMP_IS_SESSION_INFO (info), 0);
1021
1022 return info->p->y;
1023 }
1024
1025 gint
gimp_session_info_get_width(GimpSessionInfo * info)1026 gimp_session_info_get_width (GimpSessionInfo *info)
1027 {
1028 g_return_val_if_fail (GIMP_IS_SESSION_INFO (info), 0);
1029
1030 return info->p->width;
1031 }
1032
1033 gint
gimp_session_info_get_height(GimpSessionInfo * info)1034 gimp_session_info_get_height (GimpSessionInfo *info)
1035 {
1036 g_return_val_if_fail (GIMP_IS_SESSION_INFO (info), 0);
1037
1038 return info->p->height;
1039 }
1040
1041 static gint position_accuracy = 0;
1042
1043 /**
1044 * gimp_session_info_set_position_accuracy:
1045 * @accuracy:
1046 *
1047 * When writing sessionrc, make positions and sizes a multiple of
1048 * @accuracy. Meant to be used by test cases that does regression
1049 * testing on session managed window positions and sizes, to allow for
1050 * some deviations from the original setup, that the window manager
1051 * might impose.
1052 **/
1053 void
gimp_session_info_set_position_accuracy(gint accuracy)1054 gimp_session_info_set_position_accuracy (gint accuracy)
1055 {
1056 position_accuracy = accuracy;
1057 }
1058
1059 /**
1060 * gimp_session_info_apply_position_accuracy:
1061 * @position:
1062 *
1063 * Rounds @position to the nearest multiple of what was set with
1064 * gimp_session_info_set_position_accuracy().
1065 *
1066 * Returns: Result.
1067 **/
1068 gint
gimp_session_info_apply_position_accuracy(gint position)1069 gimp_session_info_apply_position_accuracy (gint position)
1070 {
1071 if (position_accuracy > 0)
1072 {
1073 gint to_floor = position + position_accuracy / 2;
1074
1075 return to_floor - to_floor % position_accuracy;
1076 }
1077
1078 return position;
1079 }
1080