/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * pikadockwindow.c * Copyright (C) 2001-2005 Michael Natterer * Copyright (C) 2009 Martin Nordholts * * 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 . */ #include "config.h" #include #include #include "libpikabase/pikabase.h" #include "libpikawidgets/pikawidgets.h" #include "widgets-types.h" #include "dialogs/dialogs.h" /* FIXME, we are in the widget layer */ #include "config/pikaguiconfig.h" #include "core/pika.h" #include "core/pikacontext.h" #include "core/pikacontainer.h" #include "core/pikacontainer.h" #include "core/pikalist.h" #include "core/pikaimage.h" #include "menus/menus.h" #include "pikacontainercombobox.h" #include "pikacontainerview.h" #include "pikadialogfactory.h" #include "pikadock.h" #include "pikadockbook.h" #include "pikadockcolumns.h" #include "pikadockcontainer.h" #include "pikadockwindow.h" #include "pikahelp-ids.h" #include "pikamenufactory.h" #include "pikasessioninfo-aux.h" #include "pikasessioninfo.h" #include "pikasessionmanaged.h" #include "pikatoolbox.h" #include "pikauimanager.h" #include "pikawidgets-utils.h" #include "pikawindow.h" #include "pika-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 _PikaDockWindowPrivate { PikaContext *context; PikaDialogFactory *dialog_factory; gchar *ui_manager_name; PikaUIManager *ui_manager; GQuark image_flush_handler_id; PikaDockColumns *dock_columns; gboolean allow_dockbook_absence; guint update_title_idle_id; gint ID; PikaContainer *image_container; PikaContainer *display_container; gboolean show_image_menu; gboolean auto_follow_active; GtkWidget *image_combo; GtkWidget *auto_button; }; static void pika_dock_window_dock_container_iface_init (PikaDockContainerInterface *iface); static void pika_dock_window_session_managed_iface_init(PikaSessionManagedInterface*iface); static void pika_dock_window_constructed (GObject *object); static void pika_dock_window_dispose (GObject *object); static void pika_dock_window_finalize (GObject *object); static void pika_dock_window_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_dock_window_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void pika_dock_window_style_updated (GtkWidget *widget); static gboolean pika_dock_window_delete_event (GtkWidget *widget, GdkEventAny *event); static GList * pika_dock_window_get_docks (PikaDockContainer *dock_container); static PikaDialogFactory * pika_dock_window_get_dialog_factory (PikaDockContainer *dock_container); static PikaUIManager * pika_dock_window_get_ui_manager (PikaDockContainer *dock_container); static void pika_dock_window_add_dock_from_session (PikaDockContainer *dock_container, PikaDock *dock, PikaSessionInfoDock *dock_info); static GList * pika_dock_window_get_aux_info (PikaSessionManaged *session_managed); static void pika_dock_window_set_aux_info (PikaSessionManaged *session_managed, GList *aux_info); static PikaAlignmentType pika_dock_window_get_dock_side (PikaDockContainer *dock_container, PikaDock *dock); static gboolean pika_dock_window_should_add_to_recent (PikaDockWindow *dock_window); static void pika_dock_window_display_changed (PikaDockWindow *dock_window, PikaDisplay *display, PikaContext *context); static void pika_dock_window_image_changed (PikaDockWindow *dock_window, PikaImage *image, PikaContext *context); static void pika_dock_window_image_flush (PikaImage *image, gboolean invalidate_preview, PikaDockWindow *dock_window); static void pika_dock_window_update_title (PikaDockWindow *dock_window); static gboolean pika_dock_window_update_title_idle (PikaDockWindow *dock_window); static gchar * pika_dock_window_get_description (PikaDockWindow *dock_window, gboolean complete); static void pika_dock_window_dock_removed (PikaDockWindow *dock_window, PikaDock *dock, PikaDockColumns *dock_columns); static void pika_dock_window_factory_display_changed (PikaContext *context, PikaDisplay *display, PikaDock *dock); static void pika_dock_window_factory_image_changed (PikaContext *context, PikaImage *image, PikaDock *dock); static void pika_dock_window_auto_clicked (GtkWidget *widget, PikaDock *dock); G_DEFINE_TYPE_WITH_CODE (PikaDockWindow, pika_dock_window, PIKA_TYPE_WINDOW, G_ADD_PRIVATE (PikaDockWindow) G_IMPLEMENT_INTERFACE (PIKA_TYPE_DOCK_CONTAINER, pika_dock_window_dock_container_iface_init) G_IMPLEMENT_INTERFACE (PIKA_TYPE_SESSION_MANAGED, pika_dock_window_session_managed_iface_init)) #define parent_class pika_dock_window_parent_class static void pika_dock_window_class_init (PikaDockWindowClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->constructed = pika_dock_window_constructed; object_class->dispose = pika_dock_window_dispose; object_class->finalize = pika_dock_window_finalize; object_class->set_property = pika_dock_window_set_property; object_class->get_property = pika_dock_window_get_property; widget_class->style_updated = pika_dock_window_style_updated; widget_class->delete_event = pika_dock_window_delete_event; g_object_class_install_property (object_class, PROP_CONTEXT, g_param_spec_object ("context", NULL, NULL, PIKA_TYPE_CONTEXT, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_DIALOG_FACTORY, g_param_spec_object ("dialog-factory", NULL, NULL, PIKA_TYPE_DIALOG_FACTORY, PIKA_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, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_IMAGE_CONTAINER, g_param_spec_object ("image-container", NULL, NULL, PIKA_TYPE_CONTAINER, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_DISPLAY_CONTAINER, g_param_spec_object ("display-container", NULL, NULL, PIKA_TYPE_CONTAINER, PIKA_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, PIKA_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, PIKA_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, PIKA_PARAM_READABLE)); } static void pika_dock_window_init (PikaDockWindow *dock_window) { static gint dock_window_ID = 1; gchar *name = NULL; dock_window->p = pika_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 ("pika-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 pika_dock_window_dock_container_iface_init (PikaDockContainerInterface *iface) { iface->get_docks = pika_dock_window_get_docks; iface->get_dialog_factory = pika_dock_window_get_dialog_factory; iface->get_ui_manager = pika_dock_window_get_ui_manager; iface->add_dock = pika_dock_window_add_dock_from_session; iface->get_dock_side = pika_dock_window_get_dock_side; } static void pika_dock_window_session_managed_iface_init (PikaSessionManagedInterface *iface) { iface->get_aux_info = pika_dock_window_get_aux_info; iface->set_aux_info = pika_dock_window_set_aux_info; } static void pika_dock_window_constructed (GObject *object) { PikaDockWindow *dock_window = PIKA_DOCK_WINDOW (object); PikaGuiConfig *config; PikaContext *factory_context; PikaMenuFactory *menu_factory; Pika *pika; gint menu_view_width = -1; gint menu_view_height = -1; G_OBJECT_CLASS (parent_class)->constructed (object); pika = PIKA (dock_window->p->context->pika); config = PIKA_GUI_CONFIG (pika->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 = pika_context_new (pika, "Dock Context", NULL); dock_window->p->image_container = pika->images; dock_window->p->display_container = pika->displays; factory_context = pika_dialog_factory_get_context (dock_window->p->dialog_factory); /* Setup hints */ pika_window_set_hint (GTK_WINDOW (dock_window), config->dock_window_hint); menu_factory = menus_get_global_menu_factory (pika); /* Make image window related keyboard shortcuts work also when a * dock window is the focused window */ dock_window->p->ui_manager = pika_menu_factory_get_manager (menu_factory, dock_window->p->ui_manager_name, dock_window); g_signal_connect_object (dock_window->p->context, "display-changed", G_CALLBACK (pika_dock_window_display_changed), dock_window, G_CONNECT_SWAPPED); g_signal_connect_object (dock_window->p->context, "image-changed", G_CALLBACK (pika_dock_window_image_changed), dock_window, G_CONNECT_SWAPPED); dock_window->p->image_flush_handler_id = pika_container_add_handler (pika->images, "flush", G_CALLBACK (pika_dock_window_image_flush), dock_window); pika_context_define_properties (dock_window->p->context, PIKA_CONTEXT_PROP_MASK_ALL & ~(PIKA_CONTEXT_PROP_MASK_IMAGE | PIKA_CONTEXT_PROP_MASK_DISPLAY), FALSE); pika_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 = pika_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); pika_help_set_help_data (dock_window->p->image_combo, NULL, PIKA_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 (pika_dock_window_auto_clicked), dock_window); pika_help_set_help_data (dock_window->p->auto_button, _("When enabled, the dialog automatically " "follows the image you are working on."), PIKA_HELP_DOCK_AUTO_BUTTON); } /* PikaDockColumns */ /* Let the PikaDockColumns mirror the context so that a PikaDock can * get it when inside a dock window. We do the same thing in the * PikaImageWindow so docks can get the PikaContext there as well */ dock_window->p->dock_columns = PIKA_DOCK_COLUMNS (pika_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 (pika_dock_window_dock_removed), dock_window, G_CONNECT_SWAPPED); g_signal_connect_object (dock_window->p->dock_columns, "dock-added", G_CALLBACK (pika_dock_window_update_title), dock_window, G_CONNECT_SWAPPED); g_signal_connect_object (dock_window->p->dock_columns, "dock-removed", G_CALLBACK (pika_dock_window_update_title), dock_window, G_CONNECT_SWAPPED); } if (dock_window->p->auto_follow_active) { if (pika_context_get_display (factory_context)) pika_context_copy_property (factory_context, dock_window->p->context, PIKA_CONTEXT_PROP_DISPLAY); else pika_context_copy_property (factory_context, dock_window->p->context, PIKA_CONTEXT_PROP_IMAGE); } g_signal_connect_object (factory_context, "display-changed", G_CALLBACK (pika_dock_window_factory_display_changed), dock_window, 0); g_signal_connect_object (factory_context, "image-changed", G_CALLBACK (pika_dock_window_factory_image_changed), dock_window, 0); gtk_icon_size_lookup (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); pika_help_connect (GTK_WIDGET (dock_window), pika_standard_help_func, PIKA_HELP_DOCK, NULL, NULL); if (dock_window->p->auto_follow_active) { if (pika_context_get_display (factory_context)) pika_context_copy_property (factory_context, dock_window->p->context, PIKA_CONTEXT_PROP_DISPLAY); else pika_context_copy_property (factory_context, dock_window->p->context, PIKA_CONTEXT_PROP_IMAGE); } } static void pika_dock_window_dispose (GObject *object) { PikaDockWindow *dock_window = PIKA_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) { pika_container_remove_handler (dock_window->p->context->pika->images, dock_window->p->image_flush_handler_id); dock_window->p->image_flush_handler_id = 0; } g_clear_object (&dock_window->p->dialog_factory); G_OBJECT_CLASS (parent_class)->dispose (object); } static void pika_dock_window_finalize (GObject *object) { PikaDockWindow *dock_window = PIKA_DOCK_WINDOW (object); PikaMenuFactory *menu_factory; Pika *pika; pika = PIKA (dock_window->p->context->pika); menu_factory = menus_get_global_menu_factory (pika); /* If the whole GUI is in destruction, global menu factory might already not * exist anymore. */ if (menu_factory != NULL) pika_menu_factory_delete_manager (menu_factory, dock_window->p->ui_manager_name, dock_window); g_clear_pointer (&dock_window->p->ui_manager_name, g_free); g_clear_object (&dock_window->p->context); G_OBJECT_CLASS (parent_class)->finalize (object); } static void pika_dock_window_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaDockWindow *dock_window = PIKA_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 pika_dock_window_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaDockWindow *dock_window = PIKA_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 pika_dock_window_style_updated (GtkWidget *widget) { PikaDockWindow *dock_window = PIKA_DOCK_WINDOW (widget); GtkStyleContext *button_style; GtkIconSize menu_view_size; gint default_height = DEFAULT_DOCK_HEIGHT; GTK_WIDGET_CLASS (parent_class)->style_updated (widget); 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); if (dock_window->p->image_combo) { GtkBorder border; gint menu_view_width = 18; gint menu_view_height = 18; gint focus_line_width; gint focus_padding; gtk_icon_size_lookup (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_context (widget); gtk_style_context_get_border (button_style, gtk_widget_get_state_flags (widget), &border); pika_container_view_set_view_size (PIKA_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 */ + border.top + border.bottom + focus_padding + focus_line_width)); } } /** * pika_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 pika_dock_window_delete_event (GtkWidget *widget, GdkEventAny *event) { PikaDockWindow *dock_window = PIKA_DOCK_WINDOW (widget); PikaSessionInfo *info = NULL; const gchar *entry_name = NULL; PikaDialogFactoryEntry *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 (! pika_dock_window_should_add_to_recent (dock_window)) return FALSE; info = pika_session_info_new (); name = pika_dock_window_get_description (dock_window, TRUE /*complete*/); pika_object_set_name (PIKA_OBJECT (info), name); g_free (name); pika_session_info_get_info_with_widget (info, GTK_WIDGET (dock_window)); entry_name = (pika_dock_window_has_toolbox (dock_window) ? "pika-toolbox-window" : "pika-dock-window"); entry = pika_dialog_factory_find_entry (dock_window->p->dialog_factory, entry_name); pika_session_info_set_factory_entry (info, entry); pika_container_add (global_recent_docks, PIKA_OBJECT (info)); g_object_unref (info); return FALSE; } static GList * pika_dock_window_get_docks (PikaDockContainer *dock_container) { PikaDockWindow *dock_window = PIKA_DOCK_WINDOW (dock_container); return g_list_copy (pika_dock_columns_get_docks (dock_window->p->dock_columns)); } static PikaDialogFactory * pika_dock_window_get_dialog_factory (PikaDockContainer *dock_container) { PikaDockWindow *dock_window = PIKA_DOCK_WINDOW (dock_container); return dock_window->p->dialog_factory; } static PikaUIManager * pika_dock_window_get_ui_manager (PikaDockContainer *dock_container) { PikaDockWindow *dock_window = PIKA_DOCK_WINDOW (dock_container); return dock_window->p->ui_manager; } static void pika_dock_window_add_dock_from_session (PikaDockContainer *dock_container, PikaDock *dock, PikaSessionInfoDock *dock_info) { PikaDockWindow *dock_window = PIKA_DOCK_WINDOW (dock_container); pika_dock_window_add_dock (dock_window, dock, -1 /*index*/); } static GList * pika_dock_window_get_aux_info (PikaSessionManaged *session_managed) { PikaDockWindow *dock_window = PIKA_DOCK_WINDOW (session_managed); GList *aux_info = NULL; PikaSessionInfoAux *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 (PIKA_IS_DOCK_WINDOW (dock_window), NULL); aux = pika_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 = pika_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 pika_dock_window_set_aux_info (PikaSessionManaged *session_managed, GList *aux_info) { PikaDockWindow *dock_window; GList *list; gboolean menu_shown; gboolean auto_follow; g_return_if_fail (PIKA_IS_DOCK_WINDOW (session_managed)); dock_window = PIKA_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)) { PikaSessionInfoAux *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) pika_dock_window_set_show_image_menu (dock_window, menu_shown); if (auto_follow != dock_window->p->auto_follow_active) pika_dock_window_set_auto_follow_active (dock_window, auto_follow); } static PikaAlignmentType pika_dock_window_get_dock_side (PikaDockContainer *dock_container, PikaDock *dock) { g_return_val_if_fail (PIKA_IS_DOCK_WINDOW (dock_container), -1); g_return_val_if_fail (PIKA_IS_DOCK (dock), -1); /* A PikaDockWindow don't have docks on different sides, it's just * one set of columns */ return -1; } /** * pika_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 pika_dock_window_should_add_to_recent (PikaDockWindow *dock_window) { GList *docks; gboolean should_add = TRUE; docks = pika_dock_container_get_docks (PIKA_DOCK_CONTAINER (dock_window)); if (! docks) { should_add = FALSE; } else if (g_list_length (docks) == 1) { PikaDock *dock = PIKA_DOCK (g_list_nth_data (docks, 0)); if (PIKA_IS_TOOLBOX (dock) && pika_dock_get_n_dockables (dock) == 0) { should_add = FALSE; } else if (! PIKA_IS_TOOLBOX (dock) && pika_dock_get_n_dockables (dock) == 1) { should_add = FALSE; } } g_list_free (docks); return should_add; } static void pika_dock_window_image_flush (PikaImage *image, gboolean invalidate_preview, PikaDockWindow *dock_window) { if (image == pika_context_get_image (dock_window->p->context)) { PikaDisplay *display = pika_context_get_display (dock_window->p->context); if (display) pika_ui_manager_update (dock_window->p->ui_manager, display); } } static void pika_dock_window_update_title (PikaDockWindow *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) pika_dock_window_update_title_idle, dock_window); } static gboolean pika_dock_window_update_title_idle (PikaDockWindow *dock_window) { gchar *desc = pika_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 * pika_dock_window_get_description (PikaDockWindow *dock_window, gboolean complete) { GString *complete_desc = g_string_new (NULL); GList *docks = NULL; GList *iter = NULL; docks = pika_dock_container_get_docks (PIKA_DOCK_CONTAINER (dock_window)); for (iter = docks; iter; iter = g_list_next (iter)) { gchar *desc = pika_dock_get_description (PIKA_DOCK (iter->data), complete); g_string_append (complete_desc, desc); g_free (desc); if (g_list_next (iter)) g_string_append (complete_desc, PIKA_DOCK_COLUMN_SEPARATOR); } g_list_free (docks); return g_string_free (complete_desc, FALSE /*free_segment*/); } static void pika_dock_window_dock_removed (PikaDockWindow *dock_window, PikaDock *dock, PikaDockColumns *dock_columns) { g_return_if_fail (PIKA_IS_DOCK (dock)); if (pika_dock_columns_get_docks (dock_columns) == NULL && ! dock_window->p->allow_dockbook_absence) gtk_widget_destroy (GTK_WIDGET (dock_window)); } static void pika_dock_window_factory_display_changed (PikaContext *context, PikaDisplay *display, PikaDock *dock) { PikaDockWindow *dock_window = PIKA_DOCK_WINDOW (dock); if (display && dock_window->p->auto_follow_active) pika_context_set_display (dock_window->p->context, display); } static void pika_dock_window_factory_image_changed (PikaContext *context, PikaImage *image, PikaDock *dock) { PikaDockWindow *dock_window = PIKA_DOCK_WINDOW (dock); /* won't do anything if we already set the display above */ if (image && dock_window->p->auto_follow_active) pika_context_set_image (dock_window->p->context, image); } static void pika_dock_window_display_changed (PikaDockWindow *dock_window, PikaDisplay *display, PikaContext *context) { /* make sure auto-follow-active works both ways */ if (display && dock_window->p->auto_follow_active) { PikaContext *factory_context = pika_dialog_factory_get_context (dock_window->p->dialog_factory); pika_context_set_display (factory_context, display); } pika_ui_manager_update (dock_window->p->ui_manager, display); } static void pika_dock_window_image_changed (PikaDockWindow *dock_window, PikaImage *image, PikaContext *context) { PikaContainer *image_container = dock_window->p->image_container; PikaContainer *display_container = dock_window->p->display_container; /* make sure auto-follow-active works both ways */ if (image && dock_window->p->auto_follow_active) { PikaContext *factory_context = pika_dialog_factory_get_context (dock_window->p->dialog_factory); pika_context_set_image (factory_context, image); } if (image == NULL && ! pika_container_is_empty (image_container)) { image = PIKA_IMAGE (pika_container_get_first_child (image_container)); /* this invokes this function recursively but we don't enter * the if() branch the second time */ pika_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 && ! pika_container_is_empty (display_container)) { PikaDisplay *display; PikaImage *display_image; gboolean find_display = TRUE; display = pika_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 = PIKA_LIST (display_container)->queue->head; list; list = g_list_next (list)) { display = 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 */ pika_context_set_display (context, display); /* don't stop signal emission here because the * context's image was not changed by the * recursive call */ break; } } } } } pika_ui_manager_update (dock_window->p->ui_manager, pika_context_get_display (context)); } static void pika_dock_window_auto_clicked (GtkWidget *widget, PikaDock *dock) { PikaDockWindow *dock_window = PIKA_DOCK_WINDOW (dock); pika_toggle_button_update (widget, &dock_window->p->auto_follow_active); if (dock_window->p->auto_follow_active) { PikaContext *context; context = pika_dialog_factory_get_context (dock_window->p->dialog_factory); pika_context_copy_properties (context, dock_window->p->context, PIKA_CONTEXT_PROP_MASK_DISPLAY | PIKA_CONTEXT_PROP_MASK_IMAGE); } } void pika_dock_window_add_dock (PikaDockWindow *dock_window, PikaDock *dock, gint index) { g_return_if_fail (PIKA_IS_DOCK_WINDOW (dock_window)); g_return_if_fail (PIKA_IS_DOCK (dock)); pika_dock_columns_add_dock (dock_window->p->dock_columns, PIKA_DOCK (dock), index); g_signal_connect_object (dock, "description-invalidated", G_CALLBACK (pika_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 */ pika_dock_set_host_geometry_hints (dock, GTK_WINDOW (dock_window)); g_signal_connect_object (dock, "geometry-invalidated", G_CALLBACK (pika_dock_set_host_geometry_hints), dock_window, 0); } void pika_dock_window_remove_dock (PikaDockWindow *dock_window, PikaDock *dock) { pika_dock_columns_remove_dock (dock_window->p->dock_columns, PIKA_DOCK (dock)); g_signal_handlers_disconnect_by_func (dock, pika_dock_window_update_title, dock_window); g_signal_handlers_disconnect_by_func (dock, pika_dock_set_host_geometry_hints, dock_window); } GtkWidget * pika_dock_window_new (const gchar *role, const gchar *ui_manager_name, gboolean allow_dockbook_absence, PikaDialogFactory *dialog_factory, PikaContext *context) { g_return_val_if_fail (PIKA_IS_DIALOG_FACTORY (dialog_factory), NULL); g_return_val_if_fail (PIKA_IS_CONTEXT (context), NULL); return g_object_new (PIKA_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 pika_dock_window_get_id (PikaDockWindow *dock_window) { g_return_val_if_fail (PIKA_IS_DOCK_WINDOW (dock_window), 0); return dock_window->p->ID; } PikaContext * pika_dock_window_get_context (PikaDockWindow *dock_window) { g_return_val_if_fail (PIKA_IS_DOCK_WINDOW (dock_window), NULL); return dock_window->p->context; } gboolean pika_dock_window_get_auto_follow_active (PikaDockWindow *dock_window) { g_return_val_if_fail (PIKA_IS_DOCK_WINDOW (dock_window), FALSE); return dock_window->p->auto_follow_active; } void pika_dock_window_set_auto_follow_active (PikaDockWindow *dock_window, gboolean auto_follow_active) { g_return_if_fail (PIKA_IS_DOCK_WINDOW (dock_window)); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dock_window->p->auto_button), auto_follow_active ? TRUE : FALSE); } gboolean pika_dock_window_get_show_image_menu (PikaDockWindow *dock_window) { g_return_val_if_fail (PIKA_IS_DOCK_WINDOW (dock_window), FALSE); return dock_window->p->show_image_menu; } void pika_dock_window_set_show_image_menu (PikaDockWindow *dock_window, gboolean show) { GtkWidget *parent; g_return_if_fail (PIKA_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 pika_dock_window_setup (PikaDockWindow *dock_window, PikaDockWindow *template) { pika_dock_window_set_auto_follow_active (PIKA_DOCK_WINDOW (dock_window), template->p->auto_follow_active); pika_dock_window_set_show_image_menu (PIKA_DOCK_WINDOW (dock_window), template->p->show_image_menu); } /** * pika_dock_window_has_toolbox: * @dock_window: * * Returns: %TRUE if the dock window has a PikaToolbox dock, %FALSE * otherwise. **/ gboolean pika_dock_window_has_toolbox (PikaDockWindow *dock_window) { GList *iter = NULL; g_return_val_if_fail (PIKA_IS_DOCK_WINDOW (dock_window), FALSE); for (iter = pika_dock_columns_get_docks (dock_window->p->dock_columns); iter; iter = g_list_next (iter)) { if (PIKA_IS_TOOLBOX (iter->data)) return TRUE; } return FALSE; } /** * pika_dock_window_from_dock: * @dock: * * For convenience. * * Returns: If the toplevel widget for the dock is a PikaDockWindow, * return that. Otherwise return %NULL. **/ PikaDockWindow * pika_dock_window_from_dock (PikaDock *dock) { GtkWidget *toplevel = NULL; g_return_val_if_fail (PIKA_IS_DOCK (dock), NULL); toplevel = gtk_widget_get_toplevel (GTK_WIDGET (dock)); if (PIKA_IS_DOCK_WINDOW (toplevel)) return PIKA_DOCK_WINDOW (toplevel); else return NULL; }