diff options
Diffstat (limited to 'app/widgets/gimpdock.c')
-rw-r--r-- | app/widgets/gimpdock.c | 768 |
1 files changed, 768 insertions, 0 deletions
diff --git a/app/widgets/gimpdock.c b/app/widgets/gimpdock.c new file mode 100644 index 0000000..c7bc81a --- /dev/null +++ b/app/widgets/gimpdock.c @@ -0,0 +1,768 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdock.c + * Copyright (C) 2001-2005 Michael Natterer <mitch@gimp.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <gegl.h> +#include <gtk/gtk.h> + +#include "libgimpwidgets/gimpwidgets.h" + +#include "widgets-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" +#include "core/gimpmarshal.h" + +#include "gimpdialogfactory.h" +#include "gimpdock.h" +#include "gimpdockable.h" +#include "gimpdockbook.h" +#include "gimpdockcolumns.h" +#include "gimpdockcontainer.h" +#include "gimpdockwindow.h" +#include "gimppanedbox.h" +#include "gimpuimanager.h" +#include "gimpwidgets-utils.h" + +#include "gimp-intl.h" + + +#define DEFAULT_DOCK_FONT_SCALE PANGO_SCALE_SMALL + + +enum +{ + BOOK_ADDED, + BOOK_REMOVED, + DESCRIPTION_INVALIDATED, + GEOMETRY_INVALIDATED, + LAST_SIGNAL +}; + + +struct _GimpDockPrivate +{ + GtkWidget *temp_vbox; + GtkWidget *main_vbox; + GtkWidget *paned_vbox; + + GList *dockbooks; + + gint ID; +}; + + +static void gimp_dock_dispose (GObject *object); + +static void gimp_dock_style_set (GtkWidget *widget, + GtkStyle *prev_style); +static gchar * gimp_dock_real_get_description (GimpDock *dock, + gboolean complete); +static void gimp_dock_real_book_added (GimpDock *dock, + GimpDockbook *dockbook); +static void gimp_dock_real_book_removed (GimpDock *dock, + GimpDockbook *dockbook); +static void gimp_dock_invalidate_description (GimpDock *dock); +static gboolean gimp_dock_dropped_cb (GtkWidget *source, + gint insert_index, + gpointer data); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpDock, gimp_dock, GTK_TYPE_BOX) + +#define parent_class gimp_dock_parent_class + +static guint dock_signals[LAST_SIGNAL] = { 0 }; + + +static void +gimp_dock_class_init (GimpDockClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + dock_signals[BOOK_ADDED] = + g_signal_new ("book-added", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDockClass, book_added), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_DOCKBOOK); + + dock_signals[BOOK_REMOVED] = + g_signal_new ("book-removed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDockClass, book_removed), + NULL, NULL, + gimp_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GIMP_TYPE_DOCKBOOK); + + dock_signals[DESCRIPTION_INVALIDATED] = + g_signal_new ("description-invalidated", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDockClass, description_invalidated), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + dock_signals[GEOMETRY_INVALIDATED] = + g_signal_new ("geometry-invalidated", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDockClass, geometry_invalidated), + NULL, NULL, + gimp_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->dispose = gimp_dock_dispose; + + widget_class->style_set = gimp_dock_style_set; + + klass->get_description = gimp_dock_real_get_description; + klass->set_host_geometry_hints = NULL; + klass->book_added = gimp_dock_real_book_added; + klass->book_removed = gimp_dock_real_book_removed; + klass->description_invalidated = NULL; + klass->geometry_invalidated = NULL; + + gtk_widget_class_install_style_property (widget_class, + g_param_spec_double ("font-scale", + NULL, NULL, + 0.0, + G_MAXDOUBLE, + DEFAULT_DOCK_FONT_SCALE, + GIMP_PARAM_READABLE)); +} + +static void +gimp_dock_init (GimpDock *dock) +{ + static gint dock_ID = 1; + gchar *name = NULL; + + gtk_orientable_set_orientation (GTK_ORIENTABLE (dock), + GTK_ORIENTATION_VERTICAL); + + dock->p = gimp_dock_get_instance_private (dock); + dock->p->ID = dock_ID++; + + name = g_strdup_printf ("gimp-internal-dock-%d", dock->p->ID); + gtk_widget_set_name (GTK_WIDGET (dock), name); + g_free (name); + + dock->p->temp_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_box_pack_start (GTK_BOX (dock), dock->p->temp_vbox, FALSE, FALSE, 0); + /* Never show it */ + + dock->p->main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_box_pack_start (GTK_BOX (dock), dock->p->main_vbox, TRUE, TRUE, 0); + gtk_widget_show (dock->p->main_vbox); + + dock->p->paned_vbox = gimp_paned_box_new (FALSE, 0, GTK_ORIENTATION_VERTICAL); + gimp_paned_box_set_dropped_cb (GIMP_PANED_BOX (dock->p->paned_vbox), + gimp_dock_dropped_cb, + dock); + gtk_box_pack_start (GTK_BOX (dock->p->main_vbox), dock->p->paned_vbox, + TRUE, TRUE, 0); + gtk_widget_show (dock->p->paned_vbox); +} + +static void +gimp_dock_dispose (GObject *object) +{ + GimpDock *dock = GIMP_DOCK (object); + + while (dock->p->dockbooks) + { + GimpDockbook *dockbook = dock->p->dockbooks->data; + + g_object_ref (dockbook); + gimp_dock_remove_book (dock, dockbook); + gtk_widget_destroy (GTK_WIDGET (dockbook)); + g_object_unref (dockbook); + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_dock_style_set (GtkWidget *widget, + GtkStyle *prev_style) +{ + GimpDock *dock = GIMP_DOCK (widget); + gdouble font_scale = 1.0; + + GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style); + + gtk_widget_style_get (widget, + "font-scale", &font_scale, + NULL); + + if (font_scale != 1.0) + { + PangoContext *context; + PangoFontDescription *font_desc; + gint font_size; + gchar *font_str; + gchar *rc_string; + + context = gtk_widget_get_pango_context (widget); + font_desc = pango_context_get_font_description (context); + font_desc = pango_font_description_copy (font_desc); + + font_size = pango_font_description_get_size (font_desc); + font_size = font_scale * font_size; + pango_font_description_set_size (font_desc, font_size); + + font_str = pango_font_description_to_string (font_desc); + pango_font_description_free (font_desc); + + rc_string = + g_strdup_printf ("style \"gimp-dock-style\"" + "{" + " font_name = \"%s\"" + "}" + "widget \"*.gimp-internal-dock-%d.*\" style \"gimp-dock-style\"", + font_str, + dock->p->ID); + g_free (font_str); + + gtk_rc_parse_string (rc_string); + g_free (rc_string); + + gtk_widget_reset_rc_styles (widget); + } +} + +static gchar * +gimp_dock_real_get_description (GimpDock *dock, + gboolean complete) +{ + GString *desc; + GList *list; + + desc = g_string_new (NULL); + + for (list = gimp_dock_get_dockbooks (dock); + list; + list = g_list_next (list)) + { + GimpDockbook *dockbook = list->data; + GList *children; + GList *child; + + if (complete) + { + /* Include all dockables */ + children = gtk_container_get_children (GTK_CONTAINER (dockbook)); + } + else + { + GtkWidget *dockable = NULL; + gint page_num = 0; + + page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (dockbook)); + dockable = gtk_notebook_get_nth_page (GTK_NOTEBOOK (dockbook), page_num); + + /* Only include active dockables */ + children = g_list_append (NULL, dockable); + } + + for (child = children; child; child = g_list_next (child)) + { + GimpDockable *dockable = child->data; + + g_string_append (desc, gimp_dockable_get_name (dockable)); + + if (g_list_next (child)) + g_string_append (desc, GIMP_DOCK_DOCKABLE_SEPARATOR); + } + + g_list_free (children); + + if (g_list_next (list)) + g_string_append (desc, GIMP_DOCK_BOOK_SEPARATOR); + } + + return g_string_free (desc, FALSE); +} + +static void +gimp_dock_real_book_added (GimpDock *dock, + GimpDockbook *dockbook) +{ + g_signal_connect_object (dockbook, "switch-page", + G_CALLBACK (gimp_dock_invalidate_description), + dock, G_CONNECT_SWAPPED); +} + +static void +gimp_dock_real_book_removed (GimpDock *dock, + GimpDockbook *dockbook) +{ + g_signal_handlers_disconnect_by_func (dockbook, + gimp_dock_invalidate_description, + dock); +} + +static void +gimp_dock_invalidate_description (GimpDock *dock) +{ + g_return_if_fail (GIMP_IS_DOCK (dock)); + + g_signal_emit (dock, dock_signals[DESCRIPTION_INVALIDATED], 0); +} + +static gboolean +gimp_dock_dropped_cb (GtkWidget *source, + gint insert_index, + gpointer data) +{ + GimpDock *dock = GIMP_DOCK (data); + GimpDockable *dockable = gimp_dockbook_drag_source_to_dockable (source); + GimpDialogFactory *factory; + GtkWidget *dockbook = NULL; + + if (!dockable ) + return FALSE; + + /* if dropping to the same dock, take care that we don't try + * to reorder the *only* dockable in the dock + */ + if (gimp_dockbook_get_dock (gimp_dockable_get_dockbook (dockable)) == dock) + { + GList *children; + gint n_books; + gint n_dockables; + + n_books = g_list_length (gimp_dock_get_dockbooks (dock)); + + children = gtk_container_get_children (GTK_CONTAINER (gimp_dockable_get_dockbook (dockable))); + n_dockables = g_list_length (children); + g_list_free (children); + + if (n_books == 1 && n_dockables == 1) + return TRUE; /* successfully do nothing */ + } + + /* Detach the dockable from the old dockbook */ + g_object_ref (dockable); + gimp_dockbook_remove (gimp_dockable_get_dockbook (dockable), dockable); + + /* Create a new dockbook */ + factory = gimp_dock_get_dialog_factory (dock); + dockbook = gimp_dockbook_new (gimp_dialog_factory_get_menu_factory (factory)); + gimp_dock_add_book (dock, GIMP_DOCKBOOK (dockbook), insert_index); + + /* Add the dockable to new new dockbook */ + gimp_dockbook_add (GIMP_DOCKBOOK (dockbook), dockable, -1); + g_object_unref (dockable); + + return TRUE; +} + + +/* public functions */ + +/** + * gimp_dock_get_description: + * @dock: + * @complete: If %TRUE, only includes the active dockables, i.e. not the + * dockables in a non-active GtkNotebook tab + * + * Returns: A string describing the contents of the dock. + **/ +gchar * +gimp_dock_get_description (GimpDock *dock, + gboolean complete) +{ + g_return_val_if_fail (GIMP_IS_DOCK (dock), NULL); + + if (GIMP_DOCK_GET_CLASS (dock)->get_description) + return GIMP_DOCK_GET_CLASS (dock)->get_description (dock, complete); + + return NULL; +} + +/** + * gimp_dock_set_host_geometry_hints: + * @dock: The dock + * @window: The #GtkWindow to adapt to hosting the dock + * + * Some docks have some specific needs on the #GtkWindow they are + * in. This function allows such docks to perform any such setup on + * the #GtkWindow they are in/will be put in. + **/ +void +gimp_dock_set_host_geometry_hints (GimpDock *dock, + GtkWindow *window) +{ + g_return_if_fail (GIMP_IS_DOCK (dock)); + g_return_if_fail (GTK_IS_WINDOW (window)); + + if (GIMP_DOCK_GET_CLASS (dock)->set_host_geometry_hints) + GIMP_DOCK_GET_CLASS (dock)->set_host_geometry_hints (dock, window); +} + +/** + * gimp_dock_invalidate_geometry: + * @dock: + * + * Call when the dock needs to setup its host #GtkWindow with + * GtkDock::set_host_geometry_hints(). + **/ +void +gimp_dock_invalidate_geometry (GimpDock *dock) +{ + g_return_if_fail (GIMP_IS_DOCK (dock)); + + g_signal_emit (dock, dock_signals[GEOMETRY_INVALIDATED], 0); +} + +/** + * gimp_dock_update_with_context: + * @dock: + * @context: + * + * Set the @context on all dockables in the @dock. + **/ +void +gimp_dock_update_with_context (GimpDock *dock, + GimpContext *context) +{ + GList *iter = NULL; + + for (iter = gimp_dock_get_dockbooks (dock); + iter; + iter = g_list_next (iter)) + { + GimpDockbook *dockbook = GIMP_DOCKBOOK (iter->data); + + gimp_dockbook_update_with_context (dockbook, context); + } +} + +/** + * gimp_dock_get_context: + * @dock: + * + * Returns: The #GimpContext for the #GimpDockWindow the @dock is in. + **/ +GimpContext * +gimp_dock_get_context (GimpDock *dock) +{ + GimpContext *context = NULL; + + g_return_val_if_fail (GIMP_IS_DOCK (dock), NULL); + + /* First try GimpDockColumns */ + if (! context) + { + GimpDockColumns *dock_columns; + + dock_columns = + GIMP_DOCK_COLUMNS (gtk_widget_get_ancestor (GTK_WIDGET (dock), + GIMP_TYPE_DOCK_COLUMNS)); + + if (dock_columns) + context = gimp_dock_columns_get_context (dock_columns); + } + + /* Then GimpDockWindow */ + if (! context) + { + GimpDockWindow *dock_window = gimp_dock_window_from_dock (dock); + + if (dock_window) + context = gimp_dock_window_get_context (dock_window); + } + + return context; +} + +/** + * gimp_dock_get_dialog_factory: + * @dock: + * + * Returns: The #GimpDialogFactory for the #GimpDockWindow the @dock + * is in. + **/ +GimpDialogFactory * +gimp_dock_get_dialog_factory (GimpDock *dock) +{ + GimpDialogFactory *dialog_factory = NULL; + + g_return_val_if_fail (GIMP_IS_DOCK (dock), NULL); + + /* First try GimpDockColumns */ + if (! dialog_factory) + { + GimpDockColumns *dock_columns; + + dock_columns = + GIMP_DOCK_COLUMNS (gtk_widget_get_ancestor (GTK_WIDGET (dock), + GIMP_TYPE_DOCK_COLUMNS)); + + if (dock_columns) + dialog_factory = gimp_dock_columns_get_dialog_factory (dock_columns); + } + + /* Then GimpDockWindow */ + if (! dialog_factory) + { + GimpDockWindow *dock_window = gimp_dock_window_from_dock (dock); + + if (dock_window) + dialog_factory = gimp_dock_container_get_dialog_factory (GIMP_DOCK_CONTAINER (dock_window)); + } + + return dialog_factory; +} + +/** + * gimp_dock_get_ui_manager: + * @dock: + * + * Returns: The #GimpUIManager for the #GimpDockWindow the @dock is + * in. + **/ +GimpUIManager * +gimp_dock_get_ui_manager (GimpDock *dock) +{ + GimpUIManager *ui_manager = NULL; + + g_return_val_if_fail (GIMP_IS_DOCK (dock), NULL); + + /* First try GimpDockColumns */ + if (! ui_manager) + { + GimpDockColumns *dock_columns; + + dock_columns = + GIMP_DOCK_COLUMNS (gtk_widget_get_ancestor (GTK_WIDGET (dock), + GIMP_TYPE_DOCK_COLUMNS)); + + if (dock_columns) + ui_manager = gimp_dock_columns_get_ui_manager (dock_columns); + } + + /* Then GimpDockContainer */ + if (! ui_manager) + { + GimpDockWindow *dock_window = gimp_dock_window_from_dock (dock); + + if (dock_window) + { + GimpDockContainer *dock_container = GIMP_DOCK_CONTAINER (dock_window); + + ui_manager = gimp_dock_container_get_ui_manager (dock_container); + } + } + + return ui_manager; +} + +GList * +gimp_dock_get_dockbooks (GimpDock *dock) +{ + g_return_val_if_fail (GIMP_IS_DOCK (dock), NULL); + + return dock->p->dockbooks; +} + +gint +gimp_dock_get_n_dockables (GimpDock *dock) +{ + GList *list = NULL; + gint n = 0; + + g_return_val_if_fail (GIMP_IS_DOCK (dock), 0); + + for (list = dock->p->dockbooks; list; list = list->next) + n += gtk_notebook_get_n_pages (GTK_NOTEBOOK (list->data)); + + return n; +} + +GtkWidget * +gimp_dock_get_main_vbox (GimpDock *dock) +{ + g_return_val_if_fail (GIMP_IS_DOCK (dock), NULL); + + return dock->p->main_vbox; +} + +GtkWidget * +gimp_dock_get_vbox (GimpDock *dock) +{ + g_return_val_if_fail (GIMP_IS_DOCK (dock), NULL); + + return dock->p->paned_vbox; +} + +gint +gimp_dock_get_id (GimpDock *dock) +{ + g_return_val_if_fail (GIMP_IS_DOCK (dock), 0); + + return dock->p->ID; +} + +void +gimp_dock_set_id (GimpDock *dock, + gint ID) +{ + g_return_if_fail (GIMP_IS_DOCK (dock)); + + dock->p->ID = ID; +} + +void +gimp_dock_add (GimpDock *dock, + GimpDockable *dockable, + gint section, + gint position) +{ + GimpDockbook *dockbook; + + g_return_if_fail (GIMP_IS_DOCK (dock)); + g_return_if_fail (GIMP_IS_DOCKABLE (dockable)); + g_return_if_fail (gimp_dockable_get_dockbook (dockable) == NULL); + + dockbook = GIMP_DOCKBOOK (dock->p->dockbooks->data); + + gimp_dockbook_add (dockbook, dockable, position); +} + +void +gimp_dock_remove (GimpDock *dock, + GimpDockable *dockable) +{ + g_return_if_fail (GIMP_IS_DOCK (dock)); + g_return_if_fail (GIMP_IS_DOCKABLE (dockable)); + g_return_if_fail (gimp_dockable_get_dockbook (dockable) != NULL); + g_return_if_fail (gimp_dockbook_get_dock (gimp_dockable_get_dockbook (dockable)) == dock); + + gimp_dockbook_remove (gimp_dockable_get_dockbook (dockable), dockable); +} + +void +gimp_dock_add_book (GimpDock *dock, + GimpDockbook *dockbook, + gint index) +{ + g_return_if_fail (GIMP_IS_DOCK (dock)); + g_return_if_fail (GIMP_IS_DOCKBOOK (dockbook)); + g_return_if_fail (gimp_dockbook_get_dock (dockbook) == NULL); + + gimp_dockbook_set_dock (dockbook, dock); + + g_signal_connect_object (dockbook, "dockable-added", + G_CALLBACK (gimp_dock_invalidate_description), + dock, G_CONNECT_SWAPPED); + g_signal_connect_object (dockbook, "dockable-removed", + G_CALLBACK (gimp_dock_invalidate_description), + dock, G_CONNECT_SWAPPED); + g_signal_connect_object (dockbook, "dockable-reordered", + G_CALLBACK (gimp_dock_invalidate_description), + dock, G_CONNECT_SWAPPED); + + dock->p->dockbooks = g_list_insert (dock->p->dockbooks, dockbook, index); + gimp_paned_box_add_widget (GIMP_PANED_BOX (dock->p->paned_vbox), + GTK_WIDGET (dockbook), + index); + gtk_widget_show (GTK_WIDGET (dockbook)); + + gimp_dock_invalidate_description (dock); + + g_signal_emit (dock, dock_signals[BOOK_ADDED], 0, dockbook); +} + +void +gimp_dock_remove_book (GimpDock *dock, + GimpDockbook *dockbook) +{ + g_return_if_fail (GIMP_IS_DOCK (dock)); + g_return_if_fail (GIMP_IS_DOCKBOOK (dockbook)); + g_return_if_fail (gimp_dockbook_get_dock (dockbook) == dock); + + gimp_dockbook_set_dock (dockbook, NULL); + + g_signal_handlers_disconnect_by_func (dockbook, + gimp_dock_invalidate_description, + dock); + + /* Ref the dockbook so we can emit the "book-removed" signal and + * pass it as a parameter before it's destroyed + */ + g_object_ref (dockbook); + + dock->p->dockbooks = g_list_remove (dock->p->dockbooks, dockbook); + gimp_paned_box_remove_widget (GIMP_PANED_BOX (dock->p->paned_vbox), + GTK_WIDGET (dockbook)); + + gimp_dock_invalidate_description (dock); + + g_signal_emit (dock, dock_signals[BOOK_REMOVED], 0, dockbook); + + g_object_unref (dockbook); +} + +/** + * gimp_dock_temp_add: + * @dock: + * @widget: + * + * Method to temporarily add a widget to the dock, for example to make + * font-scale style property to be applied temporarily to the + * child. + **/ +void +gimp_dock_temp_add (GimpDock *dock, + GtkWidget *child) +{ + g_return_if_fail (GIMP_IS_DOCK (dock)); + g_return_if_fail (GTK_IS_WIDGET (child)); + + gtk_box_pack_start (GTK_BOX (dock->p->temp_vbox), child, FALSE, FALSE, 0); +} + +/** + * gimp_dock_temp_remove: + * @dock: + * @child: + * + * Removes a temporary child added with gimp_dock_temp_add(). + **/ +void +gimp_dock_temp_remove (GimpDock *dock, + GtkWidget *child) +{ + g_return_if_fail (GIMP_IS_DOCK (dock)); + g_return_if_fail (GTK_IS_WIDGET (child)); + + gtk_container_remove (GTK_CONTAINER (dock->p->temp_vbox), child); +} |