summaryrefslogtreecommitdiffstats
path: root/app/widgets/gimpdialogfactory.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/widgets/gimpdialogfactory.c')
-rw-r--r--app/widgets/gimpdialogfactory.c1681
1 files changed, 1681 insertions, 0 deletions
diff --git a/app/widgets/gimpdialogfactory.c b/app/widgets/gimpdialogfactory.c
new file mode 100644
index 0000000..9d2ce0c
--- /dev/null
+++ b/app/widgets/gimpdialogfactory.c
@@ -0,0 +1,1681 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpdialogfactory.c
+ * Copyright (C) 2001-2008 Michael Natterer <mitch@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "widgets-types.h"
+
+#include "config/gimpguiconfig.h"
+
+#include "core/gimp.h"
+#include "core/gimpcontext.h"
+#include "core/gimpmarshal.h"
+
+#include "gimpcursor.h"
+#include "gimpdialogfactory.h"
+#include "gimpdock.h"
+#include "gimpdockbook.h"
+#include "gimpdockable.h"
+#include "gimpdockcontainer.h"
+#include "gimpdockwindow.h"
+#include "gimpmenufactory.h"
+#include "gimpsessioninfo.h"
+#include "gimpwidgets-utils.h"
+
+#include "gimp-log.h"
+
+
+enum
+{
+ DOCK_WINDOW_ADDED,
+ DOCK_WINDOW_REMOVED,
+ LAST_SIGNAL
+};
+
+
+struct _GimpDialogFactoryPrivate
+{
+ GimpContext *context;
+ GimpMenuFactory *menu_factory;
+
+ GList *open_dialogs;
+ GList *session_infos;
+
+ GList *registered_dialogs;
+
+ GimpDialogsState dialog_state;
+};
+
+
+static void gimp_dialog_factory_dispose (GObject *object);
+static void gimp_dialog_factory_finalize (GObject *object);
+static GtkWidget * gimp_dialog_factory_constructor (GimpDialogFactory *factory,
+ GimpDialogFactoryEntry *entry,
+ GimpContext *context,
+ GimpUIManager *ui_manager,
+ gint view_size);
+static void gimp_dialog_factory_config_notify (GimpDialogFactory *factory,
+ GParamSpec *pspec,
+ GimpGuiConfig *config);
+static void gimp_dialog_factory_set_widget_data (GtkWidget *dialog,
+ GimpDialogFactory *factory,
+ GimpDialogFactoryEntry *entry);
+static void gimp_dialog_factory_unset_widget_data (GtkWidget *dialog);
+static gboolean gimp_dialog_factory_set_user_pos (GtkWidget *dialog,
+ GdkEventConfigure *cevent,
+ gpointer data);
+static gboolean gimp_dialog_factory_dialog_configure (GtkWidget *dialog,
+ GdkEventConfigure *cevent,
+ GimpDialogFactory *factory);
+static void gimp_dialog_factory_hide (GimpDialogFactory *factory);
+static void gimp_dialog_factory_show (GimpDialogFactory *factory);
+
+
+G_DEFINE_TYPE_WITH_PRIVATE (GimpDialogFactory, gimp_dialog_factory,
+ GIMP_TYPE_OBJECT)
+
+#define parent_class gimp_dialog_factory_parent_class
+
+static guint factory_signals[LAST_SIGNAL] = { 0 };
+
+
+/* Is set by dialogs.c to a dialog factory initialized there.
+ *
+ * FIXME: The layer above should not do this kind of initialization of
+ * layers below.
+ */
+static GimpDialogFactory *gimp_toplevel_factory = NULL;
+
+
+static void
+gimp_dialog_factory_class_init (GimpDialogFactoryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gimp_dialog_factory_dispose;
+ object_class->finalize = gimp_dialog_factory_finalize;
+
+ factory_signals[DOCK_WINDOW_ADDED] =
+ g_signal_new ("dock-window-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpDialogFactoryClass, dock_window_added),
+ NULL, NULL,
+ gimp_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GIMP_TYPE_DOCK_WINDOW);
+
+ factory_signals[DOCK_WINDOW_REMOVED] =
+ g_signal_new ("dock-window-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GimpDialogFactoryClass, dock_window_removed),
+ NULL, NULL,
+ gimp_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GIMP_TYPE_DOCK_WINDOW);
+}
+
+static void
+gimp_dialog_factory_init (GimpDialogFactory *factory)
+{
+ factory->p = gimp_dialog_factory_get_instance_private (factory);
+ factory->p->dialog_state = GIMP_DIALOGS_SHOWN;
+}
+
+static void
+gimp_dialog_factory_dispose (GObject *object)
+{
+ GimpDialogFactory *factory = GIMP_DIALOG_FACTORY (object);
+ GList *list;
+
+ /* start iterating from the beginning each time we destroyed a
+ * toplevel because destroying a dock may cause lots of items
+ * to be removed from factory->p->open_dialogs
+ */
+ while (factory->p->open_dialogs)
+ {
+ for (list = factory->p->open_dialogs; list; list = g_list_next (list))
+ {
+ if (gtk_widget_is_toplevel (list->data))
+ {
+ gtk_widget_destroy (GTK_WIDGET (list->data));
+ break;
+ }
+ }
+
+ /* the list being non-empty without any toplevel is an error,
+ * so eek and chain up
+ */
+ if (! list)
+ {
+ g_warning ("%s: %d stale non-toplevel entries in factory->p->open_dialogs",
+ G_STRFUNC, g_list_length (factory->p->open_dialogs));
+ break;
+ }
+ }
+
+ if (factory->p->open_dialogs)
+ {
+ g_list_free (factory->p->open_dialogs);
+ factory->p->open_dialogs = NULL;
+ }
+
+ if (factory->p->session_infos)
+ {
+ g_list_free_full (factory->p->session_infos,
+ (GDestroyNotify) g_object_unref);
+ factory->p->session_infos = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_dialog_factory_finalize (GObject *object)
+{
+ GimpDialogFactory *factory = GIMP_DIALOG_FACTORY (object);
+ GList *list;
+
+ for (list = factory->p->registered_dialogs; list; list = g_list_next (list))
+ {
+ GimpDialogFactoryEntry *entry = list->data;
+
+ g_free (entry->identifier);
+ g_free (entry->name);
+ g_free (entry->blurb);
+ g_free (entry->icon_name);
+ g_free (entry->help_id);
+
+ g_slice_free (GimpDialogFactoryEntry, entry);
+ }
+
+ if (factory->p->registered_dialogs)
+ {
+ g_list_free (factory->p->registered_dialogs);
+ factory->p->registered_dialogs = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+GimpDialogFactory *
+gimp_dialog_factory_new (const gchar *name,
+ GimpContext *context,
+ GimpMenuFactory *menu_factory)
+{
+ GimpDialogFactory *factory;
+ GimpGuiConfig *config;
+
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+ g_return_val_if_fail (! menu_factory || GIMP_IS_MENU_FACTORY (menu_factory),
+ NULL);
+
+ factory = g_object_new (GIMP_TYPE_DIALOG_FACTORY, NULL);
+
+ gimp_object_set_name (GIMP_OBJECT (factory), name);
+
+ config = GIMP_GUI_CONFIG (context->gimp->config);
+
+ factory->p->context = context;
+ factory->p->menu_factory = menu_factory;
+ factory->p->dialog_state = (config->hide_docks ?
+ GIMP_DIALOGS_HIDDEN_EXPLICITLY :
+ GIMP_DIALOGS_SHOWN);
+
+ g_signal_connect_object (config, "notify::hide-docks",
+ G_CALLBACK (gimp_dialog_factory_config_notify),
+ factory, G_CONNECT_SWAPPED);
+
+ return factory;
+}
+
+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)
+{
+ GimpDialogFactoryEntry *entry;
+
+ g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
+ g_return_if_fail (identifier != NULL);
+
+ entry = g_slice_new0 (GimpDialogFactoryEntry);
+
+ entry->identifier = g_strdup (identifier);
+ entry->name = g_strdup (name);
+ entry->blurb = g_strdup (blurb);
+ entry->icon_name = g_strdup (icon_name);
+ entry->help_id = g_strdup (help_id);
+ entry->new_func = new_func;
+ entry->restore_func = restore_func;
+ entry->view_size = view_size;
+ entry->singleton = singleton ? TRUE : FALSE;
+ entry->session_managed = session_managed ? TRUE : FALSE;
+ entry->remember_size = remember_size ? TRUE : FALSE;
+ entry->remember_if_open = remember_if_open ? TRUE : FALSE;
+ entry->hideable = hideable ? TRUE : FALSE;
+ entry->image_window = image_window ? TRUE : FALSE;
+ entry->dockable = dockable ? TRUE : FALSE;
+
+ factory->p->registered_dialogs = g_list_prepend (factory->p->registered_dialogs,
+ entry);
+}
+
+GimpDialogFactoryEntry *
+gimp_dialog_factory_find_entry (GimpDialogFactory *factory,
+ const gchar *identifier)
+{
+ GList *list;
+
+ g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
+ g_return_val_if_fail (identifier != NULL, NULL);
+
+ for (list = factory->p->registered_dialogs; list; list = g_list_next (list))
+ {
+ GimpDialogFactoryEntry *entry = list->data;
+
+ if (! strcmp (identifier, entry->identifier))
+ return entry;
+ }
+
+ return NULL;
+}
+
+GimpSessionInfo *
+gimp_dialog_factory_find_session_info (GimpDialogFactory *factory,
+ const gchar *identifier)
+{
+ GList *list;
+
+ g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
+ g_return_val_if_fail (identifier != NULL, NULL);
+
+ for (list = factory->p->session_infos; list; list = g_list_next (list))
+ {
+ GimpSessionInfo *info = list->data;
+
+ if (gimp_session_info_get_factory_entry (info) &&
+ g_str_equal (identifier,
+ gimp_session_info_get_factory_entry (info)->identifier))
+ {
+ return info;
+ }
+ }
+
+ return NULL;
+}
+
+GtkWidget *
+gimp_dialog_factory_find_widget (GimpDialogFactory *factory,
+ const gchar *identifiers)
+{
+ GtkWidget *widget = NULL;
+ gchar **ids;
+ gint i;
+
+ g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
+ g_return_val_if_fail (identifiers != NULL, NULL);
+
+ ids = g_strsplit (identifiers, "|", 0);
+
+ for (i = 0; ids[i]; i++)
+ {
+ GimpSessionInfo *info;
+
+ info = gimp_dialog_factory_find_session_info (factory, ids[i]);
+
+ if (info)
+ {
+ widget = gimp_session_info_get_widget (info);
+
+ if (widget)
+ break;
+ }
+ }
+
+ g_strfreev (ids);
+
+ return widget;
+}
+
+/**
+ * gimp_dialog_factory_dialog_sane:
+ * @factory:
+ * @widget_factory:
+ * @widget_entry:
+ * @widget:
+ *
+ * Makes sure that the @widget with the given @widget_entry that was
+ * created by the given @widget_factory belongs to @efactory.
+ *
+ * Returns: %TRUE if that is the case, %FALSE otherwise.
+ **/
+static gboolean
+gimp_dialog_factory_dialog_sane (GimpDialogFactory *factory,
+ GimpDialogFactory *widget_factory,
+ GimpDialogFactoryEntry *widget_entry,
+ GtkWidget *widget)
+{
+ if (! widget_factory || ! widget_entry)
+ {
+ g_warning ("%s: dialog was not created by a GimpDialogFactory",
+ G_STRFUNC);
+ return FALSE;
+ }
+
+ if (widget_factory != factory)
+ {
+ g_warning ("%s: dialog was created by a different GimpDialogFactory",
+ G_STRFUNC);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gimp_dialog_factory_dialog_new_internal:
+ * @factory:
+ * @screen:
+ * @context:
+ * @ui_manager:
+ * @identifier:
+ * @view_size:
+ * @return_existing: If %TRUE, (or if the dialog is a singleton),
+ * don't create a new dialog if it exists, instead
+ * return the existing one
+ * @present: If %TRUE, the toplevel that contains the dialog (if any)
+ * will be gtk_window_present():ed
+ * @create_containers: If %TRUE, then containers for the
+ * dialog/dockable will be created as well. If you
+ * want to manage your own containers, pass %FALSE
+ *
+ * This is the lowest level dialog factory creation function.
+ *
+ * Returns: A created or existing #GtkWidget.
+ **/
+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)
+{
+ GimpDialogFactoryEntry *entry = NULL;
+ GtkWidget *dialog = NULL;
+ GtkWidget *toplevel = NULL;
+
+ g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
+ g_return_val_if_fail (identifier != NULL, NULL);
+
+ entry = gimp_dialog_factory_find_entry (factory, identifier);
+
+ if (! entry)
+ {
+ g_warning ("%s: no entry registered for \"%s\"",
+ G_STRFUNC, identifier);
+ return NULL;
+ }
+
+ if (! entry->new_func)
+ {
+ g_warning ("%s: entry for \"%s\" has no constructor",
+ G_STRFUNC, identifier);
+ return NULL;
+ }
+
+ /* a singleton dialog is always returned if it already exists */
+ if (return_existing || entry->singleton)
+ {
+ dialog = gimp_dialog_factory_find_widget (factory, identifier);
+ }
+
+ /* create the dialog if it was not found */
+ if (! dialog)
+ {
+ GtkWidget *dock = NULL;
+ GtkWidget *dock_window = NULL;
+
+ /* What follows is special-case code for some entries. At some
+ * point we might want to abstract this block of code away.
+ */
+ if (create_containers)
+ {
+ if (entry->dockable)
+ {
+ GtkWidget *dockbook;
+
+ /* It doesn't make sense to have a dockable without a dock
+ * so create one. Create a new dock _before_ creating the
+ * dialog. We do this because the new dockable needs to be
+ * created in its dock's context.
+ */
+ dock = gimp_dock_with_window_new (factory,
+ screen,
+ monitor,
+ FALSE /*toolbox*/);
+ dockbook = gimp_dockbook_new (factory->p->menu_factory);
+
+ gimp_dock_add_book (GIMP_DOCK (dock),
+ GIMP_DOCKBOOK (dockbook),
+ 0);
+ }
+ else if (strcmp ("gimp-toolbox", entry->identifier) == 0)
+ {
+ GimpDockContainer *dock_container;
+
+ dock_window = gimp_dialog_factory_dialog_new (factory,
+ screen,
+ monitor,
+ NULL /*ui_manager*/,
+ "gimp-toolbox-window",
+ -1 /*view_size*/,
+ FALSE /*present*/);
+
+ /* When we get a dock window, we also get a UI
+ * manager
+ */
+ dock_container = GIMP_DOCK_CONTAINER (dock_window);
+ ui_manager = gimp_dock_container_get_ui_manager (dock_container);
+ }
+ }
+
+ /* Create the new dialog in the appropriate context which is
+ * - the passed context if not NULL
+ * - the newly created dock's context if we just created it
+ * - the factory's context, which happens when raising a toplevel
+ * dialog was the original request.
+ */
+ if (view_size < GIMP_VIEW_SIZE_TINY)
+ view_size = entry->view_size;
+
+ if (context)
+ dialog = gimp_dialog_factory_constructor (factory, entry,
+ context,
+ ui_manager,
+ view_size);
+ else if (dock)
+ dialog = gimp_dialog_factory_constructor (factory, entry,
+ gimp_dock_get_context (GIMP_DOCK (dock)),
+ gimp_dock_get_ui_manager (GIMP_DOCK (dock)),
+ view_size);
+ else
+ dialog = gimp_dialog_factory_constructor (factory, entry,
+ factory->p->context,
+ ui_manager,
+ view_size);
+
+ if (dialog)
+ {
+ gimp_dialog_factory_set_widget_data (dialog, factory, entry);
+
+ /* If we created a dock before, the newly created dialog is
+ * supposed to be a GimpDockable.
+ */
+ if (dock)
+ {
+ if (GIMP_IS_DOCKABLE (dialog))
+ {
+ gimp_dock_add (GIMP_DOCK (dock), GIMP_DOCKABLE (dialog),
+ 0, 0);
+
+ gtk_widget_show (dock);
+ }
+ else
+ {
+ g_warning ("%s: GimpDialogFactory is a dockable factory "
+ "but constructor for \"%s\" did not return a "
+ "GimpDockable",
+ G_STRFUNC, identifier);
+
+ gtk_widget_destroy (dialog);
+ gtk_widget_destroy (dock);
+
+ dialog = NULL;
+ dock = NULL;
+ }
+ }
+ else if (dock_window)
+ {
+ if (GIMP_IS_DOCK (dialog))
+ {
+ gimp_dock_window_add_dock (GIMP_DOCK_WINDOW (dock_window),
+ GIMP_DOCK (dialog),
+ -1 /*index*/);
+
+ gtk_widget_set_visible (dialog, present);
+ gtk_widget_set_visible (dock_window, present);
+ }
+ else
+ {
+ g_warning ("%s: GimpDialogFactory is a dock factory entry "
+ "but constructor for \"%s\" did not return a "
+ "GimpDock",
+ G_STRFUNC, identifier);
+
+ gtk_widget_destroy (dialog);
+ gtk_widget_destroy (dock_window);
+
+ dialog = NULL;
+ dock_window = NULL;
+ }
+ }
+ }
+ else if (dock)
+ {
+ g_warning ("%s: constructor for \"%s\" returned NULL",
+ G_STRFUNC, identifier);
+
+ gtk_widget_destroy (dock);
+
+ dock = NULL;
+ }
+
+ if (dialog)
+ gimp_dialog_factory_add_dialog (factory, dialog, screen, monitor);
+ }
+
+ /* Finally, if we found an existing dialog or created a new one, raise it.
+ */
+ if (! dialog)
+ return NULL;
+
+ if (gtk_widget_is_toplevel (dialog))
+ {
+ gtk_window_set_screen (GTK_WINDOW (dialog), screen);
+
+ toplevel = dialog;
+ }
+ else if (GIMP_IS_DOCK (dialog))
+ {
+ toplevel = gtk_widget_get_toplevel (dialog);
+ }
+ else if (GIMP_IS_DOCKABLE (dialog))
+ {
+ GimpDockable *dockable = GIMP_DOCKABLE (dialog);
+
+ if (gimp_dockable_get_dockbook (dockable) &&
+ gimp_dockbook_get_dock (gimp_dockable_get_dockbook (dockable)))
+ {
+ GtkNotebook *notebook = GTK_NOTEBOOK (gimp_dockable_get_dockbook (dockable));
+ gint num = gtk_notebook_page_num (notebook, dialog);
+
+ if (num != -1)
+ {
+ gtk_notebook_set_current_page (notebook, num);
+
+ gimp_widget_blink (dialog);
+ }
+ }
+
+ toplevel = gtk_widget_get_toplevel (dialog);
+ }
+
+ if (present && GTK_IS_WINDOW (toplevel))
+ {
+ /* Work around focus-stealing protection, or whatever makes the
+ * dock appear below the one where we clicked a button to open
+ * it. See bug #630173.
+ */
+ gtk_widget_show_now (toplevel);
+ gdk_window_raise (gtk_widget_get_window (toplevel));
+ }
+
+ return dialog;
+}
+
+/**
+ * gimp_dialog_factory_dialog_new:
+ * @factory: a #GimpDialogFactory
+ * @screen: the #GdkScreen the dialog should appear on
+ * @ui_manager: A #GimpUIManager, if applicable.
+ * @identifier: the identifier of the dialog as registered with
+ * gimp_dialog_factory_register_entry()
+ * @view_size: the initial preview size
+ * @present: whether gtk_window_present() should be called
+ *
+ * Creates a new toplevel dialog or a #GimpDockable, depending on whether
+ * %factory is a toplevel of dockable factory.
+ *
+ * Return value: the newly created dialog or an already existing singleton
+ * dialog.
+ **/
+GtkWidget *
+gimp_dialog_factory_dialog_new (GimpDialogFactory *factory,
+ GdkScreen *screen,
+ gint monitor,
+ GimpUIManager *ui_manager,
+ const gchar *identifier,
+ gint view_size,
+ gboolean present)
+{
+ g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+ g_return_val_if_fail (identifier != NULL, NULL);
+
+ return gimp_dialog_factory_dialog_new_internal (factory,
+ screen,
+ monitor,
+ factory->p->context,
+ ui_manager,
+ identifier,
+ view_size,
+ FALSE /*return_existing*/,
+ present,
+ FALSE /*create_containers*/);
+}
+
+GimpContext *
+gimp_dialog_factory_get_context (GimpDialogFactory *factory)
+{
+ g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
+
+ return factory->p->context;
+}
+
+GimpMenuFactory *
+gimp_dialog_factory_get_menu_factory (GimpDialogFactory *factory)
+{
+ g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
+
+ return factory->p->menu_factory;
+}
+
+GList *
+gimp_dialog_factory_get_open_dialogs (GimpDialogFactory *factory)
+{
+ g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
+
+ return factory->p->open_dialogs;
+}
+
+GList *
+gimp_dialog_factory_get_session_infos (GimpDialogFactory *factory)
+{
+ g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
+
+ return factory->p->session_infos;
+}
+
+void
+gimp_dialog_factory_add_session_info (GimpDialogFactory *factory,
+ GimpSessionInfo *info)
+{
+ g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
+ g_return_if_fail (GIMP_IS_SESSION_INFO (info));
+
+ /* We want to append rather than prepend so that the serialized
+ * order in sessionrc remains the same
+ */
+ factory->p->session_infos = g_list_append (factory->p->session_infos,
+ g_object_ref (info));
+}
+
+/**
+ * gimp_dialog_factory_dialog_raise:
+ * @factory: a #GimpDialogFactory
+ * @screen: the #GdkScreen the dialog should appear on
+ * @identifiers: a '|' separated list of identifiers of dialogs as
+ * registered with gimp_dialog_factory_register_entry()
+ * @view_size: the initial preview size if a dialog needs to be created
+ *
+ * Raises any of a list of already existing toplevel dialog or
+ * #GimpDockable if it was already created by this %facory.
+ *
+ * Implicitly creates the first dialog in the list if none of the dialogs
+ * were found.
+ *
+ * Return value: the raised or newly created dialog.
+ **/
+GtkWidget *
+gimp_dialog_factory_dialog_raise (GimpDialogFactory *factory,
+ GdkScreen *screen,
+ gint monitor,
+ const gchar *identifiers,
+ gint view_size)
+{
+ GtkWidget *dialog;
+ gchar **ids;
+ gint i;
+
+ g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+ g_return_val_if_fail (identifiers != NULL, NULL);
+
+ /* If the identifier is a list, try to find a matching dialog and
+ * raise it. If there's no match, use the first list item.
+ *
+ * (we split the identifier list manually here because we must pass
+ * a single identifier, not a list, to new_internal() below)
+ */
+ ids = g_strsplit (identifiers, "|", 0);
+ for (i = 0; ids[i]; i++)
+ {
+ if (gimp_dialog_factory_find_widget (factory, ids[i]))
+ break;
+ }
+
+ dialog = gimp_dialog_factory_dialog_new_internal (factory,
+ screen,
+ monitor,
+ NULL,
+ NULL,
+ ids[i] ? ids[i] : ids[0],
+ view_size,
+ TRUE /*return_existing*/,
+ TRUE /*present*/,
+ TRUE /*create_containers*/);
+ g_strfreev (ids);
+
+ return dialog;
+}
+
+/**
+ * gimp_dialog_factory_dockable_new:
+ * @factory: a #GimpDialogFactory
+ * @dock: a #GimpDock created by this %factory.
+ * @identifier: the identifier of the dialog as registered with
+ * gimp_dialog_factory_register_entry()
+ * @view_size:
+ *
+ * Creates a new #GimpDockable in the context of the #GimpDock it will be
+ * added to.
+ *
+ * Implicitly raises & returns an already existing singleton dockable,
+ * so callers should check that gimp_dockable_get_dockbook (dockable)
+ * is NULL before trying to add it to it's #GimpDockbook.
+ *
+ * Return value: the newly created #GimpDockable or an already existing
+ * singleton dockable.
+ **/
+GtkWidget *
+gimp_dialog_factory_dockable_new (GimpDialogFactory *factory,
+ GimpDock *dock,
+ const gchar *identifier,
+ gint view_size)
+{
+ g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
+ g_return_val_if_fail (GIMP_IS_DOCK (dock), NULL);
+ g_return_val_if_fail (identifier != NULL, NULL);
+
+ return gimp_dialog_factory_dialog_new_internal (factory,
+ gtk_widget_get_screen (GTK_WIDGET (dock)),
+ 0,
+ gimp_dock_get_context (dock),
+ gimp_dock_get_ui_manager (dock),
+ identifier,
+ view_size,
+ FALSE /*return_existing*/,
+ FALSE /*present*/,
+ FALSE /*create_containers*/);
+}
+
+void
+gimp_dialog_factory_add_dialog (GimpDialogFactory *factory,
+ GtkWidget *dialog,
+ GdkScreen *screen,
+ gint monitor)
+{
+ GimpDialogFactory *dialog_factory = NULL;
+ GimpDialogFactoryEntry *entry = NULL;
+ GimpSessionInfo *info = NULL;
+ GList *list = NULL;
+ gboolean toplevel = FALSE;
+
+ g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
+ g_return_if_fail (GTK_IS_WIDGET (dialog));
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+
+ if (g_list_find (factory->p->open_dialogs, dialog))
+ {
+ g_warning ("%s: dialog already registered", G_STRFUNC);
+ return;
+ }
+
+ dialog_factory = gimp_dialog_factory_from_widget (dialog, &entry);
+
+ if (! gimp_dialog_factory_dialog_sane (factory,
+ dialog_factory,
+ entry,
+ dialog))
+ return;
+
+ toplevel = gtk_widget_is_toplevel (dialog);
+
+ if (entry)
+ {
+ /* dialog is a toplevel (but not a GimpDockWindow) or a GimpDockable */
+
+ GIMP_LOG (DIALOG_FACTORY, "adding %s \"%s\"",
+ toplevel ? "toplevel" : "dockable",
+ entry->identifier);
+
+ for (list = factory->p->session_infos; list; list = g_list_next (list))
+ {
+ GimpSessionInfo *current_info = list->data;
+
+ if (gimp_session_info_get_factory_entry (current_info) == entry)
+ {
+ if (gimp_session_info_get_widget (current_info))
+ {
+ if (gimp_session_info_is_singleton (current_info))
+ {
+ g_warning ("%s: singleton dialog \"%s\" created twice",
+ G_STRFUNC, entry->identifier);
+
+ GIMP_LOG (DIALOG_FACTORY,
+ "corrupt session info: %p (widget %p)",
+ current_info,
+ gimp_session_info_get_widget (current_info));
+
+ return;
+ }
+
+ continue;
+ }
+
+ gimp_session_info_set_widget (current_info, dialog);
+
+ GIMP_LOG (DIALOG_FACTORY,
+ "updating session info %p (widget %p) for %s \"%s\"",
+ current_info,
+ gimp_session_info_get_widget (current_info),
+ toplevel ? "toplevel" : "dockable",
+ entry->identifier);
+
+ if (toplevel &&
+ gimp_session_info_is_session_managed (current_info) &&
+ ! gtk_widget_get_visible (dialog))
+ {
+ GimpGuiConfig *gui_config;
+
+ gui_config = GIMP_GUI_CONFIG (factory->p->context->gimp->config);
+
+ gimp_session_info_apply_geometry (current_info,
+ screen, monitor,
+ gui_config->restore_monitor);
+ }
+
+ info = current_info;
+ break;
+ }
+ }
+
+ if (! info)
+ {
+ info = gimp_session_info_new ();
+
+ gimp_session_info_set_widget (info, dialog);
+
+ GIMP_LOG (DIALOG_FACTORY,
+ "creating session info %p (widget %p) for %s \"%s\"",
+ info,
+ gimp_session_info_get_widget (info),
+ toplevel ? "toplevel" : "dockable",
+ entry->identifier);
+
+ gimp_session_info_set_factory_entry (info, entry);
+
+ if (gimp_session_info_is_session_managed (info))
+ {
+ /* Make the dialog show up at the user position the
+ * first time it is shown. After it has been shown the
+ * first time we don't want it to show at the mouse the
+ * next time. Think of the use cases "hide and show with
+ * tab" and "change virtual desktops"
+ */
+ GIMP_LOG (WM, "setting GTK_WIN_POS_MOUSE for %p (\"%s\")\n",
+ dialog, entry->identifier);
+
+ gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
+ g_signal_connect (dialog, "configure-event",
+ G_CALLBACK (gimp_dialog_factory_set_user_pos),
+ NULL);
+ }
+
+ gimp_dialog_factory_add_session_info (factory, info);
+ g_object_unref (info);
+ }
+ }
+
+ /* Some special logic for dock windows */
+ if (GIMP_IS_DOCK_WINDOW (dialog))
+ {
+ g_signal_emit (factory, factory_signals[DOCK_WINDOW_ADDED], 0, dialog);
+ }
+
+ factory->p->open_dialogs = g_list_prepend (factory->p->open_dialogs, dialog);
+
+ g_signal_connect_object (dialog, "destroy",
+ G_CALLBACK (gimp_dialog_factory_remove_dialog),
+ factory,
+ G_CONNECT_SWAPPED);
+
+ if (gimp_session_info_is_session_managed (info))
+ g_signal_connect_object (dialog, "configure-event",
+ G_CALLBACK (gimp_dialog_factory_dialog_configure),
+ factory,
+ 0);
+}
+
+void
+gimp_dialog_factory_add_foreign (GimpDialogFactory *factory,
+ const gchar *identifier,
+ GtkWidget *dialog,
+ GdkScreen *screen,
+ gint monitor)
+{
+ GimpDialogFactory *dialog_factory;
+ GimpDialogFactoryEntry *entry;
+
+ g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
+ g_return_if_fail (identifier != NULL);
+ g_return_if_fail (GTK_IS_WIDGET (dialog));
+ g_return_if_fail (gtk_widget_is_toplevel (dialog));
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+
+ dialog_factory = gimp_dialog_factory_from_widget (dialog, &entry);
+
+ if (dialog_factory || entry)
+ {
+ g_warning ("%s: dialog was created by a GimpDialogFactory",
+ G_STRFUNC);
+ return;
+ }
+
+ entry = gimp_dialog_factory_find_entry (factory, identifier);
+
+ if (! entry)
+ {
+ g_warning ("%s: no entry registered for \"%s\"",
+ G_STRFUNC, identifier);
+ return;
+ }
+
+ if (entry->new_func)
+ {
+ g_warning ("%s: entry for \"%s\" has a constructor (is not foreign)",
+ G_STRFUNC, identifier);
+ return;
+ }
+
+ gimp_dialog_factory_set_widget_data (dialog, factory, entry);
+
+ gimp_dialog_factory_add_dialog (factory, dialog, screen, monitor);
+}
+
+/**
+ * gimp_dialog_factory_position_dialog:
+ * @factory:
+ * @identifier:
+ * @dialog:
+ * @screen:
+ * @monitor:
+ *
+ * We correctly position all newly created dialog via
+ * gimp_dialog_factory_add_dialog(), but some dialogs (like various
+ * color dialogs) are never destroyed but created only once per
+ * session. On re-showing, whatever window managing magic kicks in and
+ * the dialog sometimes goes where it shouldn't.
+ *
+ * This function correctly positions a dialog on re-showing so it
+ * appears where it was before it was hidden.
+ *
+ * See https://gitlab.gnome.org/GNOME/gimp/issues/1093
+ **/
+void
+gimp_dialog_factory_position_dialog (GimpDialogFactory *factory,
+ const gchar *identifier,
+ GtkWidget *dialog,
+ GdkScreen *screen,
+ gint monitor)
+{
+ GimpSessionInfo *info;
+ GimpGuiConfig *gui_config;
+
+ g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
+ g_return_if_fail (identifier != NULL);
+ g_return_if_fail (GTK_IS_WIDGET (dialog));
+ g_return_if_fail (gtk_widget_is_toplevel (dialog));
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+
+ info = gimp_dialog_factory_find_session_info (factory, identifier);
+
+ if (! info)
+ {
+ g_warning ("%s: no session info found for \"%s\"",
+ G_STRFUNC, identifier);
+ return;
+ }
+
+ if (gimp_session_info_get_widget (info) != dialog)
+ {
+ g_warning ("%s: session info for \"%s\" is for a different widget",
+ G_STRFUNC, identifier);
+ return;
+ }
+
+ gui_config = GIMP_GUI_CONFIG (factory->p->context->gimp->config);
+
+ gimp_session_info_apply_geometry (info,
+ screen, monitor,
+ gui_config->restore_monitor);
+}
+
+void
+gimp_dialog_factory_remove_dialog (GimpDialogFactory *factory,
+ GtkWidget *dialog)
+{
+ GimpDialogFactory *dialog_factory;
+ GimpDialogFactoryEntry *entry;
+ GList *list;
+
+ g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
+ g_return_if_fail (GTK_IS_WIDGET (dialog));
+
+ if (! g_list_find (factory->p->open_dialogs, dialog))
+ {
+ g_warning ("%s: dialog not registered", G_STRFUNC);
+ return;
+ }
+
+ factory->p->open_dialogs = g_list_remove (factory->p->open_dialogs, dialog);
+
+ dialog_factory = gimp_dialog_factory_from_widget (dialog, &entry);
+
+ if (! gimp_dialog_factory_dialog_sane (factory,
+ dialog_factory,
+ entry,
+ dialog))
+ return;
+
+ GIMP_LOG (DIALOG_FACTORY, "removing \"%s\" (dialog = %p)",
+ entry->identifier,
+ dialog);
+
+ for (list = factory->p->session_infos; list; list = g_list_next (list))
+ {
+ GimpSessionInfo *session_info = list->data;
+
+ if (gimp_session_info_get_widget (session_info) == dialog)
+ {
+ GIMP_LOG (DIALOG_FACTORY,
+ "clearing session info %p (widget %p) for \"%s\"",
+ session_info, gimp_session_info_get_widget (session_info),
+ entry->identifier);
+
+ gimp_session_info_set_widget (session_info, NULL);
+
+ gimp_dialog_factory_unset_widget_data (dialog);
+
+ g_signal_handlers_disconnect_by_func (dialog,
+ gimp_dialog_factory_set_user_pos,
+ NULL);
+ g_signal_handlers_disconnect_by_func (dialog,
+ gimp_dialog_factory_remove_dialog,
+ factory);
+
+ if (gimp_session_info_is_session_managed (session_info))
+ g_signal_handlers_disconnect_by_func (dialog,
+ gimp_dialog_factory_dialog_configure,
+ factory);
+
+ if (GIMP_IS_DOCK_WINDOW (dialog))
+ {
+ /* don't save session info for empty docks */
+ factory->p->session_infos = g_list_remove (factory->p->session_infos,
+ session_info);
+ g_object_unref (session_info);
+
+ g_signal_emit (factory, factory_signals[DOCK_WINDOW_REMOVED], 0,
+ dialog);
+ }
+
+ break;
+ }
+ }
+}
+
+void
+gimp_dialog_factory_hide_dialog (GtkWidget *dialog)
+{
+ GimpDialogFactory *factory = NULL;
+
+ g_return_if_fail (GTK_IS_WIDGET (dialog));
+ g_return_if_fail (gtk_widget_is_toplevel (dialog));
+
+ if (! (factory = gimp_dialog_factory_from_widget (dialog, NULL)))
+ {
+ g_warning ("%s: dialog was not created by a GimpDialogFactory",
+ G_STRFUNC);
+ return;
+ }
+
+ gtk_widget_hide (dialog);
+
+ if (factory->p->dialog_state != GIMP_DIALOGS_SHOWN)
+ g_object_set_data (G_OBJECT (dialog), GIMP_DIALOG_VISIBILITY_KEY,
+ GINT_TO_POINTER (GIMP_DIALOG_VISIBILITY_INVISIBLE));
+}
+
+void
+gimp_dialog_factory_set_state (GimpDialogFactory *factory,
+ GimpDialogsState state)
+{
+ g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
+
+ factory->p->dialog_state = state;
+
+ if (state == GIMP_DIALOGS_SHOWN)
+ {
+ gimp_dialog_factory_show (factory);
+ }
+ else
+ {
+ gimp_dialog_factory_hide (factory);
+ }
+}
+
+GimpDialogsState
+gimp_dialog_factory_get_state (GimpDialogFactory *factory)
+{
+ g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), 0);
+
+ return factory->p->dialog_state;
+}
+
+void
+gimp_dialog_factory_show_with_display (GimpDialogFactory *factory)
+{
+ g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
+
+ if (factory->p->dialog_state == GIMP_DIALOGS_HIDDEN_WITH_DISPLAY)
+ {
+ gimp_dialog_factory_set_state (factory, GIMP_DIALOGS_SHOWN);
+ }
+}
+
+void
+gimp_dialog_factory_hide_with_display (GimpDialogFactory *factory)
+{
+ g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
+
+ if (factory->p->dialog_state == GIMP_DIALOGS_SHOWN)
+ {
+ gimp_dialog_factory_set_state (factory, GIMP_DIALOGS_HIDDEN_WITH_DISPLAY);
+ }
+}
+
+static GQuark gimp_dialog_factory_key = 0;
+static GQuark gimp_dialog_factory_entry_key = 0;
+
+GimpDialogFactory *
+gimp_dialog_factory_from_widget (GtkWidget *dialog,
+ GimpDialogFactoryEntry **entry)
+{
+ g_return_val_if_fail (GTK_IS_WIDGET (dialog), NULL);
+
+ if (! gimp_dialog_factory_key)
+ {
+ gimp_dialog_factory_key =
+ g_quark_from_static_string ("gimp-dialog-factory");
+
+ gimp_dialog_factory_entry_key =
+ g_quark_from_static_string ("gimp-dialog-factory-entry");
+ }
+
+ if (entry)
+ *entry = g_object_get_qdata (G_OBJECT (dialog),
+ gimp_dialog_factory_entry_key);
+
+ return g_object_get_qdata (G_OBJECT (dialog), gimp_dialog_factory_key);
+}
+
+#define GIMP_DIALOG_FACTORY_MIN_SIZE_KEY "gimp-dialog-factory-min-size"
+
+void
+gimp_dialog_factory_set_has_min_size (GtkWindow *window,
+ gboolean has_min_size)
+{
+ g_return_if_fail (GTK_IS_WINDOW (window));
+
+ g_object_set_data (G_OBJECT (window), GIMP_DIALOG_FACTORY_MIN_SIZE_KEY,
+ GINT_TO_POINTER (has_min_size ? TRUE : FALSE));
+}
+
+gboolean
+gimp_dialog_factory_get_has_min_size (GtkWindow *window)
+{
+ g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
+
+ return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window),
+ GIMP_DIALOG_FACTORY_MIN_SIZE_KEY));
+}
+
+
+/* private functions */
+
+static GtkWidget *
+gimp_dialog_factory_constructor (GimpDialogFactory *factory,
+ GimpDialogFactoryEntry *entry,
+ GimpContext *context,
+ GimpUIManager *ui_manager,
+ gint view_size)
+{
+ GtkWidget *widget;
+
+ widget = entry->new_func (factory, context, ui_manager, view_size);
+
+ /* The entry is for a dockable, so we simply need to put the created
+ * widget in a dockable
+ */
+ if (widget && entry->dockable)
+ {
+ GtkWidget *dockable = NULL;
+
+ dockable = gimp_dockable_new (entry->name, entry->blurb,
+ entry->icon_name, entry->help_id);
+ gtk_container_add (GTK_CONTAINER (dockable), widget);
+ gtk_widget_show (widget);
+
+ /* EEK */
+ g_object_set_data (G_OBJECT (dockable), "gimp-dialog-identifier",
+ entry->identifier);
+
+ /* Return the dockable instead */
+ widget = dockable;
+ }
+
+ return widget;
+}
+
+static void
+gimp_dialog_factory_config_notify (GimpDialogFactory *factory,
+ GParamSpec *pspec,
+ GimpGuiConfig *config)
+{
+ GimpDialogsState state = gimp_dialog_factory_get_state (factory);
+ GimpDialogsState new_state = state;
+
+ /* Make sure the state and config are in sync */
+ if (config->hide_docks && state == GIMP_DIALOGS_SHOWN)
+ new_state = GIMP_DIALOGS_HIDDEN_EXPLICITLY;
+ else if (! config->hide_docks)
+ new_state = GIMP_DIALOGS_SHOWN;
+
+ if (state != new_state)
+ gimp_dialog_factory_set_state (factory, new_state);
+}
+
+static void
+gimp_dialog_factory_set_widget_data (GtkWidget *dialog,
+ GimpDialogFactory *factory,
+ GimpDialogFactoryEntry *entry)
+{
+ g_return_if_fail (GTK_IS_WIDGET (dialog));
+ g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
+
+ if (! gimp_dialog_factory_key)
+ {
+ gimp_dialog_factory_key =
+ g_quark_from_static_string ("gimp-dialog-factory");
+
+ gimp_dialog_factory_entry_key =
+ g_quark_from_static_string ("gimp-dialog-factory-entry");
+ }
+
+ g_object_set_qdata (G_OBJECT (dialog), gimp_dialog_factory_key, factory);
+
+ if (entry)
+ g_object_set_qdata (G_OBJECT (dialog), gimp_dialog_factory_entry_key,
+ entry);
+}
+
+static void
+gimp_dialog_factory_unset_widget_data (GtkWidget *dialog)
+{
+ g_return_if_fail (GTK_IS_WIDGET (dialog));
+
+ if (! gimp_dialog_factory_key)
+ return;
+
+ g_object_set_qdata (G_OBJECT (dialog), gimp_dialog_factory_key, NULL);
+ g_object_set_qdata (G_OBJECT (dialog), gimp_dialog_factory_entry_key, NULL);
+}
+
+static gboolean
+gimp_dialog_factory_set_user_pos (GtkWidget *dialog,
+ GdkEventConfigure *cevent,
+ gpointer data)
+{
+ GdkWindowHints geometry_mask;
+
+ /* Not only set geometry hints, also reset window position */
+ gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_NONE);
+ g_signal_handlers_disconnect_by_func (dialog,
+ gimp_dialog_factory_set_user_pos,
+ data);
+
+ GIMP_LOG (WM, "setting GDK_HINT_USER_POS for %p\n", dialog);
+ geometry_mask = GDK_HINT_USER_POS;
+
+ if (gimp_dialog_factory_get_has_min_size (GTK_WINDOW (dialog)))
+ geometry_mask |= GDK_HINT_MIN_SIZE;
+
+ gtk_window_set_geometry_hints (GTK_WINDOW (dialog), NULL, NULL,
+ geometry_mask);
+
+ return FALSE;
+}
+
+static gboolean
+gimp_dialog_factory_dialog_configure (GtkWidget *dialog,
+ GdkEventConfigure *cevent,
+ GimpDialogFactory *factory)
+{
+ GimpDialogFactory *dialog_factory;
+ GimpDialogFactoryEntry *entry;
+ GList *list;
+
+ if (! g_list_find (factory->p->open_dialogs, dialog))
+ {
+ g_warning ("%s: dialog not registered", G_STRFUNC);
+ return FALSE;
+ }
+
+ dialog_factory = gimp_dialog_factory_from_widget (dialog, &entry);
+
+ if (! gimp_dialog_factory_dialog_sane (factory,
+ dialog_factory,
+ entry,
+ dialog))
+ return FALSE;
+
+ for (list = factory->p->session_infos; list; list = g_list_next (list))
+ {
+ GimpSessionInfo *session_info = list->data;
+
+ if (gimp_session_info_get_widget (session_info) == dialog)
+ {
+ gimp_session_info_read_geometry (session_info, cevent);
+
+ GIMP_LOG (DIALOG_FACTORY,
+ "updated session info for \"%s\" from window geometry "
+ "(x=%d y=%d %dx%d)",
+ entry->identifier,
+ gimp_session_info_get_x (session_info),
+ gimp_session_info_get_y (session_info),
+ gimp_session_info_get_width (session_info),
+ gimp_session_info_get_height (session_info));
+
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+void
+gimp_dialog_factory_save (GimpDialogFactory *factory,
+ GimpConfigWriter *writer)
+{
+ GList *infos;
+
+ for (infos = factory->p->session_infos; infos; infos = g_list_next (infos))
+ {
+ GimpSessionInfo *info = infos->data;
+
+ /* we keep session info entries for all toplevel dialogs created
+ * by the factory but don't save them if they don't want to be
+ * managed
+ */
+ if (! gimp_session_info_is_session_managed (info) ||
+ gimp_session_info_get_factory_entry (info) == NULL)
+ continue;
+
+ if (gimp_session_info_get_widget (info))
+ gimp_session_info_get_info (info);
+
+ gimp_config_writer_open (writer, "session-info");
+ gimp_config_writer_string (writer,
+ gimp_object_get_name (factory));
+
+ GIMP_CONFIG_GET_INTERFACE (info)->serialize (GIMP_CONFIG (info),
+ writer,
+ NULL);
+
+ gimp_config_writer_close (writer);
+
+ if (gimp_session_info_get_widget (info))
+ gimp_session_info_clear_info (info);
+ }
+}
+
+void
+gimp_dialog_factory_restore (GimpDialogFactory *factory,
+ GdkScreen *screen,
+ gint monitor)
+{
+ GList *infos;
+
+ for (infos = factory->p->session_infos; infos; infos = g_list_next (infos))
+ {
+ GimpSessionInfo *info = infos->data;
+
+ if (gimp_session_info_get_open (info))
+ {
+ gimp_session_info_restore (info, factory, screen, monitor);
+ }
+ else
+ {
+ GIMP_LOG (DIALOG_FACTORY,
+ "skipping to restore session info %p, not open",
+ info);
+ }
+ }
+}
+
+static void
+gimp_dialog_factory_hide (GimpDialogFactory *factory)
+{
+ GList *list;
+
+ for (list = factory->p->open_dialogs; list; list = g_list_next (list))
+ {
+ GtkWidget *widget = list->data;
+
+ if (GTK_IS_WIDGET (widget) && gtk_widget_is_toplevel (widget))
+ {
+ GimpDialogFactoryEntry *entry = NULL;
+ GimpDialogVisibilityState visibility = GIMP_DIALOG_VISIBILITY_UNKNOWN;
+
+ gimp_dialog_factory_from_widget (widget, &entry);
+ if (! entry->hideable)
+ continue;
+
+ if (gtk_widget_get_visible (widget))
+ {
+ gtk_widget_hide (widget);
+ visibility = GIMP_DIALOG_VISIBILITY_HIDDEN;
+
+ GIMP_LOG (WM, "Hiding '%s' [%p]",
+ gtk_window_get_title (GTK_WINDOW (widget)),
+ widget);
+ }
+ else
+ {
+ visibility = GIMP_DIALOG_VISIBILITY_INVISIBLE;
+ }
+
+ g_object_set_data (G_OBJECT (widget),
+ GIMP_DIALOG_VISIBILITY_KEY,
+ GINT_TO_POINTER (visibility));
+ }
+ }
+}
+
+static void
+gimp_dialog_factory_show (GimpDialogFactory *factory)
+{
+ GList *list;
+
+ for (list = factory->p->open_dialogs; list; list = g_list_next (list))
+ {
+ GtkWidget *widget = list->data;
+
+ if (GTK_IS_WIDGET (widget) && gtk_widget_is_toplevel (widget))
+ {
+ GimpDialogVisibilityState visibility;
+
+ visibility =
+ GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
+ GIMP_DIALOG_VISIBILITY_KEY));
+
+ if (! gtk_widget_get_visible (widget) &&
+ visibility == GIMP_DIALOG_VISIBILITY_HIDDEN)
+ {
+ GIMP_LOG (WM, "Showing '%s' [%p]",
+ gtk_window_get_title (GTK_WINDOW (widget)),
+ widget);
+
+ /* Don't use gtk_window_present() here, we don't want the
+ * keyboard focus to move.
+ */
+ gtk_widget_show (widget);
+ g_object_set_data (G_OBJECT (widget),
+ GIMP_DIALOG_VISIBILITY_KEY,
+ GINT_TO_POINTER (GIMP_DIALOG_VISIBILITY_VISIBLE));
+
+ if (gtk_widget_get_visible (widget))
+ gdk_window_raise (gtk_widget_get_window (widget));
+ }
+ }
+ }
+}
+
+void
+gimp_dialog_factory_set_busy (GimpDialogFactory *factory)
+{
+ GList *list;
+
+ if (! factory)
+ return;
+
+ for (list = factory->p->open_dialogs; list; list = g_list_next (list))
+ {
+ GtkWidget *widget = list->data;
+
+ if (GTK_IS_WIDGET (widget) && gtk_widget_is_toplevel (widget))
+ {
+ GdkWindow *window = gtk_widget_get_window (widget);
+
+ if (window)
+ {
+ GdkCursor *cursor = gimp_cursor_new (window,
+ GIMP_HANDEDNESS_RIGHT,
+ (GimpCursorType) GDK_WATCH,
+ GIMP_TOOL_CURSOR_NONE,
+ GIMP_CURSOR_MODIFIER_NONE);
+ gdk_window_set_cursor (window, cursor);
+ gdk_cursor_unref (cursor);
+ }
+ }
+ }
+}
+
+void
+gimp_dialog_factory_unset_busy (GimpDialogFactory *factory)
+{
+ GList *list;
+
+ if (! factory)
+ return;
+
+ for (list = factory->p->open_dialogs; list; list = g_list_next (list))
+ {
+ GtkWidget *widget = list->data;
+
+ if (GTK_IS_WIDGET (widget) && gtk_widget_is_toplevel (widget))
+ {
+ GdkWindow *window = gtk_widget_get_window (widget);
+
+ if (window)
+ gdk_window_set_cursor (window, NULL);
+ }
+ }
+}
+
+/**
+ * gimp_dialog_factory_get_singleton:
+ *
+ * Returns: The toplevel GimpDialogFactory instance.
+ **/
+GimpDialogFactory *
+gimp_dialog_factory_get_singleton (void)
+{
+ g_return_val_if_fail (gimp_toplevel_factory != NULL, NULL);
+
+ return gimp_toplevel_factory;
+}
+
+/**
+ * gimp_dialog_factory_set_singleton:
+ * @:
+ *
+ * Set the toplevel GimpDialogFactory instance. Must only be called by
+ * dialogs_init()!.
+ **/
+void
+gimp_dialog_factory_set_singleton (GimpDialogFactory *factory)
+{
+ g_return_if_fail (gimp_toplevel_factory == NULL ||
+ factory == NULL);
+
+ gimp_toplevel_factory = factory;
+}