Refactor stuff a bit so that the dock window description can be both short and long. The short version only have the active dockables, and the long version have all the dockables in the description string. We use the short version for the window title and the long version for the Recently Closed Docks menu items.
1171 lines
41 KiB
C
1171 lines
41 KiB
C
/* 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 <http://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 "menus/menus.h"
|
|
|
|
#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 "gimpdockwindow.h"
|
|
#include "gimphelp-ids.h"
|
|
#include "gimpmenufactory.h"
|
|
#include "gimpsessioninfo-aux.h"
|
|
#include "gimpsessioninfo.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 GObject * gimp_dock_window_constructor (GType type,
|
|
guint n_params,
|
|
GObjectConstructParam *params);
|
|
static void gimp_dock_window_dispose (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 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 (GimpDockWindow, gimp_dock_window, GIMP_TYPE_WINDOW)
|
|
|
|
#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->constructor = gimp_dock_window_constructor;
|
|
object_class->dispose = gimp_dock_window_dispose;
|
|
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));
|
|
|
|
g_type_class_add_private (klass, sizeof (GimpDockWindowPrivate));
|
|
}
|
|
|
|
static void
|
|
gimp_dock_window_init (GimpDockWindow *dock_window)
|
|
{
|
|
static gint dock_window_ID = 1;
|
|
gchar *name = NULL;
|
|
|
|
/* Initialize members */
|
|
dock_window->p = G_TYPE_INSTANCE_GET_PRIVATE (dock_window,
|
|
GIMP_TYPE_DOCK_WINDOW,
|
|
GimpDockWindowPrivate);
|
|
dock_window->p->ID = dock_window_ID++;
|
|
dock_window->p->auto_follow_active = TRUE;
|
|
|
|
/* Initialize theming and style-setting stuff */
|
|
name = g_strdup_printf ("gimp-dock-%d", dock_window->p->ID);
|
|
gtk_widget_set_name (GTK_WIDGET (dock_window), name);
|
|
g_free (name);
|
|
|
|
/* Misc */
|
|
gtk_window_set_resizable (GTK_WINDOW (dock_window), TRUE);
|
|
gtk_window_set_focus_on_map (GTK_WINDOW (dock_window), FALSE);
|
|
}
|
|
|
|
static GObject *
|
|
gimp_dock_window_constructor (GType type,
|
|
guint n_params,
|
|
GObjectConstructParam *params)
|
|
{
|
|
GObject *object = NULL;
|
|
GimpDockWindow *dock_window = NULL;
|
|
GimpGuiConfig *config = NULL;
|
|
GtkAccelGroup *accel_group = NULL;
|
|
Gimp *gimp = NULL;
|
|
GtkSettings *settings = NULL;
|
|
gint menu_view_width = -1;
|
|
gint menu_view_height = -1;
|
|
|
|
/* Init */
|
|
object = G_OBJECT_CLASS (parent_class)->constructor (type, n_params, params);
|
|
dock_window = GIMP_DOCK_WINDOW (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;
|
|
|
|
/* Setup hints */
|
|
gimp_window_set_hint (GTK_WINDOW (dock_window), config->dock_window_hint);
|
|
|
|
/* 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 (global_menu_factory,
|
|
dock_window->p->ui_manager_name,
|
|
dock_window,
|
|
config->tearoff_menus);
|
|
accel_group =
|
|
gtk_ui_manager_get_accel_group (GTK_UI_MANAGER (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_ALL_PROPS_MASK &
|
|
~(GIMP_CONTEXT_IMAGE_MASK |
|
|
GIMP_CONTEXT_DISPLAY_MASK),
|
|
FALSE);
|
|
gimp_context_set_parent (dock_window->p->context,
|
|
gimp_dialog_factory_get_context (dock_window->p->dialog_factory));
|
|
|
|
/* Setup widget hierarchy */
|
|
{
|
|
GtkWidget *vbox = NULL;
|
|
|
|
/* Top-level GtkVBox */
|
|
vbox = gtk_vbox_new (FALSE, 0);
|
|
gtk_container_add (GTK_CONTAINER (dock_window), vbox);
|
|
gtk_widget_show (vbox);
|
|
|
|
/* Image selection menu */
|
|
{
|
|
GtkWidget *hbox = NULL;
|
|
|
|
/* GtkHBox */
|
|
hbox = gtk_hbox_new (FALSE, 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 (gimp_dialog_factory_get_context (dock_window->p->dialog_factory)))
|
|
gimp_context_copy_property (gimp_dialog_factory_get_context (dock_window->p->dialog_factory),
|
|
dock_window->p->context,
|
|
GIMP_CONTEXT_PROP_DISPLAY);
|
|
else
|
|
gimp_context_copy_property (gimp_dialog_factory_get_context (dock_window->p->dialog_factory),
|
|
dock_window->p->context,
|
|
GIMP_CONTEXT_PROP_IMAGE);
|
|
}
|
|
|
|
g_signal_connect_object (gimp_dialog_factory_get_context (dock_window->p->dialog_factory), "display-changed",
|
|
G_CALLBACK (gimp_dock_window_factory_display_changed),
|
|
dock_window,
|
|
0);
|
|
g_signal_connect_object (gimp_dialog_factory_get_context (dock_window->p->dialog_factory), "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 (gimp_dialog_factory_get_context (dock_window->p->dialog_factory)))
|
|
gimp_context_copy_property (gimp_dialog_factory_get_context (dock_window->p->dialog_factory),
|
|
dock_window->p->context,
|
|
GIMP_CONTEXT_PROP_DISPLAY);
|
|
else
|
|
gimp_context_copy_property (gimp_dialog_factory_get_context (dock_window->p->dialog_factory),
|
|
dock_window->p->context,
|
|
GIMP_CONTEXT_PROP_IMAGE);
|
|
}
|
|
|
|
/* Done! */
|
|
return object;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (dock_window->p->ui_manager)
|
|
{
|
|
g_object_unref (dock_window->p->ui_manager);
|
|
dock_window->p->ui_manager = NULL;
|
|
}
|
|
|
|
if (dock_window->p->dialog_factory)
|
|
{
|
|
g_object_unref (dock_window->p->dialog_factory);
|
|
dock_window->p->dialog_factory = NULL;
|
|
}
|
|
|
|
if (dock_window->p->context)
|
|
{
|
|
g_object_unref (dock_window->p->context);
|
|
dock_window->p->context = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (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 singe 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_set_widget (info, GTK_WIDGET (dock_window));
|
|
gimp_session_info_get_info (info);
|
|
gimp_session_info_set_widget (info, NULL);
|
|
|
|
entry_name = (gimp_dock_window_has_toolbox (dock_window) ?
|
|
"gimp-toolbox-window" :
|
|
"gimp-dock-window");
|
|
entry = gimp_dialog_factory_find_entry (gimp_dialog_factory_get_singleton (), 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;
|
|
}
|
|
|
|
/**
|
|
* 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 = gimp_dock_window_get_docks (dock_window);
|
|
gboolean should_add = TRUE;
|
|
|
|
if (g_list_length (docks) < 1)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
return should_add;
|
|
}
|
|
|
|
static void
|
|
gimp_dock_window_display_changed (GimpDockWindow *dock_window,
|
|
GimpObject *display,
|
|
GimpContext *context)
|
|
{
|
|
gimp_ui_manager_update (dock_window->p->ui_manager,
|
|
display);
|
|
}
|
|
|
|
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 != NULL)
|
|
gtk_window_set_title (GTK_WINDOW (dock_window), 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 *iter = NULL;
|
|
|
|
for (iter = gimp_dock_window_get_docks (dock_window);
|
|
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);
|
|
}
|
|
|
|
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_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;
|
|
|
|
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)->list;
|
|
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)
|
|
{
|
|
gimp_context_copy_properties (gimp_dialog_factory_get_context (dock_window->p->dialog_factory),
|
|
dock_window->p->context,
|
|
GIMP_CONTEXT_DISPLAY_MASK |
|
|
GIMP_CONTEXT_IMAGE_MASK);
|
|
}
|
|
}
|
|
|
|
|
|
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 *factory,
|
|
GimpContext *context)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (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", 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;
|
|
}
|
|
|
|
GimpUIManager *
|
|
gimp_dock_window_get_ui_manager (GimpDockWindow *dock_window)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_DOCK_WINDOW (dock_window), NULL);
|
|
|
|
return dock_window->p->ui_manager;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
GimpDialogFactory *
|
|
gimp_dock_window_get_dialog_factory (GimpDockWindow *dock_window)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_DOCK_WINDOW (dock_window), NULL);
|
|
|
|
return dock_window->p->dialog_factory;
|
|
}
|
|
|
|
/**
|
|
* gimp_dock_window_get_docks:
|
|
* @dock_window:
|
|
*
|
|
* Get a list of docks in the dock window.
|
|
*
|
|
* Returns:
|
|
**/
|
|
GList *
|
|
gimp_dock_window_get_docks (GimpDockWindow *dock_window)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_DOCK_WINDOW (dock_window), NULL);
|
|
|
|
return gimp_dock_columns_get_docks (dock_window->p->dock_columns);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void
|
|
gimp_dock_window_set_aux_info (GimpDockWindow *dock_window,
|
|
GList *aux_info)
|
|
{
|
|
GList *list;
|
|
gboolean menu_shown = dock_window->p->show_image_menu;
|
|
gboolean auto_follow = dock_window->p->auto_follow_active;
|
|
|
|
g_return_if_fail (GIMP_IS_DOCK_WINDOW (dock_window));
|
|
|
|
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);
|
|
}
|
|
|
|
GList *
|
|
gimp_dock_window_get_aux_info (GimpDockWindow *dock_window)
|
|
{
|
|
GList *aux_info = NULL;
|
|
GimpSessionInfoAux *aux = NULL;
|
|
|
|
g_return_val_if_fail (GIMP_IS_DOCK_WINDOW (dock_window), NULL);
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|