mirror of
https://gitlab.gnome.org/GNOME/gimp
synced 2024-10-24 05:22:00 +00:00
d162376d47
2001-11-01 Michael Natterer <mitch@gimp.org> * app/display/Makefile.am * app/display/gimpdisplay-callbacks.[ch] * app/display/gimpdisplay-render.[ch] * app/display/gimpdisplay-scale.[ch] * app/display/gimpdisplay-scroll.[ch]: removed and added as gimpdisplayshell-foo.[ch] because they are all methods of the shell. * app/display/gimpdisplay.[ch] * app/display/gimpdisplayshell.[ch]: moved the "offset" and "size" variables from GimpDisplay to GimpDisplayShell. GimpDisplay should know nothing about screen coordinates. The gdisplay_[un]transform_foo() methods are still part of GimpDisplay but will be moved to GimpDisplayShell as soon as the tools' vitrual functions speak in image coordinates instead of GdkEvents. * app/display/gimpdisplayshell-callbacks.[ch]: prefixed all functions with gimp_display_shell_*. Moved some stuff to a "realize" callback File still has to be renamed. * app/display/gimpdisplay-foreach.[ch]: removed gdisplays_shrink_wrap(). * app/gui/menus.c * app/gui/view-commands.[ch] * app/display/gimpdisplayshell-scale.[ch]: implemented "Zoom to Fit Window" function (#57670). * app/nav_window.c * app/display/gimpdisplay-handlers.c * app/display/gimpdisplayshell-render.[ch] * app/display/gimpdisplayshell-scale.[ch] * app/display/gimpdisplayshell-scroll.[ch] * app/gui/colormap-dialog.c * app/gui/gui.c * app/gui/preferences-dialog.c * app/tools/gimpmagnifytool.c * app/tools/gimpmovetool.c * app/widgets/gimppreview.c: changed according to variable and filename changes. * app/tools/tool_manager.c: tool_manager_select_tool(): send the active tool a "HALT" command before selecting the new one. Fixes stale tool dialogs which were there because some other hack was removed (This is IMHO the right place to shut down the active tool). * app/tools/gimpcroptool.c: don't shrink wrap after cropping but let gimprc.allow_resize_windows decide. * app/tools/gimpselectiontool.c: gimage_mask_value() takes image, not screen coordinates. A good example of how braindead it is to pass GdkEvents to tools :-) Fixes incorrect cursor and oper update of the selection tools. * app/tools/gimptransformtool.c * app/undo.c: removed (#if 0 for now) some strange code which did manual exposing of GimpDisplayShell areas. This was definitely a hack and should not be there given the image emits correct "update" signals.
1720 lines
44 KiB
C
1720 lines
44 KiB
C
/* The GIMP -- an 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 <stdlib.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
|
|
#include "libgimpmath/gimpmath.h"
|
|
#include "libgimpwidgets/gimpwidgets.h"
|
|
|
|
#include "widgets/widgets-types.h"
|
|
|
|
#include "base/temp-buf.h"
|
|
|
|
#include "core/gimp.h"
|
|
#include "core/gimpcontainer.h"
|
|
#include "core/gimpcontext.h"
|
|
#include "core/gimpimage.h"
|
|
#include "core/gimplist.h"
|
|
|
|
#include "display/gimpdisplay.h"
|
|
#include "display/gimpdisplay-foreach.h"
|
|
#include "display/gimpdisplayshell.h"
|
|
#include "display/gimpdisplayshell-scroll.h"
|
|
#include "display/gimpdisplayshell-scale.h"
|
|
|
|
#include "widgets/gimpnavigationpreview.h"
|
|
|
|
#include "app_procs.h"
|
|
#include "nav_window.h"
|
|
|
|
#include "app_procs.h"
|
|
#include "gimprc.h"
|
|
|
|
#include "libgimp/gimpintl.h"
|
|
|
|
#include "pixmaps/zoom_in.xpm"
|
|
#include "pixmaps/zoom_out.xpm"
|
|
|
|
|
|
#define PREVIEW_MASK GDK_EXPOSURE_MASK | \
|
|
GDK_BUTTON_PRESS_MASK | \
|
|
GDK_KEY_PRESS_MASK | \
|
|
GDK_KEY_RELEASE_MASK | \
|
|
GDK_POINTER_MOTION_MASK
|
|
|
|
|
|
/* Navigation preview sizes */
|
|
#define NAV_PREVIEW_WIDTH 112
|
|
#define NAV_PREVIEW_HEIGHT 112
|
|
#define BORDER_PEN_WIDTH 3
|
|
|
|
#define MAX_SCALE_BUF 20
|
|
|
|
typedef enum
|
|
{
|
|
NAV_WINDOW,
|
|
NAV_POPUP
|
|
} NavWinType;
|
|
|
|
|
|
struct _NavigationDialog
|
|
{
|
|
NavWinType ptype;
|
|
|
|
GtkWidget *shell;
|
|
|
|
GtkWidget *new_preview;
|
|
|
|
GtkWidget *preview_frame;
|
|
GtkWidget *zoom_label;
|
|
GtkObject *zoom_adjustment;
|
|
GtkWidget *preview;
|
|
GimpDisplay *gdisp; /* I'm not happy 'bout this one */
|
|
GdkGC *gc;
|
|
gint dispx; /* x pos of top left corner of display area */
|
|
gint dispy; /* y pos of top left corner of display area */
|
|
gint dispwidth; /* width of display area */
|
|
gint dispheight; /* height of display area */
|
|
|
|
gboolean sq_grabbed; /* In the process of moving the preview square */
|
|
gint motion_offsetx;
|
|
gint motion_offsety;
|
|
|
|
gint pwidth; /* real preview width */
|
|
gint pheight; /* real preview height */
|
|
gint imagewidth; /* width of the real image */
|
|
gint imageheight; /* height of real image */
|
|
gdouble ratio;
|
|
gint nav_preview_width;
|
|
gint nav_preview_height;
|
|
gboolean block_adj_sig;
|
|
gboolean frozen; /* Has the dialog been frozen ? */
|
|
guint idle_id;
|
|
};
|
|
|
|
|
|
static NavigationDialog * nav_dialog_new (GimpDisplay *gdisp,
|
|
NavWinType ptype);
|
|
static gchar * nav_dialog_title (GimpDisplay *gdisp);
|
|
|
|
static GtkWidget * nav_create_button_area (NavigationDialog *nav_dialog);
|
|
static void nav_dialog_close_callback (GtkWidget *widget,
|
|
gpointer data);
|
|
static void nav_dialog_disp_area (NavigationDialog *nav_dialog,
|
|
GimpDisplay *gdisp);
|
|
static void nav_dialog_draw_sqr (NavigationDialog *nav_dialog,
|
|
gboolean undraw,
|
|
gint x,
|
|
gint y,
|
|
gint w,
|
|
gint h);
|
|
static gboolean nav_dialog_preview_events (GtkWidget *widget,
|
|
GdkEvent *event,
|
|
gpointer data);
|
|
|
|
static void nav_dialog_idle_update_preview (NavigationDialog *nav_dialog);
|
|
static void nav_dialog_update_preview (NavigationDialog *nav_dialog);
|
|
static void nav_dialog_update_preview_blank (NavigationDialog *nav_dialog);
|
|
|
|
static void create_preview_widget (NavigationDialog *nav_dialog);
|
|
static void set_size_data (NavigationDialog *nav_dialog);
|
|
static void nav_image_need_update (GimpImage *gimage,
|
|
gpointer data);
|
|
|
|
static void nav_dialog_display_changed (GimpContext *context,
|
|
GimpDisplay *gdisp,
|
|
gpointer data);
|
|
|
|
static GimpDisplay * nav_dialog_get_gdisp (void);
|
|
|
|
static void nav_dialog_grab_pointer (NavigationDialog *nav_dialog,
|
|
GtkWidget *widget);
|
|
static void update_zoom_label (NavigationDialog *nav_dialog);
|
|
static void update_zoom_adjustment (NavigationDialog *nav_dialog);
|
|
|
|
|
|
static NavigationDialog *nav_window_auto = NULL;
|
|
|
|
|
|
/* public functions */
|
|
|
|
static void
|
|
nav_dialog_marker_changed (GimpNavigationPreview *nav_preview,
|
|
gint x,
|
|
gint y,
|
|
NavigationDialog *nav_dialog)
|
|
{
|
|
GimpDisplayShell *shell;
|
|
gdouble xratio;
|
|
gdouble yratio;
|
|
gint xoffset;
|
|
gint yoffset;
|
|
|
|
shell = GIMP_DISPLAY_SHELL (nav_dialog->gdisp->shell);
|
|
|
|
xratio = SCALEFACTOR_X (nav_dialog->gdisp);
|
|
yratio = SCALEFACTOR_Y (nav_dialog->gdisp);
|
|
|
|
xoffset = x * xratio - shell->offset_x;
|
|
yoffset = y * yratio - shell->offset_y;
|
|
|
|
gimp_display_shell_scroll (shell, xoffset, yoffset);
|
|
}
|
|
|
|
static void
|
|
nav_dialog_zoom (GimpNavigationPreview *nav_preview,
|
|
GimpZoomType direction,
|
|
NavigationDialog *nav_dialog)
|
|
{
|
|
GimpDisplayShell *shell;
|
|
|
|
shell = GIMP_DISPLAY_SHELL (nav_dialog->gdisp->shell);
|
|
|
|
gimp_display_shell_scale (shell, direction);
|
|
}
|
|
|
|
static void
|
|
nav_dialog_scroll (GimpNavigationPreview *nav_preview,
|
|
GdkScrollDirection direction,
|
|
NavigationDialog *nav_dialog)
|
|
{
|
|
GimpDisplayShell *shell;
|
|
GtkAdjustment *adj = NULL;
|
|
gdouble value;
|
|
|
|
shell = GIMP_DISPLAY_SHELL (nav_dialog->gdisp->shell);
|
|
|
|
g_print ("nav_dialog_scroll(%d)\n", direction);
|
|
|
|
switch (direction)
|
|
{
|
|
case GDK_SCROLL_LEFT:
|
|
case GDK_SCROLL_RIGHT:
|
|
adj = shell->hsbdata;
|
|
break;
|
|
|
|
case GDK_SCROLL_UP:
|
|
case GDK_SCROLL_DOWN:
|
|
adj = shell->vsbdata;
|
|
break;
|
|
}
|
|
|
|
g_assert (adj != NULL);
|
|
|
|
value = adj->value;
|
|
|
|
switch (direction)
|
|
{
|
|
case GDK_SCROLL_LEFT:
|
|
case GDK_SCROLL_UP:
|
|
value -= adj->page_increment / 2;
|
|
break;
|
|
|
|
case GDK_SCROLL_RIGHT:
|
|
case GDK_SCROLL_DOWN:
|
|
value += adj->page_increment / 2;
|
|
break;
|
|
}
|
|
|
|
value = CLAMP (value, adj->lower, adj->upper - adj->page_size);
|
|
|
|
gtk_adjustment_set_value (adj, value);
|
|
}
|
|
|
|
NavigationDialog *
|
|
nav_dialog_create (GimpDisplay *gdisp)
|
|
{
|
|
NavigationDialog *nav_dialog;
|
|
GtkWidget *button_area;
|
|
GtkWidget *abox;
|
|
gchar *title;
|
|
|
|
title = nav_dialog_title (gdisp);
|
|
|
|
nav_dialog = nav_dialog_new (gdisp, NAV_WINDOW);
|
|
|
|
nav_dialog->shell = gimp_dialog_new (title, "navigation_dialog",
|
|
gimp_standard_help_func,
|
|
"dialogs/navigation_window.html",
|
|
GTK_WIN_POS_MOUSE,
|
|
FALSE, TRUE, TRUE,
|
|
|
|
"_delete_event_",
|
|
nav_dialog_close_callback,
|
|
nav_dialog, NULL, NULL, TRUE, TRUE,
|
|
|
|
NULL);
|
|
|
|
g_free (title);
|
|
|
|
gtk_dialog_set_has_separator (GTK_DIALOG (nav_dialog->shell), FALSE);
|
|
gtk_widget_hide (GTK_DIALOG (nav_dialog->shell)->action_area);
|
|
|
|
g_object_weak_ref (G_OBJECT (nav_dialog->shell),
|
|
(GWeakNotify) g_free,
|
|
nav_dialog);
|
|
|
|
abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
|
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (nav_dialog->shell)->vbox), abox,
|
|
TRUE, TRUE, 0);
|
|
gtk_widget_show (abox);
|
|
|
|
nav_dialog->preview_frame = gtk_frame_new (NULL);
|
|
gtk_frame_set_shadow_type (GTK_FRAME (nav_dialog->preview_frame),
|
|
GTK_SHADOW_IN);
|
|
gtk_container_add (GTK_CONTAINER (abox), nav_dialog->preview_frame);
|
|
gtk_widget_show (nav_dialog->preview_frame);
|
|
|
|
create_preview_widget (nav_dialog);
|
|
|
|
{
|
|
GtkWidget *frame;
|
|
GtkWidget *preview;
|
|
|
|
abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
|
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (nav_dialog->shell)->vbox), abox,
|
|
TRUE, TRUE, 0);
|
|
gtk_widget_show (abox);
|
|
|
|
frame = gtk_frame_new (NULL);
|
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
|
|
gtk_container_add (GTK_CONTAINER (abox), frame);
|
|
gtk_widget_show (frame);
|
|
|
|
preview = gimp_navigation_preview_new (gdisp->gimage,
|
|
gimprc.nav_preview_size);
|
|
gtk_container_add (GTK_CONTAINER (frame), preview);
|
|
gtk_widget_show (preview);
|
|
|
|
g_signal_connect (G_OBJECT (preview), "marker_changed",
|
|
G_CALLBACK (nav_dialog_marker_changed),
|
|
nav_dialog);
|
|
g_signal_connect (G_OBJECT (preview), "zoom",
|
|
G_CALLBACK (nav_dialog_zoom),
|
|
nav_dialog);
|
|
g_signal_connect (G_OBJECT (preview), "scroll",
|
|
G_CALLBACK (nav_dialog_scroll),
|
|
nav_dialog);
|
|
|
|
nav_dialog->new_preview = preview;
|
|
}
|
|
|
|
button_area = nav_create_button_area (nav_dialog);
|
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (nav_dialog->shell)->vbox),
|
|
button_area, FALSE, FALSE, 0);
|
|
gtk_widget_show (button_area);
|
|
|
|
if (! g_object_get_data (G_OBJECT (gdisp->gimage), "nav_handlers_installed"))
|
|
{
|
|
g_signal_connect (G_OBJECT (gdisp->gimage), "dirty",
|
|
G_CALLBACK (nav_image_need_update),
|
|
nav_dialog);
|
|
g_signal_connect (G_OBJECT (gdisp->gimage), "clean",
|
|
G_CALLBACK (nav_image_need_update),
|
|
nav_dialog);
|
|
|
|
g_object_set_data (G_OBJECT (gdisp->gimage), "nav_handlers_installed",
|
|
nav_dialog);
|
|
}
|
|
|
|
return nav_dialog;
|
|
}
|
|
|
|
void
|
|
nav_dialog_free (GimpDisplay *del_gdisp,
|
|
NavigationDialog *nav_dialog)
|
|
{
|
|
/* So this functions works both ways..
|
|
* it will come in here with nav_dialog == null
|
|
* if the auto mode is on...
|
|
*/
|
|
|
|
if (! nav_dialog)
|
|
{
|
|
if (nav_window_auto != NULL)
|
|
{
|
|
GimpDisplay *gdisp;
|
|
|
|
nav_dialog = nav_window_auto;
|
|
|
|
/* Only freeze if we are displaying the image we have deleted */
|
|
if (nav_dialog->gdisp != del_gdisp)
|
|
return;
|
|
|
|
if (nav_dialog->idle_id)
|
|
{
|
|
g_source_remove (nav_dialog->idle_id);
|
|
nav_dialog->idle_id = 0;
|
|
}
|
|
|
|
gdisp = nav_dialog_get_gdisp ();
|
|
|
|
if (gdisp)
|
|
{
|
|
nav_dialog_display_changed (NULL, gdisp, nav_window_auto);
|
|
}
|
|
else
|
|
{
|
|
/* Clear window and freeze */
|
|
nav_dialog->frozen = TRUE;
|
|
nav_dialog_update_preview_blank (nav_window_auto);
|
|
gtk_window_set_title (GTK_WINDOW (nav_window_auto->shell),
|
|
_("Navigation: No Image"));
|
|
|
|
gtk_widget_set_sensitive (nav_window_auto->shell, FALSE);
|
|
nav_window_auto->gdisp = NULL;
|
|
gtk_widget_hide (GTK_WIDGET (nav_window_auto->shell));
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (nav_dialog->idle_id)
|
|
{
|
|
g_source_remove (nav_dialog->idle_id);
|
|
nav_dialog->idle_id = 0;
|
|
}
|
|
|
|
gtk_widget_destroy (nav_dialog->shell);
|
|
}
|
|
|
|
void
|
|
nav_dialog_popup (NavigationDialog *nav_dialog)
|
|
{
|
|
if (! nav_dialog)
|
|
return;
|
|
|
|
if (! GTK_WIDGET_VISIBLE (nav_dialog->shell))
|
|
{
|
|
gtk_widget_show (nav_dialog->shell);
|
|
|
|
nav_dialog_idle_update_preview (nav_dialog);
|
|
}
|
|
|
|
gdk_window_raise (nav_dialog->shell->window);
|
|
}
|
|
|
|
void
|
|
nav_dialog_follow_auto (void)
|
|
{
|
|
GimpContext *context;
|
|
GimpDisplay *gdisp;
|
|
|
|
context = gimp_get_user_context (the_gimp);
|
|
|
|
gdisp = gimp_context_get_display (context);
|
|
|
|
if (! gdisp)
|
|
return;
|
|
|
|
if (! nav_window_auto)
|
|
{
|
|
nav_window_auto = nav_dialog_create (gdisp);
|
|
|
|
g_signal_connect (G_OBJECT (context), "display_changed",
|
|
G_CALLBACK (nav_dialog_display_changed),
|
|
nav_window_auto);
|
|
}
|
|
|
|
nav_dialog_popup (nav_window_auto);
|
|
|
|
gtk_widget_set_sensitive (nav_window_auto->shell, TRUE);
|
|
|
|
nav_window_auto->frozen = FALSE;
|
|
}
|
|
|
|
void
|
|
nav_dialog_update_window_marker (NavigationDialog *nav_dialog)
|
|
{
|
|
/* So this functions works both ways..
|
|
* it will come in here with info_win == null
|
|
* if the auto mode is on...
|
|
*/
|
|
|
|
if (! nav_dialog && nav_window_auto)
|
|
{
|
|
nav_dialog = nav_window_auto;
|
|
|
|
if (nav_dialog->frozen)
|
|
return;
|
|
|
|
nav_dialog_update_window_marker (nav_window_auto);
|
|
|
|
return;
|
|
}
|
|
else if (! nav_dialog)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (! GTK_WIDGET_VISIBLE (nav_dialog->shell))
|
|
return;
|
|
|
|
update_zoom_label (nav_dialog);
|
|
update_zoom_adjustment (nav_dialog);
|
|
|
|
/* Undraw old size */
|
|
nav_dialog_draw_sqr (nav_dialog,
|
|
FALSE,
|
|
nav_dialog->dispx, nav_dialog->dispy,
|
|
nav_dialog->dispwidth, nav_dialog->dispheight);
|
|
|
|
/* Update to new size */
|
|
nav_dialog_disp_area (nav_dialog, nav_dialog->gdisp);
|
|
|
|
/* and redraw */
|
|
nav_dialog_draw_sqr (nav_dialog,
|
|
FALSE,
|
|
nav_dialog->dispx, nav_dialog->dispy,
|
|
nav_dialog->dispwidth, nav_dialog->dispheight);
|
|
}
|
|
|
|
void
|
|
nav_dialog_preview_resized (NavigationDialog *nav_dialog)
|
|
{
|
|
if (! nav_dialog)
|
|
return;
|
|
|
|
/* force regeneration of the widgets
|
|
* bit of a fiddle... could cause if the image really is 1x1
|
|
* but the preview would not really matter in that case.
|
|
*/
|
|
nav_dialog->imagewidth = 1;
|
|
nav_dialog->imageheight = 1;
|
|
|
|
nav_dialog->nav_preview_width =
|
|
(gimprc.nav_preview_size < 0 || gimprc.nav_preview_size > 256) ?
|
|
NAV_PREVIEW_WIDTH : gimprc.nav_preview_size;
|
|
|
|
nav_dialog->nav_preview_height =
|
|
(gimprc.nav_preview_size < 0 || gimprc.nav_preview_size > 256) ?
|
|
NAV_PREVIEW_HEIGHT : gimprc.nav_preview_size;
|
|
|
|
nav_dialog_update_window_marker (nav_dialog);
|
|
}
|
|
|
|
void
|
|
nav_popup_click_handler (GtkWidget *widget,
|
|
GdkEventButton *event,
|
|
gpointer data)
|
|
{
|
|
GdkEventButton *bevent;
|
|
GimpDisplay *gdisp;
|
|
GimpDisplayShell *shell;
|
|
NavigationDialog *nav_dialog;
|
|
gint x, y;
|
|
gint x_org, y_org;
|
|
gint scr_w, scr_h;
|
|
|
|
gdisp = GIMP_DISPLAY (data);
|
|
|
|
shell = GIMP_DISPLAY_SHELL (gdisp->shell);
|
|
|
|
bevent = (GdkEventButton *) event;
|
|
|
|
if (! shell->nav_popup)
|
|
{
|
|
GtkWidget *frame;
|
|
|
|
shell->nav_popup = nav_dialog = nav_dialog_new (gdisp, NAV_POPUP);
|
|
|
|
nav_dialog->shell = gtk_window_new (GTK_WINDOW_POPUP);
|
|
gtk_widget_set_events (nav_dialog->shell, PREVIEW_MASK);
|
|
|
|
g_object_weak_ref (G_OBJECT (nav_dialog->shell),
|
|
(GWeakNotify) g_free,
|
|
nav_dialog);
|
|
|
|
frame = gtk_frame_new (NULL);
|
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
|
|
gtk_container_add (GTK_CONTAINER (nav_dialog->shell), frame);
|
|
gtk_widget_show (frame);
|
|
|
|
nav_dialog->preview_frame = gtk_frame_new (NULL);
|
|
gtk_frame_set_shadow_type (GTK_FRAME (nav_dialog->preview_frame),
|
|
GTK_SHADOW_IN);
|
|
gtk_container_add (GTK_CONTAINER (frame), nav_dialog->preview_frame);
|
|
gtk_widget_show (nav_dialog->preview_frame);
|
|
|
|
create_preview_widget (nav_dialog);
|
|
|
|
gtk_widget_set_extension_events (nav_dialog->preview,
|
|
GDK_EXTENSION_EVENTS_ALL);
|
|
|
|
nav_dialog_disp_area (nav_dialog, nav_dialog->gdisp);
|
|
}
|
|
else
|
|
{
|
|
nav_dialog = shell->nav_popup;
|
|
|
|
gtk_widget_hide (nav_dialog->shell);
|
|
|
|
nav_dialog_disp_area (nav_dialog, nav_dialog->gdisp);
|
|
nav_dialog_update_preview (nav_dialog);
|
|
}
|
|
|
|
/* decide where to put the popup */
|
|
gdk_window_get_origin (widget->window, &x_org, &y_org);
|
|
|
|
scr_w = gdk_screen_width ();
|
|
scr_h = gdk_screen_height ();
|
|
|
|
x = x_org + bevent->x - nav_dialog->dispx -
|
|
((nav_dialog->dispwidth - BORDER_PEN_WIDTH + 1) * 0.5) - 2;
|
|
y = y_org + bevent->y - nav_dialog->dispy -
|
|
((nav_dialog->dispheight - BORDER_PEN_WIDTH + 1)* 0.5) - 2;
|
|
|
|
/* If the popup doesn't fit into the screen, we have a problem.
|
|
* We move the popup onscreen and risk that the pointer is not
|
|
* in the square representing the viewable area anymore. Moving
|
|
* the pointer will make the image scroll by a large amount,
|
|
* but then it works as usual. Probably better than a popup that
|
|
* is completely unusable in the lower right of the screen.
|
|
*
|
|
* Warping the pointer would be another solution ...
|
|
*/
|
|
x = (x < 0) ? 0 : x;
|
|
y = (y < 0) ? 0 : y;
|
|
x = (x + nav_dialog->pwidth > scr_w) ? scr_w - nav_dialog->pwidth - 2: x;
|
|
y = (y + nav_dialog->pheight > scr_h) ? scr_h - nav_dialog->pheight - 2: y;
|
|
|
|
gtk_widget_set_uposition (nav_dialog->shell, x, y);
|
|
gtk_widget_show (nav_dialog->shell);
|
|
|
|
gdk_flush();
|
|
|
|
/* fill in then set up handlers for mouse motion etc */
|
|
nav_dialog->motion_offsetx =
|
|
(nav_dialog->dispwidth - BORDER_PEN_WIDTH + 1) * 0.5;
|
|
|
|
nav_dialog->motion_offsety =
|
|
(nav_dialog->dispheight - BORDER_PEN_WIDTH + 1) * 0.5;
|
|
|
|
if (GTK_WIDGET_VISIBLE (nav_dialog->preview))
|
|
nav_dialog_grab_pointer (nav_dialog, nav_dialog->preview);
|
|
}
|
|
|
|
|
|
/* private functions */
|
|
|
|
static NavigationDialog *
|
|
nav_dialog_new (GimpDisplay *gdisp,
|
|
NavWinType ptype)
|
|
{
|
|
NavigationDialog *nav_dialog;
|
|
|
|
nav_dialog = g_new0 (NavigationDialog, 1);
|
|
|
|
nav_dialog->ptype = ptype;
|
|
nav_dialog->shell = NULL;
|
|
nav_dialog->preview = NULL;
|
|
nav_dialog->zoom_label = NULL;
|
|
nav_dialog->zoom_adjustment = NULL;
|
|
nav_dialog->gdisp = gdisp;
|
|
nav_dialog->dispx = -1;
|
|
nav_dialog->dispy = -1;
|
|
nav_dialog->dispwidth = -1;
|
|
nav_dialog->dispheight = -1;
|
|
nav_dialog->imagewidth = -1;
|
|
nav_dialog->imageheight = -1;
|
|
nav_dialog->sq_grabbed = FALSE;
|
|
nav_dialog->ratio = 1.0;
|
|
|
|
nav_dialog->nav_preview_width =
|
|
(gimprc.nav_preview_size < 0 || gimprc.nav_preview_size > 256) ?
|
|
NAV_PREVIEW_WIDTH : gimprc.nav_preview_size;
|
|
|
|
nav_dialog->nav_preview_height =
|
|
(gimprc.nav_preview_size < 0 || gimprc.nav_preview_size > 256) ?
|
|
NAV_PREVIEW_HEIGHT : gimprc.nav_preview_size;
|
|
|
|
nav_dialog->block_adj_sig = FALSE;
|
|
nav_dialog->frozen = FALSE;
|
|
nav_dialog->idle_id = 0;
|
|
|
|
return nav_dialog;
|
|
}
|
|
|
|
static gchar *
|
|
nav_dialog_title (GimpDisplay *gdisp)
|
|
{
|
|
gchar *basename;
|
|
gchar *title;
|
|
|
|
basename = g_path_get_basename (gimp_image_filename (gdisp->gimage));
|
|
|
|
title = g_strdup_printf (_("Navigation: %s-%d.%d"),
|
|
basename,
|
|
gimp_image_get_ID (gdisp->gimage),
|
|
gdisp->instance);
|
|
|
|
g_free (basename);
|
|
|
|
return title;
|
|
}
|
|
|
|
static void
|
|
nav_dialog_close_callback (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
NavigationDialog *nav_dialog;
|
|
|
|
nav_dialog = (NavigationDialog *) data;
|
|
|
|
gtk_widget_hide (nav_dialog->shell);
|
|
}
|
|
|
|
static void
|
|
nav_dialog_disp_area (NavigationDialog *nav_dialog,
|
|
GimpDisplay *gdisp)
|
|
{
|
|
GimpDisplayShell *shell;
|
|
GimpImage *gimage;
|
|
gint newwidth;
|
|
gint newheight;
|
|
gdouble xratio;
|
|
gdouble yratio; /* Screen res ratio */
|
|
gboolean need_update = FALSE;
|
|
|
|
shell = GIMP_DISPLAY_SHELL (gdisp->shell);
|
|
|
|
/* Calculate preview size */
|
|
gimage = gdisp->gimage;
|
|
|
|
xratio = SCALEFACTOR_X (gdisp);
|
|
yratio = SCALEFACTOR_Y (gdisp);
|
|
|
|
if (nav_dialog->new_preview)
|
|
{
|
|
if (GIMP_PREVIEW (nav_dialog->new_preview)->dot_for_dot !=
|
|
gdisp->dot_for_dot)
|
|
gimp_preview_set_dot_for_dot (GIMP_PREVIEW (nav_dialog->new_preview),
|
|
gdisp->dot_for_dot);
|
|
|
|
gimp_navigation_preview_set_marker
|
|
(GIMP_NAVIGATION_PREVIEW (nav_dialog->new_preview),
|
|
RINT (shell->offset_x / xratio),
|
|
RINT (shell->offset_y / yratio),
|
|
RINT (shell->disp_width / xratio),
|
|
RINT (shell->disp_height / yratio));
|
|
}
|
|
|
|
nav_dialog->dispx = shell->offset_x * nav_dialog->ratio / xratio + 0.5;
|
|
nav_dialog->dispy = shell->offset_y * nav_dialog->ratio/yratio + 0.5;
|
|
|
|
nav_dialog->dispwidth = (shell->disp_width * nav_dialog->ratio) / xratio + 0.5;
|
|
nav_dialog->dispheight = (shell->disp_height * nav_dialog->ratio) / yratio + 0.5;
|
|
|
|
newwidth = gimage->width;
|
|
newheight = gimage->height;
|
|
|
|
if (! gdisp->dot_for_dot)
|
|
{
|
|
newwidth =
|
|
(newwidth * gdisp->gimage->yresolution) / gdisp->gimage->xresolution;
|
|
|
|
nav_dialog->dispx =
|
|
((shell->offset_x *
|
|
gdisp->gimage->yresolution * nav_dialog->ratio) /
|
|
(gdisp->gimage->xresolution * xratio)) + 0.5; /*here*/
|
|
|
|
nav_dialog->dispwidth =
|
|
((shell->disp_width *
|
|
gdisp->gimage->yresolution * nav_dialog->ratio) /
|
|
(gdisp->gimage->xresolution * xratio)) + 0.5; /*here*/
|
|
}
|
|
|
|
if ((nav_dialog->imagewidth > 0 && newwidth != nav_dialog->imagewidth) ||
|
|
(nav_dialog->imageheight > 0 && newheight != nav_dialog->imageheight))
|
|
{
|
|
/* Must change the preview size */
|
|
|
|
if (nav_dialog->ptype != NAV_POPUP)
|
|
{
|
|
gtk_window_set_focus (GTK_WINDOW (nav_dialog->shell), NULL);
|
|
}
|
|
|
|
gtk_widget_destroy (nav_dialog->preview);
|
|
create_preview_widget (nav_dialog);
|
|
|
|
need_update = TRUE;
|
|
}
|
|
|
|
nav_dialog->imagewidth = newwidth;
|
|
nav_dialog->imageheight = newheight;
|
|
|
|
/* Normalise */
|
|
nav_dialog->dispwidth = MAX (nav_dialog->dispwidth, 2);
|
|
nav_dialog->dispheight = MAX (nav_dialog->dispheight, 2);
|
|
|
|
nav_dialog->dispwidth = MIN (nav_dialog->dispwidth, nav_dialog->pwidth);
|
|
nav_dialog->dispheight = MIN (nav_dialog->dispheight, nav_dialog->pheight);
|
|
|
|
if (need_update)
|
|
{
|
|
if (nav_dialog->ptype != NAV_POPUP)
|
|
{
|
|
gtk_window_set_focus (GTK_WINDOW (nav_dialog->shell),
|
|
nav_dialog->preview);
|
|
|
|
nav_dialog_idle_update_preview (nav_dialog);
|
|
}
|
|
else
|
|
{
|
|
nav_dialog_update_preview (nav_dialog);
|
|
|
|
gtk_widget_draw (nav_dialog->preview, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
nav_dialog_draw_sqr (NavigationDialog *nav_dialog,
|
|
gboolean undraw,
|
|
gint x,
|
|
gint y,
|
|
gint w,
|
|
gint h)
|
|
{
|
|
if (! nav_dialog->gc)
|
|
{
|
|
if (! GTK_WIDGET_REALIZED (nav_dialog->shell))
|
|
gtk_widget_realize (nav_dialog->shell);
|
|
|
|
nav_dialog->gc = gdk_gc_new (nav_dialog->shell->window);
|
|
|
|
gdk_gc_set_function (nav_dialog->gc, GDK_INVERT);
|
|
gdk_gc_set_line_attributes (nav_dialog->gc, BORDER_PEN_WIDTH,
|
|
GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_ROUND);
|
|
}
|
|
|
|
if (undraw)
|
|
{
|
|
if (nav_dialog->dispx != 0 ||
|
|
nav_dialog->dispy != 0 ||
|
|
nav_dialog->pwidth != nav_dialog->dispwidth ||
|
|
nav_dialog->pheight != nav_dialog->dispheight)
|
|
{
|
|
/* first undraw from last co-ords */
|
|
gdk_draw_rectangle (nav_dialog->preview->window, nav_dialog->gc,
|
|
FALSE,
|
|
nav_dialog->dispx,
|
|
nav_dialog->dispy,
|
|
nav_dialog->dispwidth - BORDER_PEN_WIDTH + 1,
|
|
nav_dialog->dispheight - BORDER_PEN_WIDTH + 1);
|
|
}
|
|
}
|
|
|
|
if (x != 0 || y != 0 ||
|
|
w != nav_dialog->pwidth || h != nav_dialog->pheight)
|
|
{
|
|
gdk_draw_rectangle (nav_dialog->preview->window, nav_dialog->gc,
|
|
FALSE,
|
|
x, y,
|
|
w - BORDER_PEN_WIDTH + 1,
|
|
h - BORDER_PEN_WIDTH + 1);
|
|
}
|
|
|
|
nav_dialog->dispx = x;
|
|
nav_dialog->dispy = y;
|
|
nav_dialog->dispwidth = w;
|
|
nav_dialog->dispheight = h;
|
|
}
|
|
|
|
static void
|
|
set_size_data (NavigationDialog *nav_dialog)
|
|
{
|
|
GimpDisplay *gdisp;
|
|
GimpImage *gimage;
|
|
gint sel_width;
|
|
gint sel_height;
|
|
gint pwidth;
|
|
gint pheight;
|
|
|
|
gdisp = nav_dialog->gdisp;
|
|
gimage = gdisp->gimage;
|
|
|
|
sel_width = gimage->width;
|
|
sel_height = gimage->height;
|
|
|
|
if (! gdisp->dot_for_dot)
|
|
sel_width =
|
|
(sel_width * gdisp->gimage->yresolution) / gdisp->gimage->xresolution;
|
|
|
|
if (sel_width > sel_height)
|
|
{
|
|
pwidth = nav_dialog->nav_preview_width;
|
|
|
|
nav_dialog->ratio = (gdouble) pwidth / (gdouble) sel_width;
|
|
}
|
|
else
|
|
{
|
|
pheight = nav_dialog->nav_preview_height;
|
|
|
|
nav_dialog->ratio = (gdouble) pheight / (gdouble) sel_height;
|
|
}
|
|
|
|
pwidth = sel_width * nav_dialog->ratio + 0.5;
|
|
pheight = sel_height * nav_dialog->ratio + 0.5;
|
|
|
|
nav_dialog->pwidth = pwidth;
|
|
nav_dialog->pheight = pheight;
|
|
}
|
|
|
|
static void
|
|
create_preview_widget (NavigationDialog *nav_dialog)
|
|
{
|
|
GtkWidget *preview;
|
|
|
|
preview = gtk_preview_new (GTK_PREVIEW_COLOR);
|
|
gtk_widget_set_events (GTK_WIDGET (preview), PREVIEW_MASK);
|
|
GTK_WIDGET_SET_FLAGS (preview, GTK_CAN_FOCUS);
|
|
gtk_preview_set_dither (GTK_PREVIEW (preview), GDK_RGB_DITHER_MAX);
|
|
gtk_container_add (GTK_CONTAINER (nav_dialog->preview_frame), preview);
|
|
gtk_widget_show (preview);
|
|
|
|
nav_dialog->preview = preview;
|
|
|
|
set_size_data (nav_dialog);
|
|
|
|
gtk_preview_size (GTK_PREVIEW (preview),
|
|
nav_dialog->pwidth, nav_dialog->pheight);
|
|
|
|
g_signal_connect (G_OBJECT (preview), "event",
|
|
G_CALLBACK (nav_dialog_preview_events),
|
|
nav_dialog);
|
|
}
|
|
|
|
static gboolean
|
|
nav_dialog_update_preview_idle_func (NavigationDialog *nav_dialog)
|
|
{
|
|
nav_dialog->idle_id = 0;
|
|
|
|
/* If the gdisp has gone then don't do anything in this timer */
|
|
if (! nav_dialog->gdisp)
|
|
return FALSE;
|
|
|
|
nav_dialog_update_preview (nav_dialog);
|
|
nav_dialog_disp_area (nav_dialog, nav_dialog->gdisp);
|
|
gtk_widget_queue_draw (nav_dialog->preview);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
nav_dialog_idle_update_preview (NavigationDialog *nav_dialog)
|
|
{
|
|
if (nav_dialog->idle_id)
|
|
return;
|
|
|
|
nav_dialog->idle_id =
|
|
g_idle_add_full (G_PRIORITY_LOW,
|
|
(GSourceFunc) nav_dialog_update_preview_idle_func,
|
|
nav_dialog,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
nav_dialog_update_preview (NavigationDialog *nav_dialog)
|
|
{
|
|
GimpDisplay *gdisp;
|
|
TempBuf *preview_buf;
|
|
TempBuf *preview_buf_ptr;
|
|
TempBuf *preview_buf_notdot = NULL;
|
|
guchar *src, *buf, *dest;
|
|
gint x, y;
|
|
gint pwidth, pheight;
|
|
GimpImage *gimage;
|
|
gdouble r, g, b, a, chk;
|
|
gint xoff = 0;
|
|
gint yoff = 0;
|
|
|
|
gdisp = nav_dialog->gdisp;
|
|
gimage = gdisp->gimage;
|
|
|
|
gimp_set_busy (gimage->gimp);
|
|
|
|
/* Min size is 2 */
|
|
pwidth = nav_dialog->pwidth;
|
|
pheight = nav_dialog->pheight;
|
|
|
|
/* we need a large normal preview which we will cut down later.
|
|
* gimp_image_construct_composite_preview() can't cope with
|
|
* dot_for_dot not been set.
|
|
*/
|
|
if (! gdisp->dot_for_dot)
|
|
{
|
|
gint sel_width = gimage->width;
|
|
gint sel_height = gimage->height;
|
|
gdouble tratio;
|
|
|
|
if (sel_width > sel_height)
|
|
tratio = (gdouble) nav_dialog->nav_preview_width / ((gdouble) sel_width);
|
|
else
|
|
tratio = (gdouble) nav_dialog->nav_preview_height / ((gdouble) sel_height);
|
|
|
|
pwidth = sel_width * tratio + 0.5;
|
|
pheight = sel_height * tratio + 0.5;
|
|
}
|
|
|
|
if (nav_dialog->ratio > 1.0) /* Preview is scaling up! */
|
|
{
|
|
TempBuf *tmp;
|
|
|
|
tmp = gimp_viewable_get_new_preview (GIMP_VIEWABLE (gimage),
|
|
gimage->width,
|
|
gimage->height);
|
|
preview_buf = temp_buf_scale (tmp,
|
|
pwidth,
|
|
pheight);
|
|
temp_buf_free (tmp);
|
|
}
|
|
else
|
|
{
|
|
preview_buf = gimp_viewable_get_new_preview (GIMP_VIEWABLE (gimage),
|
|
MAX (pwidth, 2),
|
|
MAX (pheight, 2));
|
|
}
|
|
|
|
/* reset & get new preview
|
|
*
|
|
* FIXME: should use gimp_preview_scale()
|
|
*/
|
|
if (! gdisp->dot_for_dot)
|
|
{
|
|
gint loop1, loop2;
|
|
gdouble x_ratio, y_ratio;
|
|
guchar *src_data;
|
|
guchar *dest_data;
|
|
|
|
preview_buf_notdot = temp_buf_new (nav_dialog->pwidth,
|
|
nav_dialog->pheight,
|
|
preview_buf->bytes,
|
|
0, 0, NULL);
|
|
|
|
x_ratio = (gdouble) pwidth / (gdouble) nav_dialog->pwidth;
|
|
y_ratio = (gdouble) pheight / (gdouble) nav_dialog->pheight;
|
|
|
|
src_data = temp_buf_data (preview_buf);
|
|
dest_data = temp_buf_data (preview_buf_notdot);
|
|
|
|
for (loop1 = 0 ; loop1 < nav_dialog->pheight ; loop1++)
|
|
for (loop2 = 0 ; loop2 < nav_dialog->pwidth ; loop2++)
|
|
{
|
|
gint i;
|
|
guchar *src_pixel;
|
|
guchar *dest_pixel;
|
|
|
|
src_pixel = src_data +
|
|
((gint) (loop2 * x_ratio)) * preview_buf->bytes +
|
|
((gint) (loop1 * y_ratio)) * pwidth * preview_buf->bytes;
|
|
|
|
dest_pixel = dest_data +
|
|
(loop2 + loop1 * nav_dialog->pwidth) * preview_buf->bytes;
|
|
|
|
for (i = 0 ; i < preview_buf->bytes; i++)
|
|
*dest_pixel++ = *src_pixel++;
|
|
}
|
|
|
|
pwidth = nav_dialog->pwidth;
|
|
pheight = nav_dialog->pheight;
|
|
|
|
src = temp_buf_data (preview_buf_notdot);
|
|
preview_buf_ptr = preview_buf_notdot;
|
|
}
|
|
else
|
|
{
|
|
src = temp_buf_data (preview_buf);
|
|
preview_buf_ptr = preview_buf;
|
|
}
|
|
|
|
buf = g_new (gchar, preview_buf_ptr->width * 3);
|
|
|
|
for (y = 0; y < preview_buf_ptr->height ; y++)
|
|
{
|
|
dest = buf;
|
|
switch (preview_buf_ptr->bytes)
|
|
{
|
|
case 4:
|
|
for (x = 0; x < preview_buf_ptr->width; x++)
|
|
{
|
|
r = ((gdouble) (*(src++))) / 255.0;
|
|
g = ((gdouble) (*(src++))) / 255.0;
|
|
b = ((gdouble) (*(src++))) / 255.0;
|
|
a = ((gdouble) (*(src++))) / 255.0;
|
|
chk = ((gdouble) ((( (x^y) & 4 ) << 4) | 128)) / 255.0;
|
|
*(dest++) = (guchar) ((chk + (r - chk) * a) * 255.0);
|
|
*(dest++) = (guchar) ((chk + (g - chk) * a) * 255.0);
|
|
*(dest++) = (guchar) ((chk + (b - chk) * a) * 255.0);
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
for (x = 0; x < preview_buf_ptr->width; x++)
|
|
{
|
|
r = ((gdouble) (*(src++))) / 255.0;
|
|
a = ((gdouble) (*(src++))) / 255.0;
|
|
chk = ((gdouble) ((( (x^y) & 4 ) << 4) | 128)) / 255.0;
|
|
*(dest++) = (guchar) ((chk + (r - chk) * a) * 255.0);
|
|
*(dest++) = (guchar) ((chk + (r - chk) * a) * 255.0);
|
|
*(dest++) = (guchar) ((chk + (r - chk) * a) * 255.0);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
g_warning ("nav_dialog_update_preview(): UNKNOWN TempBuf bpp");
|
|
}
|
|
|
|
gtk_preview_draw_row (GTK_PREVIEW (nav_dialog->preview),
|
|
buf,
|
|
xoff, yoff + y,
|
|
preview_buf_ptr->width);
|
|
}
|
|
|
|
g_free (buf);
|
|
|
|
temp_buf_free (preview_buf);
|
|
|
|
if (preview_buf_notdot)
|
|
temp_buf_free (preview_buf_notdot);
|
|
|
|
gimp_unset_busy (gimage->gimp);
|
|
}
|
|
|
|
static void
|
|
nav_dialog_update_preview_blank (NavigationDialog *nav_dialog)
|
|
{
|
|
guchar *buf;
|
|
guchar *dest;
|
|
gint x, y;
|
|
gdouble chk;
|
|
|
|
buf = g_new (gchar, nav_dialog->pwidth * 3);
|
|
|
|
for (y = 0; y < nav_dialog->pheight ; y++)
|
|
{
|
|
dest = buf;
|
|
|
|
for (x = 0; x < nav_dialog->pwidth; x++)
|
|
{
|
|
chk = ((gdouble) ((( (x^y) & 4 ) << 4) | 128)) / 255.0;
|
|
chk *= 128.0;
|
|
|
|
*(dest++) = (guchar) chk;
|
|
*(dest++) = (guchar) chk;
|
|
*(dest++) = (guchar) chk;
|
|
}
|
|
|
|
gtk_preview_draw_row (GTK_PREVIEW (nav_dialog->preview),
|
|
buf,
|
|
0, y, nav_dialog->pwidth);
|
|
}
|
|
|
|
g_free (buf);
|
|
}
|
|
|
|
static void
|
|
update_zoom_label (NavigationDialog *nav_dialog)
|
|
{
|
|
gchar scale_str[MAX_SCALE_BUF];
|
|
|
|
if (! nav_dialog->zoom_label)
|
|
return;
|
|
|
|
/* Update the zoom scale string */
|
|
g_snprintf (scale_str, MAX_SCALE_BUF, "%d:%d",
|
|
SCALEDEST (nav_dialog->gdisp),
|
|
SCALESRC (nav_dialog->gdisp));
|
|
|
|
gtk_label_set_text (GTK_LABEL (nav_dialog->zoom_label), scale_str);
|
|
}
|
|
|
|
static void
|
|
update_zoom_adjustment (NavigationDialog *nav_dialog)
|
|
{
|
|
GtkAdjustment *adj;
|
|
gdouble f;
|
|
gdouble val;
|
|
|
|
if (! nav_dialog->zoom_adjustment)
|
|
return;
|
|
|
|
adj = GTK_ADJUSTMENT (nav_dialog->zoom_adjustment);
|
|
|
|
f = (((gdouble) SCALEDEST (nav_dialog->gdisp)) /
|
|
((gdouble) SCALESRC (nav_dialog->gdisp)));
|
|
|
|
if (f < 1.0)
|
|
{
|
|
val = -1.0 / f;
|
|
}
|
|
else
|
|
{
|
|
val = f;
|
|
}
|
|
|
|
if (abs ((gint) adj->value) != (gint) (val - 1) &&
|
|
! nav_dialog->block_adj_sig)
|
|
{
|
|
adj->value = val;
|
|
|
|
gtk_adjustment_changed (GTK_ADJUSTMENT (nav_dialog->zoom_adjustment));
|
|
}
|
|
}
|
|
|
|
static void
|
|
nav_dialog_update_real_view (NavigationDialog *nav_dialog,
|
|
gint tx,
|
|
gint ty)
|
|
{
|
|
GimpDisplayShell *shell;
|
|
gdouble xratio;
|
|
gdouble yratio;
|
|
gint xoffset;
|
|
gint yoffset;
|
|
gint xpnt;
|
|
gint ypnt;
|
|
|
|
shell = GIMP_DISPLAY_SHELL (nav_dialog->gdisp->shell);
|
|
|
|
xratio = SCALEFACTOR_X (nav_dialog->gdisp);
|
|
yratio = SCALEFACTOR_Y (nav_dialog->gdisp);
|
|
|
|
if ((tx + nav_dialog->dispwidth) >= nav_dialog->pwidth)
|
|
{
|
|
tx = nav_dialog->pwidth; /* Actually should be less...
|
|
* but bound check will save us.
|
|
*/
|
|
}
|
|
|
|
xpnt = (gint) (((gdouble) (tx) * xratio) / nav_dialog->ratio + 0.5);
|
|
|
|
if ((ty + nav_dialog->dispheight) >= nav_dialog->pheight)
|
|
{
|
|
ty = nav_dialog->pheight; /* Same comment as for xpnt above. */
|
|
}
|
|
|
|
ypnt = (gint) (((gdouble) (ty) * yratio) / nav_dialog->ratio + 0.5);
|
|
|
|
if (! nav_dialog->gdisp->dot_for_dot) /* here */
|
|
xpnt = (((gdouble) xpnt *
|
|
nav_dialog->gdisp->gimage->xresolution) /
|
|
nav_dialog->gdisp->gimage->yresolution) + 0.5;
|
|
|
|
xoffset = xpnt - shell->offset_x;
|
|
yoffset = ypnt - shell->offset_y;
|
|
|
|
gimp_display_shell_scroll (shell, xoffset, yoffset);
|
|
}
|
|
|
|
static void
|
|
nav_dialog_move_to_point (NavigationDialog *nav_dialog,
|
|
gint tx,
|
|
gint ty)
|
|
{
|
|
tx = CLAMP (tx, 0, nav_dialog->pwidth);
|
|
ty = CLAMP (ty, 0, nav_dialog->pheight);
|
|
|
|
if ((tx + nav_dialog->dispwidth) >= nav_dialog->pwidth)
|
|
{
|
|
tx = nav_dialog->pwidth - nav_dialog->dispwidth;
|
|
}
|
|
|
|
if ((ty + nav_dialog->dispheight) >= nav_dialog->pheight)
|
|
{
|
|
ty = nav_dialog->pheight - nav_dialog->dispheight;
|
|
}
|
|
|
|
if (nav_dialog->dispx == tx && nav_dialog->dispy == ty)
|
|
return;
|
|
|
|
nav_dialog_update_real_view (nav_dialog, tx, ty);
|
|
}
|
|
|
|
static void
|
|
nav_dialog_grab_pointer (NavigationDialog *nav_dialog,
|
|
GtkWidget *widget)
|
|
{
|
|
GdkCursor *cursor;
|
|
|
|
nav_dialog->sq_grabbed = TRUE;
|
|
|
|
gtk_grab_add (widget);
|
|
|
|
cursor = gdk_cursor_new (GDK_CROSSHAIR);
|
|
|
|
gdk_pointer_grab (widget->window, TRUE,
|
|
GDK_BUTTON_RELEASE_MASK |
|
|
GDK_POINTER_MOTION_HINT_MASK |
|
|
GDK_BUTTON_MOTION_MASK |
|
|
GDK_EXTENSION_EVENTS_ALL,
|
|
widget->window, cursor, 0);
|
|
|
|
gdk_cursor_unref (cursor);
|
|
}
|
|
|
|
static gboolean
|
|
nav_dialog_inside_preview_square (NavigationDialog *nav_dialog,
|
|
gint x,
|
|
gint y)
|
|
{
|
|
return (x > nav_dialog->dispx &&
|
|
x < nav_dialog->dispx + nav_dialog->dispwidth &&
|
|
y > nav_dialog->dispy &&
|
|
y < nav_dialog->dispy + nav_dialog->dispheight);
|
|
}
|
|
|
|
static gboolean
|
|
nav_dialog_preview_events (GtkWidget *widget,
|
|
GdkEvent *event,
|
|
gpointer data)
|
|
{
|
|
NavigationDialog *nav_dialog;
|
|
GimpDisplay *gdisp;
|
|
GimpDisplayShell *shell;
|
|
GdkEventButton *bevent;
|
|
GdkEventMotion *mevent;
|
|
GdkEventKey *kevent;
|
|
GdkModifierType mask;
|
|
gint tx = 0;
|
|
gint ty = 0;
|
|
gint mx;
|
|
gint my;
|
|
gboolean arrow_key = FALSE;
|
|
|
|
nav_dialog = (NavigationDialog *) data;
|
|
|
|
if (! nav_dialog || nav_dialog->frozen)
|
|
return FALSE;
|
|
|
|
gdisp = nav_dialog->gdisp;
|
|
|
|
shell = GIMP_DISPLAY_SHELL (gdisp->shell);
|
|
|
|
switch (event->type)
|
|
{
|
|
case GDK_EXPOSE:
|
|
/* call default handler explicitly, then draw the square */
|
|
GTK_WIDGET_GET_CLASS (widget)->expose_event (widget,
|
|
(GdkEventExpose *) event);
|
|
nav_dialog_draw_sqr (nav_dialog, FALSE,
|
|
nav_dialog->dispx, nav_dialog->dispy,
|
|
nav_dialog->dispwidth, nav_dialog->dispheight);
|
|
return TRUE;
|
|
|
|
case GDK_MAP:
|
|
if (nav_dialog->ptype == NAV_POPUP)
|
|
{
|
|
nav_dialog_update_preview (nav_dialog);
|
|
|
|
/* First time displayed.... get the pointer! */
|
|
nav_dialog_grab_pointer (nav_dialog, nav_dialog->preview);
|
|
}
|
|
else
|
|
{
|
|
nav_dialog_idle_update_preview (nav_dialog);
|
|
}
|
|
break;
|
|
|
|
case GDK_BUTTON_PRESS:
|
|
bevent = (GdkEventButton *) event;
|
|
tx = bevent->x;
|
|
ty = bevent->y;
|
|
|
|
/* Must start the move */
|
|
switch (bevent->button)
|
|
{
|
|
case 1:
|
|
if (! nav_dialog_inside_preview_square (nav_dialog, tx, ty))
|
|
{
|
|
/* Direct scroll to the location
|
|
* view scrolled to the center or nearest possible point
|
|
*/
|
|
tx -= nav_dialog->dispwidth / 2;
|
|
ty -= nav_dialog->dispheight / 2;
|
|
|
|
nav_dialog_move_to_point (nav_dialog, tx, ty);
|
|
|
|
nav_dialog->motion_offsetx = nav_dialog->dispwidth / 2;
|
|
nav_dialog->motion_offsety = nav_dialog->dispheight / 2;
|
|
}
|
|
else
|
|
{
|
|
nav_dialog->motion_offsetx = tx - nav_dialog->dispx;
|
|
nav_dialog->motion_offsety = ty - nav_dialog->dispy;
|
|
}
|
|
|
|
nav_dialog_grab_pointer (nav_dialog, widget);
|
|
|
|
break;
|
|
|
|
/* wheelmouse support */
|
|
case 4:
|
|
if (bevent->state & GDK_SHIFT_MASK)
|
|
{
|
|
gimp_display_shell_scale (shell, GIMP_ZOOM_IN);
|
|
}
|
|
else
|
|
{
|
|
GtkAdjustment *adj =
|
|
(bevent->state & GDK_CONTROL_MASK) ?
|
|
shell->hsbdata : shell->vsbdata;
|
|
gfloat new_value = adj->value - adj->page_increment / 2;
|
|
new_value =
|
|
CLAMP (new_value, adj->lower, adj->upper - adj->page_size);
|
|
gtk_adjustment_set_value (adj, new_value);
|
|
}
|
|
break;
|
|
|
|
case 5:
|
|
if (bevent->state & GDK_SHIFT_MASK)
|
|
{
|
|
gimp_display_shell_scale (shell, GIMP_ZOOM_OUT);
|
|
}
|
|
else
|
|
{
|
|
GtkAdjustment *adj =
|
|
(bevent->state & GDK_CONTROL_MASK) ?
|
|
shell->hsbdata : shell->vsbdata;
|
|
gfloat new_value = adj->value + adj->page_increment / 2;
|
|
new_value = CLAMP (new_value,
|
|
adj->lower, adj->upper - adj->page_size);
|
|
gtk_adjustment_set_value (adj, new_value);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case GDK_BUTTON_RELEASE:
|
|
bevent = (GdkEventButton *) event;
|
|
tx = bevent->x;
|
|
ty = bevent->y;
|
|
|
|
switch (bevent->button)
|
|
{
|
|
case 1:
|
|
nav_dialog->sq_grabbed = FALSE;
|
|
gtk_grab_remove (widget);
|
|
gdk_pointer_ungrab (0);
|
|
if (nav_dialog->ptype == NAV_POPUP)
|
|
{
|
|
gtk_widget_hide (nav_dialog->shell);
|
|
}
|
|
gdk_flush ();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case GDK_KEY_PRESS:
|
|
/* hack for the update preview... needs to be fixed */
|
|
kevent = (GdkEventKey *) event;
|
|
|
|
switch (kevent->keyval)
|
|
{
|
|
case GDK_space:
|
|
gdk_window_raise (gdisp->shell->window);
|
|
break;
|
|
case GDK_Up:
|
|
arrow_key = TRUE;
|
|
tx = nav_dialog->dispx;
|
|
ty = nav_dialog->dispy - 1;
|
|
break;
|
|
case GDK_Left:
|
|
arrow_key = TRUE;
|
|
tx = nav_dialog->dispx - 1;
|
|
ty = nav_dialog->dispy;
|
|
break;
|
|
case GDK_Right:
|
|
arrow_key = TRUE;
|
|
tx = nav_dialog->dispx + 1;
|
|
ty = nav_dialog->dispy;
|
|
break;
|
|
case GDK_Down:
|
|
arrow_key = TRUE;
|
|
tx = nav_dialog->dispx;
|
|
ty = nav_dialog->dispy + 1;
|
|
break;
|
|
case GDK_equal:
|
|
gimp_display_shell_scale (shell, GIMP_ZOOM_IN);
|
|
break;
|
|
case GDK_minus:
|
|
gimp_display_shell_scale (shell, GIMP_ZOOM_OUT);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (arrow_key)
|
|
{
|
|
nav_dialog_move_to_point (nav_dialog, tx, ty);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case GDK_MOTION_NOTIFY:
|
|
mevent = (GdkEventMotion *) event;
|
|
|
|
if (! nav_dialog->sq_grabbed)
|
|
break;
|
|
|
|
gdk_window_get_pointer (widget->window, &mx, &my, &mask);
|
|
tx = mx;
|
|
ty = my;
|
|
|
|
tx = tx - nav_dialog->motion_offsetx;
|
|
ty = ty - nav_dialog->motion_offsety;
|
|
|
|
nav_dialog_move_to_point (nav_dialog, tx, ty);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static void
|
|
nav_image_need_update (GimpImage *gimage,
|
|
gpointer data)
|
|
{
|
|
NavigationDialog *nav_dialog;
|
|
|
|
nav_dialog = (NavigationDialog *) data;
|
|
|
|
if (! nav_dialog ||
|
|
! GTK_WIDGET_VISIBLE (nav_dialog->shell) ||
|
|
nav_dialog->frozen)
|
|
return;
|
|
|
|
nav_dialog_idle_update_preview (nav_dialog);
|
|
}
|
|
|
|
static void
|
|
navwindow_zoomin (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
NavigationDialog *nav_dialog;
|
|
|
|
nav_dialog = (NavigationDialog *) data;
|
|
|
|
if(! nav_dialog || nav_dialog->frozen)
|
|
return;
|
|
|
|
gimp_display_shell_scale (GIMP_DISPLAY_SHELL (nav_dialog->gdisp->shell),
|
|
GIMP_ZOOM_IN);
|
|
}
|
|
|
|
static void
|
|
navwindow_zoomout (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
NavigationDialog *nav_dialog;
|
|
|
|
nav_dialog = (NavigationDialog *)data;
|
|
|
|
if (! nav_dialog || nav_dialog->frozen)
|
|
return;
|
|
|
|
gimp_display_shell_scale (GIMP_DISPLAY_SHELL (nav_dialog->gdisp->shell),
|
|
GIMP_ZOOM_OUT);
|
|
}
|
|
|
|
static void
|
|
zoom_adj_changed (GtkAdjustment *adj,
|
|
gpointer data)
|
|
{
|
|
NavigationDialog *nav_dialog;
|
|
gint scalesrc;
|
|
gint scaledest;
|
|
|
|
nav_dialog = (NavigationDialog *)data;
|
|
|
|
if (! nav_dialog || nav_dialog->frozen)
|
|
return;
|
|
|
|
if (adj->value < 0.0)
|
|
{
|
|
scalesrc = abs ((gint) adj->value - 1);
|
|
scaledest = 1;
|
|
}
|
|
else
|
|
{
|
|
scaledest = abs ((gint) adj->value + 1);
|
|
scalesrc = 1;
|
|
}
|
|
|
|
nav_dialog->block_adj_sig = TRUE;
|
|
gimp_display_shell_scale (GIMP_DISPLAY_SHELL (nav_dialog->gdisp),
|
|
(scaledest * 100) + scalesrc);
|
|
nav_dialog->block_adj_sig = FALSE;
|
|
}
|
|
|
|
static GtkWidget *
|
|
nav_create_button_area (NavigationDialog *nav_dialog)
|
|
{
|
|
GtkWidget *hbox;
|
|
GtkWidget *vbox;
|
|
GtkWidget *button;
|
|
GtkWidget *hscale;
|
|
gchar scale_str[MAX_SCALE_BUF];
|
|
|
|
vbox = gtk_vbox_new (FALSE, 0);
|
|
|
|
hbox = gtk_hbox_new (FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
|
gtk_widget_show (hbox);
|
|
|
|
button = gimp_pixmap_button_new (zoom_out_xpm, NULL);
|
|
GTK_WIDGET_UNSET_FLAGS (button, GTK_RECEIVES_DEFAULT);
|
|
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_show (button);
|
|
|
|
g_signal_connect (G_OBJECT (button), "clicked",
|
|
G_CALLBACK (navwindow_zoomout),
|
|
nav_dialog);
|
|
|
|
/* user zoom ratio */
|
|
g_snprintf (scale_str, MAX_SCALE_BUF, "%d:%d",
|
|
SCALEDEST (nav_dialog->gdisp),
|
|
SCALESRC (nav_dialog->gdisp));
|
|
|
|
nav_dialog->zoom_label = gtk_label_new (scale_str);
|
|
gtk_box_pack_start (GTK_BOX (hbox), nav_dialog->zoom_label, TRUE, TRUE, 0);
|
|
gtk_widget_show (nav_dialog->zoom_label);
|
|
|
|
button = gimp_pixmap_button_new (zoom_in_xpm, NULL);
|
|
GTK_WIDGET_UNSET_FLAGS (button, GTK_RECEIVES_DEFAULT);
|
|
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_show (button);
|
|
|
|
g_signal_connect (G_OBJECT (button), "clicked",
|
|
G_CALLBACK (navwindow_zoomin),
|
|
nav_dialog);
|
|
|
|
nav_dialog->zoom_adjustment =
|
|
gtk_adjustment_new (0.0, -15.0, 16.0, 1.0, 1.0, 1.0);
|
|
|
|
g_signal_connect (G_OBJECT (nav_dialog->zoom_adjustment), "value_changed",
|
|
G_CALLBACK (zoom_adj_changed),
|
|
nav_dialog);
|
|
|
|
hscale = gtk_hscale_new (GTK_ADJUSTMENT (nav_dialog->zoom_adjustment));
|
|
gtk_scale_set_draw_value (GTK_SCALE (hscale), FALSE);
|
|
gtk_scale_set_digits (GTK_SCALE (hscale), 0);
|
|
gtk_box_pack_start (GTK_BOX (vbox), hscale, TRUE, TRUE, 0);
|
|
gtk_widget_show (hscale);
|
|
|
|
return vbox;
|
|
}
|
|
|
|
static void
|
|
nav_dialog_display_changed (GimpContext *context,
|
|
GimpDisplay *gdisp,
|
|
gpointer data)
|
|
{
|
|
GimpDisplay *old_gdisp;
|
|
GimpImage *gimage;
|
|
NavigationDialog *nav_dialog;
|
|
gchar *title;
|
|
|
|
nav_dialog = (NavigationDialog *) data;
|
|
|
|
old_gdisp = nav_dialog->gdisp;
|
|
|
|
if (! nav_dialog || gdisp == old_gdisp || ! gdisp)
|
|
return;
|
|
|
|
gtk_widget_set_sensitive (nav_window_auto->shell, TRUE);
|
|
|
|
nav_dialog->frozen = FALSE;
|
|
|
|
title = nav_dialog_title (gdisp);
|
|
|
|
gtk_window_set_title (GTK_WINDOW (nav_dialog->shell), title);
|
|
|
|
g_free (title);
|
|
|
|
gimage = gdisp->gimage;
|
|
|
|
if (gimage && gimp_container_have (context->gimp->images,
|
|
GIMP_OBJECT (gimage)))
|
|
{
|
|
nav_dialog->gdisp = gdisp;
|
|
|
|
/* Update preview to new display */
|
|
nav_dialog_preview_resized (nav_dialog);
|
|
|
|
if (nav_dialog->new_preview)
|
|
gimp_preview_set_viewable (GIMP_PREVIEW (nav_dialog->new_preview),
|
|
GIMP_VIEWABLE (gdisp->gimage));
|
|
|
|
/* Tie into the dirty signal so we can update the preview
|
|
* provided we haven't already
|
|
*/
|
|
if (! g_object_get_data (G_OBJECT (gimage), "nav_handlers_installed"))
|
|
{
|
|
g_signal_connect (G_OBJECT (gimage), "dirty",
|
|
G_CALLBACK (nav_image_need_update),
|
|
nav_dialog);
|
|
g_signal_connect (G_OBJECT (gimage), "clean",
|
|
G_CALLBACK (nav_image_need_update),
|
|
nav_dialog);
|
|
|
|
g_object_set_data (G_OBJECT (gimage),
|
|
"nav_handlers_installed",
|
|
nav_dialog);
|
|
}
|
|
}
|
|
}
|
|
|
|
static GimpDisplay *
|
|
nav_dialog_get_gdisp (void)
|
|
{
|
|
GList *list;
|
|
|
|
for (list = GIMP_LIST (the_gimp->images)->list;
|
|
list;
|
|
list = g_list_next (list))
|
|
{
|
|
GimpImage *gimage;
|
|
GimpDisplay *gdisp;
|
|
|
|
gimage = GIMP_IMAGE (list->data);
|
|
|
|
gdisp = gdisplays_check_valid (NULL, gimage);
|
|
|
|
if (gdisp)
|
|
return gdisp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|