diff options
Diffstat (limited to 'app/gui/gimpuiconfigurer.c')
-rw-r--r-- | app/gui/gimpuiconfigurer.c | 622 |
1 files changed, 622 insertions, 0 deletions
diff --git a/app/gui/gimpuiconfigurer.c b/app/gui/gimpuiconfigurer.c new file mode 100644 index 0000000..dd9aa5d --- /dev/null +++ b/app/gui/gimpuiconfigurer.c @@ -0,0 +1,622 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpuiconfigurer.c + * 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 "gui-types.h" + +#include "core/gimp.h" +#include "core/gimpcontext.h" + +#include "widgets/gimpdialogfactory.h" +#include "widgets/gimpdock.h" +#include "widgets/gimpdockcolumns.h" +#include "widgets/gimpdockcontainer.h" +#include "widgets/gimpdockwindow.h" +#include "widgets/gimptoolbox.h" +#include "widgets/gimpwidgets-utils.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" +#include "display/gimpdisplayshell-appearance.h" +#include "display/gimpimagewindow.h" + +#include "gimpuiconfigurer.h" + + +enum +{ + PROP_0, + PROP_GIMP +}; + + +struct _GimpUIConfigurerPrivate +{ + Gimp *gimp; +}; + + +static void gimp_ui_configurer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_ui_configurer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_ui_configurer_move_docks_to_columns (GimpUIConfigurer *ui_configurer, + GimpImageWindow *uber_image_window); +static void gimp_ui_configurer_move_shells (GimpUIConfigurer *ui_configurer, + GimpImageWindow *source_image_window, + GimpImageWindow *target_image_window); +static void gimp_ui_configurer_separate_docks (GimpUIConfigurer *ui_configurer, + GimpImageWindow *source_image_window); +static void gimp_ui_configurer_move_docks_to_window (GimpUIConfigurer *ui_configurer, + GimpDockColumns *dock_columns, + GimpAlignmentType screen_side); +static void gimp_ui_configurer_separate_shells (GimpUIConfigurer *ui_configurer, + GimpImageWindow *source_image_window); +static void gimp_ui_configurer_configure_for_single_window (GimpUIConfigurer *ui_configurer); +static void gimp_ui_configurer_configure_for_multi_window (GimpUIConfigurer *ui_configurer); +static GimpImageWindow * gimp_ui_configurer_get_uber_window (GimpUIConfigurer *ui_configurer); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpUIConfigurer, gimp_ui_configurer, + GIMP_TYPE_OBJECT) + +#define parent_class gimp_ui_configurer_parent_class + + +static void +gimp_ui_configurer_class_init (GimpUIConfigurerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = gimp_ui_configurer_set_property; + object_class->get_property = gimp_ui_configurer_get_property; + + g_object_class_install_property (object_class, PROP_GIMP, + g_param_spec_object ("gimp", NULL, NULL, + GIMP_TYPE_GIMP, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gimp_ui_configurer_init (GimpUIConfigurer *ui_configurer) +{ + ui_configurer->p = gimp_ui_configurer_get_instance_private (ui_configurer); +} + +static void +gimp_ui_configurer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpUIConfigurer *ui_configurer = GIMP_UI_CONFIGURER (object); + + switch (property_id) + { + case PROP_GIMP: + ui_configurer->p->gimp = g_value_get_object (value); /* don't ref */ + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_ui_configurer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpUIConfigurer *ui_configurer = GIMP_UI_CONFIGURER (object); + + switch (property_id) + { + case PROP_GIMP: + g_value_set_object (value, ui_configurer->p->gimp); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +gimp_ui_configurer_get_window_center_pos (GtkWindow *window, + gint *out_x, + gint *out_y) +{ + gint x, y, w, h; + gtk_window_get_position (window, &x, &y); + gtk_window_get_size (window, &w, &h); + + if (out_x) + *out_x = x + w / 2; + if (out_y) + *out_y = y + h / 2; +} + +/** + * gimp_ui_configurer_get_relative_window_pos: + * @window_a: + * @window_b: + * + * Returns: At what side @window_b is relative to @window_a. Either + * GIMP_ALIGN_LEFT or GIMP_ALIGN_RIGHT. + **/ +static GimpAlignmentType +gimp_ui_configurer_get_relative_window_pos (GtkWindow *window_a, + GtkWindow *window_b) +{ + gint a_x, b_x; + + gimp_ui_configurer_get_window_center_pos (window_a, &a_x, NULL); + gimp_ui_configurer_get_window_center_pos (window_b, &b_x, NULL); + + return b_x < a_x ? GIMP_ALIGN_LEFT : GIMP_ALIGN_RIGHT; +} + +static void +gimp_ui_configurer_move_docks_to_columns (GimpUIConfigurer *ui_configurer, + GimpImageWindow *uber_image_window) +{ + GList *dialogs = NULL; + GList *dialog_iter = NULL; + + dialogs = + g_list_copy (gimp_dialog_factory_get_open_dialogs (gimp_dialog_factory_get_singleton ())); + + for (dialog_iter = dialogs; dialog_iter; dialog_iter = dialog_iter->next) + { + GimpDockWindow *dock_window; + GimpDockContainer *dock_container; + GimpDockColumns *dock_columns; + GList *docks; + GList *dock_iter; + + if (!GIMP_IS_DOCK_WINDOW (dialog_iter->data)) + continue; + + dock_window = GIMP_DOCK_WINDOW (dialog_iter->data); + + /* If the dock window is on the left side of the image window, + * move the docks to the left side. If the dock window is on the + * right side, move the docks to the right side of the image + * window. + */ + if (gimp_ui_configurer_get_relative_window_pos (GTK_WINDOW (uber_image_window), + GTK_WINDOW (dock_window)) == GIMP_ALIGN_LEFT) + dock_columns = gimp_image_window_get_left_docks (uber_image_window); + else + dock_columns = gimp_image_window_get_right_docks (uber_image_window); + + dock_container = GIMP_DOCK_CONTAINER (dock_window); + g_object_add_weak_pointer (G_OBJECT (dock_window), + (gpointer) &dock_window); + + docks = gimp_dock_container_get_docks (dock_container); + for (dock_iter = docks; dock_iter; dock_iter = dock_iter->next) + { + GimpDock *dock = GIMP_DOCK (dock_iter->data); + + /* Move the dock from the image window to the dock columns + * widget. Note that we need a ref while the dock is parentless + */ + g_object_ref (dock); + gimp_dock_window_remove_dock (dock_window, dock); + gimp_dock_columns_add_dock (dock_columns, dock, -1); + g_object_unref (dock); + } + g_list_free (docks); + + if (dock_window) + g_object_remove_weak_pointer (G_OBJECT (dock_window), + (gpointer) &dock_window); + + /* Kill the window if removing the dock didn't destroy it + * already. This will be the case for the toolbox dock window + */ + if (GTK_IS_WIDGET (dock_window)) + { + guint docks_len; + + docks = gimp_dock_container_get_docks (dock_container); + docks_len = g_list_length (docks); + + if (docks_len == 0) + { + gimp_dialog_factory_remove_dialog (gimp_dialog_factory_get_singleton (), + GTK_WIDGET (dock_window)); + gtk_widget_destroy (GTK_WIDGET (dock_window)); + } + + g_list_free (docks); + } + } + + g_list_free (dialogs); +} + +/** + * gimp_ui_configurer_move_shells: + * @ui_configurer: + * @source_image_window: + * @target_image_window: + * + * Move all display shells from one image window to the another. + **/ +static void +gimp_ui_configurer_move_shells (GimpUIConfigurer *ui_configurer, + GimpImageWindow *source_image_window, + GimpImageWindow *target_image_window) +{ + while (gimp_image_window_get_n_shells (source_image_window) > 0) + { + GimpDisplayShell *shell; + + shell = gimp_image_window_get_shell (source_image_window, 0); + + g_object_ref (shell); + gimp_image_window_remove_shell (source_image_window, shell); + gimp_image_window_add_shell (target_image_window, shell); + g_object_unref (shell); + } +} + +/** + * gimp_ui_configurer_separate_docks: + * @ui_configurer: + * @image_window: + * + * Move out the docks from the image window. + **/ +static void +gimp_ui_configurer_separate_docks (GimpUIConfigurer *ui_configurer, + GimpImageWindow *image_window) +{ + GimpDockColumns *left_docks = NULL; + GimpDockColumns *right_docks = NULL; + + left_docks = gimp_image_window_get_left_docks (image_window); + right_docks = gimp_image_window_get_right_docks (image_window); + + gimp_ui_configurer_move_docks_to_window (ui_configurer, left_docks, GIMP_ALIGN_LEFT); + gimp_ui_configurer_move_docks_to_window (ui_configurer, right_docks, GIMP_ALIGN_RIGHT); +} + +/** + * gimp_ui_configurer_move_docks_to_window: + * @dock_columns: + * @screen_side: At what side of the screen the dock window should be put. + * + * Moves docks in @dock_columns into a new #GimpDockWindow and + * position it on the screen in a non-overlapping manner. + */ +static void +gimp_ui_configurer_move_docks_to_window (GimpUIConfigurer *ui_configurer, + GimpDockColumns *dock_columns, + GimpAlignmentType screen_side) +{ + GdkScreen *screen; + gint monitor; + GdkRectangle monitor_rect; + GList *docks; + GList *iter; + gboolean contains_toolbox = FALSE; + GtkWidget *dock_window; + GtkAllocation original_size; + gchar geometry[32]; + + docks = g_list_copy (gimp_dock_columns_get_docks (dock_columns)); + if (! docks) + return; + + screen = gtk_widget_get_screen (GTK_WIDGET (dock_columns)); + monitor = gimp_widget_get_monitor (GTK_WIDGET (dock_columns)); + + gdk_screen_get_monitor_workarea (screen, monitor, &monitor_rect); + + /* Remember the size so we can set the new dock window to the same + * size + */ + gtk_widget_get_allocation (GTK_WIDGET (dock_columns), &original_size); + + /* Do we need a toolbox window? */ + for (iter = docks; iter; iter = g_list_next (iter)) + { + GimpDock *dock = GIMP_DOCK (iter->data); + + if (GIMP_IS_TOOLBOX (dock)) + { + contains_toolbox = TRUE; + break; + } + } + + /* Create a dock window to put the dock in. Checking for + * GIMP_IS_TOOLBOX() is kind of ugly but not a disaster. We need + * the dock window correctly configured if we create it for the + * toolbox + */ + dock_window = + gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (), + screen, + monitor, + NULL /*ui_manager*/, + (contains_toolbox ? + "gimp-toolbox-window" : + "gimp-dock-window"), + -1 /*view_size*/, + FALSE /*present*/); + + for (iter = docks; iter; iter = g_list_next (iter)) + { + GimpDock *dock = GIMP_DOCK (iter->data); + + /* Move the dock to the window */ + g_object_ref (dock); + gimp_dock_columns_remove_dock (dock_columns, dock); + gimp_dock_window_add_dock (GIMP_DOCK_WINDOW (dock_window), dock, -1); + g_object_unref (dock); + } + + /* Position the window */ + if (screen_side == GIMP_ALIGN_LEFT) + { + g_snprintf (geometry, sizeof (geometry), "%+d%+d", + monitor_rect.x, + monitor_rect.y); + } + else if (screen_side == GIMP_ALIGN_RIGHT) + { + g_snprintf (geometry, sizeof (geometry), "%+d%+d", + monitor_rect.x + monitor_rect.width - original_size.width, + monitor_rect.y); + } + else + { + gimp_assert_not_reached (); + } + + gtk_window_parse_geometry (GTK_WINDOW (dock_window), geometry); + + /* Try to keep the same size */ + gtk_window_set_default_size (GTK_WINDOW (dock_window), + original_size.width, + original_size.height); + + /* Don't forget to show the window */ + gtk_widget_show (dock_window); + + g_list_free (docks); +} + +/** + * gimp_ui_configurer_separate_shells: + * @ui_configurer: + * @source_image_window: + * + * Create one image window per display shell and move it there. + **/ +static void +gimp_ui_configurer_separate_shells (GimpUIConfigurer *ui_configurer, + GimpImageWindow *source_image_window) +{ + GimpDisplayShell *active_shell = gimp_image_window_get_active_shell (source_image_window); + GimpImageWindow *active_window = NULL; + + /* The last display shell remains in its window */ + while (gimp_image_window_get_n_shells (source_image_window) > 1) + { + GimpImageWindow *new_image_window; + GimpDisplayShell *shell; + + /* Create a new image window */ + new_image_window = gimp_image_window_new (ui_configurer->p->gimp, + NULL, + gimp_dialog_factory_get_singleton (), + gtk_widget_get_screen (GTK_WIDGET (source_image_window)), + gimp_widget_get_monitor (GTK_WIDGET (source_image_window))); + /* Move the shell there */ + shell = gimp_image_window_get_shell (source_image_window, 1); + + if (shell == active_shell) + active_window = new_image_window; + + g_object_ref (shell); + gimp_image_window_remove_shell (source_image_window, shell); + gimp_image_window_add_shell (new_image_window, shell); + g_object_unref (shell); + + /* FIXME: If we don't set a size request here the window will be + * too small. Get rid of this hack and fix it the proper way + */ + gtk_widget_set_size_request (GTK_WIDGET (new_image_window), 640, 480); + + /* Show after we have added the shell */ + gtk_widget_show (GTK_WIDGET (new_image_window)); + } + + /* If none of the shells were active, I assume the first one is. */ + if (active_window == NULL) + active_window = source_image_window; + + /* The active tab must stay at the top of the windows stack. */ + gtk_window_present (GTK_WINDOW (active_window)); +} + +/** + * gimp_ui_configurer_configure_for_single_window: + * @ui_configurer: + * + * Move docks and display shells into a single window. + **/ +static void +gimp_ui_configurer_configure_for_single_window (GimpUIConfigurer *ui_configurer) +{ + Gimp *gimp = ui_configurer->p->gimp; + GList *windows = gimp_get_image_windows (gimp); + GList *iter = NULL; + GimpImageWindow *uber_image_window = NULL; + GimpDisplay *active_display = gimp_context_get_display (gimp_get_user_context (gimp)); + GimpDisplayShell *active_shell = gimp_display_get_shell (active_display); + + /* Get and setup the window to put everything in */ + uber_image_window = gimp_ui_configurer_get_uber_window (ui_configurer); + + /* Mve docks to the left and right side of the image window */ + gimp_ui_configurer_move_docks_to_columns (ui_configurer, + uber_image_window); + + /* Move image shells from other windows to the uber image window */ + for (iter = windows; iter; iter = g_list_next (iter)) + { + GimpImageWindow *image_window = GIMP_IMAGE_WINDOW (iter->data); + + /* Don't move stuff to itself */ + if (image_window == uber_image_window) + continue; + + /* Put the displays in the rest of the image windows into + * the uber image window + */ + gimp_ui_configurer_move_shells (ui_configurer, + image_window, + uber_image_window); + /* Destroy the window */ + gimp_image_window_destroy (image_window); + } + + /* Ensure the context shell remains active after mode switch. */ + gimp_image_window_set_active_shell (uber_image_window, active_shell); + + g_list_free (windows); +} + +/** + * gimp_ui_configurer_configure_for_multi_window: + * @ui_configurer: + * + * Moves all display shells into their own image window. + **/ +static void +gimp_ui_configurer_configure_for_multi_window (GimpUIConfigurer *ui_configurer) +{ + Gimp *gimp = ui_configurer->p->gimp; + GList *windows = gimp_get_image_windows (gimp); + GList *iter = NULL; + + for (iter = windows; iter; iter = g_list_next (iter)) + { + GimpImageWindow *image_window = GIMP_IMAGE_WINDOW (iter->data); + + gimp_ui_configurer_separate_docks (ui_configurer, image_window); + + gimp_ui_configurer_separate_shells (ui_configurer, image_window); + } + + g_list_free (windows); +} + +/** + * gimp_ui_configurer_get_uber_window: + * @ui_configurer: + * + * Returns: The window to be used as the main window for single-window + * mode. + **/ +static GimpImageWindow * +gimp_ui_configurer_get_uber_window (GimpUIConfigurer *ui_configurer) +{ + Gimp *gimp = ui_configurer->p->gimp; + GimpDisplay *display = gimp_get_display_iter (gimp)->data; + GimpDisplayShell *shell = gimp_display_get_shell (display); + GimpImageWindow *image_window = gimp_display_shell_get_window (shell); + + return image_window; +} + +/** + * gimp_ui_configurer_update_appearance: + * @ui_configurer: + * + * Updates the appearance of all shells in all image windows, so they + * do whatever they deem necessary to fit the new UI mode mode. + **/ +static void +gimp_ui_configurer_update_appearance (GimpUIConfigurer *ui_configurer) +{ + Gimp *gimp = ui_configurer->p->gimp; + GList *windows = gimp_get_image_windows (gimp); + GList *list; + + for (list = windows; list; list = g_list_next (list)) + { + GimpImageWindow *image_window = GIMP_IMAGE_WINDOW (list->data); + gint n_shells; + gint i; + + n_shells = gimp_image_window_get_n_shells (image_window); + + for (i = 0; i < n_shells; i++) + { + GimpDisplayShell *shell; + + shell = gimp_image_window_get_shell (image_window, i); + + gimp_display_shell_appearance_update (shell); + } + } + + g_list_free (windows); +} + +/** + * gimp_ui_configurer_configure: + * @ui_configurer: + * @single_window_mode: + * + * Configure the UI. + **/ +void +gimp_ui_configurer_configure (GimpUIConfigurer *ui_configurer, + gboolean single_window_mode) +{ + if (single_window_mode) + gimp_ui_configurer_configure_for_single_window (ui_configurer); + else + gimp_ui_configurer_configure_for_multi_window (ui_configurer); + + gimp_ui_configurer_update_appearance (ui_configurer); +} |