mirror of
https://gitlab.gnome.org/GNOME/gimp
synced 2024-10-22 12:32:37 +00:00
1bcd3e1834
2001-07-07 Michael Natterer <mitch@gimp.org> * app/Makefile.am * app/context_manager.[ch]: removed. * app/app_procs.c: call tool_mananger instead of context_manager functions, pass "the_gimp" to some more functions. * app/drawable.[ch]: pass a GimpContext to drawable_fill(). * app/errors.c: behave according to "stack_trace_mode" when using the debugging signal handler. * app/gimprc.[ch]: removed the core/ config variables. * app/selection.c: set the selection's state to INVISIBLE in selection_pause(). * app/core/Makefile.am * app/core/gimpcoreconfig.[ch]: new files (the configuration variables used by core/). * app/core/gimpcontext.[ch]: removed the global contexts (user, default, ...) and their functions. It's no longer possible to pass NULL to the context functions to manipulate the current context (gimpcontext.c doesn't know the current context any more). * app/core/gimp.[ch]: added them here. The functions are now called gimp_[set|get]_*_context(). Added gimp_create_context() which is the only function to create contexts now. * app/gui/dialogs.[ch] * app/gui/gui.[ch]: pass "gimp" to all functions. * app/tools/tool_manager.[ch] * app/tools/tools.[ch]: pass "gimp" to lots of functions. Added the "global_tool_context" logic and the global/non-global paint options switching from the context_manager. Pass "gimp" to all tools' "register" functions. * app/tools/*: changed accordingly. * app/devices.c * app/disp_callbacks.c * app/file-open.[ch] * app/file-save.c * app/gdisplay.c * app/gimage.c * app/libgimp_glue.c * app/module_db.c * app/nav_window.c * app/plug_in.c * app/qmask.c * app/undo.c * app/base/base-config.c * app/core/gimpbrushpipe.c * app/core/gimpdrawable-offset.c * app/core/gimpgradient.c * app/core/gimpimage-duplicate.c * app/core/gimpimage-mask.c * app/core/gimpimage-new.c * app/core/gimpimage.c * app/core/gimppalette.c * app/core/gimptoolinfo.[ch] * app/core/gimpundo.c * app/gui/brush-select.c * app/gui/channels-commands.c * app/gui/color-area.c * app/gui/dialogs-constructors.c * app/gui/file-new-dialog.c * app/gui/file-open-dialog.c * app/gui/gradient-editor.c * app/gui/gradient-select.c * app/gui/info-window.c * app/gui/layers-commands.c * app/gui/menus.c * app/gui/palette-editor.c * app/gui/palette-import-dialog.c * app/gui/palette-select.c * app/gui/paths-dialog.c * app/gui/pattern-select.c * app/gui/preferences-dialog.c * app/gui/resize-dialog.c * app/gui/test-commands.c * app/gui/tool-options-dialog.c * app/gui/toolbox.c * app/gui/tools-commands.c * app/xcf/xcf-load.c * app/xcf/xcf-save.c * app/widgets/gimpchannellistview.c * app/widgets/gimpdnd.c * app/widgets/gimpdrawablelistview.[ch] * app/widgets/gimpimagedock.c * app/widgets/gimplayerlistview.c * app/pdb/brushes_cmds.c * app/pdb/drawable_cmds.c * app/pdb/gradient_select_cmds.c * app/pdb/gradients_cmds.c * app/pdb/palette_cmds.c * app/pdb/patterns_cmds.c * app/pdb/procedural_db.c * tools/pdbgen/pdb/brushes.pdb * tools/pdbgen/pdb/drawable.pdb * tools/pdbgen/pdb/gradient_select.pdb * tools/pdbgen/pdb/gradients.pdb * tools/pdbgen/pdb/palette.pdb * tools/pdbgen/pdb/patterns.pdb: changed accordingly: remove usage of gimp_context_[get|set]_*(NULL), create contexts with gimp_create_context(). Get the user/current context with gimp_get_[user|current]_context(). Added/removed access to the global "the_gimp" variable in some places. Get the core's config variables from "core_config".
1692 lines
43 KiB
C
1692 lines
43 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 "widgets/gimpnavigationpreview.h"
|
|
|
|
#include "app_procs.h"
|
|
#include "dialog_handler.h"
|
|
#include "gdisplay.h"
|
|
#include "nav_window.h"
|
|
|
|
#include "app_procs.h"
|
|
#include "gimprc.h"
|
|
#include "scroll.h"
|
|
#include "scale.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;
|
|
GDisplay *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 (GDisplay *gdisp,
|
|
NavWinType ptype);
|
|
static gchar * nav_dialog_title (GDisplay *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,
|
|
GDisplay *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 gboolean nav_dialog_expose_event (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,
|
|
GDisplay *gdisp,
|
|
gpointer data);
|
|
|
|
static GDisplay * 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)
|
|
{
|
|
GDisplay *gdisp;
|
|
gdouble xratio;
|
|
gdouble yratio;
|
|
gint xoffset;
|
|
gint yoffset;
|
|
|
|
gdisp = nav_dialog->gdisp;
|
|
|
|
xratio = SCALEFACTOR_X (gdisp);
|
|
yratio = SCALEFACTOR_Y (gdisp);
|
|
|
|
xoffset = x * xratio - gdisp->offset_x;
|
|
yoffset = y * yratio - gdisp->offset_y;
|
|
|
|
scroll_display (gdisp, xoffset, yoffset);
|
|
}
|
|
|
|
NavigationDialog *
|
|
nav_dialog_create (GDisplay *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_widget_hide (GTK_WIDGET (g_list_nth_data (gtk_container_children (GTK_CONTAINER (GTK_BIN (nav_dialog->shell)->child)), 0)));
|
|
|
|
gtk_widget_hide (GTK_DIALOG (nav_dialog->shell)->action_area);
|
|
|
|
dialog_register (nav_dialog->shell);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (nav_dialog->shell), "destroy",
|
|
GTK_SIGNAL_FUNC (dialog_unregister),
|
|
nav_dialog);
|
|
gtk_signal_connect_object (GTK_OBJECT (nav_dialog->shell), "destroy",
|
|
GTK_SIGNAL_FUNC (g_free),
|
|
(GtkObject *) 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);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (preview), "marker_changed",
|
|
GTK_SIGNAL_FUNC (nav_dialog_marker_changed),
|
|
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 (! gtk_object_get_data (GTK_OBJECT (gdisp->gimage),
|
|
"nav_handlers_installed"))
|
|
{
|
|
gtk_signal_connect_after (GTK_OBJECT (gdisp->gimage), "dirty",
|
|
GTK_SIGNAL_FUNC (nav_image_need_update),
|
|
nav_dialog);
|
|
gtk_signal_connect_after (GTK_OBJECT (gdisp->gimage), "clean",
|
|
GTK_SIGNAL_FUNC (nav_image_need_update),
|
|
nav_dialog);
|
|
|
|
gtk_object_set_data (GTK_OBJECT (gdisp->gimage),
|
|
"nav_handlers_installed",
|
|
nav_dialog);
|
|
}
|
|
|
|
return nav_dialog;
|
|
}
|
|
|
|
void
|
|
nav_dialog_free (GDisplay *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)
|
|
{
|
|
GDisplay *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;
|
|
GDisplay *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);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (context), "display_changed",
|
|
GTK_SIGNAL_FUNC (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;
|
|
GDisplay *gdisp = data;
|
|
NavigationDialog *nav_dialog;
|
|
gint x, y;
|
|
gint x_org, y_org;
|
|
gint scr_w, scr_h;
|
|
|
|
bevent = (GdkEventButton *) event;
|
|
|
|
if (! gdisp->nav_popup)
|
|
{
|
|
GtkWidget *frame;
|
|
|
|
gdisp->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);
|
|
|
|
gtk_signal_connect_object (GTK_OBJECT (nav_dialog->shell), "destroy",
|
|
GTK_SIGNAL_FUNC (g_free),
|
|
(GtkObject *) 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 = gdisp->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_popup (nav_dialog->shell, x, y);
|
|
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 (GDisplay *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 (GDisplay *gdisp)
|
|
{
|
|
gchar *basename;
|
|
gchar *title;
|
|
|
|
basename = g_basename (gimp_image_filename (gdisp->gimage));
|
|
|
|
title = g_strdup_printf (_("Navigation: %s-%d.%d"),
|
|
basename,
|
|
gimp_image_get_ID (gdisp->gimage),
|
|
gdisp->instance);
|
|
|
|
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,
|
|
GDisplay *gdisp)
|
|
{
|
|
GimpImage *gimage;
|
|
gint newwidth;
|
|
gint newheight;
|
|
gdouble xratio;
|
|
gdouble yratio; /* Screen res ratio */
|
|
gboolean need_update = FALSE;
|
|
|
|
/* 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 (gdisp->offset_x / xratio),
|
|
RINT (gdisp->offset_y / yratio),
|
|
RINT (gdisp->disp_width / xratio),
|
|
RINT (gdisp->disp_height / yratio));
|
|
}
|
|
|
|
nav_dialog->dispx = gdisp->offset_x * nav_dialog->ratio / xratio + 0.5;
|
|
nav_dialog->dispy = gdisp->offset_y * nav_dialog->ratio/yratio + 0.5;
|
|
|
|
nav_dialog->dispwidth = (gdisp->disp_width * nav_dialog->ratio) / xratio + 0.5;
|
|
nav_dialog->dispheight = (gdisp->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 =
|
|
((gdisp->offset_x *
|
|
gdisp->gimage->yresolution * nav_dialog->ratio) /
|
|
(gdisp->gimage->xresolution * xratio)) + 0.5; /*here*/
|
|
|
|
nav_dialog->dispwidth =
|
|
((gdisp->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)
|
|
{
|
|
GDisplay *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 (nav_dialog->preview),
|
|
nav_dialog->pwidth,
|
|
nav_dialog->pheight);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (preview), "event",
|
|
GTK_SIGNAL_FUNC (nav_dialog_preview_events),
|
|
nav_dialog);
|
|
|
|
gtk_signal_connect_after (GTK_OBJECT (preview), "expose_event",
|
|
GTK_SIGNAL_FUNC (nav_dialog_expose_event),
|
|
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)
|
|
{
|
|
GDisplay *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;
|
|
|
|
gimp_set_busy ();
|
|
|
|
gdisp = nav_dialog->gdisp;
|
|
gimage = gdisp->gimage;
|
|
|
|
/* 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 ();
|
|
}
|
|
|
|
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)
|
|
{
|
|
GDisplay *gdisp;
|
|
gdouble xratio;
|
|
gdouble yratio;
|
|
gint xoffset;
|
|
gint yoffset;
|
|
gint xpnt;
|
|
gint ypnt;
|
|
|
|
gdisp = nav_dialog->gdisp;
|
|
|
|
xratio = SCALEFACTOR_X (gdisp);
|
|
yratio = SCALEFACTOR_Y (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 (! gdisp->dot_for_dot) /* here */
|
|
xpnt = ((gdouble) xpnt *
|
|
gdisp->gimage->xresolution) / gdisp->gimage->yresolution + 0.5;
|
|
|
|
xoffset = xpnt - gdisp->offset_x;
|
|
yoffset = ypnt - gdisp->offset_y;
|
|
|
|
scroll_display (nav_dialog->gdisp, 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_destroy (cursor);
|
|
}
|
|
|
|
static gboolean
|
|
nav_dialog_inside_preview_square (NavigationDialog *nav_dialog,
|
|
gint x,
|
|
gint y)
|
|
{
|
|
if (x > nav_dialog->dispx &&
|
|
x < (nav_dialog->dispx + nav_dialog->dispwidth) &&
|
|
y > nav_dialog->dispy &&
|
|
y < nav_dialog->dispy + nav_dialog->dispheight)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
nav_dialog_preview_events (GtkWidget *widget,
|
|
GdkEvent *event,
|
|
gpointer data)
|
|
{
|
|
NavigationDialog *nav_dialog;
|
|
GDisplay *gdisp;
|
|
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;
|
|
|
|
switch (event->type)
|
|
{
|
|
case GDK_EXPOSE:
|
|
break;
|
|
|
|
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)
|
|
{
|
|
change_scale (gdisp, GIMP_ZOOM_IN);
|
|
}
|
|
else
|
|
{
|
|
GtkAdjustment *adj =
|
|
(bevent->state & GDK_CONTROL_MASK) ?
|
|
gdisp->hsbdata : gdisp->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)
|
|
{
|
|
change_scale (gdisp, GIMP_ZOOM_OUT);
|
|
}
|
|
else
|
|
{
|
|
GtkAdjustment *adj =
|
|
(bevent->state & GDK_CONTROL_MASK) ?
|
|
gdisp->hsbdata : gdisp->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:
|
|
change_scale (gdisp, GIMP_ZOOM_IN);
|
|
break;
|
|
case GDK_minus:
|
|
change_scale (gdisp, 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 gboolean
|
|
nav_dialog_expose_event (GtkWidget *widget,
|
|
GdkEvent *event,
|
|
gpointer data)
|
|
{
|
|
NavigationDialog *nav_dialog;
|
|
GDisplay *gdisp;
|
|
|
|
nav_dialog = (NavigationDialog *) data;
|
|
|
|
if (! nav_dialog || nav_dialog->frozen)
|
|
return FALSE;
|
|
|
|
gdisp = nav_dialog->gdisp;
|
|
|
|
switch (event->type)
|
|
{
|
|
case GDK_EXPOSE:
|
|
/* we must have the grab if in nav_popup */
|
|
|
|
gtk_signal_handler_block_by_func
|
|
(GTK_OBJECT (widget),
|
|
GTK_SIGNAL_FUNC (nav_dialog_expose_event),
|
|
data);
|
|
|
|
gtk_widget_draw (nav_dialog->preview, NULL);
|
|
|
|
gtk_signal_handler_unblock_by_func
|
|
(GTK_OBJECT (widget),
|
|
GTK_SIGNAL_FUNC (nav_dialog_expose_event),
|
|
data);
|
|
|
|
nav_dialog_draw_sqr (nav_dialog, FALSE,
|
|
nav_dialog->dispx, nav_dialog->dispy,
|
|
nav_dialog->dispwidth, nav_dialog->dispheight);
|
|
|
|
if (! gtk_grab_get_current () && nav_dialog->ptype == NAV_POPUP)
|
|
nav_dialog_grab_pointer (nav_dialog, nav_dialog->preview);
|
|
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;
|
|
|
|
change_scale (nav_dialog->gdisp, 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;
|
|
|
|
change_scale (nav_dialog->gdisp, 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;
|
|
change_scale (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);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
|
GTK_SIGNAL_FUNC (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);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
|
GTK_SIGNAL_FUNC (navwindow_zoomin),
|
|
nav_dialog);
|
|
|
|
nav_dialog->zoom_adjustment =
|
|
gtk_adjustment_new (0.0, -15.0, 16.0, 1.0, 1.0, 1.0);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (nav_dialog->zoom_adjustment), "value_changed",
|
|
GTK_SIGNAL_FUNC (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,
|
|
GDisplay *gdisp,
|
|
gpointer data)
|
|
{
|
|
GDisplay *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 (! gtk_object_get_data (GTK_OBJECT (gimage),
|
|
"nav_handlers_installed"))
|
|
{
|
|
gtk_signal_connect_after (GTK_OBJECT (gimage), "dirty",
|
|
GTK_SIGNAL_FUNC (nav_image_need_update),
|
|
nav_dialog);
|
|
gtk_signal_connect_after (GTK_OBJECT (gimage), "clean",
|
|
GTK_SIGNAL_FUNC (nav_image_need_update),
|
|
nav_dialog);
|
|
|
|
gtk_object_set_data (GTK_OBJECT (gimage),
|
|
"nav_handlers_installed",
|
|
nav_dialog);
|
|
}
|
|
}
|
|
}
|
|
|
|
static GDisplay *
|
|
nav_dialog_get_gdisp (void)
|
|
{
|
|
GList *list;
|
|
|
|
for (list = GIMP_LIST (the_gimp->images)->list;
|
|
list;
|
|
list = g_list_next (list))
|
|
{
|
|
GimpImage *gimage;
|
|
GDisplay *gdisp;
|
|
|
|
gimage = GIMP_IMAGE (list->data);
|
|
|
|
gdisp = gdisplays_check_valid (NULL, gimage);
|
|
|
|
if (gdisp)
|
|
return gdisp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|