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