mirror of https://github.com/GNOME/gimp.git
1752 lines
42 KiB
C
1752 lines
42 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 "libgimpwidgets/gimpwidgets.h"
|
|
|
|
#include "core/core-types.h"
|
|
|
|
#include "base/temp-buf.h"
|
|
|
|
#include "core/gimpcontainer.h"
|
|
#include "core/gimpcontext.h"
|
|
#include "core/gimpimage.h"
|
|
|
|
#include "app_procs.h"
|
|
#include "dialog_handler.h"
|
|
#include "gui/info-dialog.h"
|
|
#include "gui/info-window.h"
|
|
#include "gdisplay.h"
|
|
#include "nav_window.h"
|
|
|
|
#include "context_manager.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 MAX_BUF 256
|
|
|
|
#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;
|
|
|
|
/* Timeout before preview is updated */
|
|
#define PREVIEW_UPDATE_TIMEOUT 1100
|
|
|
|
typedef struct _NavWinData NavWinData;
|
|
|
|
struct _NavWinData
|
|
{
|
|
NavWinType ptype;
|
|
gboolean showingPreview;
|
|
gboolean installedDirtyTimer;
|
|
InfoDialog *info_win;
|
|
GtkWidget *previewBox;
|
|
GtkWidget *previewAlign;
|
|
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 */
|
|
gint sig_hand_id;
|
|
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;
|
|
gboolean block_window_marker; /* Block redraws of window marker */
|
|
gint nav_preview_width;
|
|
gint nav_preview_height;
|
|
gboolean block_adj_sig;
|
|
gboolean frozen; /* Has the dialog been frozen ? */
|
|
guint timer_id;
|
|
};
|
|
|
|
|
|
static gint nav_window_preview_events (GtkWidget *widget,
|
|
GdkEvent *event,
|
|
gpointer data);
|
|
static gint nav_window_expose_events (GtkWidget *widget,
|
|
GdkEvent *event,
|
|
gpointer data);
|
|
static void nav_window_update_preview (NavWinData *iwd);
|
|
static void nav_window_update_preview_blank (NavWinData *iwd);
|
|
static void destroy_preview_widget (NavWinData *iwd);
|
|
static void create_preview_widget (NavWinData *iwd);
|
|
static void nav_window_draw_sqr (NavWinData *iwd,
|
|
gboolean undraw,
|
|
gint x,
|
|
gint y,
|
|
gint w,
|
|
gint h);
|
|
static void set_size_data (NavWinData *iwd);
|
|
static gint nav_preview_update_do_timer (NavWinData *iwd);
|
|
|
|
|
|
static void
|
|
nav_window_destroy_callback (GtkWidget *widget,
|
|
gpointer client_data)
|
|
{
|
|
InfoDialog *info_win;
|
|
|
|
info_win = (InfoDialog *)client_data;
|
|
dialog_unregister(info_win->shell);
|
|
}
|
|
|
|
static void
|
|
nav_window_close_callback (GtkWidget *widget,
|
|
gpointer client_data)
|
|
{
|
|
InfoDialog *info_win;
|
|
NavWinData *iwd;
|
|
|
|
info_win = (InfoDialog *)client_data;
|
|
iwd = (NavWinData *)info_win->user_data;
|
|
|
|
iwd->showingPreview = FALSE;
|
|
info_dialog_popdown ((InfoDialog *) client_data);
|
|
}
|
|
|
|
static void
|
|
nav_window_disp_area (NavWinData *iwd,
|
|
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);
|
|
|
|
iwd->dispx = gdisp->offset_x * iwd->ratio / xratio + 0.5;
|
|
iwd->dispy = gdisp->offset_y * iwd->ratio/yratio + 0.5;
|
|
iwd->dispwidth = (gdisp->disp_width * iwd->ratio) / xratio + 0.5;
|
|
iwd->dispheight = (gdisp->disp_height * iwd->ratio) / yratio + 0.5;
|
|
|
|
newwidth = gimage->width;
|
|
newheight = gimage->height;
|
|
|
|
if (!gdisp->dot_for_dot)
|
|
{
|
|
newwidth = (newwidth *
|
|
gdisp->gimage->yresolution) / gdisp->gimage->xresolution;
|
|
iwd->dispx = ((gdisp->offset_x *
|
|
gdisp->gimage->yresolution * iwd->ratio) /
|
|
(gdisp->gimage->xresolution * xratio)) + 0.5; /*here*/
|
|
iwd->dispwidth = ((gdisp->disp_width *
|
|
gdisp->gimage->yresolution * iwd->ratio) /
|
|
(gdisp->gimage->xresolution * xratio)) + 0.5; /*here*/
|
|
}
|
|
|
|
if ((iwd->imagewidth > 0 && newwidth != iwd->imagewidth) ||
|
|
(iwd->imageheight > 0 && newheight != iwd->imageheight))
|
|
{
|
|
/* Must change the preview size */
|
|
if (iwd->ptype != NAV_POPUP)
|
|
{
|
|
gtk_window_set_focus (GTK_WINDOW (iwd->info_win->shell), NULL);
|
|
}
|
|
destroy_preview_widget (iwd);
|
|
create_preview_widget (iwd);
|
|
need_update = TRUE;
|
|
}
|
|
|
|
iwd->imagewidth = newwidth;
|
|
iwd->imageheight = newheight;
|
|
|
|
/* Normalise */
|
|
iwd->dispwidth = MAX (iwd->dispwidth, 2);
|
|
iwd->dispheight = MAX (iwd->dispheight, 2);
|
|
|
|
iwd->dispwidth = MIN (iwd->dispwidth, iwd->pwidth /* - BORDER_PEN_WIDTH */);
|
|
iwd->dispheight = MIN (iwd->dispheight, iwd->pheight /* - BORDER_PEN_WIDTH */);
|
|
|
|
if (need_update)
|
|
{
|
|
gtk_widget_hide (iwd->previewAlign);
|
|
nav_window_update_preview_blank (iwd);
|
|
gtk_widget_show (iwd->preview);
|
|
gtk_widget_draw (iwd->preview, NULL);
|
|
gtk_widget_show (iwd->previewAlign);
|
|
nav_window_draw_sqr (iwd, FALSE,
|
|
iwd->dispx, iwd->dispy,
|
|
iwd->dispwidth, iwd->dispheight);
|
|
if (iwd->ptype != NAV_POPUP)
|
|
{
|
|
gtk_window_set_focus (GTK_WINDOW (iwd->info_win->shell),
|
|
iwd->preview);
|
|
iwd->timer_id =
|
|
gtk_timeout_add (PREVIEW_UPDATE_TIMEOUT,
|
|
(GtkFunction) nav_preview_update_do_timer,
|
|
(gpointer) iwd);
|
|
}
|
|
else
|
|
{
|
|
nav_window_update_preview (iwd);
|
|
gtk_widget_draw (iwd->preview, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
nav_window_draw_sqr (NavWinData *iwd,
|
|
gboolean undraw,
|
|
gint x,
|
|
gint y,
|
|
gint w,
|
|
gint h)
|
|
{
|
|
gdk_gc_set_function (iwd->gc, GDK_INVERT);
|
|
|
|
if (undraw)
|
|
{
|
|
if (iwd->dispx != 0 || iwd->dispy != 0 ||
|
|
iwd->pwidth != iwd->dispwidth || iwd->pheight != iwd->dispheight)
|
|
{
|
|
/* first undraw from last co-ords */
|
|
gdk_draw_rectangle (iwd->preview->window, iwd->gc, FALSE,
|
|
iwd->dispx, iwd->dispy,
|
|
iwd->dispwidth - BORDER_PEN_WIDTH + 1,
|
|
iwd->dispheight - BORDER_PEN_WIDTH + 1);
|
|
}
|
|
}
|
|
|
|
if (x != 0 || y != 0 ||
|
|
w != iwd->pwidth || h != iwd->pheight)
|
|
{
|
|
gdk_draw_rectangle (iwd->preview->window, iwd->gc, FALSE,
|
|
x, y,
|
|
w - BORDER_PEN_WIDTH + 1,
|
|
h - BORDER_PEN_WIDTH + 1);
|
|
}
|
|
|
|
iwd->dispx = x;
|
|
iwd->dispy = y;
|
|
iwd->dispwidth = w;
|
|
iwd->dispheight = h;
|
|
}
|
|
|
|
static void
|
|
destroy_preview_widget (NavWinData *iwd)
|
|
{
|
|
if(!iwd->preview)
|
|
return;
|
|
|
|
gtk_widget_hide(iwd->previewBox);
|
|
gtk_widget_destroy(iwd->previewBox);
|
|
iwd->previewBox = NULL;
|
|
iwd->preview = NULL;
|
|
}
|
|
|
|
static void
|
|
set_size_data (NavWinData *iwd)
|
|
{
|
|
gint sel_width;
|
|
gint sel_height;
|
|
gint pwidth;
|
|
gint pheight;
|
|
GDisplay *gdisp;
|
|
GimpImage *gimage;
|
|
|
|
gdisp = iwd->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 = iwd->nav_preview_width;
|
|
/* pheight = sel_height * pwidth / sel_width; */
|
|
/*iwd->ratio = MIN (1.0, (gdouble) pwidth / (gdouble) sel_width);*/
|
|
iwd->ratio = (gdouble) pwidth / (gdouble) sel_width;
|
|
}
|
|
else
|
|
{
|
|
pheight = iwd->nav_preview_height;
|
|
/* pwidth = sel_width * pheight / sel_height; */
|
|
/*iwd->ratio = MIN (1.0, (gdouble) pheight / (gdouble) sel_height);*/
|
|
iwd->ratio = (gdouble) pheight / (gdouble) sel_height;
|
|
}
|
|
|
|
pwidth = sel_width * iwd->ratio + 0.5;
|
|
pheight = sel_height * iwd->ratio + 0.5;
|
|
|
|
iwd->pwidth = pwidth;
|
|
iwd->pheight = pheight;
|
|
}
|
|
|
|
static void
|
|
create_preview_widget (NavWinData *iwd)
|
|
{
|
|
GtkWidget *hbox;
|
|
GtkWidget *image;
|
|
GtkWidget *frame;
|
|
|
|
hbox = gtk_hbox_new (FALSE,0);
|
|
iwd->previewBox = hbox;
|
|
gtk_widget_show (hbox);
|
|
gtk_container_add (GTK_CONTAINER (iwd->previewAlign), hbox);
|
|
|
|
image = gtk_preview_new (GTK_PREVIEW_COLOR);
|
|
gtk_widget_set_events (GTK_WIDGET(image), PREVIEW_MASK);
|
|
iwd->preview = image;
|
|
gtk_widget_show (image);
|
|
|
|
gtk_preview_set_dither (GTK_PREVIEW (image), GDK_RGB_DITHER_MAX);
|
|
|
|
set_size_data (iwd);
|
|
|
|
gtk_preview_size (GTK_PREVIEW (iwd->preview),
|
|
iwd->pwidth,
|
|
iwd->pheight);
|
|
|
|
gtk_widget_set_usize (iwd->preview,
|
|
iwd->pwidth,
|
|
iwd->pheight);
|
|
|
|
frame = gtk_frame_new(NULL);
|
|
gtk_frame_set_shadow_type (GTK_FRAME(frame), GTK_SHADOW_IN);
|
|
gtk_container_add (GTK_CONTAINER (iwd->previewBox), frame);
|
|
gtk_container_add (GTK_CONTAINER (frame), iwd->preview);
|
|
gtk_widget_show (frame);
|
|
|
|
iwd->sig_hand_id =
|
|
gtk_signal_connect_after (GTK_OBJECT (image), "expose_event",
|
|
(GtkSignalFunc) nav_window_expose_events,
|
|
iwd);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (image), "event",
|
|
(GtkSignalFunc) nav_window_preview_events,
|
|
iwd);
|
|
|
|
/* gtk_signal_connect (GTK_OBJECT (image), "size_allocate", */
|
|
/* (GtkSignalFunc) nav_window_preview_resized, */
|
|
/* iwd); */
|
|
|
|
GTK_WIDGET_SET_FLAGS (image, GTK_CAN_FOCUS);
|
|
}
|
|
|
|
static void
|
|
update_real_view (NavWinData *iwd,
|
|
gint tx,
|
|
gint ty)
|
|
{
|
|
GDisplay *gdisp;
|
|
gdouble xratio;
|
|
gdouble yratio;
|
|
gint xoffset;
|
|
gint yoffset;
|
|
gint xpnt;
|
|
gint ypnt;
|
|
|
|
gdisp = iwd->gdisp;
|
|
|
|
xratio = SCALEFACTOR_X (gdisp);
|
|
yratio = SCALEFACTOR_Y (gdisp);
|
|
|
|
if ((tx + iwd->dispwidth) >= iwd->pwidth)
|
|
{
|
|
tx = iwd->pwidth; /* Actually should be less...
|
|
* but bound check will save us.
|
|
*/
|
|
}
|
|
|
|
xpnt = (gint) (((gdouble) (tx) * xratio) / iwd->ratio + 0.5);
|
|
|
|
if ((ty + iwd->dispheight) >= iwd->pheight)
|
|
ty = iwd->pheight; /* Same comment as for xpnt above. */
|
|
|
|
ypnt = (gint) (((gdouble) (ty) * yratio) / iwd->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;
|
|
|
|
iwd->block_window_marker = TRUE;
|
|
scroll_display (iwd->gdisp, xoffset, yoffset);
|
|
iwd->block_window_marker = FALSE;
|
|
}
|
|
|
|
static void
|
|
nav_window_update_preview (NavWinData *iwd)
|
|
{
|
|
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 = iwd->gdisp;
|
|
gimage = gdisp->gimage;
|
|
|
|
/* Min size is 2 */
|
|
pwidth = iwd->pwidth;
|
|
pheight = iwd->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) /* ALT */
|
|
{
|
|
gint sel_width = gimage->width;
|
|
gint sel_height = gimage->height;
|
|
gdouble tratio;
|
|
|
|
if (sel_width > sel_height)
|
|
tratio = (gdouble) iwd->nav_preview_width / ((gdouble) sel_width);
|
|
else
|
|
tratio = (gdouble) iwd->nav_preview_height / ((gdouble) sel_height);
|
|
|
|
pwidth = sel_width * tratio + 0.5;
|
|
pheight = sel_height * tratio + 0.5;
|
|
}
|
|
|
|
if (iwd->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 (iwd->pwidth,
|
|
iwd->pheight,
|
|
preview_buf->bytes,
|
|
0, 0, NULL);
|
|
|
|
x_ratio = (gdouble) pwidth / (gdouble) iwd->pwidth;
|
|
y_ratio = (gdouble) pheight / (gdouble) iwd->pheight;
|
|
|
|
src_data = temp_buf_data (preview_buf);
|
|
dest_data = temp_buf_data (preview_buf_notdot);
|
|
|
|
for (loop1 = 0 ; loop1 < iwd->pheight ; loop1++)
|
|
for (loop2 = 0 ; loop2 < iwd->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 * iwd->pwidth) * preview_buf->bytes;
|
|
|
|
for (i = 0 ; i < preview_buf->bytes; i++)
|
|
*dest_pixel++ = *src_pixel++;
|
|
}
|
|
|
|
pwidth = iwd->pwidth;
|
|
pheight = iwd->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 ("UNKNOWN TempBuf bpp in nav_window_update_preview()");
|
|
}
|
|
|
|
gtk_preview_draw_row (GTK_PREVIEW (iwd->preview),
|
|
(guchar *) 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_window_update_preview_blank (NavWinData *iwd)
|
|
{
|
|
|
|
guchar *buf;
|
|
guchar *dest;
|
|
gint x, y;
|
|
gdouble chk;
|
|
|
|
#if 0
|
|
GimpImage *gimage;
|
|
GDisplay *gdisp;
|
|
|
|
gdisp = iwd->gdisp;
|
|
gimage = gdisp->gimage;
|
|
|
|
#endif /* 0 */
|
|
|
|
buf = g_new (gchar, iwd->pwidth * 3);
|
|
|
|
for (y = 0; y < iwd->pheight ; y++)
|
|
{
|
|
dest = buf;
|
|
for (x = 0; x < iwd->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 (iwd->preview),
|
|
(guchar *) buf,
|
|
0, y, iwd->pwidth);
|
|
}
|
|
|
|
g_free (buf);
|
|
|
|
gdk_flush ();
|
|
}
|
|
|
|
static gint
|
|
inside_preview_square (NavWinData *iwd,
|
|
gint x,
|
|
gint y)
|
|
{
|
|
if (x > iwd->dispx &&
|
|
x < (iwd->dispx + iwd->dispwidth) &&
|
|
y > iwd->dispy &&
|
|
y < iwd->dispy + iwd->dispheight)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
update_zoom_label (NavWinData *iwd)
|
|
{
|
|
gchar scale_str[MAX_SCALE_BUF];
|
|
|
|
if(!iwd->zoom_label)
|
|
return;
|
|
|
|
/* Update the zoom scale string */
|
|
g_snprintf (scale_str, MAX_SCALE_BUF, "%d:%d",
|
|
SCALEDEST (iwd->gdisp),
|
|
SCALESRC (iwd->gdisp));
|
|
|
|
gtk_label_set_text (GTK_LABEL (iwd->zoom_label), scale_str);
|
|
}
|
|
|
|
static void
|
|
update_zoom_adjustment (NavWinData *iwd)
|
|
{
|
|
GtkAdjustment *adj;
|
|
gdouble f;
|
|
gdouble val;
|
|
|
|
if (! iwd->zoom_adjustment)
|
|
return;
|
|
|
|
adj = GTK_ADJUSTMENT (iwd->zoom_adjustment);
|
|
|
|
f =
|
|
((gdouble) SCALEDEST (iwd->gdisp)) /
|
|
((gdouble) SCALESRC (iwd->gdisp));
|
|
|
|
if (f < 1.0)
|
|
{
|
|
val = -1.0 / f;
|
|
}
|
|
else
|
|
{
|
|
val = f;
|
|
}
|
|
|
|
if (abs ((gint) adj->value) != (gint)(val - 1) && iwd->block_adj_sig != TRUE)
|
|
{
|
|
adj->value = val;
|
|
gtk_signal_emit_by_name (GTK_OBJECT (iwd->zoom_adjustment), "changed");
|
|
}
|
|
}
|
|
|
|
static void
|
|
move_to_point (NavWinData *iwd,
|
|
gint tx,
|
|
gint ty)
|
|
{
|
|
tx = CLAMP (tx, 0, iwd->pwidth);
|
|
ty = CLAMP (ty, 0, iwd->pheight);
|
|
|
|
if ((tx + iwd->dispwidth) >= iwd->pwidth)
|
|
{
|
|
tx = iwd->pwidth - iwd->dispwidth;
|
|
}
|
|
|
|
if ((ty + iwd->dispheight) >= iwd->pheight)
|
|
{
|
|
ty = iwd->pheight - iwd->dispheight;
|
|
}
|
|
|
|
if (iwd->dispx == tx && iwd->dispy == ty)
|
|
return;
|
|
|
|
/* Update the real display */
|
|
update_real_view (iwd, tx, ty);
|
|
|
|
nav_window_draw_sqr (iwd,
|
|
TRUE,
|
|
tx, ty,
|
|
iwd->dispwidth, iwd->dispheight);
|
|
|
|
}
|
|
|
|
static void
|
|
nav_window_grab_pointer (NavWinData *iwd,
|
|
GtkWidget *widget)
|
|
{
|
|
GdkCursor *cursor;
|
|
|
|
iwd->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 gint
|
|
nav_window_preview_events (GtkWidget *widget,
|
|
GdkEvent *event,
|
|
gpointer data)
|
|
{
|
|
NavWinData *iwd;
|
|
GDisplay *gdisp;
|
|
GdkEventButton *bevent;
|
|
GdkEventMotion *mevent;
|
|
GdkEventKey *kevent;
|
|
GdkModifierType mask;
|
|
gint tx = 0;
|
|
gint ty = 0;
|
|
gint mx;
|
|
gint my;
|
|
gboolean arrowKey = FALSE;
|
|
|
|
iwd = (NavWinData *)data;
|
|
|
|
if(!iwd || iwd->frozen == TRUE)
|
|
return FALSE;
|
|
|
|
gdisp = iwd->gdisp;
|
|
|
|
switch (event->type)
|
|
{
|
|
case GDK_EXPOSE:
|
|
break;
|
|
|
|
case GDK_MAP:
|
|
if (iwd->ptype == NAV_POPUP)
|
|
{
|
|
nav_window_update_preview (iwd);
|
|
/* First time displayed.... get the pointer! */
|
|
nav_window_grab_pointer (iwd, iwd->preview);
|
|
}
|
|
else
|
|
{
|
|
nav_window_update_preview_blank (iwd);
|
|
iwd->timer_id =
|
|
gtk_timeout_add (PREVIEW_UPDATE_TIMEOUT,
|
|
(GtkFunction) nav_preview_update_do_timer,
|
|
(gpointer) iwd);
|
|
}
|
|
break;
|
|
|
|
case GDK_BUTTON_PRESS:
|
|
bevent = (GdkEventButton *) event;
|
|
tx = bevent->x;
|
|
ty = bevent->y;
|
|
|
|
/* Must start the move */
|
|
switch (bevent->button)
|
|
{
|
|
case 1:
|
|
if (! inside_preview_square (iwd, tx, ty))
|
|
{
|
|
/* Direct scroll to the location */
|
|
/* view scrolled to the center or nearest possible point */
|
|
|
|
tx -= iwd->dispwidth / 2;
|
|
ty -= iwd->dispheight / 2;
|
|
|
|
if(tx < 0)
|
|
tx = 0;
|
|
|
|
if((tx + iwd->dispwidth) > iwd->pwidth)
|
|
tx = iwd->pwidth - iwd->dispwidth;
|
|
|
|
if(ty < 0)
|
|
ty = 0;
|
|
|
|
if((ty + iwd->dispheight) > iwd->pheight)
|
|
ty = iwd->pheight - iwd->dispheight;
|
|
|
|
update_real_view(iwd,tx,ty);
|
|
|
|
nav_window_draw_sqr (iwd,
|
|
TRUE,
|
|
tx, ty,
|
|
iwd->dispwidth, iwd->dispheight);
|
|
iwd->motion_offsetx = iwd->dispwidth / 2;
|
|
iwd->motion_offsety = iwd->dispheight / 2;
|
|
}
|
|
else
|
|
{
|
|
iwd->motion_offsetx = tx - iwd->dispx;
|
|
iwd->motion_offsety = ty - iwd->dispy;
|
|
}
|
|
|
|
nav_window_grab_pointer (iwd, 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:
|
|
iwd->sq_grabbed = FALSE;
|
|
gtk_grab_remove (widget);
|
|
gdk_pointer_ungrab (0);
|
|
if(iwd->ptype == NAV_POPUP)
|
|
{
|
|
gtk_widget_hide (gdisp->nav_popup);
|
|
}
|
|
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:
|
|
arrowKey = TRUE;
|
|
tx = iwd->dispx;
|
|
ty = iwd->dispy - 1;
|
|
break;
|
|
case GDK_Left:
|
|
arrowKey = TRUE;
|
|
tx = iwd->dispx - 1;
|
|
ty = iwd->dispy;
|
|
break;
|
|
case GDK_Right:
|
|
arrowKey = TRUE;
|
|
tx = iwd->dispx + 1;
|
|
ty = iwd->dispy;
|
|
break;
|
|
case GDK_Down:
|
|
arrowKey = TRUE;
|
|
tx = iwd->dispx;
|
|
ty = iwd->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 (arrowKey)
|
|
{
|
|
move_to_point (iwd, tx, ty);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case GDK_MOTION_NOTIFY:
|
|
mevent = (GdkEventMotion *) event;
|
|
|
|
if (!iwd->sq_grabbed)
|
|
break;
|
|
|
|
gdk_window_get_pointer (widget->window, &mx, &my, &mask);
|
|
tx = mx;
|
|
ty = my;
|
|
|
|
tx = tx - iwd->motion_offsetx;
|
|
ty = ty - iwd->motion_offsety;
|
|
|
|
move_to_point (iwd, tx, ty);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
nav_window_expose_events (GtkWidget *widget,
|
|
GdkEvent *event,
|
|
gpointer data)
|
|
{
|
|
NavWinData *iwd;
|
|
GDisplay *gdisp;
|
|
|
|
iwd = (NavWinData *) data;
|
|
|
|
if(!iwd || iwd->frozen == TRUE)
|
|
return FALSE;
|
|
|
|
gdisp = iwd->gdisp;
|
|
|
|
switch (event->type)
|
|
{
|
|
case GDK_EXPOSE:
|
|
/* we must have the grab if in nav_popup*/
|
|
gtk_signal_handler_block (GTK_OBJECT(widget), iwd->sig_hand_id);
|
|
gtk_widget_draw (iwd->preview, NULL);
|
|
gtk_signal_handler_unblock (GTK_OBJECT(widget), iwd->sig_hand_id);
|
|
|
|
nav_window_draw_sqr (iwd,FALSE,
|
|
iwd->dispx, iwd->dispy,
|
|
iwd->dispwidth, iwd->dispheight);
|
|
if (! gtk_grab_get_current() && iwd->ptype == NAV_POPUP)
|
|
nav_window_grab_pointer (iwd, iwd->preview);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
nav_preview_update_do (NavWinData *iwd)
|
|
{
|
|
/* If the gdisp has gone then don't do anything in this timer */
|
|
if (!iwd->gdisp)
|
|
return FALSE;
|
|
|
|
nav_window_update_preview (iwd);
|
|
nav_window_disp_area (iwd, iwd->gdisp);
|
|
gtk_widget_queue_draw (iwd->preview);
|
|
|
|
iwd->installedDirtyTimer = FALSE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gint
|
|
nav_preview_update_do_timer (NavWinData *iwd)
|
|
{
|
|
iwd->timer_id = 0;
|
|
gtk_idle_add ((GtkFunction) nav_preview_update_do, (gpointer) iwd);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
nav_image_need_update (GtkObject *obj,
|
|
gpointer client_data)
|
|
{
|
|
NavWinData *iwd;
|
|
|
|
iwd = (NavWinData *)client_data;
|
|
|
|
if(!iwd ||
|
|
!iwd->showingPreview ||
|
|
iwd->installedDirtyTimer == TRUE ||
|
|
iwd->frozen == TRUE)
|
|
return;
|
|
|
|
iwd->installedDirtyTimer = TRUE;
|
|
|
|
/* Update preview at a less busy time */
|
|
nav_window_update_preview_blank (iwd);
|
|
gtk_widget_draw (iwd->preview, NULL);
|
|
iwd->timer_id = gtk_timeout_add (PREVIEW_UPDATE_TIMEOUT,
|
|
(GtkFunction) nav_preview_update_do_timer,
|
|
(gpointer) iwd);
|
|
}
|
|
|
|
static void
|
|
navwindow_zoomin (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
NavWinData *iwd;
|
|
|
|
iwd = (NavWinData *) data;
|
|
|
|
if(!iwd || iwd->frozen == TRUE)
|
|
return;
|
|
|
|
change_scale (iwd->gdisp, GIMP_ZOOM_IN);
|
|
}
|
|
|
|
static void
|
|
navwindow_zoomout (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
NavWinData *iwd;
|
|
|
|
iwd = (NavWinData *)data;
|
|
|
|
if (!iwd || iwd->frozen == TRUE)
|
|
return;
|
|
|
|
change_scale (iwd->gdisp, GIMP_ZOOM_OUT);
|
|
}
|
|
|
|
static void
|
|
zoom_adj_changed (GtkAdjustment *adj,
|
|
gpointer data)
|
|
{
|
|
NavWinData *iwd;
|
|
gint scalesrc;
|
|
gint scaledest;
|
|
|
|
iwd = (NavWinData *)data;
|
|
|
|
if (!iwd || iwd->frozen == TRUE)
|
|
return;
|
|
|
|
if (adj->value < 0.0)
|
|
{
|
|
scalesrc = abs ((gint) adj->value - 1);
|
|
scaledest = 1;
|
|
}
|
|
else
|
|
{
|
|
scaledest = abs ((gint) adj->value + 1);
|
|
scalesrc = 1;
|
|
}
|
|
|
|
iwd->block_adj_sig = TRUE;
|
|
change_scale (iwd->gdisp, (scaledest * 100) + scalesrc);
|
|
iwd->block_adj_sig = FALSE;
|
|
}
|
|
|
|
static GtkWidget *
|
|
nav_create_button_area (InfoDialog *info_win)
|
|
{
|
|
GtkWidget *hbox1;
|
|
GtkWidget *vbox1;
|
|
GtkWidget *button;
|
|
GtkWidget *hscale1;
|
|
GtkWidget *label1;
|
|
GtkObject *adjustment;
|
|
NavWinData *iwd;
|
|
gchar scale_str[MAX_SCALE_BUF];
|
|
|
|
iwd = (NavWinData *) info_win->user_data;
|
|
|
|
hbox1 = gtk_hbox_new (FALSE, 0);
|
|
gtk_widget_show (hbox1);
|
|
|
|
button = gimp_pixmap_button_new (zoom_out_xpm, NULL);
|
|
GTK_WIDGET_UNSET_FLAGS (button, GTK_RECEIVES_DEFAULT);
|
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
|
GTK_SIGNAL_FUNC (navwindow_zoomout),
|
|
(gpointer) info_win->user_data);
|
|
gtk_box_pack_start (GTK_BOX (hbox1), button, FALSE, FALSE, 0);
|
|
gtk_widget_show (button);
|
|
|
|
vbox1 = gtk_vbox_new (FALSE, 0);
|
|
gtk_widget_show (vbox1);
|
|
gtk_box_pack_start (GTK_BOX (vbox1), hbox1, TRUE, TRUE, 0);
|
|
|
|
/* user zoom ratio */
|
|
g_snprintf (scale_str, MAX_SCALE_BUF, "%d:%d",
|
|
SCALEDEST (iwd->gdisp),
|
|
SCALESRC (iwd->gdisp));
|
|
|
|
label1 = gtk_label_new (scale_str);
|
|
gtk_widget_show (label1);
|
|
iwd->zoom_label = label1;
|
|
gtk_box_pack_start (GTK_BOX (hbox1), label1, TRUE, TRUE, 0);
|
|
|
|
adjustment = gtk_adjustment_new (0.0, -15.0, 16.0, 1.0, 1.0, 1.0);
|
|
hscale1 = gtk_hscale_new (GTK_ADJUSTMENT (adjustment));
|
|
gtk_scale_set_digits (GTK_SCALE (hscale1), 0);
|
|
iwd->zoom_adjustment = adjustment;
|
|
gtk_widget_show (hscale1);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
|
|
GTK_SIGNAL_FUNC (zoom_adj_changed),
|
|
iwd);
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox1), hscale1, TRUE, TRUE, 0);
|
|
gtk_scale_set_draw_value (GTK_SCALE (hscale1), FALSE);
|
|
|
|
button = gimp_pixmap_button_new (zoom_in_xpm, NULL);
|
|
GTK_WIDGET_UNSET_FLAGS (button, GTK_RECEIVES_DEFAULT);
|
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
|
GTK_SIGNAL_FUNC (navwindow_zoomin),
|
|
(gpointer) info_win->user_data);
|
|
gtk_box_pack_start (GTK_BOX (hbox1), button, FALSE, FALSE, 0);
|
|
gtk_widget_show (button);
|
|
|
|
return vbox1;
|
|
}
|
|
|
|
static GtkWidget *
|
|
create_preview_containers (NavWinData *iwd,
|
|
GtkWidget *shell)
|
|
{
|
|
GtkWidget *hbox1;
|
|
GtkWidget *vbox1;
|
|
GtkWidget *alignment;
|
|
|
|
hbox1 = gtk_hbox_new (FALSE, 0);
|
|
gtk_widget_show (hbox1);
|
|
|
|
vbox1 = gtk_vbox_new (FALSE, 0);
|
|
gtk_widget_show (vbox1);
|
|
|
|
gtk_widget_realize (shell);
|
|
|
|
/* need gc to draw the preview sqr with */
|
|
iwd->gc = gdk_gc_new (shell->window);
|
|
gdk_gc_set_function (iwd->gc, GDK_INVERT);
|
|
gdk_gc_set_line_attributes (iwd->gc, BORDER_PEN_WIDTH,
|
|
GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_ROUND);
|
|
|
|
alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
|
|
iwd->previewAlign = alignment;
|
|
gtk_widget_show (alignment);
|
|
|
|
/* Create the preview in which to draw the thumbnail image */
|
|
|
|
create_preview_widget (iwd);
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox1), alignment, TRUE, TRUE, 0);
|
|
gtk_box_pack_start (GTK_BOX (vbox1), hbox1, TRUE, TRUE, 0);
|
|
|
|
return vbox1;
|
|
}
|
|
|
|
static GtkWidget *
|
|
info_window_image_preview_new (InfoDialog *info_win)
|
|
{
|
|
GtkWidget *button_area;
|
|
GtkWidget *vbox1;
|
|
|
|
NavWinData *iwd;
|
|
|
|
iwd = (NavWinData *) info_win->user_data;
|
|
|
|
vbox1 = create_preview_containers (iwd, info_win->shell);
|
|
|
|
/* This one has (may) have the buttons in */
|
|
button_area = nav_create_button_area (info_win);
|
|
gtk_box_pack_start (GTK_BOX (vbox1), button_area, TRUE, TRUE, 0);
|
|
|
|
return vbox1;
|
|
}
|
|
|
|
NavWinData *
|
|
create_dummy_iwd (GDisplay *gdisp,
|
|
NavWinType ptype)
|
|
{
|
|
NavWinData *iwd;
|
|
|
|
iwd = g_new (NavWinData, 1);
|
|
iwd->ptype = ptype;
|
|
iwd->info_win = NULL;
|
|
iwd->showingPreview = TRUE;
|
|
iwd->installedDirtyTimer = FALSE;
|
|
iwd->preview = NULL;
|
|
iwd->zoom_label = NULL;
|
|
iwd->zoom_adjustment = NULL;
|
|
iwd->gdisp = gdisp;
|
|
iwd->dispx = -1;
|
|
iwd->dispy = -1;
|
|
iwd->dispwidth = -1;
|
|
iwd->dispheight = -1;
|
|
iwd->imagewidth = -1;
|
|
iwd->imageheight = -1;
|
|
iwd->sq_grabbed = FALSE;
|
|
iwd->ratio = 1.0;
|
|
iwd->block_window_marker = FALSE;
|
|
iwd->nav_preview_width =
|
|
(nav_preview_size < 0 || nav_preview_size > 256) ?
|
|
NAV_PREVIEW_WIDTH : nav_preview_size;
|
|
iwd->nav_preview_height =
|
|
(nav_preview_size < 0 || nav_preview_size > 256) ?
|
|
NAV_PREVIEW_HEIGHT : nav_preview_size;
|
|
iwd->block_adj_sig = FALSE;
|
|
iwd->frozen = FALSE;
|
|
iwd->timer_id = 0;
|
|
|
|
return(iwd);
|
|
}
|
|
|
|
/* Create a version of the nav window that follows the active
|
|
* image.
|
|
*/
|
|
|
|
static InfoDialog *nav_window_auto = NULL;
|
|
|
|
static gchar *
|
|
nav_window_title (GDisplay *gdisp)
|
|
{
|
|
gchar *title;
|
|
gchar *title_buf;
|
|
|
|
title = g_basename (gimp_image_filename (gdisp->gimage));
|
|
|
|
/* create the info dialog */
|
|
title_buf = g_strdup_printf (_("Navigation: %s-%d.%d"),
|
|
title,
|
|
gimp_image_get_ID (gdisp->gimage),
|
|
gdisp->instance);
|
|
|
|
return title_buf;
|
|
}
|
|
|
|
static void
|
|
nav_window_change_display (GimpContext *context, /* NOT USED */
|
|
GDisplay *newdisp,
|
|
gpointer data /* Not used */)
|
|
{
|
|
GDisplay *gdisp = newdisp;
|
|
GDisplay *old_gdisp;
|
|
GimpImage *gimage;
|
|
NavWinData *iwd;
|
|
gchar *title_buf;
|
|
|
|
iwd = (NavWinData *) nav_window_auto->user_data;
|
|
old_gdisp = iwd->gdisp;
|
|
|
|
if (!nav_window_auto || gdisp == old_gdisp || !gdisp)
|
|
{
|
|
return;
|
|
}
|
|
|
|
gtk_widget_set_sensitive (nav_window_auto->vbox, TRUE);
|
|
iwd->frozen = FALSE;
|
|
|
|
title_buf = nav_window_title (gdisp);
|
|
|
|
gtk_window_set_title (GTK_WINDOW (nav_window_auto->shell), title_buf);
|
|
|
|
g_free (title_buf);
|
|
|
|
gimage = gdisp->gimage;
|
|
|
|
if (gimage && gimp_container_have (image_context, GIMP_OBJECT (gimage)))
|
|
{
|
|
iwd->gdisp = gdisp;
|
|
|
|
/* Update preview to new display */
|
|
nav_window_preview_resized (nav_window_auto);
|
|
|
|
/* Tie into the dirty signal so we can update the preview */
|
|
/* provided we haven't already */
|
|
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),
|
|
iwd);
|
|
|
|
/* Also clean signal so undos get caught as well..*/
|
|
gtk_signal_connect_after (GTK_OBJECT (gdisp->gimage), "clean",
|
|
GTK_SIGNAL_FUNC (nav_image_need_update),
|
|
iwd);
|
|
gtk_object_set_data (GTK_OBJECT (gdisp->gimage),
|
|
"nav_handlers_installed",iwd);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nav_window_follow_auto (void)
|
|
{
|
|
GDisplay *gdisp;
|
|
NavWinData *iwd;
|
|
|
|
gdisp = gdisplay_active ();
|
|
|
|
if (!gdisp)
|
|
return;
|
|
|
|
if (!nav_window_auto)
|
|
{
|
|
nav_window_auto = nav_window_create (gdisp);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (gimp_context_get_user ()),
|
|
"display_changed",
|
|
GTK_SIGNAL_FUNC (nav_window_change_display),
|
|
NULL);
|
|
}
|
|
|
|
nav_dialog_popup (nav_window_auto);
|
|
|
|
iwd = (NavWinData *) nav_window_auto->user_data;
|
|
gtk_widget_set_sensitive (nav_window_auto->vbox, TRUE);
|
|
iwd->frozen = FALSE;
|
|
}
|
|
|
|
|
|
InfoDialog *
|
|
nav_window_create (GDisplay *gdisp)
|
|
{
|
|
InfoDialog *info_win;
|
|
NavWinData *iwd;
|
|
GtkWidget *container;
|
|
gchar *title_buf;
|
|
GimpImageBaseType type;
|
|
|
|
type = gimp_image_base_type (gdisp->gimage);
|
|
|
|
title_buf = nav_window_title (gdisp);
|
|
|
|
info_win = info_dialog_new (title_buf,
|
|
gimp_standard_help_func,
|
|
"dialogs/navigation_window.html");
|
|
g_free (title_buf);
|
|
|
|
dialog_register (info_win->shell);
|
|
|
|
/* create the action area */
|
|
gimp_dialog_create_action_area (GTK_DIALOG (info_win->shell),
|
|
|
|
_("Close"), nav_window_close_callback,
|
|
info_win, NULL, NULL, TRUE, FALSE,
|
|
|
|
NULL);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (info_win->shell), "destroy",
|
|
(GtkSignalFunc) nav_window_destroy_callback,
|
|
info_win);
|
|
|
|
iwd = create_dummy_iwd (gdisp, NAV_WINDOW);
|
|
info_win->user_data = iwd;
|
|
iwd->info_win = info_win;
|
|
|
|
/* Add preview */
|
|
container = info_window_image_preview_new (info_win);
|
|
gtk_table_attach_defaults (GTK_TABLE (info_win->info_table), container,
|
|
0, 2, 0, 1);
|
|
|
|
/* Tie into the dirty signal so we can update the preview */
|
|
gtk_signal_connect_after (GTK_OBJECT (gdisp->gimage), "dirty",
|
|
GTK_SIGNAL_FUNC(nav_image_need_update),iwd);
|
|
|
|
/* Also clean signal so undos get caught as well..*/
|
|
gtk_signal_connect_after (GTK_OBJECT (gdisp->gimage), "clean",
|
|
GTK_SIGNAL_FUNC(nav_image_need_update),iwd);
|
|
|
|
|
|
/* Mark the image as having the handlers installed...
|
|
*/
|
|
gtk_object_set_data (GTK_OBJECT (gdisp->gimage),
|
|
"nav_handlers_installed", iwd);
|
|
|
|
return info_win;
|
|
}
|
|
|
|
void
|
|
nav_window_update_window_marker (InfoDialog *info_win)
|
|
{
|
|
NavWinData *iwd;
|
|
|
|
/* So this functions works both ways..
|
|
* it will come in here with info_win == null
|
|
* if the auto mode is on...
|
|
*/
|
|
|
|
if (!info_win && nav_window_auto)
|
|
{
|
|
iwd = (NavWinData *) nav_window_auto->user_data;
|
|
if(iwd->frozen)
|
|
return;
|
|
|
|
nav_window_update_window_marker (nav_window_auto);
|
|
|
|
return;
|
|
}
|
|
|
|
if (!info_win)
|
|
return;
|
|
|
|
iwd = (NavWinData *) info_win->user_data;
|
|
|
|
if (!iwd ||
|
|
iwd->showingPreview == FALSE ||
|
|
iwd->block_window_marker == TRUE)
|
|
return;
|
|
|
|
update_zoom_label (iwd);
|
|
update_zoom_adjustment (iwd);
|
|
|
|
/* Undraw old size */
|
|
nav_window_draw_sqr (iwd,
|
|
FALSE,
|
|
iwd->dispx, iwd->dispy,
|
|
iwd->dispwidth, iwd->dispheight);
|
|
|
|
/* Update to new size */
|
|
nav_window_disp_area (iwd, iwd->gdisp);
|
|
|
|
/* and redraw */
|
|
nav_window_draw_sqr (iwd,
|
|
FALSE,
|
|
iwd->dispx, iwd->dispy,
|
|
iwd->dispwidth, iwd->dispheight);
|
|
}
|
|
|
|
void
|
|
nav_dialog_popup (InfoDialog *idialog)
|
|
{
|
|
NavWinData *iwd;
|
|
|
|
if (!idialog)
|
|
return;
|
|
|
|
iwd = (NavWinData *) idialog->user_data;
|
|
iwd->showingPreview = TRUE;
|
|
|
|
if (! GTK_WIDGET_VISIBLE (idialog->shell))
|
|
{
|
|
gtk_widget_show (idialog->shell);
|
|
nav_window_update_preview_blank (iwd);
|
|
nav_window_update_window_marker (idialog);
|
|
iwd->timer_id =
|
|
gtk_timeout_add (PREVIEW_UPDATE_TIMEOUT,
|
|
(GtkFunction) nav_preview_update_do_timer,
|
|
(gpointer) iwd);
|
|
}
|
|
|
|
gdk_window_raise (GTK_WIDGET (idialog->shell)->window);
|
|
}
|
|
|
|
/* Chose the first image.. */
|
|
static void
|
|
gimlist_cb (gpointer im,
|
|
gpointer data)
|
|
{
|
|
GSList **list_ptr;
|
|
|
|
list_ptr = (GSList **) data;
|
|
*list_ptr = g_slist_prepend (*list_ptr, im);
|
|
}
|
|
|
|
static GDisplay *
|
|
nav_window_get_gdisp (void)
|
|
{
|
|
GSList *list = NULL;
|
|
GSList *listPtr = NULL;
|
|
GDisplay *gdisp = NULL;
|
|
GimpImage *gimage;
|
|
|
|
gimp_container_foreach (image_context, gimlist_cb, &list);
|
|
|
|
if (!list)
|
|
return NULL;
|
|
|
|
/* Find first valid image */
|
|
for (listPtr = list; listPtr; listPtr = g_slist_next (listPtr))
|
|
{
|
|
gimage = GIMP_IMAGE (listPtr->data);
|
|
gdisp = gdisplays_check_valid (NULL, gimage);
|
|
if (gdisp)
|
|
break;
|
|
}
|
|
|
|
g_slist_free (list);
|
|
|
|
return gdisp;
|
|
}
|
|
|
|
|
|
void
|
|
nav_window_free (GDisplay *del_gdisp,
|
|
InfoDialog *info_win)
|
|
{
|
|
NavWinData *iwd;
|
|
|
|
/* So this functions works both ways..
|
|
* it will come in here with info_win == null
|
|
* if the auto mode is on...
|
|
*/
|
|
|
|
if (!info_win)
|
|
{
|
|
if (nav_window_auto != NULL)
|
|
{
|
|
GDisplay *gdisp;
|
|
|
|
iwd = (NavWinData *) nav_window_auto->user_data;
|
|
|
|
/* Only freeze if we are displaying the image we have deleted */
|
|
if (iwd->gdisp != del_gdisp)
|
|
return;
|
|
|
|
if (iwd->timer_id)
|
|
gtk_timeout_remove (iwd->timer_id);
|
|
|
|
iwd->timer_id = 0;
|
|
|
|
gdisp = nav_window_get_gdisp ();
|
|
|
|
if (gdisp)
|
|
{
|
|
nav_window_change_display (NULL, gdisp, NULL);
|
|
}
|
|
else
|
|
{
|
|
/* Clear window and freeze */
|
|
iwd->frozen = TRUE;
|
|
nav_window_update_preview_blank (iwd);
|
|
gtk_window_set_title (GTK_WINDOW (nav_window_auto->shell),
|
|
_("Navigation: No Image"));
|
|
|
|
gtk_widget_set_sensitive (nav_window_auto->vbox, FALSE);
|
|
iwd->gdisp = NULL;
|
|
gtk_widget_hide (GTK_WIDGET (nav_window_auto->shell));
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* We are actually freeing a window here so remove any timers left. */
|
|
iwd = (NavWinData *)info_win->user_data;
|
|
if (iwd->timer_id)
|
|
gtk_timeout_remove (iwd->timer_id);
|
|
|
|
g_free (info_win->user_data);
|
|
info_dialog_free (info_win);
|
|
}
|
|
|
|
|
|
/* nav popup ...
|
|
* should share code with the nav_window dialog since they are
|
|
* essentially the same.
|
|
*/
|
|
|
|
void
|
|
nav_popup_click_handler (GtkWidget *widget,
|
|
GdkEventButton *event,
|
|
gpointer data)
|
|
{
|
|
GdkEventButton *bevent;
|
|
GDisplay *gdisp = data;
|
|
NavWinData *iwp; /* dummy shorter version for the popups */
|
|
gint x, y;
|
|
gint x_org, y_org;
|
|
gint scr_w, scr_h;
|
|
|
|
bevent = (GdkEventButton *)event;
|
|
|
|
if (! gdisp->nav_popup)
|
|
{
|
|
/* popup a simplfied window with the nav box in it */
|
|
GtkWidget *frame;
|
|
GtkWidget *vbox;
|
|
|
|
iwp = create_dummy_iwd (gdisp, NAV_POPUP);
|
|
gdisp->nav_popup = gtk_window_new (GTK_WINDOW_POPUP);
|
|
gtk_widget_set_events (gdisp->nav_popup, PREVIEW_MASK);
|
|
|
|
gtk_window_set_policy (GTK_WINDOW (gdisp->nav_popup),
|
|
FALSE, FALSE, TRUE);
|
|
frame = gtk_frame_new (NULL);
|
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
|
|
gtk_container_add (GTK_CONTAINER (gdisp->nav_popup), frame);
|
|
gtk_widget_show (frame);
|
|
|
|
vbox = create_preview_containers (iwp,gdisp->nav_popup);
|
|
gtk_widget_set_extension_events (iwp->preview, GDK_EXTENSION_EVENTS_ALL);
|
|
|
|
gtk_container_add (GTK_CONTAINER (frame), vbox);
|
|
gtk_object_set_data (GTK_OBJECT (gdisp->nav_popup),"navpop_prt",
|
|
(gpointer) iwp);
|
|
nav_window_disp_area (iwp, iwp->gdisp);
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_hide (gdisp->nav_popup);
|
|
iwp = (NavWinData *) gtk_object_get_data (GTK_OBJECT (gdisp->nav_popup),
|
|
"navpop_prt");
|
|
nav_window_disp_area (iwp, iwp->gdisp);
|
|
nav_window_update_preview (iwp);
|
|
}
|
|
|
|
/* 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 - iwp->dispx -
|
|
((iwp->dispwidth - BORDER_PEN_WIDTH + 1) * 0.5) - 2;
|
|
y = y_org + bevent->y - iwp->dispy -
|
|
((iwp->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 + iwp->pwidth > scr_w) ? scr_w - iwp->pwidth - 2: x;
|
|
y = (y + iwp->pheight > scr_h) ? scr_h - iwp->pheight - 2: y;
|
|
|
|
gtk_widget_popup (gdisp->nav_popup, x, y);
|
|
gdk_flush();
|
|
|
|
/* fill in then set up handlers for mouse motion etc */
|
|
iwp->motion_offsetx = (iwp->dispwidth - BORDER_PEN_WIDTH + 1) * 0.5;
|
|
iwp->motion_offsety = (iwp->dispheight - BORDER_PEN_WIDTH + 1) * 0.5;
|
|
|
|
if(GTK_WIDGET_VISIBLE(iwp->preview))
|
|
nav_window_grab_pointer (iwp, iwp->preview);
|
|
}
|
|
|
|
void
|
|
nav_popup_free (GtkWidget *nav_popup)
|
|
{
|
|
gtk_widget_destroy (nav_popup);
|
|
}
|
|
|
|
void
|
|
nav_window_preview_resized (InfoDialog *idialog)
|
|
{
|
|
NavWinData *iwd;
|
|
|
|
if (!idialog)
|
|
return;
|
|
|
|
iwd = (NavWinData *)idialog->user_data;
|
|
|
|
/* 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.
|
|
*/
|
|
iwd->imagewidth = 1;
|
|
iwd->imageheight = 1;
|
|
|
|
iwd->nav_preview_width =
|
|
(nav_preview_size < 0 || nav_preview_size > 256) ?
|
|
NAV_PREVIEW_WIDTH : nav_preview_size;
|
|
iwd->nav_preview_height =
|
|
(nav_preview_size < 0 || nav_preview_size > 256) ?
|
|
NAV_PREVIEW_HEIGHT : nav_preview_size;
|
|
|
|
nav_window_update_window_marker (idialog);
|
|
}
|
|
|
|
void
|
|
nav_window_popup_preview_resized (GtkWidget **widget)
|
|
{
|
|
NavWinData *iwp; /* dummy shorter version for the popups */
|
|
|
|
iwp = (NavWinData *) gtk_object_get_data (GTK_OBJECT (*widget),
|
|
"navpop_prt");
|
|
|
|
g_free (iwp);
|
|
|
|
gtk_widget_destroy (*widget);
|
|
|
|
*widget = NULL;
|
|
}
|