1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpdialogfactory.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 <stdlib.h>
24 #include <string.h>
25 
26 #include <gegl.h>
27 #include <gtk/gtk.h>
28 
29 #include "libgimpconfig/gimpconfig.h"
30 #include "libgimpwidgets/gimpwidgets.h"
31 
32 #include "widgets-types.h"
33 
34 #include "config/gimpguiconfig.h"
35 
36 #include "core/gimp.h"
37 #include "core/gimpcontext.h"
38 #include "core/gimpmarshal.h"
39 
40 #include "gimpcursor.h"
41 #include "gimpdialogfactory.h"
42 #include "gimpdock.h"
43 #include "gimpdockbook.h"
44 #include "gimpdockable.h"
45 #include "gimpdockcontainer.h"
46 #include "gimpdockwindow.h"
47 #include "gimpmenufactory.h"
48 #include "gimpsessioninfo.h"
49 #include "gimpwidgets-utils.h"
50 
51 #include "gimp-log.h"
52 
53 
54 enum
55 {
56   DOCK_WINDOW_ADDED,
57   DOCK_WINDOW_REMOVED,
58   LAST_SIGNAL
59 };
60 
61 
62 struct _GimpDialogFactoryPrivate
63 {
64   GimpContext      *context;
65   GimpMenuFactory  *menu_factory;
66 
67   GList            *open_dialogs;
68   GList            *session_infos;
69 
70   GList            *registered_dialogs;
71 
72   GimpDialogsState  dialog_state;
73 };
74 
75 
76 static void        gimp_dialog_factory_dispose              (GObject                *object);
77 static void        gimp_dialog_factory_finalize             (GObject                *object);
78 static GtkWidget * gimp_dialog_factory_constructor          (GimpDialogFactory      *factory,
79                                                              GimpDialogFactoryEntry *entry,
80                                                              GimpContext            *context,
81                                                              GimpUIManager          *ui_manager,
82                                                              gint                    view_size);
83 static void        gimp_dialog_factory_config_notify        (GimpDialogFactory      *factory,
84                                                              GParamSpec             *pspec,
85                                                              GimpGuiConfig          *config);
86 static void        gimp_dialog_factory_set_widget_data      (GtkWidget              *dialog,
87                                                              GimpDialogFactory      *factory,
88                                                              GimpDialogFactoryEntry *entry);
89 static void        gimp_dialog_factory_unset_widget_data    (GtkWidget              *dialog);
90 static gboolean    gimp_dialog_factory_set_user_pos         (GtkWidget              *dialog,
91                                                              GdkEventConfigure      *cevent,
92                                                              gpointer                data);
93 static gboolean    gimp_dialog_factory_dialog_configure     (GtkWidget              *dialog,
94                                                              GdkEventConfigure      *cevent,
95                                                              GimpDialogFactory      *factory);
96 static void        gimp_dialog_factory_hide                 (GimpDialogFactory      *factory);
97 static void        gimp_dialog_factory_show                 (GimpDialogFactory      *factory);
98 
99 
100 G_DEFINE_TYPE_WITH_PRIVATE (GimpDialogFactory, gimp_dialog_factory,
101                             GIMP_TYPE_OBJECT)
102 
103 #define parent_class gimp_dialog_factory_parent_class
104 
105 static guint factory_signals[LAST_SIGNAL] = { 0 };
106 
107 
108 /* Is set by dialogs.c to a dialog factory initialized there.
109  *
110  * FIXME: The layer above should not do this kind of initialization of
111  * layers below.
112  */
113 static GimpDialogFactory *gimp_toplevel_factory = NULL;
114 
115 
116 static void
gimp_dialog_factory_class_init(GimpDialogFactoryClass * klass)117 gimp_dialog_factory_class_init (GimpDialogFactoryClass *klass)
118 {
119   GObjectClass *object_class = G_OBJECT_CLASS (klass);
120 
121   object_class->dispose  = gimp_dialog_factory_dispose;
122   object_class->finalize = gimp_dialog_factory_finalize;
123 
124   factory_signals[DOCK_WINDOW_ADDED] =
125     g_signal_new ("dock-window-added",
126                   G_TYPE_FROM_CLASS (klass),
127                   G_SIGNAL_RUN_LAST,
128                   G_STRUCT_OFFSET (GimpDialogFactoryClass, dock_window_added),
129                   NULL, NULL,
130                   gimp_marshal_VOID__OBJECT,
131                   G_TYPE_NONE, 1,
132                   GIMP_TYPE_DOCK_WINDOW);
133 
134   factory_signals[DOCK_WINDOW_REMOVED] =
135     g_signal_new ("dock-window-removed",
136                   G_TYPE_FROM_CLASS (klass),
137                   G_SIGNAL_RUN_LAST,
138                   G_STRUCT_OFFSET (GimpDialogFactoryClass, dock_window_removed),
139                   NULL, NULL,
140                   gimp_marshal_VOID__OBJECT,
141                   G_TYPE_NONE, 1,
142                   GIMP_TYPE_DOCK_WINDOW);
143 }
144 
145 static void
gimp_dialog_factory_init(GimpDialogFactory * factory)146 gimp_dialog_factory_init (GimpDialogFactory *factory)
147 {
148   factory->p = gimp_dialog_factory_get_instance_private (factory);
149   factory->p->dialog_state = GIMP_DIALOGS_SHOWN;
150 }
151 
152 static void
gimp_dialog_factory_dispose(GObject * object)153 gimp_dialog_factory_dispose (GObject *object)
154 {
155   GimpDialogFactory *factory = GIMP_DIALOG_FACTORY (object);
156   GList             *list;
157 
158   /*  start iterating from the beginning each time we destroyed a
159    *  toplevel because destroying a dock may cause lots of items
160    *  to be removed from factory->p->open_dialogs
161    */
162   while (factory->p->open_dialogs)
163     {
164       for (list = factory->p->open_dialogs; list; list = g_list_next (list))
165         {
166           if (gtk_widget_is_toplevel (list->data))
167             {
168               gtk_widget_destroy (GTK_WIDGET (list->data));
169               break;
170             }
171         }
172 
173       /*  the list being non-empty without any toplevel is an error,
174        *  so eek and chain up
175        */
176       if (! list)
177         {
178           g_warning ("%s: %d stale non-toplevel entries in factory->p->open_dialogs",
179                      G_STRFUNC, g_list_length (factory->p->open_dialogs));
180           break;
181         }
182     }
183 
184   if (factory->p->open_dialogs)
185     {
186       g_list_free (factory->p->open_dialogs);
187       factory->p->open_dialogs = NULL;
188     }
189 
190   if (factory->p->session_infos)
191     {
192       g_list_free_full (factory->p->session_infos,
193                         (GDestroyNotify) g_object_unref);
194       factory->p->session_infos = NULL;
195     }
196 
197   G_OBJECT_CLASS (parent_class)->dispose (object);
198 }
199 
200 static void
gimp_dialog_factory_finalize(GObject * object)201 gimp_dialog_factory_finalize (GObject *object)
202 {
203   GimpDialogFactory *factory = GIMP_DIALOG_FACTORY (object);
204   GList             *list;
205 
206   for (list = factory->p->registered_dialogs; list; list = g_list_next (list))
207     {
208       GimpDialogFactoryEntry *entry = list->data;
209 
210       g_free (entry->identifier);
211       g_free (entry->name);
212       g_free (entry->blurb);
213       g_free (entry->icon_name);
214       g_free (entry->help_id);
215 
216       g_slice_free (GimpDialogFactoryEntry, entry);
217     }
218 
219   if (factory->p->registered_dialogs)
220     {
221       g_list_free (factory->p->registered_dialogs);
222       factory->p->registered_dialogs = NULL;
223     }
224 
225   G_OBJECT_CLASS (parent_class)->finalize (object);
226 }
227 
228 GimpDialogFactory *
gimp_dialog_factory_new(const gchar * name,GimpContext * context,GimpMenuFactory * menu_factory)229 gimp_dialog_factory_new (const gchar           *name,
230                          GimpContext           *context,
231                          GimpMenuFactory       *menu_factory)
232 {
233   GimpDialogFactory *factory;
234   GimpGuiConfig     *config;
235 
236   g_return_val_if_fail (name != NULL, NULL);
237   g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
238   g_return_val_if_fail (! menu_factory || GIMP_IS_MENU_FACTORY (menu_factory),
239                         NULL);
240 
241   factory = g_object_new (GIMP_TYPE_DIALOG_FACTORY, NULL);
242 
243   gimp_object_set_name (GIMP_OBJECT (factory), name);
244 
245   config = GIMP_GUI_CONFIG (context->gimp->config);
246 
247   factory->p->context      = context;
248   factory->p->menu_factory = menu_factory;
249   factory->p->dialog_state = (config->hide_docks ?
250                               GIMP_DIALOGS_HIDDEN_EXPLICITLY :
251                               GIMP_DIALOGS_SHOWN);
252 
253   g_signal_connect_object (config, "notify::hide-docks",
254                            G_CALLBACK (gimp_dialog_factory_config_notify),
255                            factory, G_CONNECT_SWAPPED);
256 
257   return factory;
258 }
259 
260 void
gimp_dialog_factory_register_entry(GimpDialogFactory * factory,const gchar * identifier,const gchar * name,const gchar * blurb,const gchar * icon_name,const gchar * help_id,GimpDialogNewFunc new_func,GimpDialogRestoreFunc restore_func,gint view_size,gboolean singleton,gboolean session_managed,gboolean remember_size,gboolean remember_if_open,gboolean hideable,gboolean image_window,gboolean dockable)261 gimp_dialog_factory_register_entry (GimpDialogFactory    *factory,
262                                     const gchar          *identifier,
263                                     const gchar          *name,
264                                     const gchar          *blurb,
265                                     const gchar          *icon_name,
266                                     const gchar          *help_id,
267                                     GimpDialogNewFunc     new_func,
268                                     GimpDialogRestoreFunc restore_func,
269                                     gint                  view_size,
270                                     gboolean              singleton,
271                                     gboolean              session_managed,
272                                     gboolean              remember_size,
273                                     gboolean              remember_if_open,
274                                     gboolean              hideable,
275                                     gboolean              image_window,
276                                     gboolean              dockable)
277 {
278   GimpDialogFactoryEntry *entry;
279 
280   g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
281   g_return_if_fail (identifier != NULL);
282 
283   entry = g_slice_new0 (GimpDialogFactoryEntry);
284 
285   entry->identifier       = g_strdup (identifier);
286   entry->name             = g_strdup (name);
287   entry->blurb            = g_strdup (blurb);
288   entry->icon_name        = g_strdup (icon_name);
289   entry->help_id          = g_strdup (help_id);
290   entry->new_func         = new_func;
291   entry->restore_func     = restore_func;
292   entry->view_size        = view_size;
293   entry->singleton        = singleton ? TRUE : FALSE;
294   entry->session_managed  = session_managed ? TRUE : FALSE;
295   entry->remember_size    = remember_size ? TRUE : FALSE;
296   entry->remember_if_open = remember_if_open ? TRUE : FALSE;
297   entry->hideable         = hideable ? TRUE : FALSE;
298   entry->image_window     = image_window ? TRUE : FALSE;
299   entry->dockable         = dockable ? TRUE : FALSE;
300 
301   factory->p->registered_dialogs = g_list_prepend (factory->p->registered_dialogs,
302                                                    entry);
303 }
304 
305 GimpDialogFactoryEntry *
gimp_dialog_factory_find_entry(GimpDialogFactory * factory,const gchar * identifier)306 gimp_dialog_factory_find_entry (GimpDialogFactory *factory,
307                                 const gchar       *identifier)
308 {
309   GList *list;
310 
311   g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
312   g_return_val_if_fail (identifier != NULL, NULL);
313 
314   for (list = factory->p->registered_dialogs; list; list = g_list_next (list))
315     {
316       GimpDialogFactoryEntry *entry = list->data;
317 
318       if (! strcmp (identifier, entry->identifier))
319         return entry;
320     }
321 
322   return NULL;
323 }
324 
325 GimpSessionInfo *
gimp_dialog_factory_find_session_info(GimpDialogFactory * factory,const gchar * identifier)326 gimp_dialog_factory_find_session_info (GimpDialogFactory *factory,
327                                        const gchar       *identifier)
328 {
329   GList *list;
330 
331   g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
332   g_return_val_if_fail (identifier != NULL, NULL);
333 
334   for (list = factory->p->session_infos; list; list = g_list_next (list))
335     {
336       GimpSessionInfo *info = list->data;
337 
338       if (gimp_session_info_get_factory_entry (info) &&
339           g_str_equal (identifier,
340                        gimp_session_info_get_factory_entry (info)->identifier))
341         {
342           return info;
343         }
344     }
345 
346   return NULL;
347 }
348 
349 GtkWidget *
gimp_dialog_factory_find_widget(GimpDialogFactory * factory,const gchar * identifiers)350 gimp_dialog_factory_find_widget (GimpDialogFactory *factory,
351                                  const gchar       *identifiers)
352 {
353   GtkWidget  *widget = NULL;
354   gchar     **ids;
355   gint        i;
356 
357   g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
358   g_return_val_if_fail (identifiers != NULL, NULL);
359 
360   ids = g_strsplit (identifiers, "|", 0);
361 
362   for (i = 0; ids[i]; i++)
363     {
364       GimpSessionInfo *info;
365 
366       info = gimp_dialog_factory_find_session_info (factory, ids[i]);
367 
368       if (info)
369         {
370           widget =  gimp_session_info_get_widget (info);
371 
372           if (widget)
373             break;
374         }
375     }
376 
377   g_strfreev (ids);
378 
379   return widget;
380 }
381 
382 /**
383  * gimp_dialog_factory_dialog_sane:
384  * @factory:
385  * @widget_factory:
386  * @widget_entry:
387  * @widget:
388  *
389  * Makes sure that the @widget with the given @widget_entry that was
390  * created by the given @widget_factory belongs to @efactory.
391  *
392  * Returns: %TRUE if that is the case, %FALSE otherwise.
393  **/
394 static gboolean
gimp_dialog_factory_dialog_sane(GimpDialogFactory * factory,GimpDialogFactory * widget_factory,GimpDialogFactoryEntry * widget_entry,GtkWidget * widget)395 gimp_dialog_factory_dialog_sane (GimpDialogFactory      *factory,
396                                  GimpDialogFactory      *widget_factory,
397                                  GimpDialogFactoryEntry *widget_entry,
398                                  GtkWidget              *widget)
399 {
400   if (! widget_factory || ! widget_entry)
401     {
402       g_warning ("%s: dialog was not created by a GimpDialogFactory",
403                  G_STRFUNC);
404       return FALSE;
405     }
406 
407   if (widget_factory != factory)
408     {
409       g_warning ("%s: dialog was created by a different GimpDialogFactory",
410                  G_STRFUNC);
411       return FALSE;
412     }
413 
414   return TRUE;
415 }
416 
417 /**
418  * gimp_dialog_factory_dialog_new_internal:
419  * @factory:
420  * @screen:
421  * @context:
422  * @ui_manager:
423  * @identifier:
424  * @view_size:
425  * @return_existing:   If %TRUE, (or if the dialog is a singleton),
426  *                     don't create a new dialog if it exists, instead
427  *                     return the existing one
428  * @present:           If %TRUE, the toplevel that contains the dialog (if any)
429  *                     will be gtk_window_present():ed
430  * @create_containers: If %TRUE, then containers for the
431  *                     dialog/dockable will be created as well. If you
432  *                     want to manage your own containers, pass %FALSE
433  *
434  * This is the lowest level dialog factory creation function.
435  *
436  * Returns: A created or existing #GtkWidget.
437  **/
438 static GtkWidget *
gimp_dialog_factory_dialog_new_internal(GimpDialogFactory * factory,GdkScreen * screen,gint monitor,GimpContext * context,GimpUIManager * ui_manager,const gchar * identifier,gint view_size,gboolean return_existing,gboolean present,gboolean create_containers)439 gimp_dialog_factory_dialog_new_internal (GimpDialogFactory *factory,
440                                          GdkScreen         *screen,
441                                          gint               monitor,
442                                          GimpContext       *context,
443                                          GimpUIManager     *ui_manager,
444                                          const gchar       *identifier,
445                                          gint               view_size,
446                                          gboolean           return_existing,
447                                          gboolean           present,
448                                          gboolean           create_containers)
449 {
450   GimpDialogFactoryEntry *entry    = NULL;
451   GtkWidget              *dialog   = NULL;
452   GtkWidget              *toplevel = NULL;
453 
454   g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
455   g_return_val_if_fail (identifier != NULL, NULL);
456 
457   entry = gimp_dialog_factory_find_entry (factory, identifier);
458 
459   if (! entry)
460     {
461       g_warning ("%s: no entry registered for \"%s\"",
462                  G_STRFUNC, identifier);
463       return NULL;
464     }
465 
466   if (! entry->new_func)
467     {
468       g_warning ("%s: entry for \"%s\" has no constructor",
469                  G_STRFUNC, identifier);
470       return NULL;
471     }
472 
473   /*  a singleton dialog is always returned if it already exists  */
474   if (return_existing || entry->singleton)
475     {
476       dialog = gimp_dialog_factory_find_widget (factory, identifier);
477     }
478 
479   /*  create the dialog if it was not found  */
480   if (! dialog)
481     {
482       GtkWidget *dock              = NULL;
483       GtkWidget *dock_window       = NULL;
484 
485       /* What follows is special-case code for some entries. At some
486        * point we might want to abstract this block of code away.
487        */
488       if (create_containers)
489         {
490           if (entry->dockable)
491             {
492               GtkWidget *dockbook;
493 
494               /*  It doesn't make sense to have a dockable without a dock
495                *  so create one. Create a new dock _before_ creating the
496                *  dialog. We do this because the new dockable needs to be
497                *  created in its dock's context.
498                */
499               dock     = gimp_dock_with_window_new (factory,
500                                                     screen,
501                                                     monitor,
502                                                     FALSE /*toolbox*/);
503               dockbook = gimp_dockbook_new (factory->p->menu_factory);
504 
505               gimp_dock_add_book (GIMP_DOCK (dock),
506                                   GIMP_DOCKBOOK (dockbook),
507                                   0);
508             }
509           else if (strcmp ("gimp-toolbox", entry->identifier) == 0)
510             {
511               GimpDockContainer *dock_container;
512 
513               dock_window = gimp_dialog_factory_dialog_new (factory,
514                                                             screen,
515                                                             monitor,
516                                                             NULL /*ui_manager*/,
517                                                             "gimp-toolbox-window",
518                                                             -1 /*view_size*/,
519                                                             FALSE /*present*/);
520 
521               /* When we get a dock window, we also get a UI
522                * manager
523                */
524               dock_container = GIMP_DOCK_CONTAINER (dock_window);
525               ui_manager     = gimp_dock_container_get_ui_manager (dock_container);
526             }
527         }
528 
529       /*  Create the new dialog in the appropriate context which is
530        *  - the passed context if not NULL
531        *  - the newly created dock's context if we just created it
532        *  - the factory's context, which happens when raising a toplevel
533        *    dialog was the original request.
534        */
535       if (view_size < GIMP_VIEW_SIZE_TINY)
536         view_size = entry->view_size;
537 
538       if (context)
539         dialog = gimp_dialog_factory_constructor (factory, entry,
540                                                   context,
541                                                   ui_manager,
542                                                   view_size);
543       else if (dock)
544         dialog = gimp_dialog_factory_constructor (factory, entry,
545                                                   gimp_dock_get_context (GIMP_DOCK (dock)),
546                                                   gimp_dock_get_ui_manager (GIMP_DOCK (dock)),
547                                                   view_size);
548       else
549         dialog = gimp_dialog_factory_constructor (factory, entry,
550                                                   factory->p->context,
551                                                   ui_manager,
552                                                   view_size);
553 
554       if (dialog)
555         {
556           gimp_dialog_factory_set_widget_data (dialog, factory, entry);
557 
558           /*  If we created a dock before, the newly created dialog is
559            *  supposed to be a GimpDockable.
560            */
561           if (dock)
562             {
563               if (GIMP_IS_DOCKABLE (dialog))
564                 {
565                   gimp_dock_add (GIMP_DOCK (dock), GIMP_DOCKABLE (dialog),
566                                  0, 0);
567 
568                   gtk_widget_show (dock);
569                 }
570               else
571                 {
572                   g_warning ("%s: GimpDialogFactory is a dockable factory "
573                              "but constructor for \"%s\" did not return a "
574                              "GimpDockable",
575                              G_STRFUNC, identifier);
576 
577                   gtk_widget_destroy (dialog);
578                   gtk_widget_destroy (dock);
579 
580                   dialog = NULL;
581                   dock   = NULL;
582                 }
583             }
584           else if (dock_window)
585             {
586               if (GIMP_IS_DOCK (dialog))
587                 {
588                   gimp_dock_window_add_dock (GIMP_DOCK_WINDOW (dock_window),
589                                              GIMP_DOCK (dialog),
590                                              -1 /*index*/);
591 
592                   gtk_widget_set_visible (dialog, present);
593                   gtk_widget_set_visible (dock_window, present);
594                 }
595               else
596                 {
597                   g_warning ("%s: GimpDialogFactory is a dock factory entry "
598                              "but constructor for \"%s\" did not return a "
599                              "GimpDock",
600                              G_STRFUNC, identifier);
601 
602                   gtk_widget_destroy (dialog);
603                   gtk_widget_destroy (dock_window);
604 
605                   dialog      = NULL;
606                   dock_window = NULL;
607                 }
608             }
609         }
610       else if (dock)
611         {
612           g_warning ("%s: constructor for \"%s\" returned NULL",
613                      G_STRFUNC, identifier);
614 
615           gtk_widget_destroy (dock);
616 
617           dock = NULL;
618         }
619 
620       if (dialog)
621         gimp_dialog_factory_add_dialog (factory, dialog, screen, monitor);
622     }
623 
624   /*  Finally, if we found an existing dialog or created a new one, raise it.
625    */
626   if (! dialog)
627     return NULL;
628 
629   if (gtk_widget_is_toplevel (dialog))
630     {
631       gtk_window_set_screen (GTK_WINDOW (dialog), screen);
632 
633       toplevel = dialog;
634     }
635   else if (GIMP_IS_DOCK (dialog))
636     {
637       toplevel = gtk_widget_get_toplevel (dialog);
638     }
639   else if (GIMP_IS_DOCKABLE (dialog))
640     {
641       GimpDockable *dockable = GIMP_DOCKABLE (dialog);
642 
643       if (gimp_dockable_get_dockbook (dockable) &&
644           gimp_dockbook_get_dock (gimp_dockable_get_dockbook (dockable)))
645         {
646           GtkNotebook *notebook = GTK_NOTEBOOK (gimp_dockable_get_dockbook (dockable));
647           gint         num      = gtk_notebook_page_num (notebook, dialog);
648 
649           if (num != -1)
650             {
651               gtk_notebook_set_current_page (notebook, num);
652 
653               gimp_widget_blink (dialog);
654             }
655         }
656 
657       toplevel = gtk_widget_get_toplevel (dialog);
658     }
659 
660   if (present && GTK_IS_WINDOW (toplevel))
661     {
662       /*  Work around focus-stealing protection, or whatever makes the
663        *  dock appear below the one where we clicked a button to open
664        *  it. See bug #630173.
665        */
666       gtk_widget_show_now (toplevel);
667       gdk_window_raise (gtk_widget_get_window (toplevel));
668     }
669 
670   return dialog;
671 }
672 
673 /**
674  * gimp_dialog_factory_dialog_new:
675  * @factory:      a #GimpDialogFactory
676  * @screen:       the #GdkScreen the dialog should appear on
677  * @ui_manager:   A #GimpUIManager, if applicable.
678  * @identifier:   the identifier of the dialog as registered with
679  *                gimp_dialog_factory_register_entry()
680  * @view_size:    the initial preview size
681  * @present:      whether gtk_window_present() should be called
682  *
683  * Creates a new toplevel dialog or a #GimpDockable, depending on whether
684  * %factory is a toplevel of dockable factory.
685  *
686  * Return value: the newly created dialog or an already existing singleton
687  *               dialog.
688  **/
689 GtkWidget *
gimp_dialog_factory_dialog_new(GimpDialogFactory * factory,GdkScreen * screen,gint monitor,GimpUIManager * ui_manager,const gchar * identifier,gint view_size,gboolean present)690 gimp_dialog_factory_dialog_new (GimpDialogFactory *factory,
691                                 GdkScreen         *screen,
692                                 gint               monitor,
693                                 GimpUIManager     *ui_manager,
694                                 const gchar       *identifier,
695                                 gint               view_size,
696                                 gboolean           present)
697 {
698   g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
699   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
700   g_return_val_if_fail (identifier != NULL, NULL);
701 
702   return gimp_dialog_factory_dialog_new_internal (factory,
703                                                   screen,
704                                                   monitor,
705                                                   factory->p->context,
706                                                   ui_manager,
707                                                   identifier,
708                                                   view_size,
709                                                   FALSE /*return_existing*/,
710                                                   present,
711                                                   FALSE /*create_containers*/);
712 }
713 
714 GimpContext *
gimp_dialog_factory_get_context(GimpDialogFactory * factory)715 gimp_dialog_factory_get_context (GimpDialogFactory *factory)
716 {
717   g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
718 
719   return factory->p->context;
720 }
721 
722 GimpMenuFactory *
gimp_dialog_factory_get_menu_factory(GimpDialogFactory * factory)723 gimp_dialog_factory_get_menu_factory (GimpDialogFactory *factory)
724 {
725   g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
726 
727   return factory->p->menu_factory;
728 }
729 
730 GList *
gimp_dialog_factory_get_open_dialogs(GimpDialogFactory * factory)731 gimp_dialog_factory_get_open_dialogs (GimpDialogFactory *factory)
732 {
733   g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
734 
735   return factory->p->open_dialogs;
736 }
737 
738 GList *
gimp_dialog_factory_get_session_infos(GimpDialogFactory * factory)739 gimp_dialog_factory_get_session_infos (GimpDialogFactory *factory)
740 {
741   g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
742 
743   return factory->p->session_infos;
744 }
745 
746 void
gimp_dialog_factory_add_session_info(GimpDialogFactory * factory,GimpSessionInfo * info)747 gimp_dialog_factory_add_session_info (GimpDialogFactory *factory,
748                                       GimpSessionInfo   *info)
749 {
750   g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
751   g_return_if_fail (GIMP_IS_SESSION_INFO (info));
752 
753   /* We want to append rather than prepend so that the serialized
754    * order in sessionrc remains the same
755    */
756   factory->p->session_infos = g_list_append (factory->p->session_infos,
757                                              g_object_ref (info));
758 }
759 
760 /**
761  * gimp_dialog_factory_dialog_raise:
762  * @factory:      a #GimpDialogFactory
763  * @screen:       the #GdkScreen the dialog should appear on
764  * @identifiers:  a '|' separated list of identifiers of dialogs as
765  *                registered with gimp_dialog_factory_register_entry()
766  * @view_size:    the initial preview size if a dialog needs to be created
767  *
768  * Raises any of a list of already existing toplevel dialog or
769  * #GimpDockable if it was already created by this %facory.
770  *
771  * Implicitly creates the first dialog in the list if none of the dialogs
772  * were found.
773  *
774  * Return value: the raised or newly created dialog.
775  **/
776 GtkWidget *
gimp_dialog_factory_dialog_raise(GimpDialogFactory * factory,GdkScreen * screen,gint monitor,const gchar * identifiers,gint view_size)777 gimp_dialog_factory_dialog_raise (GimpDialogFactory *factory,
778                                   GdkScreen         *screen,
779                                   gint               monitor,
780                                   const gchar       *identifiers,
781                                   gint               view_size)
782 {
783   GtkWidget *dialog;
784   gchar    **ids;
785   gint       i;
786 
787   g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
788   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
789   g_return_val_if_fail (identifiers != NULL, NULL);
790 
791   /*  If the identifier is a list, try to find a matching dialog and
792    *  raise it. If there's no match, use the first list item.
793    *
794    *  (we split the identifier list manually here because we must pass
795    *  a single identifier, not a list, to new_internal() below)
796    */
797   ids = g_strsplit (identifiers, "|", 0);
798   for (i = 0; ids[i]; i++)
799     {
800       if (gimp_dialog_factory_find_widget (factory, ids[i]))
801         break;
802     }
803 
804   dialog = gimp_dialog_factory_dialog_new_internal (factory,
805                                                     screen,
806                                                     monitor,
807                                                     NULL,
808                                                     NULL,
809                                                     ids[i] ? ids[i] : ids[0],
810                                                     view_size,
811                                                     TRUE /*return_existing*/,
812                                                     TRUE /*present*/,
813                                                     TRUE /*create_containers*/);
814   g_strfreev (ids);
815 
816   return dialog;
817 }
818 
819 /**
820  * gimp_dialog_factory_dockable_new:
821  * @factory:      a #GimpDialogFactory
822  * @dock:         a #GimpDock created by this %factory.
823  * @identifier:   the identifier of the dialog as registered with
824  *                gimp_dialog_factory_register_entry()
825  * @view_size:
826  *
827  * Creates a new #GimpDockable in the context of the #GimpDock it will be
828  * added to.
829  *
830  * Implicitly raises & returns an already existing singleton dockable,
831  * so callers should check that gimp_dockable_get_dockbook (dockable)
832  * is NULL before trying to add it to it's #GimpDockbook.
833  *
834  * Return value: the newly created #GimpDockable or an already existing
835  *               singleton dockable.
836  **/
837 GtkWidget *
gimp_dialog_factory_dockable_new(GimpDialogFactory * factory,GimpDock * dock,const gchar * identifier,gint view_size)838 gimp_dialog_factory_dockable_new (GimpDialogFactory *factory,
839                                   GimpDock          *dock,
840                                   const gchar       *identifier,
841                                   gint               view_size)
842 {
843   g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
844   g_return_val_if_fail (GIMP_IS_DOCK (dock), NULL);
845   g_return_val_if_fail (identifier != NULL, NULL);
846 
847   return gimp_dialog_factory_dialog_new_internal (factory,
848                                                   gtk_widget_get_screen (GTK_WIDGET (dock)),
849                                                   0,
850                                                   gimp_dock_get_context (dock),
851                                                   gimp_dock_get_ui_manager (dock),
852                                                   identifier,
853                                                   view_size,
854                                                   FALSE /*return_existing*/,
855                                                   FALSE /*present*/,
856                                                   FALSE /*create_containers*/);
857 }
858 
859 void
gimp_dialog_factory_add_dialog(GimpDialogFactory * factory,GtkWidget * dialog,GdkScreen * screen,gint monitor)860 gimp_dialog_factory_add_dialog (GimpDialogFactory *factory,
861                                 GtkWidget         *dialog,
862                                 GdkScreen         *screen,
863                                 gint               monitor)
864 {
865   GimpDialogFactory      *dialog_factory = NULL;
866   GimpDialogFactoryEntry *entry          = NULL;
867   GimpSessionInfo        *info           = NULL;
868   GList                  *list           = NULL;
869   gboolean                toplevel       = FALSE;
870 
871   g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
872   g_return_if_fail (GTK_IS_WIDGET (dialog));
873   g_return_if_fail (GDK_IS_SCREEN (screen));
874 
875   if (g_list_find (factory->p->open_dialogs, dialog))
876     {
877       g_warning ("%s: dialog already registered", G_STRFUNC);
878       return;
879     }
880 
881   dialog_factory = gimp_dialog_factory_from_widget (dialog, &entry);
882 
883   if (! gimp_dialog_factory_dialog_sane (factory,
884                                          dialog_factory,
885                                          entry,
886                                          dialog))
887     return;
888 
889   toplevel = gtk_widget_is_toplevel (dialog);
890 
891   if (entry)
892     {
893       /* dialog is a toplevel (but not a GimpDockWindow) or a GimpDockable */
894 
895       GIMP_LOG (DIALOG_FACTORY, "adding %s \"%s\"",
896                 toplevel ? "toplevel" : "dockable",
897                 entry->identifier);
898 
899       for (list = factory->p->session_infos; list; list = g_list_next (list))
900         {
901           GimpSessionInfo *current_info = list->data;
902 
903           if (gimp_session_info_get_factory_entry (current_info) == entry)
904             {
905               if (gimp_session_info_get_widget (current_info))
906                 {
907                   if (gimp_session_info_is_singleton (current_info))
908                     {
909                       g_warning ("%s: singleton dialog \"%s\" created twice",
910                                  G_STRFUNC, entry->identifier);
911 
912                       GIMP_LOG (DIALOG_FACTORY,
913                                 "corrupt session info: %p (widget %p)",
914                                 current_info,
915                                 gimp_session_info_get_widget (current_info));
916 
917                       return;
918                     }
919 
920                   continue;
921                 }
922 
923               gimp_session_info_set_widget (current_info, dialog);
924 
925               GIMP_LOG (DIALOG_FACTORY,
926                         "updating session info %p (widget %p) for %s \"%s\"",
927                         current_info,
928                         gimp_session_info_get_widget (current_info),
929                         toplevel ? "toplevel" : "dockable",
930                         entry->identifier);
931 
932               if (toplevel &&
933                   gimp_session_info_is_session_managed (current_info) &&
934                   ! gtk_widget_get_visible (dialog))
935                 {
936                   GimpGuiConfig *gui_config;
937 
938                   gui_config = GIMP_GUI_CONFIG (factory->p->context->gimp->config);
939 
940                   gimp_session_info_apply_geometry (current_info,
941                                                     screen, monitor,
942                                                     gui_config->restore_monitor);
943                 }
944 
945               info = current_info;
946               break;
947             }
948         }
949 
950       if (! info)
951         {
952           info = gimp_session_info_new ();
953 
954           gimp_session_info_set_widget (info, dialog);
955 
956           GIMP_LOG (DIALOG_FACTORY,
957                     "creating session info %p (widget %p) for %s \"%s\"",
958                     info,
959                     gimp_session_info_get_widget (info),
960                     toplevel ? "toplevel" : "dockable",
961                     entry->identifier);
962 
963           gimp_session_info_set_factory_entry (info, entry);
964 
965           if (gimp_session_info_is_session_managed (info))
966             {
967               /* Make the dialog show up at the user position the
968                * first time it is shown. After it has been shown the
969                * first time we don't want it to show at the mouse the
970                * next time. Think of the use cases "hide and show with
971                * tab" and "change virtual desktops"
972                */
973               GIMP_LOG (WM, "setting GTK_WIN_POS_MOUSE for %p (\"%s\")\n",
974                         dialog, entry->identifier);
975 
976               gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
977               g_signal_connect (dialog, "configure-event",
978                                 G_CALLBACK (gimp_dialog_factory_set_user_pos),
979                                 NULL);
980             }
981 
982           gimp_dialog_factory_add_session_info (factory, info);
983           g_object_unref (info);
984         }
985     }
986 
987   /* Some special logic for dock windows */
988   if (GIMP_IS_DOCK_WINDOW (dialog))
989     {
990       g_signal_emit (factory, factory_signals[DOCK_WINDOW_ADDED], 0, dialog);
991     }
992 
993   factory->p->open_dialogs = g_list_prepend (factory->p->open_dialogs, dialog);
994 
995   g_signal_connect_object (dialog, "destroy",
996                            G_CALLBACK (gimp_dialog_factory_remove_dialog),
997                            factory,
998                            G_CONNECT_SWAPPED);
999 
1000   if (gimp_session_info_is_session_managed (info))
1001     g_signal_connect_object (dialog, "configure-event",
1002                              G_CALLBACK (gimp_dialog_factory_dialog_configure),
1003                              factory,
1004                              0);
1005 }
1006 
1007 void
gimp_dialog_factory_add_foreign(GimpDialogFactory * factory,const gchar * identifier,GtkWidget * dialog,GdkScreen * screen,gint monitor)1008 gimp_dialog_factory_add_foreign (GimpDialogFactory *factory,
1009                                  const gchar       *identifier,
1010                                  GtkWidget         *dialog,
1011                                  GdkScreen         *screen,
1012                                  gint               monitor)
1013 {
1014   GimpDialogFactory      *dialog_factory;
1015   GimpDialogFactoryEntry *entry;
1016 
1017   g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
1018   g_return_if_fail (identifier != NULL);
1019   g_return_if_fail (GTK_IS_WIDGET (dialog));
1020   g_return_if_fail (gtk_widget_is_toplevel (dialog));
1021   g_return_if_fail (GDK_IS_SCREEN (screen));
1022 
1023   dialog_factory = gimp_dialog_factory_from_widget (dialog, &entry);
1024 
1025   if (dialog_factory || entry)
1026     {
1027       g_warning ("%s: dialog was created by a GimpDialogFactory",
1028                  G_STRFUNC);
1029       return;
1030     }
1031 
1032   entry = gimp_dialog_factory_find_entry (factory, identifier);
1033 
1034   if (! entry)
1035     {
1036       g_warning ("%s: no entry registered for \"%s\"",
1037                  G_STRFUNC, identifier);
1038       return;
1039     }
1040 
1041   if (entry->new_func)
1042     {
1043       g_warning ("%s: entry for \"%s\" has a constructor (is not foreign)",
1044                  G_STRFUNC, identifier);
1045       return;
1046     }
1047 
1048   gimp_dialog_factory_set_widget_data (dialog, factory, entry);
1049 
1050   gimp_dialog_factory_add_dialog (factory, dialog, screen, monitor);
1051 }
1052 
1053 /**
1054  * gimp_dialog_factory_position_dialog:
1055  * @factory:
1056  * @identifier:
1057  * @dialog:
1058  * @screen:
1059  * @monitor:
1060  *
1061  * We correctly position all newly created dialog via
1062  * gimp_dialog_factory_add_dialog(), but some dialogs (like various
1063  * color dialogs) are never destroyed but created only once per
1064  * session. On re-showing, whatever window managing magic kicks in and
1065  * the dialog sometimes goes where it shouldn't.
1066  *
1067  * This function correctly positions a dialog on re-showing so it
1068  * appears where it was before it was hidden.
1069  *
1070  * See https://gitlab.gnome.org/GNOME/gimp/issues/1093
1071  **/
1072 void
gimp_dialog_factory_position_dialog(GimpDialogFactory * factory,const gchar * identifier,GtkWidget * dialog,GdkScreen * screen,gint monitor)1073 gimp_dialog_factory_position_dialog (GimpDialogFactory *factory,
1074                                      const gchar       *identifier,
1075                                      GtkWidget         *dialog,
1076                                      GdkScreen         *screen,
1077                                      gint               monitor)
1078 {
1079   GimpSessionInfo *info;
1080   GimpGuiConfig   *gui_config;
1081 
1082   g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
1083   g_return_if_fail (identifier != NULL);
1084   g_return_if_fail (GTK_IS_WIDGET (dialog));
1085   g_return_if_fail (gtk_widget_is_toplevel (dialog));
1086   g_return_if_fail (GDK_IS_SCREEN (screen));
1087 
1088   info = gimp_dialog_factory_find_session_info (factory, identifier);
1089 
1090   if (! info)
1091     {
1092       g_warning ("%s: no session info found for \"%s\"",
1093                  G_STRFUNC, identifier);
1094       return;
1095     }
1096 
1097   if (gimp_session_info_get_widget (info) != dialog)
1098     {
1099       g_warning ("%s: session info for \"%s\" is for a different widget",
1100                  G_STRFUNC, identifier);
1101       return;
1102     }
1103 
1104   gui_config = GIMP_GUI_CONFIG (factory->p->context->gimp->config);
1105 
1106   gimp_session_info_apply_geometry (info,
1107                                     screen, monitor,
1108                                     gui_config->restore_monitor);
1109 }
1110 
1111 void
gimp_dialog_factory_remove_dialog(GimpDialogFactory * factory,GtkWidget * dialog)1112 gimp_dialog_factory_remove_dialog (GimpDialogFactory *factory,
1113                                    GtkWidget         *dialog)
1114 {
1115   GimpDialogFactory      *dialog_factory;
1116   GimpDialogFactoryEntry *entry;
1117   GList                  *list;
1118 
1119   g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
1120   g_return_if_fail (GTK_IS_WIDGET (dialog));
1121 
1122   if (! g_list_find (factory->p->open_dialogs, dialog))
1123     {
1124       g_warning ("%s: dialog not registered", G_STRFUNC);
1125       return;
1126     }
1127 
1128   factory->p->open_dialogs = g_list_remove (factory->p->open_dialogs, dialog);
1129 
1130   dialog_factory = gimp_dialog_factory_from_widget (dialog, &entry);
1131 
1132   if (! gimp_dialog_factory_dialog_sane (factory,
1133                                          dialog_factory,
1134                                          entry,
1135                                          dialog))
1136     return;
1137 
1138   GIMP_LOG (DIALOG_FACTORY, "removing \"%s\" (dialog = %p)",
1139             entry->identifier,
1140             dialog);
1141 
1142   for (list = factory->p->session_infos; list; list = g_list_next (list))
1143     {
1144       GimpSessionInfo *session_info = list->data;
1145 
1146       if (gimp_session_info_get_widget (session_info) == dialog)
1147         {
1148           GIMP_LOG (DIALOG_FACTORY,
1149                     "clearing session info %p (widget %p) for \"%s\"",
1150                     session_info, gimp_session_info_get_widget (session_info),
1151                     entry->identifier);
1152 
1153           gimp_session_info_set_widget (session_info, NULL);
1154 
1155           gimp_dialog_factory_unset_widget_data (dialog);
1156 
1157           g_signal_handlers_disconnect_by_func (dialog,
1158                                                 gimp_dialog_factory_set_user_pos,
1159                                                 NULL);
1160           g_signal_handlers_disconnect_by_func (dialog,
1161                                                 gimp_dialog_factory_remove_dialog,
1162                                                 factory);
1163 
1164           if (gimp_session_info_is_session_managed (session_info))
1165             g_signal_handlers_disconnect_by_func (dialog,
1166                                                   gimp_dialog_factory_dialog_configure,
1167                                                   factory);
1168 
1169           if (GIMP_IS_DOCK_WINDOW (dialog))
1170             {
1171               /*  don't save session info for empty docks  */
1172               factory->p->session_infos = g_list_remove (factory->p->session_infos,
1173                                                          session_info);
1174               g_object_unref (session_info);
1175 
1176               g_signal_emit (factory, factory_signals[DOCK_WINDOW_REMOVED], 0,
1177                              dialog);
1178             }
1179 
1180           break;
1181         }
1182     }
1183 }
1184 
1185 void
gimp_dialog_factory_hide_dialog(GtkWidget * dialog)1186 gimp_dialog_factory_hide_dialog (GtkWidget *dialog)
1187 {
1188   GimpDialogFactory *factory = NULL;
1189 
1190   g_return_if_fail (GTK_IS_WIDGET (dialog));
1191   g_return_if_fail (gtk_widget_is_toplevel (dialog));
1192 
1193   if (! (factory = gimp_dialog_factory_from_widget (dialog, NULL)))
1194     {
1195       g_warning ("%s: dialog was not created by a GimpDialogFactory",
1196                  G_STRFUNC);
1197       return;
1198     }
1199 
1200   gtk_widget_hide (dialog);
1201 
1202   if (factory->p->dialog_state != GIMP_DIALOGS_SHOWN)
1203     g_object_set_data (G_OBJECT (dialog), GIMP_DIALOG_VISIBILITY_KEY,
1204                        GINT_TO_POINTER (GIMP_DIALOG_VISIBILITY_INVISIBLE));
1205 }
1206 
1207 void
gimp_dialog_factory_set_state(GimpDialogFactory * factory,GimpDialogsState state)1208 gimp_dialog_factory_set_state (GimpDialogFactory *factory,
1209                                GimpDialogsState   state)
1210 {
1211   g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
1212 
1213   factory->p->dialog_state = state;
1214 
1215   if (state == GIMP_DIALOGS_SHOWN)
1216     {
1217       gimp_dialog_factory_show (factory);
1218     }
1219   else
1220     {
1221       gimp_dialog_factory_hide (factory);
1222     }
1223 }
1224 
1225 GimpDialogsState
gimp_dialog_factory_get_state(GimpDialogFactory * factory)1226 gimp_dialog_factory_get_state (GimpDialogFactory *factory)
1227 {
1228   g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), 0);
1229 
1230   return factory->p->dialog_state;
1231 }
1232 
1233 void
gimp_dialog_factory_show_with_display(GimpDialogFactory * factory)1234 gimp_dialog_factory_show_with_display (GimpDialogFactory *factory)
1235 {
1236   g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
1237 
1238   if (factory->p->dialog_state == GIMP_DIALOGS_HIDDEN_WITH_DISPLAY)
1239     {
1240       gimp_dialog_factory_set_state (factory, GIMP_DIALOGS_SHOWN);
1241     }
1242 }
1243 
1244 void
gimp_dialog_factory_hide_with_display(GimpDialogFactory * factory)1245 gimp_dialog_factory_hide_with_display (GimpDialogFactory *factory)
1246 {
1247   g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
1248 
1249   if (factory->p->dialog_state == GIMP_DIALOGS_SHOWN)
1250     {
1251       gimp_dialog_factory_set_state (factory, GIMP_DIALOGS_HIDDEN_WITH_DISPLAY);
1252     }
1253 }
1254 
1255 static GQuark gimp_dialog_factory_key       = 0;
1256 static GQuark gimp_dialog_factory_entry_key = 0;
1257 
1258 GimpDialogFactory *
gimp_dialog_factory_from_widget(GtkWidget * dialog,GimpDialogFactoryEntry ** entry)1259 gimp_dialog_factory_from_widget (GtkWidget               *dialog,
1260                                  GimpDialogFactoryEntry **entry)
1261 {
1262   g_return_val_if_fail (GTK_IS_WIDGET (dialog), NULL);
1263 
1264   if (! gimp_dialog_factory_key)
1265     {
1266       gimp_dialog_factory_key =
1267         g_quark_from_static_string ("gimp-dialog-factory");
1268 
1269       gimp_dialog_factory_entry_key =
1270         g_quark_from_static_string ("gimp-dialog-factory-entry");
1271     }
1272 
1273   if (entry)
1274     *entry = g_object_get_qdata (G_OBJECT (dialog),
1275                                  gimp_dialog_factory_entry_key);
1276 
1277   return g_object_get_qdata (G_OBJECT (dialog), gimp_dialog_factory_key);
1278 }
1279 
1280 #define GIMP_DIALOG_FACTORY_MIN_SIZE_KEY "gimp-dialog-factory-min-size"
1281 
1282 void
gimp_dialog_factory_set_has_min_size(GtkWindow * window,gboolean has_min_size)1283 gimp_dialog_factory_set_has_min_size (GtkWindow *window,
1284                                       gboolean   has_min_size)
1285 {
1286   g_return_if_fail (GTK_IS_WINDOW (window));
1287 
1288   g_object_set_data (G_OBJECT (window), GIMP_DIALOG_FACTORY_MIN_SIZE_KEY,
1289                      GINT_TO_POINTER (has_min_size ? TRUE : FALSE));
1290 }
1291 
1292 gboolean
gimp_dialog_factory_get_has_min_size(GtkWindow * window)1293 gimp_dialog_factory_get_has_min_size (GtkWindow *window)
1294 {
1295   g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
1296 
1297   return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window),
1298                                              GIMP_DIALOG_FACTORY_MIN_SIZE_KEY));
1299 }
1300 
1301 
1302 /*  private functions  */
1303 
1304 static GtkWidget *
gimp_dialog_factory_constructor(GimpDialogFactory * factory,GimpDialogFactoryEntry * entry,GimpContext * context,GimpUIManager * ui_manager,gint view_size)1305 gimp_dialog_factory_constructor (GimpDialogFactory      *factory,
1306                                  GimpDialogFactoryEntry *entry,
1307                                  GimpContext            *context,
1308                                  GimpUIManager          *ui_manager,
1309                                  gint                    view_size)
1310 {
1311   GtkWidget *widget;
1312 
1313   widget = entry->new_func (factory, context, ui_manager, view_size);
1314 
1315   /* The entry is for a dockable, so we simply need to put the created
1316    * widget in a dockable
1317    */
1318   if (widget && entry->dockable)
1319     {
1320       GtkWidget *dockable = NULL;
1321 
1322       dockable = gimp_dockable_new (entry->name, entry->blurb,
1323                                     entry->icon_name, entry->help_id);
1324       gtk_container_add (GTK_CONTAINER (dockable), widget);
1325       gtk_widget_show (widget);
1326 
1327       /* EEK */
1328       g_object_set_data (G_OBJECT (dockable), "gimp-dialog-identifier",
1329                          entry->identifier);
1330 
1331       /* Return the dockable instead */
1332       widget = dockable;
1333     }
1334 
1335   return widget;
1336 }
1337 
1338 static void
gimp_dialog_factory_config_notify(GimpDialogFactory * factory,GParamSpec * pspec,GimpGuiConfig * config)1339 gimp_dialog_factory_config_notify (GimpDialogFactory *factory,
1340                                    GParamSpec        *pspec,
1341                                    GimpGuiConfig     *config)
1342 {
1343   GimpDialogsState state     = gimp_dialog_factory_get_state (factory);
1344   GimpDialogsState new_state = state;
1345 
1346   /* Make sure the state and config are in sync */
1347   if (config->hide_docks && state == GIMP_DIALOGS_SHOWN)
1348     new_state = GIMP_DIALOGS_HIDDEN_EXPLICITLY;
1349   else if (! config->hide_docks)
1350     new_state = GIMP_DIALOGS_SHOWN;
1351 
1352   if (state != new_state)
1353     gimp_dialog_factory_set_state (factory, new_state);
1354 }
1355 
1356 static void
gimp_dialog_factory_set_widget_data(GtkWidget * dialog,GimpDialogFactory * factory,GimpDialogFactoryEntry * entry)1357 gimp_dialog_factory_set_widget_data (GtkWidget              *dialog,
1358                                      GimpDialogFactory      *factory,
1359                                      GimpDialogFactoryEntry *entry)
1360 {
1361   g_return_if_fail (GTK_IS_WIDGET (dialog));
1362   g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
1363 
1364   if (! gimp_dialog_factory_key)
1365     {
1366       gimp_dialog_factory_key =
1367         g_quark_from_static_string ("gimp-dialog-factory");
1368 
1369       gimp_dialog_factory_entry_key =
1370         g_quark_from_static_string ("gimp-dialog-factory-entry");
1371     }
1372 
1373   g_object_set_qdata (G_OBJECT (dialog), gimp_dialog_factory_key, factory);
1374 
1375   if (entry)
1376     g_object_set_qdata (G_OBJECT (dialog), gimp_dialog_factory_entry_key,
1377                         entry);
1378 }
1379 
1380 static void
gimp_dialog_factory_unset_widget_data(GtkWidget * dialog)1381 gimp_dialog_factory_unset_widget_data (GtkWidget *dialog)
1382 {
1383   g_return_if_fail (GTK_IS_WIDGET (dialog));
1384 
1385   if (! gimp_dialog_factory_key)
1386     return;
1387 
1388   g_object_set_qdata (G_OBJECT (dialog), gimp_dialog_factory_key,       NULL);
1389   g_object_set_qdata (G_OBJECT (dialog), gimp_dialog_factory_entry_key, NULL);
1390 }
1391 
1392 static gboolean
gimp_dialog_factory_set_user_pos(GtkWidget * dialog,GdkEventConfigure * cevent,gpointer data)1393 gimp_dialog_factory_set_user_pos (GtkWidget         *dialog,
1394                                   GdkEventConfigure *cevent,
1395                                   gpointer           data)
1396 {
1397   GdkWindowHints geometry_mask;
1398 
1399   /* Not only set geometry hints, also reset window position */
1400   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_NONE);
1401   g_signal_handlers_disconnect_by_func (dialog,
1402                                         gimp_dialog_factory_set_user_pos,
1403                                         data);
1404 
1405   GIMP_LOG (WM, "setting GDK_HINT_USER_POS for %p\n", dialog);
1406   geometry_mask = GDK_HINT_USER_POS;
1407 
1408   if (gimp_dialog_factory_get_has_min_size (GTK_WINDOW (dialog)))
1409     geometry_mask |= GDK_HINT_MIN_SIZE;
1410 
1411   gtk_window_set_geometry_hints (GTK_WINDOW (dialog), NULL, NULL,
1412                                  geometry_mask);
1413 
1414   return FALSE;
1415 }
1416 
1417 static gboolean
gimp_dialog_factory_dialog_configure(GtkWidget * dialog,GdkEventConfigure * cevent,GimpDialogFactory * factory)1418 gimp_dialog_factory_dialog_configure (GtkWidget         *dialog,
1419                                       GdkEventConfigure *cevent,
1420                                       GimpDialogFactory *factory)
1421 {
1422   GimpDialogFactory      *dialog_factory;
1423   GimpDialogFactoryEntry *entry;
1424   GList                  *list;
1425 
1426   if (! g_list_find (factory->p->open_dialogs, dialog))
1427     {
1428       g_warning ("%s: dialog not registered", G_STRFUNC);
1429       return FALSE;
1430     }
1431 
1432   dialog_factory = gimp_dialog_factory_from_widget (dialog, &entry);
1433 
1434   if (! gimp_dialog_factory_dialog_sane (factory,
1435                                          dialog_factory,
1436                                          entry,
1437                                          dialog))
1438     return FALSE;
1439 
1440   for (list = factory->p->session_infos; list; list = g_list_next (list))
1441     {
1442       GimpSessionInfo *session_info = list->data;
1443 
1444       if (gimp_session_info_get_widget (session_info) == dialog)
1445         {
1446           gimp_session_info_read_geometry (session_info, cevent);
1447 
1448           GIMP_LOG (DIALOG_FACTORY,
1449                     "updated session info for \"%s\" from window geometry "
1450                     "(x=%d y=%d  %dx%d)",
1451                     entry->identifier,
1452                     gimp_session_info_get_x (session_info),
1453                     gimp_session_info_get_y (session_info),
1454                     gimp_session_info_get_width (session_info),
1455                     gimp_session_info_get_height (session_info));
1456 
1457           break;
1458         }
1459     }
1460 
1461   return FALSE;
1462 }
1463 
1464 void
gimp_dialog_factory_save(GimpDialogFactory * factory,GimpConfigWriter * writer)1465 gimp_dialog_factory_save (GimpDialogFactory *factory,
1466                           GimpConfigWriter  *writer)
1467 {
1468   GList *infos;
1469 
1470   for (infos = factory->p->session_infos; infos; infos = g_list_next (infos))
1471     {
1472       GimpSessionInfo *info = infos->data;
1473 
1474       /*  we keep session info entries for all toplevel dialogs created
1475        *  by the factory but don't save them if they don't want to be
1476        *  managed
1477        */
1478       if (! gimp_session_info_is_session_managed (info) ||
1479           gimp_session_info_get_factory_entry (info) == NULL)
1480         continue;
1481 
1482       if (gimp_session_info_get_widget (info))
1483         gimp_session_info_get_info (info);
1484 
1485       gimp_config_writer_open (writer, "session-info");
1486       gimp_config_writer_string (writer,
1487                                  gimp_object_get_name (factory));
1488 
1489       GIMP_CONFIG_GET_INTERFACE (info)->serialize (GIMP_CONFIG (info),
1490                                                    writer,
1491                                                    NULL);
1492 
1493       gimp_config_writer_close (writer);
1494 
1495       if (gimp_session_info_get_widget (info))
1496         gimp_session_info_clear_info (info);
1497     }
1498 }
1499 
1500 void
gimp_dialog_factory_restore(GimpDialogFactory * factory,GdkScreen * screen,gint monitor)1501 gimp_dialog_factory_restore (GimpDialogFactory *factory,
1502                              GdkScreen         *screen,
1503                              gint               monitor)
1504 {
1505   GList *infos;
1506 
1507   for (infos = factory->p->session_infos; infos; infos = g_list_next (infos))
1508     {
1509       GimpSessionInfo *info = infos->data;
1510 
1511       if (gimp_session_info_get_open (info))
1512         {
1513           gimp_session_info_restore (info, factory, screen, monitor);
1514         }
1515       else
1516         {
1517           GIMP_LOG (DIALOG_FACTORY,
1518                     "skipping to restore session info %p, not open",
1519                     info);
1520         }
1521     }
1522 }
1523 
1524 static void
gimp_dialog_factory_hide(GimpDialogFactory * factory)1525 gimp_dialog_factory_hide (GimpDialogFactory *factory)
1526 {
1527   GList *list;
1528 
1529   for (list = factory->p->open_dialogs; list; list = g_list_next (list))
1530     {
1531       GtkWidget *widget = list->data;
1532 
1533       if (GTK_IS_WIDGET (widget) && gtk_widget_is_toplevel (widget))
1534         {
1535           GimpDialogFactoryEntry    *entry      = NULL;
1536           GimpDialogVisibilityState  visibility = GIMP_DIALOG_VISIBILITY_UNKNOWN;
1537 
1538           gimp_dialog_factory_from_widget (widget, &entry);
1539           if (! entry->hideable)
1540             continue;
1541 
1542           if (gtk_widget_get_visible (widget))
1543             {
1544               gtk_widget_hide (widget);
1545               visibility = GIMP_DIALOG_VISIBILITY_HIDDEN;
1546 
1547               GIMP_LOG (WM, "Hiding '%s' [%p]",
1548                         gtk_window_get_title (GTK_WINDOW (widget)),
1549                         widget);
1550             }
1551           else
1552             {
1553               visibility = GIMP_DIALOG_VISIBILITY_INVISIBLE;
1554             }
1555 
1556           g_object_set_data (G_OBJECT (widget),
1557                              GIMP_DIALOG_VISIBILITY_KEY,
1558                              GINT_TO_POINTER (visibility));
1559         }
1560     }
1561 }
1562 
1563 static void
gimp_dialog_factory_show(GimpDialogFactory * factory)1564 gimp_dialog_factory_show (GimpDialogFactory *factory)
1565 {
1566   GList *list;
1567 
1568   for (list = factory->p->open_dialogs; list; list = g_list_next (list))
1569     {
1570       GtkWidget *widget = list->data;
1571 
1572       if (GTK_IS_WIDGET (widget) && gtk_widget_is_toplevel (widget))
1573         {
1574           GimpDialogVisibilityState visibility;
1575 
1576           visibility =
1577             GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
1578                                                 GIMP_DIALOG_VISIBILITY_KEY));
1579 
1580           if (! gtk_widget_get_visible (widget) &&
1581               visibility == GIMP_DIALOG_VISIBILITY_HIDDEN)
1582             {
1583               GIMP_LOG (WM, "Showing '%s' [%p]",
1584                         gtk_window_get_title (GTK_WINDOW (widget)),
1585                         widget);
1586 
1587               /* Don't use gtk_window_present() here, we don't want the
1588                * keyboard focus to move.
1589                */
1590               gtk_widget_show (widget);
1591               g_object_set_data (G_OBJECT (widget),
1592                                  GIMP_DIALOG_VISIBILITY_KEY,
1593                                  GINT_TO_POINTER (GIMP_DIALOG_VISIBILITY_VISIBLE));
1594 
1595               if (gtk_widget_get_visible (widget))
1596                 gdk_window_raise (gtk_widget_get_window (widget));
1597             }
1598         }
1599     }
1600 }
1601 
1602 void
gimp_dialog_factory_set_busy(GimpDialogFactory * factory)1603 gimp_dialog_factory_set_busy (GimpDialogFactory *factory)
1604 {
1605   GList *list;
1606 
1607   if (! factory)
1608     return;
1609 
1610   for (list = factory->p->open_dialogs; list; list = g_list_next (list))
1611     {
1612       GtkWidget *widget = list->data;
1613 
1614       if (GTK_IS_WIDGET (widget) && gtk_widget_is_toplevel (widget))
1615         {
1616           GdkWindow *window = gtk_widget_get_window (widget);
1617 
1618           if (window)
1619             {
1620               GdkCursor *cursor = gimp_cursor_new (window,
1621                                                    GIMP_HANDEDNESS_RIGHT,
1622                                                    (GimpCursorType) GDK_WATCH,
1623                                                    GIMP_TOOL_CURSOR_NONE,
1624                                                    GIMP_CURSOR_MODIFIER_NONE);
1625               gdk_window_set_cursor (window, cursor);
1626               gdk_cursor_unref (cursor);
1627             }
1628         }
1629     }
1630 }
1631 
1632 void
gimp_dialog_factory_unset_busy(GimpDialogFactory * factory)1633 gimp_dialog_factory_unset_busy (GimpDialogFactory *factory)
1634 {
1635   GList *list;
1636 
1637   if (! factory)
1638     return;
1639 
1640   for (list = factory->p->open_dialogs; list; list = g_list_next (list))
1641     {
1642       GtkWidget *widget = list->data;
1643 
1644       if (GTK_IS_WIDGET (widget) && gtk_widget_is_toplevel (widget))
1645         {
1646           GdkWindow *window = gtk_widget_get_window (widget);
1647 
1648           if (window)
1649             gdk_window_set_cursor (window, NULL);
1650         }
1651     }
1652 }
1653 
1654 /**
1655  * gimp_dialog_factory_get_singleton:
1656  *
1657  * Returns: The toplevel GimpDialogFactory instance.
1658  **/
1659 GimpDialogFactory *
gimp_dialog_factory_get_singleton(void)1660 gimp_dialog_factory_get_singleton (void)
1661 {
1662   g_return_val_if_fail (gimp_toplevel_factory != NULL, NULL);
1663 
1664   return gimp_toplevel_factory;
1665 }
1666 
1667 /**
1668  * gimp_dialog_factory_set_singleton:
1669  * @:
1670  *
1671  * Set the toplevel GimpDialogFactory instance. Must only be called by
1672  * dialogs_init()!.
1673  **/
1674 void
gimp_dialog_factory_set_singleton(GimpDialogFactory * factory)1675 gimp_dialog_factory_set_singleton (GimpDialogFactory *factory)
1676 {
1677   g_return_if_fail (gimp_toplevel_factory == NULL ||
1678                     factory               == NULL);
1679 
1680   gimp_toplevel_factory = factory;
1681 }
1682