/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * gimpdialogfactory.c * Copyright (C) 2001 Michael Natterer * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include "libgimpwidgets/gimpwidgets.h" #include "widgets-types.h" #include "core/gimpcontext.h" #include "config/gimpconfigwriter.h" #include "gimpcontainerview.h" #include "gimpcontainerview-utils.h" #include "gimpcursor.h" #include "gimpdialogfactory.h" #include "gimpdock.h" #include "gimpdockbook.h" #include "gimpdockable.h" #include "gimpimagedock.h" #include "gimpmenufactory.h" /* #define DEBUG_FACTORY */ #ifdef DEBUG_FACTORY #define D(stmnt) stmnt #else #define D(stmnt) #endif typedef enum { GIMP_DIALOG_VISIBILITY_UNKNOWN = 0, GIMP_DIALOG_VISIBILITY_INVISIBLE, GIMP_DIALOG_VISIBILITY_VISIBLE } GimpDialogVisibilityState; typedef enum { GIMP_DIALOG_SHOW_ALL, GIMP_DIALOG_HIDE_ALL, GIMP_DIALOG_SHOW_TOOLBOX } GimpDialogShowState; static void gimp_dialog_factory_class_init (GimpDialogFactoryClass *klass); static void gimp_dialog_factory_init (GimpDialogFactory *factory); static void gimp_dialog_factory_dispose (GObject *object); static void gimp_dialog_factory_finalize (GObject *object); static void gimp_dialog_factories_save_foreach (gchar *name, GimpDialogFactory *factory, GimpConfigWriter *writer); static void gimp_dialog_factories_restore_foreach (gchar *name, GimpDialogFactory *factory, gpointer data); static void gimp_dialog_factories_clear_foreach (gchar *name, GimpDialogFactory *factory, gpointer data); static void gimp_dialog_factory_get_window_info (GtkWidget *window, GimpSessionInfo *info); static void gimp_dialog_factory_set_window_geometry (GtkWidget *window, GimpSessionInfo *info); static void gimp_dialog_factory_get_aux_info (GtkWidget *dialog, GimpSessionInfo *info); static void gimp_dialog_factory_set_aux_info (GtkWidget *dialog, GimpSessionInfo *info); static void gimp_dialog_factories_hide_foreach (gchar *name, GimpDialogFactory *factory, gpointer data); static void gimp_dialog_factories_show_foreach (gchar *name, GimpDialogFactory *factory, gpointer data); static void gimp_dialog_factories_idle_foreach (gchar *name, GimpDialogFactory *factory, gpointer data); static void gimp_dialog_factories_unidle_foreach (gchar *name, GimpDialogFactory *factory, gpointer data); static GimpObjectClass *parent_class = NULL; GType gimp_dialog_factory_get_type (void) { static GType factory_type = 0; if (! factory_type) { static const GTypeInfo factory_info = { sizeof (GimpDialogFactoryClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) gimp_dialog_factory_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GimpDialogFactory), 0, /* n_preallocs */ (GInstanceInitFunc) gimp_dialog_factory_init, }; factory_type = g_type_register_static (GIMP_TYPE_OBJECT, "GimpDialogFactory", &factory_info, 0); } return factory_type; } static void gimp_dialog_factory_class_init (GimpDialogFactoryClass *klass) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->dispose = gimp_dialog_factory_dispose; object_class->finalize = gimp_dialog_factory_finalize; klass->factories = g_hash_table_new (g_str_hash, g_str_equal); } static void gimp_dialog_factory_init (GimpDialogFactory *factory) { factory->menu_factory = NULL; factory->new_dock_func = NULL; factory->registered_dialogs = NULL; factory->session_infos = NULL; factory->open_dialogs = NULL; } static void gimp_dialog_factory_dispose (GObject *object) { GimpDialogFactory *factory; GList *list; factory = GIMP_DIALOG_FACTORY (object); /* 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->open_dialogs */ while (factory->open_dialogs) { for (list = factory->open_dialogs; list; list = g_list_next (list)) { if (GTK_WIDGET_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: stale non-toplevel entries in factory->open_dialogs", G_GNUC_FUNCTION); break; } } G_OBJECT_CLASS (parent_class)->dispose (object); } static void gimp_dialog_factory_finalize (GObject *object) { GimpDialogFactory *factory; GList *list; factory = GIMP_DIALOG_FACTORY (object); g_hash_table_remove (GIMP_DIALOG_FACTORY_GET_CLASS (object)->factories, GIMP_OBJECT (factory)->name); for (list = factory->registered_dialogs; list; list = g_list_next (list)) { GimpDialogFactoryEntry *entry; entry = (GimpDialogFactoryEntry *) list->data; g_free (entry->identifier); g_free (entry); } if (factory->registered_dialogs) { g_list_free (factory->registered_dialogs); factory->registered_dialogs = NULL; } if (factory->open_dialogs) { g_list_free (factory->open_dialogs); factory->open_dialogs = NULL; } G_OBJECT_CLASS (parent_class)->finalize (object); } GimpDialogFactory * gimp_dialog_factory_new (const gchar *name, GimpContext *context, GimpMenuFactory *menu_factory, GimpDialogNewFunc new_dock_func) { GimpDialogFactoryClass *factory_class; GimpDialogFactory *factory; 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); /* EEK */ factory_class = g_type_class_ref (GIMP_TYPE_DIALOG_FACTORY); if (g_hash_table_lookup (factory_class->factories, name)) { g_warning ("%s: dialog factory \"%s\" already exists", G_GNUC_FUNCTION, name); return NULL; } factory = g_object_new (GIMP_TYPE_DIALOG_FACTORY, NULL); gimp_object_set_name (GIMP_OBJECT (factory), name); g_hash_table_insert (factory_class->factories, GIMP_OBJECT (factory)->name, factory); factory->context = context; factory->menu_factory = menu_factory; factory->new_dock_func = new_dock_func; return factory; } GimpDialogFactory * gimp_dialog_factory_from_name (const gchar *name) { GimpDialogFactoryClass *factory_class; GimpDialogFactory *factory; g_return_val_if_fail (name != NULL, NULL); factory_class = g_type_class_peek (GIMP_TYPE_DIALOG_FACTORY); factory = (GimpDialogFactory *) g_hash_table_lookup (factory_class->factories, name); return factory; } void gimp_dialog_factory_register_entry (GimpDialogFactory *factory, const gchar *identifier, GimpDialogNewFunc new_func, gint preview_size, gboolean singleton, gboolean session_managed, gboolean remember_size, gboolean remember_if_open) { GimpDialogFactoryEntry *entry; g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory)); g_return_if_fail (identifier != NULL); entry = g_new0 (GimpDialogFactoryEntry, 1); entry->identifier = g_strdup (identifier); entry->new_func = new_func; entry->preview_size = preview_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; factory->registered_dialogs = g_list_prepend (factory->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->registered_dialogs; list; list = g_list_next (list)) { GimpDialogFactoryEntry *entry; entry = (GimpDialogFactoryEntry *) 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->session_infos; list; list = g_list_next (list)) { GimpSessionInfo *info; info = (GimpSessionInfo *) list->data; if ((info->toplevel_entry && ! strcmp (identifier, info->toplevel_entry->identifier)) || (info->dockable_entry && ! strcmp (identifier, info->dockable_entry->identifier))) { return info; } } return NULL; } static GtkWidget * gimp_dialog_factory_dialog_new_internal (GimpDialogFactory *factory, GimpContext *context, const gchar *identifier, gint preview_size, gboolean raise_if_found) { GimpDialogFactoryEntry *entry; GtkWidget *dialog = 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_GNUC_FUNCTION, identifier); return NULL; } if (! entry->new_func) { g_warning ("%s: entry for \"%s\" has no constructor", G_GNUC_FUNCTION, identifier); return NULL; } /* a singleton dialog is always only raised if it already exisits */ if (raise_if_found || entry->singleton) { GimpSessionInfo *info; info = gimp_dialog_factory_find_session_info (factory, identifier); if (info) dialog = info->widget; } /* create the dialog if it was not found */ if (! dialog) { GtkWidget *dock = NULL; /* If the dialog will be a dockable (factory->new_dock_func) and * we are called from gimp_dialog_factory_dialog_raise() (! context), * create a new dock _before_ creating the dialog. * We do this because the new dockable needs to be created in it's * dock's context. */ if (factory->new_dock_func && ! context) { GtkWidget *dockbook; dock = gimp_dialog_factory_dock_new (factory); dockbook = gimp_dockbook_new (factory->menu_factory); gimp_dock_add_book (GIMP_DOCK (dock), GIMP_DOCKBOOK (dockbook), 0); } /* 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 (preview_size < GIMP_PREVIEW_SIZE_TINY) preview_size = entry->preview_size; if (context) dialog = entry->new_func (factory, context, preview_size); else if (dock) dialog = entry->new_func (factory, GIMP_DOCK (dock)->context, preview_size); else dialog = entry->new_func (factory, factory->context, preview_size); if (dialog) { g_object_set_data (G_OBJECT (dialog), "gimp-dialog-factory", factory); g_object_set_data (G_OBJECT (dialog), "gimp-dialog-factory-entry", 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_GNUC_FUNCTION, identifier); gtk_widget_destroy (dialog); gtk_widget_destroy (dock); dialog = NULL; dock = NULL; } } } else if (dock) { g_warning ("%s: constructor for \"%s\" returned NULL", G_GNUC_FUNCTION, identifier); gtk_widget_destroy (dock); dock = NULL; } if (dialog) gimp_dialog_factory_add_dialog (factory, dialog); } /* finally, if we found an existing dialog or created a new one, * raise it */ if (dialog) { if (GTK_WIDGET_TOPLEVEL (dialog)) { gtk_window_present (GTK_WINDOW (dialog)); } else if (GIMP_IS_DOCKABLE (dialog)) { GimpDockable *dockable; gint page_num; dockable = GIMP_DOCKABLE (dialog); if (dockable->dockbook && dockable->dockbook->dock) { page_num = gtk_notebook_page_num (GTK_NOTEBOOK (dockable->dockbook), dialog); if (page_num != -1) { GtkWidget *toplevel; gtk_notebook_set_current_page (GTK_NOTEBOOK (dockable->dockbook), page_num); toplevel = gtk_widget_get_toplevel (dialog); gtk_window_present (GTK_WINDOW (toplevel)); } } } } return dialog; } /** * gimp_dialog_factory_dialog_new: * @factory: a #GimpDialogFactory * @identifier: the identifier of the dialog as registered with * gimp_dialog_factory_register_entry() * * Creates a new toplevel dialog or a #GimpDockable, depending on whether * %factory is a toplevel of dockable factory. * * Implicitly raises singleton dialogs. * * Return value: the newly created dialog or an already existing singleton * dialog. **/ GtkWidget * gimp_dialog_factory_dialog_new (GimpDialogFactory *factory, const gchar *identifier, gint preview_size) { g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL); g_return_val_if_fail (identifier != NULL, NULL); return gimp_dialog_factory_dialog_new_internal (factory, factory->context, identifier, preview_size, FALSE); } /** * gimp_dialog_factory_dialog_raise: * @factory : a #GimpDialogFactory * @identifier: the identifier of the dialog as registered with * gimp_dialog_factory_register_entry() * * Raises an already existing toplevel dialog or #GimpDockable if it was * already created by this %facory. * * Implicitly creates a new dialog if it was not found. * * Return value: the raised or newly created dialog. **/ GtkWidget * gimp_dialog_factory_dialog_raise (GimpDialogFactory *factory, const gchar *identifier, gint preview_size) { g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL); g_return_val_if_fail (identifier != NULL, NULL); return gimp_dialog_factory_dialog_new_internal (factory, NULL, identifier, preview_size, TRUE); } /** * gimp_dialog_factory_dockable_new: * @factory : a #GimpDialogFactory * @dock : a #GimpDock crated by this %factory. * @identifier: the identifier of the dialog as registered with * gimp_dialog_factory_register_entry() * * 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 dockable->dockbook 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 preview_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, dock->context, identifier, preview_size, FALSE); } /** * gimp_dialog_factory_dock_new: * @factory: a #GimpDialogFacotry * * Returns a new #GimpDock in this %factory's context. We use a function * pointer passed to this %factory's constructor instead of simply * gimp_dock_new() because we may want different instances of * #GimpDialogFactory create different subclasses of #GimpDock. * * Return value: the newly created #GimpDock. **/ GtkWidget * gimp_dialog_factory_dock_new (GimpDialogFactory *factory) { GtkWidget *dock; g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL); g_return_val_if_fail (factory->new_dock_func != NULL, NULL); dock = factory->new_dock_func (factory, factory->context, 0); if (dock) { g_object_set_data (G_OBJECT (dock), "gimp-dialog-factory", factory); gimp_dialog_factory_add_dialog (factory, dock); } return dock; } void gimp_dialog_factory_add_dialog (GimpDialogFactory *factory, GtkWidget *dialog) { GimpDialogFactory *dialog_factory; GimpDialogFactoryEntry *entry; GimpSessionInfo *info; GList *list; g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory)); g_return_if_fail (GTK_IS_WIDGET (dialog)); if (g_list_find (factory->open_dialogs, dialog)) { g_warning ("%s: dialog already registered", G_GNUC_FUNCTION); return; } dialog_factory = g_object_get_data (G_OBJECT (dialog), "gimp-dialog-factory"); entry = g_object_get_data (G_OBJECT (dialog), "gimp-dialog-factory-entry"); if (! (dialog_factory && (entry || GIMP_IS_DOCK (dialog)))) { g_warning ("%s: dialog was not created by a GimpDialogFactory", G_GNUC_FUNCTION); return; } if (dialog_factory != factory) { g_warning ("%s: dialog was created by a different GimpDialogFactory", G_GNUC_FUNCTION); return; } if (entry) /* dialog is a toplevel (but not a GimpDock) or a GimpDockable */ { gboolean toplevel; toplevel = GTK_WIDGET_TOPLEVEL (dialog); D (g_print ("%s: adding %s \"%s\"\n", G_GNUC_FUNCTION, toplevel ? "toplevel" : "dockable", entry->identifier)); for (list = factory->session_infos; list; list = g_list_next (list)) { info = (GimpSessionInfo *) list->data; if ((info->toplevel_entry == entry) || (info->dockable_entry == entry)) { if (info->widget) { if (entry->singleton) { g_warning ("%s: singleton dialog \"%s\" created twice", G_GNUC_FUNCTION, entry->identifier); D (g_print ("%s: corrupt session info: %p (widget %p)\n", G_GNUC_FUNCTION, info, info->widget)); return; } continue; } info->widget = dialog; D (g_print ("%s: updating session info %p (widget %p) for %s \"%s\"\n", G_GNUC_FUNCTION, info, info->widget, toplevel ? "toplevel" : "dockable", entry->identifier)); if (entry->session_managed) { gimp_dialog_factory_set_window_geometry (info->widget, info); } break; } } if (! list) /* didn't find a session info */ { info = g_new0 (GimpSessionInfo, 1); info->widget = dialog; D (g_print ("%s: creating session info %p (widget %p) for %s \"%s\"\n", G_GNUC_FUNCTION, info, info->widget, toplevel ? "toplevel" : "dockable", entry->identifier)); if (toplevel) info->toplevel_entry = entry; else info->dockable_entry = entry; factory->session_infos = g_list_append (factory->session_infos, info); } } else /* dialog is a GimpDock */ { D (g_print ("%s: adding dock\n", G_GNUC_FUNCTION)); for (list = factory->session_infos; list; list = g_list_next (list)) { info = (GimpSessionInfo *) list->data; /* take the first empty slot */ if (! info->toplevel_entry && ! info->dockable_entry && ! info->widget) { info->widget = dialog; D (g_print ("%s: updating session info %p (widget %p) for dock\n", G_GNUC_FUNCTION, info, info->widget)); gimp_dialog_factory_set_window_geometry (info->widget, info); break; } } if (! list) /* didn't find a session info */ { info = g_new0 (GimpSessionInfo, 1); info->widget = dialog; D (g_print ("%s: creating session info %p (widget %p) for dock\n", G_GNUC_FUNCTION, info, info->widget)); factory->session_infos = g_list_append (factory->session_infos, info); } } factory->open_dialogs = g_list_prepend (factory->open_dialogs, dialog); g_signal_connect_object (dialog, "destroy", G_CALLBACK (gimp_dialog_factory_remove_dialog), factory, G_CONNECT_SWAPPED); } void gimp_dialog_factory_remove_dialog (GimpDialogFactory *factory, GtkWidget *dialog) { GimpDialogFactory *dialog_factory; GimpDialogFactoryEntry *entry; GimpSessionInfo *session_info; GList *list; g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory)); g_return_if_fail (GTK_IS_WIDGET (dialog)); if (! g_list_find (factory->open_dialogs, dialog)) { g_warning ("%s: dialog not registered", G_GNUC_FUNCTION); return; } factory->open_dialogs = g_list_remove (factory->open_dialogs, dialog); dialog_factory = g_object_get_data (G_OBJECT (dialog), "gimp-dialog-factory"); entry = g_object_get_data (G_OBJECT (dialog), "gimp-dialog-factory-entry"); if (! (dialog_factory && (entry || GIMP_IS_DOCK (dialog)))) { g_warning ("%s: dialog was not created by a GimpDialogFactory", G_GNUC_FUNCTION); return; } if (dialog_factory != factory) { g_warning ("%s: dialog was created by a different GimpDialogFactory", G_GNUC_FUNCTION); return; } D (g_print ("%s: removing \"%s\"\n", G_GNUC_FUNCTION, entry ? entry->identifier : "dock")); for (list = factory->session_infos; list; list = g_list_next (list)) { session_info = (GimpSessionInfo *) list->data; if (session_info->widget == dialog) { D (g_print ("%s: clearing session info %p (widget %p) for \"%s\"\n", G_GNUC_FUNCTION, session_info, session_info->widget, entry ? entry->identifier : "dock")); session_info->widget = NULL; /* don't save session info for empty docks */ if (GIMP_IS_DOCK (dialog)) { factory->session_infos = g_list_remove (factory->session_infos, session_info); g_free (session_info); } break; } } } void gimp_dialog_factories_session_save (GimpConfigWriter *writer) { GimpDialogFactoryClass *factory_class; g_return_if_fail (writer != NULL); factory_class = g_type_class_peek (GIMP_TYPE_DIALOG_FACTORY); g_hash_table_foreach (factory_class->factories, (GHFunc) gimp_dialog_factories_save_foreach, writer); } void gimp_dialog_factories_session_restore (void) { GimpDialogFactoryClass *factory_class; factory_class = g_type_class_peek (GIMP_TYPE_DIALOG_FACTORY); g_hash_table_foreach (factory_class->factories, (GHFunc) gimp_dialog_factories_restore_foreach, NULL); } void gimp_dialog_factories_session_clear (void) { GimpDialogFactoryClass *factory_class; factory_class = g_type_class_peek (GIMP_TYPE_DIALOG_FACTORY); g_hash_table_foreach (factory_class->factories, (GHFunc) gimp_dialog_factories_clear_foreach, NULL); } void gimp_dialog_factories_toggle (GimpDialogFactory *toolbox_factory) { static GimpDialogShowState toggle_state = GIMP_DIALOG_SHOW_ALL; static gboolean doing_update = FALSE; GimpDialogFactoryClass *factory_class; if (doing_update) return; doing_update = TRUE; factory_class = g_type_class_peek (GIMP_TYPE_DIALOG_FACTORY); switch (toggle_state) { case GIMP_DIALOG_SHOW_ALL: toggle_state = GIMP_DIALOG_HIDE_ALL; g_hash_table_foreach (factory_class->factories, (GHFunc) gimp_dialog_factories_hide_foreach, NULL); break; case GIMP_DIALOG_HIDE_ALL: toggle_state = GIMP_DIALOG_SHOW_TOOLBOX; gimp_dialog_factories_show_foreach (GIMP_OBJECT (toolbox_factory)->name, toolbox_factory, NULL); break; case GIMP_DIALOG_SHOW_TOOLBOX: toggle_state = GIMP_DIALOG_SHOW_ALL; g_hash_table_foreach (factory_class->factories, (GHFunc) gimp_dialog_factories_show_foreach, NULL); default: break; } doing_update = FALSE; } void gimp_dialog_factories_idle (void) { GimpDialogFactoryClass *factory_class; factory_class = g_type_class_peek (GIMP_TYPE_DIALOG_FACTORY); g_hash_table_foreach (factory_class->factories, (GHFunc) gimp_dialog_factories_idle_foreach, NULL); } void gimp_dialog_factories_unidle (void) { GimpDialogFactoryClass *factory_class; factory_class = g_type_class_peek (GIMP_TYPE_DIALOG_FACTORY); g_hash_table_foreach (factory_class->factories, (GHFunc) gimp_dialog_factories_unidle_foreach, NULL); } /* private functions */ static void gimp_dialog_factories_save_foreach (gchar *name, GimpDialogFactory *factory, GimpConfigWriter *writer) { GList *list; for (list = factory->session_infos; list; list = g_list_next (list)) { GimpSessionInfo *info; const gchar *dialog_name; info = (GimpSessionInfo *) list->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 (info->dockable_entry || (info->toplevel_entry && ! info->toplevel_entry->session_managed)) continue; if (info->widget) gimp_dialog_factory_get_window_info (info->widget, info); if (info->toplevel_entry) dialog_name = info->toplevel_entry->identifier; else dialog_name = "dock"; gimp_config_writer_open (writer, "session-info"); gimp_config_writer_string (writer, name); gimp_config_writer_string (writer, dialog_name); gimp_config_writer_open (writer, "position"); gimp_config_writer_printf (writer, "%d %d", info->x, info->y); gimp_config_writer_close (writer); if (info->width > 0 && info->height > 0) { gimp_config_writer_open (writer, "size"); gimp_config_writer_printf (writer, "%d %d", info->width, info->height); gimp_config_writer_close (writer); } if (info->open) { gimp_config_writer_open (writer, "open-on-exit"); gimp_config_writer_close (writer); } /* save aux-info */ if (info->widget) { gimp_dialog_factory_get_aux_info (info->widget, info); if (info->aux_info) { GList *aux; gimp_config_writer_open (writer, "aux-info"); for (aux = info->aux_info; aux; aux = g_list_next (aux)) gimp_config_writer_string (writer, (gchar *) aux->data); gimp_config_writer_close (writer); g_list_foreach (info->aux_info, (GFunc) g_free, NULL); g_list_free (info->aux_info); info->aux_info = NULL; } } if (! info->toplevel_entry && info->widget) { GimpDock *dock; GList *books; dock = GIMP_DOCK (info->widget); gimp_config_writer_open (writer, "dock"); for (books = dock->dockbooks; books; books = g_list_next (books)) { GimpDockbook *dockbook; GList *children; GList *pages; dockbook = (GimpDockbook *) books->data; gimp_config_writer_open (writer, "book"); children = gtk_container_get_children (GTK_CONTAINER (dockbook)); for (pages = children; pages; pages = g_list_next (pages)) { GimpDockable *dockable; GimpDialogFactoryEntry *entry; dockable = (GimpDockable *) pages->data; entry = g_object_get_data (G_OBJECT (dockable), "gimp-dialog-factory-entry"); if (entry) { GimpContainerView *view; gint preview_size = -1; gimp_config_writer_linefeed (writer); view = gimp_container_view_get_by_dockable (dockable); if (view && view->preview_size >= GIMP_PREVIEW_SIZE_TINY) { preview_size = view->preview_size; } if (preview_size > 0 && preview_size != entry->preview_size) { gimp_config_writer_printf (writer, "\"%s@%d\"", entry->identifier, preview_size); } else { gimp_config_writer_printf (writer, "\"%s\"", entry->identifier); } } } g_list_free (children); gimp_config_writer_close (writer); /* book */ } gimp_config_writer_close (writer); /* dock */ } gimp_config_writer_close (writer); /* session-info */ } } static void gimp_dialog_factories_restore_foreach (gchar *name, GimpDialogFactory *factory, gpointer data) { GList *list; for (list = factory->session_infos; list; list = g_list_next (list)) { GimpSessionInfo *info; info = (GimpSessionInfo *) list->data; if (! info->open) continue; info->open = FALSE; if (info->toplevel_entry) { GtkWidget *dialog; dialog = gimp_dialog_factory_dialog_new (factory, info->toplevel_entry->identifier, info->toplevel_entry->preview_size); if (dialog && info->aux_info) gimp_dialog_factory_set_aux_info (dialog, info); } else { GimpDock *dock; GList *books; dock = GIMP_DOCK (gimp_dialog_factory_dock_new (factory)); if (dock && info->aux_info) gimp_dialog_factory_set_aux_info (GTK_WIDGET (dock), info); for (books = info->sub_dialogs; books; books = g_list_next (books)) { GtkWidget *dockbook; GList *pages; dockbook = gimp_dockbook_new (factory->menu_factory); gimp_dock_add_book (dock, GIMP_DOCKBOOK (dockbook), -1); for (pages = books->data; pages; pages = g_list_next (pages)) { GtkWidget *dockable; gchar *identifier; gchar *substring; gint preview_size = -1; identifier = (gchar *) pages->data; if ((substring = strstr (identifier, "@"))) { *substring = '\0'; preview_size = atoi (substring + 1); if (preview_size < GIMP_PREVIEW_SIZE_TINY || preview_size > GIMP_PREVIEW_SIZE_GIGANTIC) preview_size = -1; } /* use the new dock's dialog factory to create dockables * because it may be different from the dialog factory * the dock was created from. */ dockable = gimp_dialog_factory_dockable_new (dock->dialog_factory, dock, identifier, preview_size); if (dockable) gimp_dockbook_add (GIMP_DOCKBOOK (dockbook), GIMP_DOCKABLE (dockable), -1); g_free (identifier); } g_list_free (books->data); } g_list_free (info->sub_dialogs); info->sub_dialogs = NULL; gtk_widget_show (GTK_WIDGET (dock)); } g_list_foreach (info->aux_info, (GFunc) g_free, NULL); g_list_free (info->aux_info); info->aux_info = NULL; } } static void gimp_dialog_factories_clear_foreach (gchar *name, GimpDialogFactory *factory, gpointer data) { GList *list; for (list = factory->session_infos; list; list = g_list_next (list)) { GimpSessionInfo *info; info = (GimpSessionInfo *) list->data; if (info->widget) continue; #ifdef __GNUC__ #warning FIXME: implement session info deletion #endif } } static void gimp_dialog_factory_get_window_info (GtkWidget *window, GimpSessionInfo *info) { g_return_if_fail (GTK_IS_WIDGET (window)); g_return_if_fail (GTK_WIDGET_TOPLEVEL (window)); g_return_if_fail (info != NULL); if (window->window) { gdk_window_get_root_origin (window->window, &info->x, &info->y); if (! info->toplevel_entry || info->toplevel_entry->remember_size) { info->width = window->allocation.width; info->height = window->allocation.height; } else { info->width = 0; info->height = 0; } } if (! info->toplevel_entry || info->toplevel_entry->remember_if_open) info->open = GTK_WIDGET_VISIBLE (window); else info->open = FALSE; } static void gimp_dialog_factory_set_window_geometry (GtkWidget *window, GimpSessionInfo *info) { static gint screen_width = 0; static gint screen_height = 0; gchar geom[32]; g_return_if_fail (GTK_IS_WINDOW (window)); g_return_if_fail (GTK_WIDGET_TOPLEVEL (window)); g_return_if_fail (info != NULL); if (screen_width == 0 || screen_height == 0) { screen_width = gdk_screen_width (); screen_height = gdk_screen_height (); } info->x = CLAMP (info->x, 0, screen_width - 128); info->y = CLAMP (info->y, 0, screen_height - 128); g_snprintf (geom, sizeof (geom), "+%d+%d", info->x, info->y); gtk_window_parse_geometry (GTK_WINDOW (window), geom); if (! info->toplevel_entry || info->toplevel_entry->remember_size) { if (info->width > 0 && info->height > 0) gtk_window_set_default_size (GTK_WINDOW (window), info->width, info->height); } } static void gimp_dialog_factory_get_aux_info (GtkWidget *dialog, GimpSessionInfo *info) { /* FIXME: make the aux-info stuff generic */ if (GIMP_IS_IMAGE_DOCK (dialog)) { GimpImageDock *dock; dock = GIMP_IMAGE_DOCK (dialog); info->aux_info = g_list_append (info->aux_info, dock->show_image_menu ? g_strdup ("menu-shown") : g_strdup ("menu-hidden")); info->aux_info = g_list_append (info->aux_info, dock->auto_follow_active ? g_strdup ("follow-active-image") : g_strdup ("dont-follow-active-image")); } } static void gimp_dialog_factory_set_aux_info (GtkWidget *dialog, GimpSessionInfo *info) { GList *aux; /* FIXME: make the aux-info stuff generic */ if (GIMP_IS_IMAGE_DOCK (dialog)) { gboolean menu_shown = TRUE; gboolean auto_follow = TRUE; for (aux = info->aux_info; aux; aux = g_list_next (aux)) { gchar *str; str = (gchar *) aux->data; if (! strcmp (str, "menu-shown")) menu_shown = TRUE; else if (! strcmp (str, "menu-hidden")) menu_shown = FALSE; else if (! strcmp (str, "follow-active-image")) auto_follow = TRUE; else if (! strcmp (str, "dont-follow-active-image")) auto_follow = FALSE; } gimp_image_dock_set_show_image_menu (GIMP_IMAGE_DOCK (dialog), menu_shown); gimp_image_dock_set_auto_follow_active (GIMP_IMAGE_DOCK (dialog), auto_follow); } } static void gimp_dialog_factories_hide_foreach (gchar *name, GimpDialogFactory *factory, gpointer data) { GList *list; for (list = factory->open_dialogs; list; list = g_list_next (list)) { if (GTK_IS_WIDGET (list->data) && GTK_WIDGET_TOPLEVEL (list->data)) { GimpDialogVisibilityState visibility = GIMP_DIALOG_VISIBILITY_UNKNOWN; if (GTK_WIDGET_VISIBLE (list->data)) { visibility = GIMP_DIALOG_VISIBILITY_VISIBLE; gtk_widget_hide (GTK_WIDGET (list->data)); } else { visibility = GIMP_DIALOG_VISIBILITY_INVISIBLE; } g_object_set_data (G_OBJECT (list->data), "gimp-dialog-visibility", GINT_TO_POINTER (visibility)); } } } static void gimp_dialog_factories_show_foreach (gchar *name, GimpDialogFactory *factory, gpointer data) { GList *list; for (list = factory->open_dialogs; list; list = g_list_next (list)) { if (GTK_IS_WIDGET (list->data) && GTK_WIDGET_TOPLEVEL (list->data)) { GimpDialogVisibilityState visibility; visibility = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (list->data), "gimp-dialog-visibility")); if (! GTK_WIDGET_VISIBLE (list->data) && visibility == GIMP_DIALOG_VISIBILITY_VISIBLE) { gtk_widget_show (GTK_WIDGET (list->data)); } } } } static void gimp_dialog_factories_idle_foreach (gchar *name, GimpDialogFactory *factory, gpointer data) { GdkCursor *cursor; GList *list; cursor = gimp_cursor_new (GDK_WATCH, GIMP_TOOL_CURSOR_NONE, GIMP_CURSOR_MODIFIER_NONE); for (list = factory->open_dialogs; list; list = g_list_next (list)) { if (GTK_IS_WIDGET (list->data) && GTK_WIDGET_TOPLEVEL (list->data) && GTK_WIDGET_VISIBLE (list->data)) { gdk_window_set_cursor (GTK_WIDGET (list->data)->window, cursor); } } gdk_cursor_unref (cursor); } static void gimp_dialog_factories_unidle_foreach (gchar *name, GimpDialogFactory *factory, gpointer data) { GList *list; for (list = factory->open_dialogs; list; list = g_list_next (list)) { if (GTK_IS_WIDGET (list->data) && GTK_WIDGET_TOPLEVEL (list->data) && GTK_WIDGET_VISIBLE (list->data)) { gdk_window_set_cursor (GTK_WIDGET (list->data)->window, NULL); } } }