Files
gimp/app/display/gimpdisplayshell.c
Michael Natterer cee3baea0f First draft of the "no image open" window, which is implemented as a
2008-03-18  Michael Natterer  <mitch@gimp.org>

	First draft of the "no image open" window, which is implemented as
	a display without image (a view with NULL model). Didn't change
	the display's appearance yet so I can first make sure the display
	without image works properly in all details before hiding these
	details.

	* app/core/gimp-gui.[ch]: add "gimp" parameter to display_create()
	and allow "image" to be NULL.

	* app/core/gimpcontext.c (gimp_context_real_set_display): a
	display's image can be NULL now.

	* app/display/gimpdisplay.[ch]: add Gimp and GimpDisplayConfig
	members.  Add Gimp parameter to gimp_display_shell_new(). Changed
	gimp_display_reconnect() to gimp_display_set_image() and allow to
	set a NULL image.

	* app/gui/gui-vtable.c (gui_display_create): if there is a single
	display without an image, call gimp_display_set_image() on that
	display instead of creating a new one.

	* app/display/gimpdisplayshell-close.c: if the last display is
	closed, don't close it but make it empty. Factored out that code
	to gimp_display_shell_really_close().

	* app/display/gimpdisplayshell-dnd.c: when dropping uris on an
	empty display, open the first one into that display and the other
	ones as layers of the newly opened image. This is consistent with
	dropping on an existing image but maybe needs some discussion.

	* app/display/gimpdisplayshell-callbacks.c: bail out early in the
	tool event callback so tools never have to deal with empty
	displays. In expose(), draw the drop zone on the empty display.

	* app/display/gimpdisplayshell-title.c: set the empty display's
	title to "Gimp - Drop Files".

	* app/display/gimpdisplay-foreach.c
	* app/display/gimpdisplay-handlers.c
	* app/display/gimpdisplayshell-appearance.c
	* app/display/gimpdisplayshell-autoscroll.c
	* app/display/gimpdisplayshell-callbacks.c
	* app/display/gimpdisplayshell-cursor.c
	* app/display/gimpdisplayshell-dnd.c
	* app/display/gimpdisplayshell-draw.c
	* app/display/gimpdisplayshell-filter-dialog.c
	* app/display/gimpdisplayshell-handlers.c
	* app/display/gimpdisplayshell-layer-select.c
	* app/display/gimpdisplayshell-preview.c
	* app/display/gimpdisplayshell-render.c
	* app/display/gimpdisplayshell-scale.c
	* app/display/gimpdisplayshell-scroll.c
	* app/display/gimpdisplayshell-selection.c
	* app/display/gimpdisplayshell-title.c
	* app/display/gimpdisplayshell.c
	* app/display/gimpnavigationeditor.c
	* app/display/gimpstatusbar.c: use display->gimp and
	display->config instead of going via the image. Guard against
	empty displays in some few places (most places can't be
	called). Where needed, use the canvas' dimensions instead of the
	image's dimensions so scroll offsets and scrollbars still have
	sane values instead of the last image's ones.

	* app/actions/actions.c (action_data_get_gimp)
	(action_data_get_context): use display->gimp instead of
	display->image->gimp.

	* app/actions/edit-commands.c (edit_paste_cmd_callback): redirect
	to "paste as new" if there is an empty display.

	* app/actions/tools-commands.c (tools_select_cmd_callback): don't
	initialize the new tool on an empty display.

	* app/actions/view-actions.c (view_actions_update): changed lots
	of sensitivity settings to be insensitive when there is no image
	(instead of no display).

	* app/actions/view-commands.c: use the display's config object
	instead of gimp's.


svn path=/trunk/; revision=25113
2008-03-18 21:22:21 +00:00

1755 lines
57 KiB
C

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpcolor/gimpcolor.h"
#include "libgimpconfig/gimpconfig.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "display-types.h"
#include "tools/tools-types.h"
#include "config/gimpcoreconfig.h"
#include "config/gimpdisplayconfig.h"
#include "core/gimp.h"
#include "core/gimpchannel.h"
#include "core/gimpcontext.h"
#include "core/gimpguide.h"
#include "core/gimpimage.h"
#include "core/gimpimage-grid.h"
#include "core/gimpimage-guides.h"
#include "core/gimpimage-snap.h"
#include "core/gimpprojection.h"
#include "core/gimpmarshal.h"
#include "core/gimpsamplepoint.h"
#include "widgets/gimphelp-ids.h"
#include "widgets/gimpmenufactory.h"
#include "widgets/gimpuimanager.h"
#include "widgets/gimpwidgets-utils.h"
#include "tools/tool_manager.h"
#include "gimpcanvas.h"
#include "gimpdisplay.h"
#include "gimpdisplayoptions.h"
#include "gimpdisplayshell.h"
#include "gimpdisplayshell-appearance.h"
#include "gimpdisplayshell-callbacks.h"
#include "gimpdisplayshell-close.h"
#include "gimpdisplayshell-cursor.h"
#include "gimpdisplayshell-dnd.h"
#include "gimpdisplayshell-draw.h"
#include "gimpdisplayshell-filter.h"
#include "gimpdisplayshell-handlers.h"
#include "gimpdisplayshell-progress.h"
#include "gimpdisplayshell-scale.h"
#include "gimpdisplayshell-selection.h"
#include "gimpdisplayshell-title.h"
#include "gimpdisplayshell-transform.h"
#include "gimpstatusbar.h"
#include "gimp-intl.h"
enum
{
PROP_0,
PROP_UNIT
};
enum
{
SCALED,
SCROLLED,
RECONNECT,
LAST_SIGNAL
};
/* local function prototypes */
static void gimp_color_managed_iface_init (GimpColorManagedInterface *iface);
static void gimp_display_shell_finalize (GObject *object);
static void gimp_display_shell_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_display_shell_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_display_shell_destroy (GtkObject *object);
static void gimp_display_shell_unrealize (GtkWidget *widget);
static void gimp_display_shell_screen_changed (GtkWidget *widget,
GdkScreen *previous);
static gboolean gimp_display_shell_delete_event (GtkWidget *widget,
GdkEventAny *aevent);
static gboolean gimp_display_shell_popup_menu (GtkWidget *widget);
static void gimp_display_shell_real_scaled (GimpDisplayShell *shell);
static void gimp_display_shell_menu_position (GtkMenu *menu,
gint *x,
gint *y,
gpointer data);
static void gimp_display_shell_show_tooltip (GimpUIManager *manager,
const gchar *tooltip,
GimpDisplayShell *shell);
static void gimp_display_shell_hide_tooltip (GimpUIManager *manager,
GimpDisplayShell *shell);
static const guint8 * gimp_display_shell_get_icc_profile
(GimpColorManaged *managed,
gsize *len);
G_DEFINE_TYPE_WITH_CODE (GimpDisplayShell, gimp_display_shell, GTK_TYPE_WINDOW,
G_IMPLEMENT_INTERFACE (GIMP_TYPE_PROGRESS,
gimp_display_shell_progress_iface_init)
G_IMPLEMENT_INTERFACE (GIMP_TYPE_COLOR_MANAGED,
gimp_color_managed_iface_init))
#define parent_class gimp_display_shell_parent_class
static guint display_shell_signals[LAST_SIGNAL] = { 0 };
static const gchar display_rc_style[] =
"style \"fullscreen-menubar-style\"\n"
"{\n"
" GtkMenuBar::shadow-type = none\n"
" GtkMenuBar::internal-padding = 0\n"
"}\n"
"widget \"*.gimp-menubar-fullscreen\" style \"fullscreen-menubar-style\"\n"
"\n"
"style \"check-button-style\"\n"
"{\n"
" GtkToggleButton::child-displacement-x = 0\n"
" GtkToggleButton::child-displacement-y = 0\n"
"}\n"
"widget \"*\" style \"check-button-style\"";
static void
gimp_display_shell_class_init (GimpDisplayShellClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
display_shell_signals[SCALED] =
g_signal_new ("scaled",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpDisplayShellClass, scaled),
NULL, NULL,
gimp_marshal_VOID__VOID,
G_TYPE_NONE, 0);
display_shell_signals[SCROLLED] =
g_signal_new ("scrolled",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpDisplayShellClass, scrolled),
NULL, NULL,
gimp_marshal_VOID__VOID,
G_TYPE_NONE, 0);
display_shell_signals[RECONNECT] =
g_signal_new ("reconnect",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpDisplayShellClass, reconnect),
NULL, NULL,
gimp_marshal_VOID__VOID,
G_TYPE_NONE, 0);
object_class->finalize = gimp_display_shell_finalize;
object_class->set_property = gimp_display_shell_set_property;
object_class->get_property = gimp_display_shell_get_property;
gtk_object_class->destroy = gimp_display_shell_destroy;
widget_class->unrealize = gimp_display_shell_unrealize;
widget_class->screen_changed = gimp_display_shell_screen_changed;
widget_class->delete_event = gimp_display_shell_delete_event;
widget_class->popup_menu = gimp_display_shell_popup_menu;
klass->scaled = gimp_display_shell_real_scaled;
klass->scrolled = NULL;
klass->reconnect = NULL;
g_object_class_install_property (object_class, PROP_UNIT,
gimp_param_spec_unit ("unit", NULL, NULL,
TRUE, FALSE,
GIMP_UNIT_PIXEL,
GIMP_PARAM_READWRITE));
gtk_rc_parse_string (display_rc_style);
}
static void
gimp_display_shell_init (GimpDisplayShell *shell)
{
shell->display = NULL;
shell->menubar_manager = NULL;
shell->popup_manager = NULL;
shell->unit = GIMP_UNIT_PIXEL;
shell->zoom = gimp_zoom_model_new ();
shell->other_scale = 0.0;
shell->dot_for_dot = TRUE;
shell->offset_x = 0;
shell->offset_y = 0;
shell->scale_x = 1.0;
shell->scale_y = 1.0;
shell->x_dest_inc = 1;
shell->y_dest_inc = 1;
shell->x_src_dec = 1;
shell->y_src_dec = 1;
shell->last_scale = 0.0;
shell->last_scale_time = 0;
shell->last_offset_x = 0;
shell->last_offset_y = 0;
shell->disp_width = 0;
shell->disp_height = 0;
shell->disp_xoffset = 0;
shell->disp_yoffset = 0;
shell->proximity = FALSE;
shell->snap_to_guides = TRUE;
shell->snap_to_grid = FALSE;
shell->snap_to_canvas = FALSE;
shell->snap_to_vectors = FALSE;
shell->selection = NULL;
shell->canvas = NULL;
shell->grid_gc = NULL;
shell->pen_gc = NULL;
shell->hsbdata = NULL;
shell->vsbdata = NULL;
shell->hsb = NULL;
shell->vsb = NULL;
shell->hrule = NULL;
shell->vrule = NULL;
shell->origin = NULL;
shell->quick_mask_button = NULL;
shell->zoom_button = NULL;
shell->nav_ebox = NULL;
shell->menubar = NULL;
shell->statusbar = NULL;
shell->render_buf = g_new (guchar,
GIMP_DISPLAY_RENDER_BUF_WIDTH *
GIMP_DISPLAY_RENDER_BUF_HEIGHT * 3);
shell->title_idle_id = 0;
shell->icon_size = 32;
shell->icon_idle_id = 0;
shell->cursor_format = GIMP_CURSOR_FORMAT_BITMAP;
shell->current_cursor = (GimpCursorType) -1;
shell->tool_cursor = GIMP_TOOL_CURSOR_NONE;
shell->cursor_modifier = GIMP_CURSOR_MODIFIER_NONE;
shell->override_cursor = (GimpCursorType) -1;
shell->using_override_cursor = FALSE;
shell->draw_cursor = FALSE;
shell->have_cursor = FALSE;
shell->cursor_x = 0;
shell->cursor_y = 0;
shell->close_dialog = NULL;
shell->scale_dialog = NULL;
shell->nav_popup = NULL;
shell->grid_dialog = NULL;
shell->filter_stack = NULL;
shell->filter_idle_id = 0;
shell->filters_dialog = NULL;
shell->paused_count = 0;
shell->window_state = 0;
shell->zoom_on_resize = FALSE;
shell->show_transform_preview = FALSE;
shell->options = g_object_new (GIMP_TYPE_DISPLAY_OPTIONS, NULL);
shell->fullscreen_options = g_object_new (GIMP_TYPE_DISPLAY_OPTIONS_FULLSCREEN, NULL);
shell->space_pressed = FALSE;
shell->space_release_pending = FALSE;
shell->space_shaded_tool = NULL;
shell->scrolling = FALSE;
shell->scroll_start_x = 0;
shell->scroll_start_y = 0;
shell->button_press_before_focus = FALSE;
shell->highlight = NULL;
shell->mask = NULL;
gtk_window_set_role (GTK_WINDOW (shell), "gimp-image-window");
gtk_window_set_resizable (GTK_WINDOW (shell), TRUE);
gtk_widget_set_events (GTK_WIDGET (shell), (GDK_POINTER_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_KEY_PRESS_MASK |
GDK_KEY_RELEASE_MASK |
GDK_FOCUS_CHANGE_MASK |
GDK_VISIBILITY_NOTIFY_MASK |
GDK_SCROLL_MASK));
/* zoom model callback */
g_signal_connect_swapped (shell->zoom, "zoomed",
G_CALLBACK (gimp_display_shell_scale_changed),
shell);
/* active display callback */
g_signal_connect (shell, "button-press-event",
G_CALLBACK (gimp_display_shell_events),
shell);
g_signal_connect (shell, "button-release-event",
G_CALLBACK (gimp_display_shell_events),
shell);
g_signal_connect (shell, "key-press-event",
G_CALLBACK (gimp_display_shell_events),
shell);
g_signal_connect (shell, "window-state-event",
G_CALLBACK (gimp_display_shell_events),
shell);
gimp_display_shell_dnd_init (shell);
gimp_help_connect (GTK_WIDGET (shell), gimp_standard_help_func,
GIMP_HELP_IMAGE_WINDOW, NULL);
}
static void
gimp_color_managed_iface_init (GimpColorManagedInterface *iface)
{
iface->get_icc_profile = gimp_display_shell_get_icc_profile;
}
static void
gimp_display_shell_finalize (GObject *object)
{
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (object);
g_object_unref (shell->zoom);
if (shell->options)
g_object_unref (shell->options);
if (shell->fullscreen_options)
g_object_unref (shell->fullscreen_options);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_display_shell_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (object);
switch (property_id)
{
case PROP_UNIT:
gimp_display_shell_set_unit (shell, g_value_get_int (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_display_shell_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (object);
switch (property_id)
{
case PROP_UNIT:
g_value_set_int (value, shell->unit);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_display_shell_destroy (GtkObject *object)
{
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (object);
if (shell->display && shell->display->image)
gimp_display_shell_disconnect (shell);
if (shell->menubar_manager)
{
g_object_unref (shell->menubar_manager);
shell->menubar_manager = NULL;
}
shell->popup_manager = NULL;
gimp_display_shell_selection_free (shell);
if (shell->filter_stack)
gimp_display_shell_filter_set (shell, NULL);
if (shell->filter_idle_id)
{
g_source_remove (shell->filter_idle_id);
shell->filter_idle_id = 0;
}
if (shell->render_buf)
{
g_free (shell->render_buf);
shell->render_buf = NULL;
}
if (shell->highlight)
{
g_slice_free (GdkRectangle, shell->highlight);
shell->highlight = NULL;
}
if (shell->mask)
{
g_object_unref (shell->mask);
shell->mask = NULL;
}
if (shell->title_idle_id)
{
g_source_remove (shell->title_idle_id);
shell->title_idle_id = 0;
}
if (shell->nav_popup)
{
gtk_widget_destroy (shell->nav_popup);
shell->nav_popup = NULL;
}
if (shell->grid_dialog)
{
gtk_widget_destroy (shell->grid_dialog);
shell->grid_dialog = NULL;
}
shell->display = NULL;
GTK_OBJECT_CLASS (parent_class)->destroy (object);
}
static void
gimp_display_shell_unrealize (GtkWidget *widget)
{
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (widget);
if (shell->grid_gc)
{
g_object_unref (shell->grid_gc);
shell->grid_gc = NULL;
}
if (shell->pen_gc)
{
g_object_unref (shell->pen_gc);
shell->pen_gc = NULL;
}
if (shell->nav_popup)
gtk_widget_unrealize (shell->nav_popup);
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}
static void
gimp_display_shell_screen_changed (GtkWidget *widget,
GdkScreen *previous)
{
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (widget);
if (GTK_WIDGET_CLASS (parent_class)->screen_changed)
GTK_WIDGET_CLASS (parent_class)->screen_changed (widget, previous);
if (shell->display->config->monitor_res_from_gdk)
{
gimp_get_screen_resolution (gtk_widget_get_screen (widget),
&shell->monitor_xres,
&shell->monitor_yres);
}
else
{
shell->monitor_xres = shell->display->config->monitor_xres;
shell->monitor_yres = shell->display->config->monitor_yres;
}
}
static gboolean
gimp_display_shell_delete_event (GtkWidget *widget,
GdkEventAny *aevent)
{
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (widget);
gimp_display_shell_close (shell, FALSE);
return TRUE;
}
static gboolean
gimp_display_shell_popup_menu (GtkWidget *widget)
{
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (widget);
gimp_context_set_display (gimp_get_user_context (shell->display->gimp),
shell->display);
gimp_ui_manager_ui_popup (shell->popup_manager, "/dummy-menubar/image-popup",
GTK_WIDGET (shell),
gimp_display_shell_menu_position,
shell->origin,
NULL, NULL);
return TRUE;
}
static void
gimp_display_shell_real_scaled (GimpDisplayShell *shell)
{
GimpContext *user_context;
if (! shell->display)
return;
gimp_display_shell_title_update (shell);
/* update the <Image>/View/Zoom menu */
gimp_ui_manager_update (shell->menubar_manager, shell->display);
user_context = gimp_get_user_context (shell->display->gimp);
if (shell->display == gimp_context_get_display (user_context))
gimp_ui_manager_update (shell->popup_manager, shell->display);
}
static void
gimp_display_shell_menu_position (GtkMenu *menu,
gint *x,
gint *y,
gpointer data)
{
gimp_button_menu_position (GTK_WIDGET (data), menu, GTK_POS_RIGHT, x, y);
}
static void
gimp_display_shell_show_tooltip (GimpUIManager *manager,
const gchar *tooltip,
GimpDisplayShell *shell)
{
gimp_statusbar_push (GIMP_STATUSBAR (shell->statusbar), "menu-tooltip",
"%s", tooltip);
}
static void
gimp_display_shell_hide_tooltip (GimpUIManager *manager,
GimpDisplayShell *shell)
{
gimp_statusbar_pop (GIMP_STATUSBAR (shell->statusbar), "menu-tooltip");
}
static const guint8 *
gimp_display_shell_get_icc_profile (GimpColorManaged *managed,
gsize *len)
{
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (managed);
GimpImage *image = shell->display->image;
if (image)
return gimp_color_managed_get_icc_profile (GIMP_COLOR_MANAGED (image), len);
return NULL;
}
/* public functions */
GtkWidget *
gimp_display_shell_new (GimpDisplay *display,
GimpUnit unit,
gdouble scale,
GimpMenuFactory *menu_factory,
GimpUIManager *popup_manager)
{
GimpDisplayShell *shell;
GimpColorDisplayStack *filter;
GtkWidget *main_vbox;
GtkWidget *disp_vbox;
GtkWidget *upper_hbox;
GtkWidget *right_vbox;
GtkWidget *lower_hbox;
GtkWidget *inner_table;
GtkWidget *image;
GdkScreen *screen;
GtkAction *action;
gint image_width, image_height;
gint n_width, n_height;
gint s_width, s_height;
gdouble new_scale;
g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL);
g_return_val_if_fail (GIMP_IS_MENU_FACTORY (menu_factory), NULL);
g_return_val_if_fail (GIMP_IS_UI_MANAGER (popup_manager), NULL);
/* the toplevel shell */
shell = g_object_new (GIMP_TYPE_DISPLAY_SHELL,
/* "gravity", GDK_GRAVITY_CENTER, */
"unit", unit,
NULL);
shell->display = display;
image_width = gimp_image_get_width (display->image);
image_height = gimp_image_get_height (display->image);
shell->dot_for_dot = shell->display->config->default_dot_for_dot;
gimp_config_sync (G_OBJECT (shell->display->config->default_view),
G_OBJECT (shell->options), 0);
gimp_config_sync (G_OBJECT (shell->display->config->default_fullscreen_view),
G_OBJECT (shell->fullscreen_options), 0);
gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO, scale);
/* adjust the initial scale -- so that window fits on screen the 75%
* value is the same as in gimp_display_shell_shrink_wrap. It
* probably should be a user-configurable option.
*/
screen = gtk_widget_get_screen (GTK_WIDGET (shell));
if (shell->display->config->monitor_res_from_gdk)
{
gimp_get_screen_resolution (screen,
&shell->monitor_xres, &shell->monitor_yres);
}
else
{
shell->monitor_xres = shell->display->config->monitor_xres;
shell->monitor_yres = shell->display->config->monitor_yres;
}
s_width = gdk_screen_get_width (screen) * 0.75;
s_height = gdk_screen_get_height (screen) * 0.75;
n_width = SCALEX (shell, image_width);
n_height = SCALEY (shell, image_height);
if (shell->display->config->initial_zoom_to_fit)
{
/* Limit to the size of the screen... */
if (n_width > s_width || n_height > s_height)
{
gdouble current = gimp_zoom_model_get_factor (shell->zoom);
new_scale = current * MIN (((gdouble) s_height) / n_height,
((gdouble) s_width) / n_width);
new_scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_OUT, new_scale);
/* Since zooming out might skip a zoom step we zoom in again
* and test if we are small enough.
*/
gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO,
gimp_zoom_model_zoom_step (GIMP_ZOOM_IN,
new_scale));
if (SCALEX (shell, image_width) > s_width ||
SCALEY (shell, image_height) > s_height)
gimp_zoom_model_zoom (shell->zoom, GIMP_ZOOM_TO, new_scale);
n_width = SCALEX (shell, image_width);
n_height = SCALEY (shell, image_height);
}
}
else
{
/* Set up size like above, but do not zoom to fit.
Useful when working on large images. */
if (n_width > s_width)
n_width = s_width;
if (n_height > s_height)
n_height = s_height;
}
shell->menubar_manager = gimp_menu_factory_manager_new (menu_factory,
"<Image>",
display,
FALSE);
shell->popup_manager = popup_manager;
gtk_window_add_accel_group (GTK_WINDOW (shell),
gtk_ui_manager_get_accel_group (GTK_UI_MANAGER (shell->menubar_manager)));
g_signal_connect (shell->menubar_manager, "show-tooltip",
G_CALLBACK (gimp_display_shell_show_tooltip),
shell);
g_signal_connect (shell->menubar_manager, "hide-tooltip",
G_CALLBACK (gimp_display_shell_hide_tooltip),
shell);
/* GtkTable widgets are not able to shrink a row/column correctly if
* widgets are attached with GTK_EXPAND even if those widgets have
* other rows/columns in their rowspan/colspan where they could
* nicely expand without disturbing the row/column which is supposed
* to shrink. --Mitch
*
* Changed the packing to use hboxes and vboxes which behave nicer:
*
* main_vbox
* |
* +-- menubar
* |
* +-- disp_vbox
* | |
* | +-- upper_hbox
* | | |
* | | +-- inner_table
* | | | |
* | | | +-- origin
* | | | +-- hruler
* | | | +-- vruler
* | | | +-- canvas
* | | |
* | | +-- right_vbox
* | | |
* | | +-- zoom_on_resize_button
* | | +-- vscrollbar
* | |
* | +-- lower_hbox
* | |
* | +-- quick_mask
* | +-- hscrollbar
* | +-- navbutton
* |
* +-- statusbar
*/
/* first, set up the container hierarchy *********************************/
/* the vbox containing all widgets */
main_vbox = gtk_vbox_new (FALSE, 1);
gtk_container_add (GTK_CONTAINER (shell), main_vbox);
#ifndef GDK_WINDOWING_QUARTZ
shell->menubar =
gtk_ui_manager_get_widget (GTK_UI_MANAGER (shell->menubar_manager),
"/image-menubar");
#endif /* !GDK_WINDOWING_QUARTZ */
if (shell->menubar)
{
gtk_box_pack_start (GTK_BOX (main_vbox), shell->menubar, FALSE, FALSE, 0);
if (shell->options->show_menubar)
gtk_widget_show (shell->menubar);
else
gtk_widget_hide (shell->menubar);
/* make sure we can activate accels even if the menubar is invisible
* (see http://bugzilla.gnome.org/show_bug.cgi?id=137151)
*/
g_signal_connect (shell->menubar, "can-activate-accel",
G_CALLBACK (gtk_true),
NULL);
/* active display callback */
g_signal_connect (shell->menubar, "button-press-event",
G_CALLBACK (gimp_display_shell_events),
shell);
g_signal_connect (shell->menubar, "button-release-event",
G_CALLBACK (gimp_display_shell_events),
shell);
g_signal_connect (shell->menubar, "key-press-event",
G_CALLBACK (gimp_display_shell_events),
shell);
}
/* another vbox for everything except the statusbar */
disp_vbox = gtk_vbox_new (FALSE, 1);
gtk_box_pack_start (GTK_BOX (main_vbox), disp_vbox, TRUE, TRUE, 0);
gtk_widget_show (disp_vbox);
/* a hbox for the inner_table and the vertical scrollbar */
upper_hbox = gtk_hbox_new (FALSE, 1);
gtk_box_pack_start (GTK_BOX (disp_vbox), upper_hbox, TRUE, TRUE, 0);
gtk_widget_show (upper_hbox);
/* the table containing origin, rulers and the canvas */
inner_table = gtk_table_new (2, 2, FALSE);
gtk_table_set_col_spacing (GTK_TABLE (inner_table), 0, 1);
gtk_table_set_row_spacing (GTK_TABLE (inner_table), 0, 1);
gtk_box_pack_start (GTK_BOX (upper_hbox), inner_table, TRUE, TRUE, 0);
gtk_widget_show (inner_table);
/* the vbox containing the color button and the vertical scrollbar */
right_vbox = gtk_vbox_new (FALSE, 1);
gtk_box_pack_start (GTK_BOX (upper_hbox), right_vbox, FALSE, FALSE, 0);
gtk_widget_show (right_vbox);
/* the hbox containing the quickmask button, vertical scrollbar and
the navigation button */
lower_hbox = gtk_hbox_new (FALSE, 1);
gtk_box_pack_start (GTK_BOX (disp_vbox), lower_hbox, FALSE, FALSE, 0);
gtk_widget_show (lower_hbox);
/* create the scrollbars *************************************************/
/* the horizontal scrollbar */
shell->hsbdata = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, image_width,
1, 1, image_width));
shell->hsb = gtk_hscrollbar_new (shell->hsbdata);
GTK_WIDGET_UNSET_FLAGS (shell->hsb, GTK_CAN_FOCUS);
/* the vertical scrollbar */
shell->vsbdata = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, image_height,
1, 1, image_height));
shell->vsb = gtk_vscrollbar_new (shell->vsbdata);
GTK_WIDGET_UNSET_FLAGS (shell->vsb, GTK_CAN_FOCUS);
/* create the contents of the inner_table ********************************/
/* the menu popup button */
shell->origin = gtk_event_box_new ();
image = gtk_image_new_from_stock (GIMP_STOCK_MENU_RIGHT, GTK_ICON_SIZE_MENU);
gtk_container_add (GTK_CONTAINER (shell->origin), image);
gtk_widget_show (image);
g_signal_connect (shell->origin, "button-press-event",
G_CALLBACK (gimp_display_shell_origin_button_press),
shell);
gimp_help_set_help_data (shell->origin,
_("Access the image menu"),
GIMP_HELP_IMAGE_WINDOW_ORIGIN);
shell->canvas = gimp_canvas_new (display->config);
gimp_display_shell_selection_init (shell);
/* the horizontal ruler */
shell->hrule = gtk_hruler_new ();
gtk_widget_set_events (GTK_WIDGET (shell->hrule),
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
g_signal_connect_swapped (shell->canvas, "motion-notify-event",
G_CALLBACK (GTK_WIDGET_GET_CLASS (shell->hrule)->motion_notify_event),
shell->hrule);
g_signal_connect (shell->hrule, "button-press-event",
G_CALLBACK (gimp_display_shell_hruler_button_press),
shell);
gimp_help_set_help_data (shell->hrule, NULL, GIMP_HELP_IMAGE_WINDOW_RULER);
/* the vertical ruler */
shell->vrule = gtk_vruler_new ();
gtk_widget_set_events (GTK_WIDGET (shell->vrule),
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
g_signal_connect_swapped (shell->canvas, "motion-notify-event",
G_CALLBACK (GTK_WIDGET_GET_CLASS (shell->vrule)->motion_notify_event),
shell->vrule);
g_signal_connect (shell->vrule, "button-press-event",
G_CALLBACK (gimp_display_shell_vruler_button_press),
shell);
gimp_help_set_help_data (shell->vrule, NULL, GIMP_HELP_IMAGE_WINDOW_RULER);
/* Workaround for GTK+ Wintab bug on Windows when creating guides by
* dragging from the rulers. See bug #168516.
*/
gtk_widget_set_extension_events (shell->hrule, GDK_EXTENSION_EVENTS_ALL);
gtk_widget_set_extension_events (shell->vrule, GDK_EXTENSION_EVENTS_ALL);
/* the canvas */
gtk_widget_set_size_request (shell->canvas, n_width, n_height);
gtk_widget_set_events (shell->canvas, GIMP_DISPLAY_SHELL_CANVAS_EVENT_MASK);
gtk_widget_set_extension_events (shell->canvas, GDK_EXTENSION_EVENTS_ALL);
GTK_WIDGET_SET_FLAGS (shell->canvas, GTK_CAN_FOCUS);
g_signal_connect (shell->canvas, "realize",
G_CALLBACK (gimp_display_shell_canvas_realize),
shell);
g_signal_connect (shell->canvas, "size-allocate",
G_CALLBACK (gimp_display_shell_canvas_size_allocate),
shell);
g_signal_connect (shell->canvas, "expose-event",
G_CALLBACK (gimp_display_shell_canvas_expose),
shell);
g_signal_connect (shell->canvas, "enter-notify-event",
G_CALLBACK (gimp_display_shell_canvas_tool_events),
shell);
g_signal_connect (shell->canvas, "leave-notify-event",
G_CALLBACK (gimp_display_shell_canvas_tool_events),
shell);
g_signal_connect (shell->canvas, "proximity-in-event",
G_CALLBACK (gimp_display_shell_canvas_tool_events),
shell);
g_signal_connect (shell->canvas, "proximity-out-event",
G_CALLBACK (gimp_display_shell_canvas_tool_events),
shell);
g_signal_connect (shell->canvas, "focus-in-event",
G_CALLBACK (gimp_display_shell_canvas_tool_events),
shell);
g_signal_connect (shell->canvas, "focus-out-event",
G_CALLBACK (gimp_display_shell_canvas_tool_events),
shell);
g_signal_connect (shell->canvas, "button-press-event",
G_CALLBACK (gimp_display_shell_canvas_tool_events),
shell);
g_signal_connect (shell->canvas, "button-release-event",
G_CALLBACK (gimp_display_shell_canvas_tool_events),
shell);
g_signal_connect (shell->canvas, "scroll-event",
G_CALLBACK (gimp_display_shell_canvas_tool_events),
shell);
g_signal_connect (shell->canvas, "motion-notify-event",
G_CALLBACK (gimp_display_shell_canvas_tool_events),
shell);
g_signal_connect (shell->canvas, "key-press-event",
G_CALLBACK (gimp_display_shell_canvas_tool_events),
shell);
g_signal_connect (shell->canvas, "key-release-event",
G_CALLBACK (gimp_display_shell_canvas_tool_events),
shell);
/* create the contents of the right_vbox *********************************/
shell->zoom_button = g_object_new (GTK_TYPE_CHECK_BUTTON,
"draw-indicator", FALSE,
"relief", GTK_RELIEF_NONE,
"width-request", 18,
"height-request", 18,
NULL);
GTK_WIDGET_UNSET_FLAGS (shell->zoom_button, GTK_CAN_FOCUS);
image = gtk_image_new_from_stock (GIMP_STOCK_ZOOM_FOLLOW_WINDOW,
GTK_ICON_SIZE_MENU);
gtk_container_add (GTK_CONTAINER (shell->zoom_button), image);
gtk_widget_show (image);
gimp_help_set_help_data (shell->zoom_button,
_("Zoom image when window size changes"),
GIMP_HELP_IMAGE_WINDOW_ZOOM_FOLLOW_BUTTON);
g_signal_connect (shell->zoom_button, "toggled",
G_CALLBACK (gimp_toggle_button_update),
&shell->zoom_on_resize);
/* create the contents of the lower_hbox *********************************/
/* the quick mask button */
shell->quick_mask_button = g_object_new (GTK_TYPE_CHECK_BUTTON,
"draw-indicator", FALSE,
"relief", GTK_RELIEF_NONE,
"width-request", 18,
"height-request", 18,
NULL);
GTK_WIDGET_UNSET_FLAGS (shell->quick_mask_button, GTK_CAN_FOCUS);
image = gtk_image_new_from_stock (GIMP_STOCK_QUICK_MASK_OFF,
GTK_ICON_SIZE_MENU);
gtk_container_add (GTK_CONTAINER (shell->quick_mask_button), image);
gtk_widget_show (image);
action = gimp_ui_manager_find_action (shell->menubar_manager,
"quick-mask", "quick-mask-toggle");
if (action)
gimp_widget_set_accel_help (shell->quick_mask_button, action);
else
gimp_help_set_help_data (shell->quick_mask_button,
_("Toggle Quick Mask"),
GIMP_HELP_IMAGE_WINDOW_QUICK_MASK_BUTTON);
g_signal_connect (shell->quick_mask_button, "toggled",
G_CALLBACK (gimp_display_shell_quick_mask_toggled),
shell);
g_signal_connect (shell->quick_mask_button, "button-press-event",
G_CALLBACK (gimp_display_shell_quick_mask_button_press),
shell);
/* the navigation window button */
shell->nav_ebox = gtk_event_box_new ();
image = gtk_image_new_from_stock (GIMP_STOCK_NAVIGATION, GTK_ICON_SIZE_MENU);
gtk_container_add (GTK_CONTAINER (shell->nav_ebox), image);
gtk_widget_show (image);
g_signal_connect (shell->nav_ebox, "button-press-event",
G_CALLBACK (gimp_display_shell_nav_button_press),
shell);
gimp_help_set_help_data (shell->nav_ebox,
_("Navigate the image display"),
GIMP_HELP_IMAGE_WINDOW_NAV_BUTTON);
/* create the contents of the status area *********************************/
/* the statusbar */
shell->statusbar = gimp_statusbar_new (shell);
gimp_help_set_help_data (shell->statusbar, NULL,
GIMP_HELP_IMAGE_WINDOW_STATUS_BAR);
/* pack all the widgets **************************************************/
/* fill the inner_table */
gtk_table_attach (GTK_TABLE (inner_table), shell->origin, 0, 1, 0, 1,
GTK_FILL, GTK_FILL, 0, 0);
gtk_table_attach (GTK_TABLE (inner_table), shell->hrule, 1, 2, 0, 1,
GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_FILL, 0, 0);
gtk_table_attach (GTK_TABLE (inner_table), shell->vrule, 0, 1, 1, 2,
GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
gtk_table_attach (GTK_TABLE (inner_table), shell->canvas, 1, 2, 1, 2,
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
/* fill the right_vbox */
gtk_box_pack_start (GTK_BOX (right_vbox),
shell->zoom_button, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (right_vbox),
shell->vsb, TRUE, TRUE, 0);
/* fill the lower_hbox */
gtk_box_pack_start (GTK_BOX (lower_hbox),
shell->quick_mask_button, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (lower_hbox),
shell->hsb, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (lower_hbox),
shell->nav_ebox, FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (main_vbox),
shell->statusbar, FALSE, FALSE, 0);
/* show everything *******************************************************/
if (shell->options->show_rulers)
{
gtk_widget_show (shell->origin);
gtk_widget_show (shell->hrule);
gtk_widget_show (shell->vrule);
}
gtk_widget_show (GTK_WIDGET (shell->canvas));
if (shell->options->show_scrollbars)
{
gtk_widget_show (shell->vsb);
gtk_widget_show (shell->hsb);
gtk_widget_show (shell->zoom_button);
gtk_widget_show (shell->quick_mask_button);
gtk_widget_show (shell->nav_ebox);
}
if (shell->options->show_statusbar)
gtk_widget_show (shell->statusbar);
gtk_widget_show (main_vbox);
filter = gimp_display_shell_filter_new (shell,
GIMP_CORE_CONFIG (shell->display->config)->color_management);
if (filter)
{
gimp_display_shell_filter_set (shell, filter);
g_object_unref (filter);
}
gimp_display_shell_connect (shell);
gimp_display_shell_title_init (shell);
/* make sure the information is up-to-date */
gimp_display_shell_scale_changed (shell);
return GTK_WIDGET (shell);
}
void
gimp_display_shell_reconnect (GimpDisplayShell *shell)
{
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
g_return_if_fail (GIMP_IS_DISPLAY (shell->display));
g_return_if_fail (GIMP_IS_IMAGE (shell->display->image));
gimp_display_shell_connect (shell);
g_signal_emit (shell, display_shell_signals[RECONNECT], 0);
gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (shell));
gimp_display_shell_scale_setup (shell);
gimp_display_shell_expose_full (shell);
gimp_display_shell_scaled (shell);
}
/*
* We used to calculate the scale factor in the SCALEFACTOR_X() and
* SCALEFACTOR_Y() macros. But since these are rather frequently
* called and the values rarely change, we now store them in the
* shell and call this function whenever they need to be recalculated.
*/
void
gimp_display_shell_scale_changed (GimpDisplayShell *shell)
{
GimpImage *image;
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
image = shell->display->image;
if (image)
{
gdouble xres;
gdouble yres;
gimp_image_get_resolution (image, &xres, &yres);
shell->scale_x = (gimp_zoom_model_get_factor (shell->zoom) *
SCREEN_XRES (shell) / xres);
shell->scale_y = (gimp_zoom_model_get_factor (shell->zoom) *
SCREEN_YRES (shell) / yres);
shell->x_dest_inc = gimp_image_get_width (image);
shell->y_dest_inc = gimp_image_get_height (image);
shell->x_src_dec = SCALEX (shell, gimp_image_get_width (image));
shell->y_src_dec = SCALEY (shell, gimp_image_get_height (image));
}
else
{
shell->scale_x = 1.0;
shell->scale_y = 1.0;
shell->x_dest_inc = shell->disp_width;
shell->y_dest_inc = shell->disp_height;
shell->x_src_dec = shell->disp_width;
shell->y_src_dec = shell->disp_height;
}
}
void
gimp_display_shell_scaled (GimpDisplayShell *shell)
{
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
g_signal_emit (shell, display_shell_signals[SCALED], 0);
}
void
gimp_display_shell_scrolled (GimpDisplayShell *shell)
{
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
g_signal_emit (shell, display_shell_signals[SCROLLED], 0);
}
void
gimp_display_shell_set_unit (GimpDisplayShell *shell,
GimpUnit unit)
{
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
if (shell->unit != unit)
{
shell->unit = unit;
gimp_display_shell_scale_setup (shell);
gimp_display_shell_scaled (shell);
g_object_notify (G_OBJECT (shell), "unit");
}
}
GimpUnit
gimp_display_shell_get_unit (GimpDisplayShell *shell)
{
g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), GIMP_UNIT_PIXEL);
return shell->unit;
}
gboolean
gimp_display_shell_snap_coords (GimpDisplayShell *shell,
GimpCoords *coords,
gint snap_offset_x,
gint snap_offset_y,
gint snap_width,
gint snap_height)
{
GimpImage *image;
gboolean snap_to_guides = FALSE;
gboolean snap_to_grid = FALSE;
gboolean snap_to_canvas = FALSE;
gboolean snap_to_vectors = FALSE;
gboolean snapped = FALSE;
g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE);
g_return_val_if_fail (coords != NULL, FALSE);
image = shell->display->image;
if (gimp_display_shell_get_snap_to_guides (shell) &&
gimp_image_get_guides (image))
{
snap_to_guides = TRUE;
}
if (gimp_display_shell_get_snap_to_grid (shell) &&
gimp_image_get_grid (image))
{
snap_to_grid = TRUE;
}
snap_to_canvas = gimp_display_shell_get_snap_to_canvas (shell);
if (gimp_display_shell_get_snap_to_vectors (shell) &&
gimp_image_get_active_vectors (image))
{
snap_to_vectors = TRUE;
}
if (snap_to_guides || snap_to_grid || snap_to_canvas || snap_to_vectors)
{
gint snap_distance;
gdouble tx, ty;
snap_distance = shell->display->config->snap_distance;
if (snap_width > 0 && snap_height > 0)
{
snapped = gimp_image_snap_rectangle (shell->display->image,
coords->x + snap_offset_x,
coords->y + snap_offset_y,
coords->x + snap_offset_x +
snap_width,
coords->y + snap_offset_y +
snap_height,
&tx,
&ty,
FUNSCALEX (shell, snap_distance),
FUNSCALEY (shell, snap_distance),
snap_to_guides,
snap_to_grid,
snap_to_canvas,
snap_to_vectors);
}
else
{
snapped = gimp_image_snap_point (shell->display->image,
coords->x + snap_offset_x,
coords->y + snap_offset_y,
&tx,
&ty,
FUNSCALEX (shell, snap_distance),
FUNSCALEY (shell, snap_distance),
snap_to_guides,
snap_to_grid,
snap_to_canvas,
snap_to_vectors);
}
if (snapped)
{
coords->x = tx - snap_offset_x;
coords->y = ty - snap_offset_y;
}
}
return snapped;
}
gboolean
gimp_display_shell_mask_bounds (GimpDisplayShell *shell,
gint *x1,
gint *y1,
gint *x2,
gint *y2)
{
GimpLayer *layer;
g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), FALSE);
g_return_val_if_fail (x1 != NULL, FALSE);
g_return_val_if_fail (y1 != NULL, FALSE);
g_return_val_if_fail (x2 != NULL, FALSE);
g_return_val_if_fail (y2 != NULL, FALSE);
/* If there is a floating selection, handle things differently */
if ((layer = gimp_image_floating_sel (shell->display->image)))
{
gint off_x;
gint off_y;
gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y);
if (! gimp_channel_bounds (gimp_image_get_mask (shell->display->image),
x1, y1, x2, y2))
{
*x1 = off_x;
*y1 = off_y;
*x2 = off_x + gimp_item_width (GIMP_ITEM (layer));
*y2 = off_y + gimp_item_height (GIMP_ITEM (layer));
}
else
{
*x1 = MIN (off_x, *x1);
*y1 = MIN (off_y, *y1);
*x2 = MAX (off_x + gimp_item_width (GIMP_ITEM (layer)), *x2);
*y2 = MAX (off_y + gimp_item_height (GIMP_ITEM (layer)), *y2);
}
}
else if (! gimp_channel_bounds (gimp_image_get_mask (shell->display->image),
x1, y1, x2, y2))
{
return FALSE;
}
gimp_display_shell_transform_xy (shell, *x1, *y1, x1, y1, FALSE);
gimp_display_shell_transform_xy (shell, *x2, *y2, x2, y2, FALSE);
/* Make sure the extents are within bounds */
*x1 = CLAMP (*x1, 0, shell->disp_width);
*y1 = CLAMP (*y1, 0, shell->disp_height);
*x2 = CLAMP (*x2, 0, shell->disp_width);
*y2 = CLAMP (*y2, 0, shell->disp_height);
return ((*x2 - *x1) > 0) && ((*y2 - *y1) > 0);
}
void
gimp_display_shell_expose_area (GimpDisplayShell *shell,
gint x,
gint y,
gint w,
gint h)
{
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
gtk_widget_queue_draw_area (shell->canvas, x, y, w, h);
}
void
gimp_display_shell_expose_guide (GimpDisplayShell *shell,
GimpGuide *guide)
{
gint position;
gint x, y;
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
g_return_if_fail (GIMP_IS_GUIDE (guide));
position = gimp_guide_get_position (guide);
if (position < 0)
return;
gimp_display_shell_transform_xy (shell,
position, position,
&x, &y,
FALSE);
switch (gimp_guide_get_orientation (guide))
{
case GIMP_ORIENTATION_HORIZONTAL:
gimp_display_shell_expose_area (shell, 0, y, shell->disp_width, 1);
break;
case GIMP_ORIENTATION_VERTICAL:
gimp_display_shell_expose_area (shell, x, 0, 1, shell->disp_height);
break;
default:
break;
}
}
void
gimp_display_shell_expose_sample_point (GimpDisplayShell *shell,
GimpSamplePoint *sample_point)
{
gdouble x, y;
gint x1, y1, x2, y2;
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
g_return_if_fail (sample_point != NULL);
if (sample_point->x < 0)
return;
gimp_display_shell_transform_xy_f (shell,
sample_point->x + 0.5,
sample_point->y + 0.5,
&x, &y,
FALSE);
x1 = MAX (0, floor (x - GIMP_SAMPLE_POINT_DRAW_SIZE));
y1 = MAX (0, floor (y - GIMP_SAMPLE_POINT_DRAW_SIZE));
x2 = MIN (shell->disp_width, ceil (x + GIMP_SAMPLE_POINT_DRAW_SIZE));
y2 = MIN (shell->disp_height, ceil (y + GIMP_SAMPLE_POINT_DRAW_SIZE));
/* HACK: add 3 instead of 1 so the number gets cleared too */
gimp_display_shell_expose_area (shell, x1, y1, x2 - x1 + 3, y2 - y1 + 3);
}
void
gimp_display_shell_expose_full (GimpDisplayShell *shell)
{
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
gtk_widget_queue_draw (shell->canvas);
}
void
gimp_display_shell_flush (GimpDisplayShell *shell,
gboolean now)
{
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
gimp_display_shell_title_update (shell);
/* make sure the information is up-to-date */
gimp_display_shell_scale_changed (shell);
if (now)
{
gdk_window_process_updates (shell->canvas->window, FALSE);
}
else
{
GimpContext *user_context;
gimp_ui_manager_update (shell->menubar_manager, shell->display);
user_context = gimp_get_user_context (shell->display->gimp);
if (shell->display == gimp_context_get_display (user_context))
gimp_ui_manager_update (shell->popup_manager, shell->display);
}
}
/**
* gimp_display_shell_pause:
* @shell: a display shell
*
* This function increments the pause count or the display shell.
* If it was zero coming in, then the function pauses the active tool,
* so that operations on the display can take place without corrupting
* anything that the tool has drawn. It "undraws" the current tool
* drawing, and must be followed by gimp_display_shell_resume() after
* the operation in question is completed.
**/
void
gimp_display_shell_pause (GimpDisplayShell *shell)
{
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
shell->paused_count++;
if (shell->paused_count == 1)
{
/* pause the currently active tool */
tool_manager_control_active (shell->display->gimp,
GIMP_TOOL_ACTION_PAUSE,
shell->display);
gimp_display_shell_draw_vectors (shell);
}
}
/**
* gimp_display_shell_resume:
* @shell: a display shell
*
* This function decrements the pause count for the display shell.
* If this brings it to zero, then the current tool is resumed.
* It is an error to call this function without having previously
* called gimp_display_shell_pause().
**/
void
gimp_display_shell_resume (GimpDisplayShell *shell)
{
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
g_return_if_fail (shell->paused_count > 0);
shell->paused_count--;
if (shell->paused_count == 0)
{
gimp_display_shell_draw_vectors (shell);
/* start the currently active tool */
tool_manager_control_active (shell->display->gimp,
GIMP_TOOL_ACTION_RESUME,
shell->display);
}
}
void
gimp_display_shell_update_icon (GimpDisplayShell *shell)
{
GimpImage *image;
GdkPixbuf *pixbuf;
gint width, height;
gdouble factor;
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
image = shell->display->image;
factor = ((gdouble) gimp_image_get_height (image) /
(gdouble) gimp_image_get_width (image));
if (factor >= 1)
{
height = MAX (shell->icon_size, 1);
width = MAX (((gdouble) shell->icon_size) / factor, 1);
}
else
{
height = MAX (((gdouble) shell->icon_size) * factor, 1);
width = MAX (shell->icon_size, 1);
}
pixbuf = gimp_viewable_get_pixbuf (GIMP_VIEWABLE (image),
gimp_get_user_context (shell->display->gimp),
width, height);
gtk_window_set_icon (GTK_WINDOW (shell), pixbuf);
}
void
gimp_display_shell_shrink_wrap (GimpDisplayShell *shell)
{
GtkWidget *widget;
GdkScreen *screen;
GdkRectangle rect;
gint monitor;
gint disp_width, disp_height;
gint width, height;
gint max_auto_width, max_auto_height;
gint border_x, border_y;
gboolean resize = FALSE;
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
if (! GTK_WIDGET_REALIZED (shell))
return;
widget = GTK_WIDGET (shell);
screen = gtk_widget_get_screen (widget);
monitor = gdk_screen_get_monitor_at_window (screen, widget->window);
gdk_screen_get_monitor_geometry (screen, monitor, &rect);
width = SCALEX (shell, gimp_image_get_width (shell->display->image));
height = SCALEY (shell, gimp_image_get_height (shell->display->image));
disp_width = shell->disp_width;
disp_height = shell->disp_height;
border_x = widget->allocation.width - disp_width;
border_y = widget->allocation.height - disp_height;
max_auto_width = (rect.width - border_x) * 0.75;
max_auto_height = (rect.height - border_y) * 0.75;
/* If one of the display dimensions has changed and one of the
* dimensions fits inside the screen
*/
if (((width + border_x) < rect.width || (height + border_y) < rect.height) &&
(width != disp_width || height != disp_height))
{
width = ((width + border_x) < rect.width) ? width : max_auto_width;
height = ((height + border_y) < rect.height) ? height : max_auto_height;
resize = TRUE;
}
/* If the projected dimension is greater than current, but less than
* 3/4 of the screen size, expand automagically
*/
else if ((width > disp_width || height > disp_height) &&
(disp_width < max_auto_width || disp_height < max_auto_height))
{
width = MIN (max_auto_width, width);
height = MIN (max_auto_height, height);
resize = TRUE;
}
if (resize)
{
if (width < shell->statusbar->requisition.width)
width = shell->statusbar->requisition.width;
gtk_window_resize (GTK_WINDOW (shell),
width + border_x,
height + border_y);
}
}
/**
* gimp_display_shell_set_highlight:
* @shell: a #GimpDisplayShell
* @highlight: a rectangle in image coordinates that should be brought out
*
* This function allows to set an area of the image that should be
* accentuated. The actual implementation is to dim all pixels outside
* this rectangle. Passing %NULL for @highlight unsets the rectangle.
**/
void
gimp_display_shell_set_highlight (GimpDisplayShell *shell,
const GdkRectangle *highlight)
{
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
if (shell->highlight)
{
if (highlight)
{
GdkRectangle *rects;
GdkRegion *old;
GdkRegion *new;
gint num_rects, i;
if (memcmp (shell->highlight, highlight, sizeof (GdkRectangle)) == 0)
return;
old = gdk_region_rectangle (shell->highlight);
*shell->highlight = *highlight;
new = gdk_region_rectangle (shell->highlight);
gdk_region_xor (old, new);
gdk_region_get_rectangles (old, &rects, &num_rects);
gdk_region_destroy (old);
gdk_region_destroy (new);
for (i = 0; i < num_rects; i++)
gimp_display_update_area (shell->display, TRUE,
rects[i].x,
rects[i].y,
rects[i].width,
rects[i].height);
g_free (rects);
}
else
{
g_slice_free (GdkRectangle, shell->highlight);
shell->highlight = NULL;
gimp_display_shell_expose_full (shell);
}
}
else if (highlight)
{
shell->highlight = g_slice_new (GdkRectangle);
*shell->highlight = *highlight;
gimp_display_shell_expose_full (shell);
}
}
/**
* gimp_display_shell_set_mask:
* @shell: a #GimpDisplayShell
* @mask: a #GimpDrawable (1 byte per pixel)
* @color: the color to use for drawing the mask
*
* Allows to preview a selection (used by the foreground selection
* tool). Pixels that are not selected (> 127) in the mask are tinted
* with dark blue.
**/
void
gimp_display_shell_set_mask (GimpDisplayShell *shell,
GimpDrawable *mask,
GimpChannelType color)
{
g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));
g_return_if_fail (mask == NULL ||
(GIMP_IS_DRAWABLE (mask) &&
gimp_drawable_bytes (mask) == 1));
if (shell->mask == mask && shell->mask_color == color)
return;
if (mask)
g_object_ref (mask);
if (shell->mask)
g_object_unref (shell->mask);
shell->mask = mask;
shell->mask_color = color;
gimp_display_shell_expose_full (shell);
}