summaryrefslogtreecommitdiffstats
path: root/app/widgets/gimpdockwindow.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/widgets/gimpdockwindow.c')
-rw-r--r--app/widgets/gimpdockwindow.c1250
1 files changed, 1250 insertions, 0 deletions
diff --git a/app/widgets/gimpdockwindow.c b/app/widgets/gimpdockwindow.c
new file mode 100644
index 0000000..e8d9556
--- /dev/null
+++ b/app/widgets/gimpdockwindow.c
@@ -0,0 +1,1250 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpdockwindow.c
+ * Copyright (C) 2001-2005 Michael Natterer <mitch@gimp.org>
+ * Copyright (C) 2009 Martin Nordholts <martinn@src.gnome.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 <gegl.h>
+
+#include <gtk/gtk.h>
+
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "widgets-types.h"
+
+#include "dialogs/dialogs.h" /* FIXME, we are in the widget layer */
+
+#include "config/gimpguiconfig.h"
+
+#include "core/gimp.h"
+#include "core/gimpcontext.h"
+#include "core/gimpcontainer.h"
+#include "core/gimpcontainer.h"
+#include "core/gimplist.h"
+#include "core/gimpimage.h"
+
+#include "gimpcontainercombobox.h"
+#include "gimpcontainerview.h"
+#include "gimpdialogfactory.h"
+#include "gimpdock.h"
+#include "gimpdockbook.h"
+#include "gimpdockcolumns.h"
+#include "gimpdockcontainer.h"
+#include "gimpdockwindow.h"
+#include "gimphelp-ids.h"
+#include "gimpmenufactory.h"
+#include "gimpsessioninfo-aux.h"
+#include "gimpsessioninfo.h"
+#include "gimpsessionmanaged.h"
+#include "gimptoolbox.h"
+#include "gimpuimanager.h"
+#include "gimpwidgets-utils.h"
+#include "gimpwindow.h"
+
+#include "gimp-intl.h"
+
+
+#define DEFAULT_DOCK_HEIGHT 300
+#define DEFAULT_MENU_VIEW_SIZE GTK_ICON_SIZE_SMALL_TOOLBAR
+#define AUX_INFO_SHOW_IMAGE_MENU "show-image-menu"
+#define AUX_INFO_FOLLOW_ACTIVE_IMAGE "follow-active-image"
+
+
+enum
+{
+ PROP_0,
+ PROP_CONTEXT,
+ PROP_DIALOG_FACTORY,
+ PROP_UI_MANAGER_NAME,
+ PROP_IMAGE_CONTAINER,
+ PROP_DISPLAY_CONTAINER,
+ PROP_ALLOW_DOCKBOOK_ABSENCE
+};
+
+
+struct _GimpDockWindowPrivate
+{
+ GimpContext *context;
+
+ GimpDialogFactory *dialog_factory;
+
+ gchar *ui_manager_name;
+ GimpUIManager *ui_manager;
+ GQuark image_flush_handler_id;
+
+ GimpDockColumns *dock_columns;
+
+ gboolean allow_dockbook_absence;
+
+ guint update_title_idle_id;
+
+ gint ID;
+
+ GimpContainer *image_container;
+ GimpContainer *display_container;
+
+ gboolean show_image_menu;
+ gboolean auto_follow_active;
+
+ GtkWidget *image_combo;
+ GtkWidget *auto_button;
+};
+
+
+static void gimp_dock_window_dock_container_iface_init (GimpDockContainerInterface *iface);
+static void gimp_dock_window_session_managed_iface_init(GimpSessionManagedInterface*iface);
+static void gimp_dock_window_constructed (GObject *object);
+static void gimp_dock_window_dispose (GObject *object);
+static void gimp_dock_window_finalize (GObject *object);
+static void gimp_dock_window_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_dock_window_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_dock_window_style_set (GtkWidget *widget,
+ GtkStyle *prev_style);
+static gboolean gimp_dock_window_delete_event (GtkWidget *widget,
+ GdkEventAny *event);
+static GList * gimp_dock_window_get_docks (GimpDockContainer *dock_container);
+static GimpDialogFactory * gimp_dock_window_get_dialog_factory (GimpDockContainer *dock_container);
+static GimpUIManager * gimp_dock_window_get_ui_manager (GimpDockContainer *dock_container);
+static void gimp_dock_window_add_dock_from_session (GimpDockContainer *dock_container,
+ GimpDock *dock,
+ GimpSessionInfoDock *dock_info);
+static GList * gimp_dock_window_get_aux_info (GimpSessionManaged *session_managed);
+static void gimp_dock_window_set_aux_info (GimpSessionManaged *session_managed,
+ GList *aux_info);
+static GimpAlignmentType
+ gimp_dock_window_get_dock_side (GimpDockContainer *dock_container,
+ GimpDock *dock);
+static gboolean gimp_dock_window_should_add_to_recent (GimpDockWindow *dock_window);
+static void gimp_dock_window_display_changed (GimpDockWindow *dock_window,
+ GimpObject *display,
+ GimpContext *context);
+static void gimp_dock_window_image_changed (GimpDockWindow *dock_window,
+ GimpImage *image,
+ GimpContext *context);
+static void gimp_dock_window_image_flush (GimpImage *image,
+ gboolean invalidate_preview,
+ GimpDockWindow *dock_window);
+static void gimp_dock_window_update_title (GimpDockWindow *dock_window);
+static gboolean gimp_dock_window_update_title_idle (GimpDockWindow *dock_window);
+static gchar * gimp_dock_window_get_description (GimpDockWindow *dock_window,
+ gboolean complete);
+static void gimp_dock_window_dock_removed (GimpDockWindow *dock_window,
+ GimpDock *dock,
+ GimpDockColumns *dock_columns);
+static void gimp_dock_window_factory_display_changed (GimpContext *context,
+ GimpObject *display,
+ GimpDock *dock);
+static void gimp_dock_window_factory_image_changed (GimpContext *context,
+ GimpImage *image,
+ GimpDock *dock);
+static void gimp_dock_window_auto_clicked (GtkWidget *widget,
+ GimpDock *dock);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpDockWindow, gimp_dock_window, GIMP_TYPE_WINDOW,
+ G_ADD_PRIVATE (GimpDockWindow)
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_DOCK_CONTAINER,
+ gimp_dock_window_dock_container_iface_init)
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_SESSION_MANAGED,
+ gimp_dock_window_session_managed_iface_init))
+
+#define parent_class gimp_dock_window_parent_class
+
+static void
+gimp_dock_window_class_init (GimpDockWindowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->constructed = gimp_dock_window_constructed;
+ object_class->dispose = gimp_dock_window_dispose;
+ object_class->finalize = gimp_dock_window_finalize;
+ object_class->set_property = gimp_dock_window_set_property;
+ object_class->get_property = gimp_dock_window_get_property;
+
+ widget_class->style_set = gimp_dock_window_style_set;
+ widget_class->delete_event = gimp_dock_window_delete_event;
+
+ g_object_class_install_property (object_class, PROP_CONTEXT,
+ g_param_spec_object ("context", NULL, NULL,
+ GIMP_TYPE_CONTEXT,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class, PROP_DIALOG_FACTORY,
+ g_param_spec_object ("dialog-factory",
+ NULL, NULL,
+ GIMP_TYPE_DIALOG_FACTORY,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class, PROP_UI_MANAGER_NAME,
+ g_param_spec_string ("ui-manager-name",
+ NULL, NULL,
+ NULL,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class, PROP_IMAGE_CONTAINER,
+ g_param_spec_object ("image-container",
+ NULL, NULL,
+ GIMP_TYPE_CONTAINER,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class, PROP_DISPLAY_CONTAINER,
+ g_param_spec_object ("display-container",
+ NULL, NULL,
+ GIMP_TYPE_CONTAINER,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class, PROP_ALLOW_DOCKBOOK_ABSENCE,
+ g_param_spec_boolean ("allow-dockbook-absence",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_int ("default-height",
+ NULL, NULL,
+ -1, G_MAXINT,
+ DEFAULT_DOCK_HEIGHT,
+ GIMP_PARAM_READABLE));
+
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_enum ("menu-preview-size",
+ NULL, NULL,
+ GTK_TYPE_ICON_SIZE,
+ DEFAULT_MENU_VIEW_SIZE,
+ GIMP_PARAM_READABLE));
+}
+
+static void
+gimp_dock_window_init (GimpDockWindow *dock_window)
+{
+ static gint dock_window_ID = 1;
+ gchar *name = NULL;
+
+ dock_window->p = gimp_dock_window_get_instance_private (dock_window);
+ dock_window->p->ID = dock_window_ID++;
+ dock_window->p->auto_follow_active = TRUE;
+
+ name = g_strdup_printf ("gimp-dock-%d", dock_window->p->ID);
+ gtk_widget_set_name (GTK_WIDGET (dock_window), name);
+ g_free (name);
+
+ gtk_window_set_resizable (GTK_WINDOW (dock_window), TRUE);
+ gtk_window_set_focus_on_map (GTK_WINDOW (dock_window), FALSE);
+ gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dock_window), FALSE);
+}
+
+static void
+gimp_dock_window_dock_container_iface_init (GimpDockContainerInterface *iface)
+{
+ iface->get_docks = gimp_dock_window_get_docks;
+ iface->get_dialog_factory = gimp_dock_window_get_dialog_factory;
+ iface->get_ui_manager = gimp_dock_window_get_ui_manager;
+ iface->add_dock = gimp_dock_window_add_dock_from_session;
+ iface->get_dock_side = gimp_dock_window_get_dock_side;
+}
+
+static void
+gimp_dock_window_session_managed_iface_init (GimpSessionManagedInterface *iface)
+{
+ iface->get_aux_info = gimp_dock_window_get_aux_info;
+ iface->set_aux_info = gimp_dock_window_set_aux_info;
+}
+
+static void
+gimp_dock_window_constructed (GObject *object)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (object);
+ GimpGuiConfig *config;
+ GimpContext *factory_context;
+ GimpMenuFactory *menu_factory;
+ GtkAccelGroup *accel_group;
+ Gimp *gimp;
+ GtkSettings *settings;
+ gint menu_view_width = -1;
+ gint menu_view_height = -1;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ gimp = GIMP (dock_window->p->context->gimp);
+ config = GIMP_GUI_CONFIG (gimp->config);
+
+ /* Create a separate context per dock so that docks can be bound to
+ * a specific image and does not necessarily have to follow the
+ * active image in the user context
+ */
+ g_object_unref (dock_window->p->context);
+ dock_window->p->context = gimp_context_new (gimp, "Dock Context", NULL);
+ dock_window->p->image_container = gimp->images;
+ dock_window->p->display_container = gimp->displays;
+
+ factory_context =
+ gimp_dialog_factory_get_context (dock_window->p->dialog_factory);
+
+ /* Setup hints */
+ gimp_window_set_hint (GTK_WINDOW (dock_window), config->dock_window_hint);
+
+ menu_factory =
+ gimp_dialog_factory_get_menu_factory (dock_window->p->dialog_factory);
+
+ /* Make image window related keyboard shortcuts work also when a
+ * dock window is the focused window
+ */
+ dock_window->p->ui_manager =
+ gimp_menu_factory_manager_new (menu_factory,
+ dock_window->p->ui_manager_name,
+ dock_window,
+ config->tearoff_menus);
+ accel_group = gimp_ui_manager_get_accel_group (dock_window->p->ui_manager);
+ gtk_window_add_accel_group (GTK_WINDOW (dock_window), accel_group);
+
+ g_signal_connect_object (dock_window->p->context, "display-changed",
+ G_CALLBACK (gimp_dock_window_display_changed),
+ dock_window,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (dock_window->p->context, "image-changed",
+ G_CALLBACK (gimp_dock_window_image_changed),
+ dock_window,
+ G_CONNECT_SWAPPED);
+
+ dock_window->p->image_flush_handler_id =
+ gimp_container_add_handler (gimp->images, "flush",
+ G_CALLBACK (gimp_dock_window_image_flush),
+ dock_window);
+
+ gimp_context_define_properties (dock_window->p->context,
+ GIMP_CONTEXT_PROP_MASK_ALL &
+ ~(GIMP_CONTEXT_PROP_MASK_IMAGE |
+ GIMP_CONTEXT_PROP_MASK_DISPLAY),
+ FALSE);
+ gimp_context_set_parent (dock_window->p->context,
+ factory_context);
+
+ /* Setup widget hierarchy */
+ {
+ GtkWidget *vbox = NULL;
+
+ /* Top-level GtkVBox */
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (dock_window), vbox);
+ gtk_widget_show (vbox);
+
+ /* Image selection menu */
+ {
+ GtkWidget *hbox = NULL;
+
+ /* GtkHBox */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ if (dock_window->p->show_image_menu)
+ gtk_widget_show (hbox);
+
+ /* Image combo */
+ dock_window->p->image_combo = gimp_container_combo_box_new (NULL, NULL, 16, 1);
+ gtk_box_pack_start (GTK_BOX (hbox), dock_window->p->image_combo, TRUE, TRUE, 0);
+ g_signal_connect (dock_window->p->image_combo, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &dock_window->p->image_combo);
+ gimp_help_set_help_data (dock_window->p->image_combo,
+ NULL, GIMP_HELP_DOCK_IMAGE_MENU);
+ gtk_widget_show (dock_window->p->image_combo);
+
+ /* Auto button */
+ dock_window->p->auto_button = gtk_toggle_button_new_with_label (_("Auto"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dock_window->p->auto_button),
+ dock_window->p->auto_follow_active);
+ gtk_box_pack_start (GTK_BOX (hbox), dock_window->p->auto_button, FALSE, FALSE, 0);
+ gtk_widget_show (dock_window->p->auto_button);
+
+ g_signal_connect (dock_window->p->auto_button, "clicked",
+ G_CALLBACK (gimp_dock_window_auto_clicked),
+ dock_window);
+
+ gimp_help_set_help_data (dock_window->p->auto_button,
+ _("When enabled, the dialog automatically "
+ "follows the image you are working on."),
+ GIMP_HELP_DOCK_AUTO_BUTTON);
+ }
+
+ /* GimpDockColumns */
+ /* Let the GimpDockColumns mirror the context so that a GimpDock can
+ * get it when inside a dock window. We do the same thing in the
+ * GimpImageWindow so docks can get the GimpContext there as well
+ */
+ dock_window->p->dock_columns =
+ GIMP_DOCK_COLUMNS (gimp_dock_columns_new (dock_window->p->context,
+ dock_window->p->dialog_factory,
+ dock_window->p->ui_manager));
+ gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (dock_window->p->dock_columns),
+ TRUE, TRUE, 0);
+ gtk_widget_show (GTK_WIDGET (dock_window->p->dock_columns));
+ g_signal_connect_object (dock_window->p->dock_columns, "dock-removed",
+ G_CALLBACK (gimp_dock_window_dock_removed),
+ dock_window,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (dock_window->p->dock_columns, "dock-added",
+ G_CALLBACK (gimp_dock_window_update_title),
+ dock_window,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (dock_window->p->dock_columns, "dock-removed",
+ G_CALLBACK (gimp_dock_window_update_title),
+ dock_window,
+ G_CONNECT_SWAPPED);
+ }
+
+ if (dock_window->p->auto_follow_active)
+ {
+ if (gimp_context_get_display (factory_context))
+ gimp_context_copy_property (factory_context,
+ dock_window->p->context,
+ GIMP_CONTEXT_PROP_DISPLAY);
+ else
+ gimp_context_copy_property (factory_context,
+ dock_window->p->context,
+ GIMP_CONTEXT_PROP_IMAGE);
+ }
+
+ g_signal_connect_object (factory_context, "display-changed",
+ G_CALLBACK (gimp_dock_window_factory_display_changed),
+ dock_window,
+ 0);
+ g_signal_connect_object (factory_context, "image-changed",
+ G_CALLBACK (gimp_dock_window_factory_image_changed),
+ dock_window,
+ 0);
+
+ settings = gtk_widget_get_settings (GTK_WIDGET (dock_window));
+ gtk_icon_size_lookup_for_settings (settings,
+ DEFAULT_MENU_VIEW_SIZE,
+ &menu_view_width,
+ &menu_view_height);
+
+ g_object_set (dock_window->p->image_combo,
+ "container", dock_window->p->image_container,
+ "context", dock_window->p->context,
+ NULL);
+
+ gimp_help_connect (GTK_WIDGET (dock_window), gimp_standard_help_func,
+ GIMP_HELP_DOCK, NULL);
+
+ if (dock_window->p->auto_follow_active)
+ {
+ if (gimp_context_get_display (factory_context))
+ gimp_context_copy_property (factory_context,
+ dock_window->p->context,
+ GIMP_CONTEXT_PROP_DISPLAY);
+ else
+ gimp_context_copy_property (factory_context,
+ dock_window->p->context,
+ GIMP_CONTEXT_PROP_IMAGE);
+ }
+}
+
+static void
+gimp_dock_window_dispose (GObject *object)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (object);
+
+ if (dock_window->p->update_title_idle_id)
+ {
+ g_source_remove (dock_window->p->update_title_idle_id);
+ dock_window->p->update_title_idle_id = 0;
+ }
+
+ if (dock_window->p->image_flush_handler_id)
+ {
+ gimp_container_remove_handler (dock_window->p->context->gimp->images,
+ dock_window->p->image_flush_handler_id);
+ dock_window->p->image_flush_handler_id = 0;
+ }
+
+ g_clear_object (&dock_window->p->ui_manager);
+ g_clear_object (&dock_window->p->dialog_factory);
+ g_clear_object (&dock_window->p->context);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gimp_dock_window_finalize (GObject *object)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (object);
+
+ g_clear_pointer (&dock_window->p->ui_manager_name, g_free);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_dock_window_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (object);
+
+ switch (property_id)
+ {
+ case PROP_CONTEXT:
+ dock_window->p->context = g_value_dup_object (value);
+ break;
+
+ case PROP_DIALOG_FACTORY:
+ dock_window->p->dialog_factory = g_value_dup_object (value);
+ break;
+
+ case PROP_UI_MANAGER_NAME:
+ g_free (dock_window->p->ui_manager_name);
+ dock_window->p->ui_manager_name = g_value_dup_string (value);
+ break;
+
+ case PROP_IMAGE_CONTAINER:
+ dock_window->p->image_container = g_value_dup_object (value);
+ break;
+
+ case PROP_DISPLAY_CONTAINER:
+ dock_window->p->display_container = g_value_dup_object (value);
+ break;
+
+ case PROP_ALLOW_DOCKBOOK_ABSENCE:
+ dock_window->p->allow_dockbook_absence = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_dock_window_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (object);
+
+ switch (property_id)
+ {
+ case PROP_CONTEXT:
+ g_value_set_object (value, dock_window->p->context);
+ break;
+
+ case PROP_DIALOG_FACTORY:
+ g_value_set_object (value, dock_window->p->dialog_factory);
+ break;
+
+ case PROP_UI_MANAGER_NAME:
+ g_value_set_string (value, dock_window->p->ui_manager_name);
+ break;
+
+ case PROP_IMAGE_CONTAINER:
+ g_value_set_object (value, dock_window->p->image_container);
+ break;
+
+ case PROP_DISPLAY_CONTAINER:
+ g_value_set_object (value, dock_window->p->display_container);
+ break;
+
+ case PROP_ALLOW_DOCKBOOK_ABSENCE:
+ g_value_set_boolean (value, dock_window->p->allow_dockbook_absence);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_dock_window_style_set (GtkWidget *widget,
+ GtkStyle *prev_style)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (widget);
+ GtkStyle *button_style;
+ GtkIconSize menu_view_size;
+ GtkSettings *settings;
+ gint menu_view_width = 18;
+ gint menu_view_height = 18;
+ gint focus_line_width;
+ gint focus_padding;
+ gint ythickness;
+
+ gint default_height = DEFAULT_DOCK_HEIGHT;
+
+ GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
+
+ gtk_widget_style_get (widget,
+ "default-height", &default_height,
+ "menu-preview-size", &menu_view_size,
+ NULL);
+
+ gtk_window_set_default_size (GTK_WINDOW (widget), -1, default_height);
+
+ settings = gtk_widget_get_settings (dock_window->p->image_combo);
+ gtk_icon_size_lookup_for_settings (settings,
+ menu_view_size,
+ &menu_view_width,
+ &menu_view_height);
+
+ gtk_widget_style_get (dock_window->p->auto_button,
+ "focus-line-width", &focus_line_width,
+ "focus-padding", &focus_padding,
+ NULL);
+
+ button_style = gtk_widget_get_style (widget);
+ ythickness = button_style->ythickness;
+
+ gimp_container_view_set_view_size (GIMP_CONTAINER_VIEW (dock_window->p->image_combo),
+ menu_view_height, 1);
+
+ gtk_widget_set_size_request (dock_window->p->auto_button, -1,
+ menu_view_height +
+ 2 * (1 /* CHILD_SPACING */ +
+ ythickness +
+ focus_padding +
+ focus_line_width));
+}
+
+/**
+ * gimp_dock_window_delete_event:
+ * @widget:
+ * @event:
+ *
+ * Makes sure that when dock windows are closed they are added to the
+ * list of recently closed docks so that they are easy to bring back.
+ **/
+static gboolean
+gimp_dock_window_delete_event (GtkWidget *widget,
+ GdkEventAny *event)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (widget);
+ GimpSessionInfo *info = NULL;
+ const gchar *entry_name = NULL;
+ GimpDialogFactoryEntry *entry = NULL;
+ gchar *name = NULL;
+
+ /* Don't add docks with just a single dockable to the list of
+ * recently closed dock since those can be brought back through the
+ * normal Windows->Dockable Dialogs menu
+ */
+ if (! gimp_dock_window_should_add_to_recent (dock_window))
+ return FALSE;
+
+ info = gimp_session_info_new ();
+
+ name = gimp_dock_window_get_description (dock_window, TRUE /*complete*/);
+ gimp_object_set_name (GIMP_OBJECT (info), name);
+ g_free (name);
+
+ gimp_session_info_get_info_with_widget (info, GTK_WIDGET (dock_window));
+
+ entry_name = (gimp_dock_window_has_toolbox (dock_window) ?
+ "gimp-toolbox-window" :
+ "gimp-dock-window");
+ entry = gimp_dialog_factory_find_entry (dock_window->p->dialog_factory,
+ entry_name);
+ gimp_session_info_set_factory_entry (info, entry);
+
+ gimp_container_add (global_recent_docks, GIMP_OBJECT (info));
+ g_object_unref (info);
+
+ return FALSE;
+}
+
+static GList *
+gimp_dock_window_get_docks (GimpDockContainer *dock_container)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (dock_container);
+
+ return g_list_copy (gimp_dock_columns_get_docks (dock_window->p->dock_columns));
+}
+
+static GimpDialogFactory *
+gimp_dock_window_get_dialog_factory (GimpDockContainer *dock_container)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (dock_container);
+
+ return dock_window->p->dialog_factory;
+}
+
+static GimpUIManager *
+gimp_dock_window_get_ui_manager (GimpDockContainer *dock_container)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (dock_container);
+
+ return dock_window->p->ui_manager;
+}
+
+static void
+gimp_dock_window_add_dock_from_session (GimpDockContainer *dock_container,
+ GimpDock *dock,
+ GimpSessionInfoDock *dock_info)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (dock_container);
+
+ gimp_dock_window_add_dock (dock_window,
+ dock,
+ -1 /*index*/);
+}
+
+static GList *
+gimp_dock_window_get_aux_info (GimpSessionManaged *session_managed)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (session_managed);
+ GList *aux_info = NULL;
+ GimpSessionInfoAux *aux;
+
+ if (dock_window->p->allow_dockbook_absence)
+ {
+ /* Assume it is the toolbox; it does not have aux info */
+ return NULL;
+ }
+
+ g_return_val_if_fail (GIMP_IS_DOCK_WINDOW (dock_window), NULL);
+
+ aux = gimp_session_info_aux_new (AUX_INFO_SHOW_IMAGE_MENU,
+ dock_window->p->show_image_menu ?
+ "true" : "false");
+ aux_info = g_list_append (aux_info, aux);
+
+ aux = gimp_session_info_aux_new (AUX_INFO_FOLLOW_ACTIVE_IMAGE,
+ dock_window->p->auto_follow_active ?
+ "true" : "false");
+ aux_info = g_list_append (aux_info, aux);
+
+ return aux_info;
+}
+
+static void
+gimp_dock_window_set_aux_info (GimpSessionManaged *session_managed,
+ GList *aux_info)
+{
+ GimpDockWindow *dock_window;
+ GList *list;
+ gboolean menu_shown;
+ gboolean auto_follow;
+
+ g_return_if_fail (GIMP_IS_DOCK_WINDOW (session_managed));
+
+ dock_window = GIMP_DOCK_WINDOW (session_managed);
+ menu_shown = dock_window->p->show_image_menu;
+ auto_follow = dock_window->p->auto_follow_active;
+
+ for (list = aux_info; list; list = g_list_next (list))
+ {
+ GimpSessionInfoAux *aux = list->data;
+
+ if (! strcmp (aux->name, AUX_INFO_SHOW_IMAGE_MENU))
+ {
+ menu_shown = ! g_ascii_strcasecmp (aux->value, "true");
+ }
+ else if (! strcmp (aux->name, AUX_INFO_FOLLOW_ACTIVE_IMAGE))
+ {
+ auto_follow = ! g_ascii_strcasecmp (aux->value, "true");
+ }
+ }
+
+ if (menu_shown != dock_window->p->show_image_menu)
+ gimp_dock_window_set_show_image_menu (dock_window, menu_shown);
+
+ if (auto_follow != dock_window->p->auto_follow_active)
+ gimp_dock_window_set_auto_follow_active (dock_window, auto_follow);
+}
+
+static GimpAlignmentType
+gimp_dock_window_get_dock_side (GimpDockContainer *dock_container,
+ GimpDock *dock)
+{
+ g_return_val_if_fail (GIMP_IS_DOCK_WINDOW (dock_container), -1);
+ g_return_val_if_fail (GIMP_IS_DOCK (dock), -1);
+
+ /* A GimpDockWindow don't have docks on different sides, it's just
+ * one set of columns
+ */
+ return -1;
+}
+
+/**
+ * gimp_dock_window_should_add_to_recent:
+ * @dock_window:
+ *
+ * Returns: %FALSE if the dock window can be recreated with one
+ * Windows menu item such as Windows->Toolbox or
+ * Windows->Dockable Dialogs->Layers, %TRUE if not. It should
+ * then be added to the list of recently closed docks.
+ **/
+static gboolean
+gimp_dock_window_should_add_to_recent (GimpDockWindow *dock_window)
+{
+ GList *docks;
+ gboolean should_add = TRUE;
+
+ docks = gimp_dock_container_get_docks (GIMP_DOCK_CONTAINER (dock_window));
+
+ if (! docks)
+ {
+ should_add = FALSE;
+ }
+ else if (g_list_length (docks) == 1)
+ {
+ GimpDock *dock = GIMP_DOCK (g_list_nth_data (docks, 0));
+
+ if (GIMP_IS_TOOLBOX (dock) &&
+ gimp_dock_get_n_dockables (dock) == 0)
+ {
+ should_add = FALSE;
+ }
+ else if (! GIMP_IS_TOOLBOX (dock) &&
+ gimp_dock_get_n_dockables (dock) == 1)
+ {
+ should_add = FALSE;
+ }
+ }
+
+ g_list_free (docks);
+
+ return should_add;
+}
+
+static void
+gimp_dock_window_image_flush (GimpImage *image,
+ gboolean invalidate_preview,
+ GimpDockWindow *dock_window)
+{
+ if (image == gimp_context_get_image (dock_window->p->context))
+ {
+ GimpObject *display = gimp_context_get_display (dock_window->p->context);
+
+ if (display)
+ gimp_ui_manager_update (dock_window->p->ui_manager, display);
+ }
+}
+
+static void
+gimp_dock_window_update_title (GimpDockWindow *dock_window)
+{
+ if (dock_window->p->update_title_idle_id)
+ g_source_remove (dock_window->p->update_title_idle_id);
+
+ dock_window->p->update_title_idle_id =
+ g_idle_add ((GSourceFunc) gimp_dock_window_update_title_idle,
+ dock_window);
+}
+
+static gboolean
+gimp_dock_window_update_title_idle (GimpDockWindow *dock_window)
+{
+ gchar *desc = gimp_dock_window_get_description (dock_window,
+ FALSE /*complete*/);
+ if (desc)
+ {
+ gtk_window_set_title (GTK_WINDOW (dock_window), desc);
+ g_free (desc);
+ }
+
+ dock_window->p->update_title_idle_id = 0;
+
+ return FALSE;
+}
+
+static gchar *
+gimp_dock_window_get_description (GimpDockWindow *dock_window,
+ gboolean complete)
+{
+ GString *complete_desc = g_string_new (NULL);
+ GList *docks = NULL;
+ GList *iter = NULL;
+
+ docks = gimp_dock_container_get_docks (GIMP_DOCK_CONTAINER (dock_window));
+
+ for (iter = docks;
+ iter;
+ iter = g_list_next (iter))
+ {
+ gchar *desc = gimp_dock_get_description (GIMP_DOCK (iter->data), complete);
+ g_string_append (complete_desc, desc);
+ g_free (desc);
+
+ if (g_list_next (iter))
+ g_string_append (complete_desc, GIMP_DOCK_COLUMN_SEPARATOR);
+ }
+
+ g_list_free (docks);
+
+ return g_string_free (complete_desc, FALSE /*free_segment*/);
+}
+
+static void
+gimp_dock_window_dock_removed (GimpDockWindow *dock_window,
+ GimpDock *dock,
+ GimpDockColumns *dock_columns)
+{
+ g_return_if_fail (GIMP_IS_DOCK (dock));
+
+ if (gimp_dock_columns_get_docks (dock_columns) == NULL &&
+ ! dock_window->p->allow_dockbook_absence)
+ gtk_widget_destroy (GTK_WIDGET (dock_window));
+}
+
+static void
+gimp_dock_window_factory_display_changed (GimpContext *context,
+ GimpObject *display,
+ GimpDock *dock)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (dock);
+
+ if (display && dock_window->p->auto_follow_active)
+ gimp_context_set_display (dock_window->p->context, display);
+}
+
+static void
+gimp_dock_window_factory_image_changed (GimpContext *context,
+ GimpImage *image,
+ GimpDock *dock)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (dock);
+
+ /* won't do anything if we already set the display above */
+ if (image && dock_window->p->auto_follow_active)
+ gimp_context_set_image (dock_window->p->context, image);
+}
+
+static void
+gimp_dock_window_display_changed (GimpDockWindow *dock_window,
+ GimpObject *display,
+ GimpContext *context)
+{
+ /* make sure auto-follow-active works both ways */
+ if (display && dock_window->p->auto_follow_active)
+ {
+ GimpContext *factory_context =
+ gimp_dialog_factory_get_context (dock_window->p->dialog_factory);
+
+ gimp_context_set_display (factory_context, display);
+ }
+
+ gimp_ui_manager_update (dock_window->p->ui_manager,
+ display);
+}
+
+static void
+gimp_dock_window_image_changed (GimpDockWindow *dock_window,
+ GimpImage *image,
+ GimpContext *context)
+{
+ GimpContainer *image_container = dock_window->p->image_container;
+ GimpContainer *display_container = dock_window->p->display_container;
+
+ /* make sure auto-follow-active works both ways */
+ if (image && dock_window->p->auto_follow_active)
+ {
+ GimpContext *factory_context =
+ gimp_dialog_factory_get_context (dock_window->p->dialog_factory);
+
+ gimp_context_set_image (factory_context, image);
+ }
+
+ if (image == NULL && ! gimp_container_is_empty (image_container))
+ {
+ image = GIMP_IMAGE (gimp_container_get_first_child (image_container));
+
+ /* this invokes this function recursively but we don't enter
+ * the if() branch the second time
+ */
+ gimp_context_set_image (context, image);
+
+ /* stop the emission of the original signal (the emission of
+ * the recursive signal is finished)
+ */
+ g_signal_stop_emission_by_name (context, "image-changed");
+ }
+ else if (image != NULL && ! gimp_container_is_empty (display_container))
+ {
+ GimpObject *display;
+ GimpImage *display_image;
+ gboolean find_display = TRUE;
+
+ display = gimp_context_get_display (context);
+
+ if (display)
+ {
+ g_object_get (display, "image", &display_image, NULL);
+
+ if (display_image)
+ {
+ g_object_unref (display_image);
+
+ if (display_image == image)
+ find_display = FALSE;
+ }
+ }
+
+ if (find_display)
+ {
+ GList *list;
+
+ for (list = GIMP_LIST (display_container)->queue->head;
+ list;
+ list = g_list_next (list))
+ {
+ display = GIMP_OBJECT (list->data);
+
+ g_object_get (display, "image", &display_image, NULL);
+
+ if (display_image)
+ {
+ g_object_unref (display_image);
+
+ if (display_image == image)
+ {
+ /* this invokes this function recursively but we
+ * don't enter the if(find_display) branch the
+ * second time
+ */
+ gimp_context_set_display (context, display);
+
+ /* don't stop signal emission here because the
+ * context's image was not changed by the
+ * recursive call
+ */
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ gimp_ui_manager_update (dock_window->p->ui_manager,
+ gimp_context_get_display (context));
+}
+
+static void
+gimp_dock_window_auto_clicked (GtkWidget *widget,
+ GimpDock *dock)
+{
+ GimpDockWindow *dock_window = GIMP_DOCK_WINDOW (dock);
+
+ gimp_toggle_button_update (widget, &dock_window->p->auto_follow_active);
+
+ if (dock_window->p->auto_follow_active)
+ {
+ GimpContext *context;
+
+ context = gimp_dialog_factory_get_context (dock_window->p->dialog_factory);
+
+ gimp_context_copy_properties (context,
+ dock_window->p->context,
+ GIMP_CONTEXT_PROP_MASK_DISPLAY |
+ GIMP_CONTEXT_PROP_MASK_IMAGE);
+ }
+}
+
+
+void
+gimp_dock_window_add_dock (GimpDockWindow *dock_window,
+ GimpDock *dock,
+ gint index)
+{
+ g_return_if_fail (GIMP_IS_DOCK_WINDOW (dock_window));
+ g_return_if_fail (GIMP_IS_DOCK (dock));
+
+ gimp_dock_columns_add_dock (dock_window->p->dock_columns,
+ GIMP_DOCK (dock),
+ index);
+
+ g_signal_connect_object (dock, "description-invalidated",
+ G_CALLBACK (gimp_dock_window_update_title),
+ dock_window,
+ G_CONNECT_SWAPPED);
+
+ /* Some docks like the toolbox dock needs to maintain special hints
+ * on its container GtkWindow, allow those to do so
+ */
+ gimp_dock_set_host_geometry_hints (dock, GTK_WINDOW (dock_window));
+ g_signal_connect_object (dock, "geometry-invalidated",
+ G_CALLBACK (gimp_dock_set_host_geometry_hints),
+ dock_window, 0);
+}
+
+void
+gimp_dock_window_remove_dock (GimpDockWindow *dock_window,
+ GimpDock *dock)
+{
+ gimp_dock_columns_remove_dock (dock_window->p->dock_columns,
+ GIMP_DOCK (dock));
+
+ g_signal_handlers_disconnect_by_func (dock,
+ gimp_dock_window_update_title,
+ dock_window);
+ g_signal_handlers_disconnect_by_func (dock,
+ gimp_dock_set_host_geometry_hints,
+ dock_window);
+}
+
+GtkWidget *
+gimp_dock_window_new (const gchar *role,
+ const gchar *ui_manager_name,
+ gboolean allow_dockbook_absence,
+ GimpDialogFactory *dialog_factory,
+ GimpContext *context)
+{
+ g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (dialog_factory), NULL);
+ g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
+
+ return g_object_new (GIMP_TYPE_DOCK_WINDOW,
+ "role", role,
+ "ui-manager-name", ui_manager_name,
+ "allow-dockbook-absence", allow_dockbook_absence,
+ "dialog-factory", dialog_factory,
+ "context", context,
+ NULL);
+}
+
+gint
+gimp_dock_window_get_id (GimpDockWindow *dock_window)
+{
+ g_return_val_if_fail (GIMP_IS_DOCK_WINDOW (dock_window), 0);
+
+ return dock_window->p->ID;
+}
+
+GimpContext *
+gimp_dock_window_get_context (GimpDockWindow *dock_window)
+{
+ g_return_val_if_fail (GIMP_IS_DOCK_WINDOW (dock_window), NULL);
+
+ return dock_window->p->context;
+}
+
+gboolean
+gimp_dock_window_get_auto_follow_active (GimpDockWindow *dock_window)
+{
+ g_return_val_if_fail (GIMP_IS_DOCK_WINDOW (dock_window), FALSE);
+
+ return dock_window->p->auto_follow_active;
+}
+
+void
+gimp_dock_window_set_auto_follow_active (GimpDockWindow *dock_window,
+ gboolean auto_follow_active)
+{
+ g_return_if_fail (GIMP_IS_DOCK_WINDOW (dock_window));
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dock_window->p->auto_button),
+ auto_follow_active ? TRUE : FALSE);
+}
+
+gboolean
+gimp_dock_window_get_show_image_menu (GimpDockWindow *dock_window)
+{
+ g_return_val_if_fail (GIMP_IS_DOCK_WINDOW (dock_window), FALSE);
+
+ return dock_window->p->show_image_menu;
+}
+
+void
+gimp_dock_window_set_show_image_menu (GimpDockWindow *dock_window,
+ gboolean show)
+{
+ GtkWidget *parent;
+
+ g_return_if_fail (GIMP_IS_DOCK_WINDOW (dock_window));
+
+ parent = gtk_widget_get_parent (dock_window->p->image_combo);
+
+ gtk_widget_set_visible (parent, show);
+
+ dock_window->p->show_image_menu = show ? TRUE : FALSE;
+}
+
+void
+gimp_dock_window_setup (GimpDockWindow *dock_window,
+ GimpDockWindow *template)
+{
+ gimp_dock_window_set_auto_follow_active (GIMP_DOCK_WINDOW (dock_window),
+ template->p->auto_follow_active);
+ gimp_dock_window_set_show_image_menu (GIMP_DOCK_WINDOW (dock_window),
+ template->p->show_image_menu);
+}
+
+/**
+ * gimp_dock_window_has_toolbox:
+ * @dock_window:
+ *
+ * Returns: %TRUE if the dock window has a GimpToolbox dock, %FALSE
+ * otherwise.
+ **/
+gboolean
+gimp_dock_window_has_toolbox (GimpDockWindow *dock_window)
+{
+ GList *iter = NULL;
+
+ g_return_val_if_fail (GIMP_IS_DOCK_WINDOW (dock_window), FALSE);
+
+ for (iter = gimp_dock_columns_get_docks (dock_window->p->dock_columns);
+ iter;
+ iter = g_list_next (iter))
+ {
+ if (GIMP_IS_TOOLBOX (iter->data))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ * gimp_dock_window_from_dock:
+ * @dock:
+ *
+ * For convenience.
+ *
+ * Returns: If the toplevel widget for the dock is a GimpDockWindow,
+ * return that. Otherwise return %NULL.
+ **/
+GimpDockWindow *
+gimp_dock_window_from_dock (GimpDock *dock)
+{
+ GtkWidget *toplevel = NULL;
+
+ g_return_val_if_fail (GIMP_IS_DOCK (dock), NULL);
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (dock));
+
+ if (GIMP_IS_DOCK_WINDOW (toplevel))
+ return GIMP_DOCK_WINDOW (toplevel);
+ else
+ return NULL;
+}