/*
* Photos - access, organize and share your photos on GNOME
* Copyright © 2006 – 2008 The Free Software Foundation
* Copyright © 2013 – 2019 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/* Based on code from:
* + Eye of GNOME
*/
#include
#include
#include
#include "photos-print-preview.h"
struct _PhotosPrintPreview
{
GtkAspectFrame parent_instance;
GtkWidget *area;
GdkPixbuf *pixbuf;
GdkPixbuf *pixbuf_scaled;
/* The surface to set to the cairo context, created from the image */
cairo_surface_t *surface;
/* Flag whether we have to create surface */
gboolean flag_create_surface;
/* the alignment of the pixbuf in the page */
gfloat pixbuf_x_align, pixbuf_y_align;
/* real paper size, in inches */
gfloat p_width, p_height;
/* page margins, in inches */
gfloat l_margin, r_margin, t_margin, b_margin;
/* page margins, relatives to the widget size */
gint l_rmargin, r_rmargin, t_rmargin, b_rmargin;
/* pixbuf width, relative to the widget size */
gint r_width, r_height;
/* scale of the pixbuf, as defined by the user */
gfloat i_scale;
/* scale of the page, relative to the widget size */
gfloat p_scale;
/* whether we are currently grabbing the pixbuf */
gboolean grabbed;
/* the last cursor position */
gdouble cursorx, cursory;
/* if we reject to move the pixbuf, store the delta here */
gdouble r_dx, r_dy;
};
enum
{
PROP_0,
PROP_PIXBUF,
PROP_PIXBUF_X_ALIGN,
PROP_PIXBUF_Y_ALIGN,
PROP_PIXBUF_SCALE,
PROP_PAPER_WIDTH,
PROP_PAPER_HEIGHT,
PROP_PAGE_LEFT_MARGIN,
PROP_PAGE_RIGHT_MARGIN,
PROP_PAGE_TOP_MARGIN,
PROP_PAGE_BOTTOM_MARGIN
};
enum
{
SIGNAL_PIXBUF_MOVED,
SIGNAL_PIXBUF_SCALED,
SIGNAL_LAST
};
static gint preview_signals [SIGNAL_LAST];
G_DEFINE_TYPE (PhotosPrintPreview, photos_print_preview, GTK_TYPE_ASPECT_FRAME);
static void photos_print_preview_draw (PhotosPrintPreview *preview, cairo_t *cr);
static void photos_print_preview_finalize (GObject *object);
static void update_relative_sizes (PhotosPrintPreview *preview);
static void create_surface (PhotosPrintPreview *preview);
static void create_image_scaled (PhotosPrintPreview *preview);
static gboolean create_surface_when_idle (PhotosPrintPreview *preview);
static void
photos_print_preview_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
PhotosPrintPreview *self = PHOTOS_PRINT_PREVIEW (object);
switch (prop_id) {
case PROP_PIXBUF:
g_value_set_object (value, self->pixbuf);
break;
case PROP_PIXBUF_X_ALIGN:
g_value_set_float (value, self->pixbuf_x_align);
break;
case PROP_PIXBUF_Y_ALIGN:
g_value_set_float (value, self->pixbuf_y_align);
break;
case PROP_PIXBUF_SCALE:
g_value_set_float (value, self->i_scale);
break;
case PROP_PAPER_WIDTH:
g_value_set_float (value, self->p_width);
break;
case PROP_PAPER_HEIGHT:
g_value_set_float (value, self->p_height);
break;
case PROP_PAGE_LEFT_MARGIN:
g_value_set_float (value, self->l_margin);
break;
case PROP_PAGE_RIGHT_MARGIN:
g_value_set_float (value, self->r_margin);
break;
case PROP_PAGE_TOP_MARGIN:
g_value_set_float (value, self->t_margin);
break;
case PROP_PAGE_BOTTOM_MARGIN:
g_value_set_float (value, self->b_margin);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
photos_print_preview_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
PhotosPrintPreview *self = PHOTOS_PRINT_PREVIEW (object);
gboolean paper_size_changed = FALSE;
switch (prop_id) {
case PROP_PIXBUF:
if (self->pixbuf) {
g_object_unref (self->pixbuf);
}
self->pixbuf = GDK_PIXBUF (g_value_dup_object (value));
if (self->pixbuf_scaled) {
g_object_unref (self->pixbuf_scaled);
self->pixbuf_scaled = NULL;
}
self->flag_create_surface = TRUE;
break;
case PROP_PIXBUF_X_ALIGN:
self->pixbuf_x_align = g_value_get_float (value);
break;
case PROP_PIXBUF_Y_ALIGN:
self->pixbuf_y_align = g_value_get_float (value);
break;
case PROP_PIXBUF_SCALE:
self->i_scale = g_value_get_float (value);
self->flag_create_surface = TRUE;
break;
case PROP_PAPER_WIDTH:
self->p_width = g_value_get_float (value);
paper_size_changed = TRUE;
break;
case PROP_PAPER_HEIGHT:
self->p_height = g_value_get_float (value);
paper_size_changed = TRUE;
break;
case PROP_PAGE_LEFT_MARGIN:
self->l_margin = g_value_get_float (value);
break;
case PROP_PAGE_RIGHT_MARGIN:
self->r_margin = g_value_get_float (value);
break;
case PROP_PAGE_TOP_MARGIN:
self->t_margin = g_value_get_float (value);
break;
case PROP_PAGE_BOTTOM_MARGIN:
self->b_margin = g_value_get_float (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
if (paper_size_changed) {
g_object_set (object,
"ratio", self->p_width/self->p_height,
NULL);
}
update_relative_sizes (PHOTOS_PRINT_PREVIEW (object));
gtk_widget_queue_draw (self->area);
}
static void
photos_print_preview_class_init (PhotosPrintPreviewClass *klass)
{
GObjectClass *gobject_class;
gobject_class = (GObjectClass*) klass;
gobject_class->get_property = photos_print_preview_get_property;
gobject_class->set_property = photos_print_preview_set_property;
gobject_class->finalize = photos_print_preview_finalize;
/**
* PhotosPrintPreview:image:
*
* The "image" property defines the image that is previewed
* in the widget.
*/
g_object_class_install_property (gobject_class,
PROP_PIXBUF,
g_param_spec_object ("pixbuf",
"GdkPixbuf object",
"",
GDK_TYPE_PIXBUF,
G_PARAM_READWRITE));
/**
* PhotosPrintPreview:pixbuf-x-align:
*
* The "pixbuf-x-align" property defines the horizontal alignment
* of the image in the widget.
*/
g_object_class_install_property (gobject_class,
PROP_PIXBUF_X_ALIGN,
g_param_spec_float ("pixbuf-x-align",
"Horizontal alignment for the image",
"",
0,
1,
0.5,
G_PARAM_READWRITE));
/**
* PhotosPrintPreview:pixbuf-y-align:
*
* The "pixbuf-y-align" property defines the horizontal alignment
* of the image in the widget.
*/
g_object_class_install_property (gobject_class,
PROP_PIXBUF_Y_ALIGN,
g_param_spec_float ("pixbuf-y-align",
"Vertical alignment for the image",
"",
0,
1,
0.5,
G_PARAM_READWRITE));
/**
* PhotosPrintPreview:pixbuf-scale:
*
* The "pixbuf-scale" property defines the scaling of the image
* that the user wants for the printing.
*/
g_object_class_install_property (gobject_class,
PROP_PIXBUF_SCALE,
g_param_spec_float ("pixbuf-scale",
"The scale for the image",
"",
0,
1,
1,
G_PARAM_READWRITE));
/**
* PhotosPrintPreview:paper-width:
*
* The width of the previewed paper, in inches.
*/
g_object_class_install_property (gobject_class,
PROP_PAPER_WIDTH,
g_param_spec_float ("paper-width",
"Real paper width in inches",
"",
0,
100,
8.5,
G_PARAM_READWRITE));
/**
* PhotosPrintPreview:paper-height:
*
* The height of the previewed paper, in inches.
*/
g_object_class_install_property (gobject_class,
PROP_PAPER_HEIGHT,
g_param_spec_float ("paper-height",
"Real paper height in inches",
"",
0,
200,
11,
G_PARAM_READWRITE));
/**
* PhotosPrintPreview:page-left-margin:
*
* The size of the page's left margin, in inches.
*/
g_object_class_install_property (gobject_class,
PROP_PAGE_LEFT_MARGIN,
g_param_spec_float ("page-left-margin",
"Left margin of the page in inches",
"",
0,
100,
0.25,
G_PARAM_READWRITE));
/**
* PhotosPrintPreview:page-right-margin:
*
* The size of the page's right margin, in inches.
*/
g_object_class_install_property (gobject_class,
PROP_PAGE_RIGHT_MARGIN,
g_param_spec_float ("page-right-margin",
"Right margin of the page in inches",
"",
0,
200,
0.25,
G_PARAM_READWRITE));
/**
* PhotosPrintPreview:page-top-margin:
*
* The size of the page's top margin, in inches.
*/
g_object_class_install_property (gobject_class,
PROP_PAGE_TOP_MARGIN,
g_param_spec_float ("page-top-margin",
"Top margin of the page in inches",
"",
0,
100,
0.25,
G_PARAM_READWRITE));
/**
* PhotosPrintPreview:page-bottom-margin:
*
* The size of the page's bottom margin, in inches.
*/
g_object_class_install_property (gobject_class,
PROP_PAGE_BOTTOM_MARGIN,
g_param_spec_float ("page-bottom-margin",
"Bottom margin of the page in inches",
"",
0,
200,
0.56,
G_PARAM_READWRITE));
/**
* PhotosPrintPreview::pixbuf-moved:
* @preview: the object which received the signal
*
* The #PhotosPrintPreview::pixbuf-moved signal is emitted when the position
* of the image is changed.
*/
preview_signals [SIGNAL_PIXBUF_MOVED] =
g_signal_new ("pixbuf_moved",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
0, NULL);
/**
* PhotosPrintPreview::pixbuf-scaled:
* @preview: the object which received the signal
*
* The ::pixbuf-scaled signal is emmited when the scale of the image is changed.
*/
preview_signals [SIGNAL_PIXBUF_SCALED] =
g_signal_new ("pixbuf_scaled",
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
0, NULL);
}
static void
photos_print_preview_finalize (GObject *object)
{
PhotosPrintPreview *self = PHOTOS_PRINT_PREVIEW (object);
if (self->pixbuf) {
g_object_unref (self->pixbuf);
self->pixbuf = NULL;
}
if (self->pixbuf_scaled) {
g_object_unref (self->pixbuf_scaled);
self->pixbuf_scaled = NULL;
}
if (self->surface) {
cairo_surface_destroy (self->surface);
self->surface = NULL;
}
G_OBJECT_CLASS (photos_print_preview_parent_class)->finalize (object);
}
static void
photos_print_preview_init (PhotosPrintPreview *preview)
{
gfloat ratio;
preview->area = GTK_WIDGET (gtk_drawing_area_new ());
gtk_container_add (GTK_CONTAINER (preview), preview->area);
preview->p_width = 8.5;
preview->p_height = 11.0;
ratio = preview->p_width/preview->p_height;
gtk_aspect_frame_set (GTK_ASPECT_FRAME (preview),
0.5, 0.5, ratio, FALSE);
preview->pixbuf = NULL;
preview->pixbuf_scaled = NULL;
preview->pixbuf_x_align = 0.5;
preview->pixbuf_y_align = 0.5;
preview->i_scale = 1;
preview->surface = NULL;
preview->flag_create_surface = TRUE;
preview->p_scale = 0;
preview->l_margin = 0.25;
preview->r_margin = 0.25;
preview->t_margin = 0.25;
preview->b_margin = 0.56;
preview->grabbed = FALSE;
preview->cursorx = 0;
preview->cursory = 0;
preview->r_dx = 0;
preview->r_dy = 0;
}
static gboolean button_press_event_cb (GtkWidget *widget, GdkEventButton *bev, gpointer user_data);
static gboolean button_release_event_cb (GtkWidget *widget, GdkEventButton *bev, gpointer user_data);
static gboolean motion_notify_event_cb (GtkWidget *widget, GdkEventMotion *mev, gpointer user_data);
static gboolean key_press_event_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data);
static gboolean draw_cb (GtkDrawingArea *drawing_area, cairo_t *cr, gpointer user_data);
static void size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation, gpointer user_data);
/**
* photos_print_preview_new_with_pixbuf:
* @pixbuf: a #GdkPixbuf
*
* Creates a new #PhotosPrintPreview widget, and sets the #GdkPixbuf to preview
* on it.
*
* Returns: A new #PhotosPrintPreview widget.
**/
GtkWidget *
photos_print_preview_new_with_pixbuf (GdkPixbuf *pixbuf)
{
PhotosPrintPreview *preview;
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
preview = PHOTOS_PRINT_PREVIEW (photos_print_preview_new ());
preview->pixbuf = g_object_ref (pixbuf);
update_relative_sizes (preview);
return GTK_WIDGET (preview);
}
/**
* photos_print_preview_new:
*
* Creates a new #PhotosPrintPreview widget, setting it to the default values,
* and leaving the page empty. You still need to set the #PhotosPrintPreview:image
* property to make it useful.
*
* Returns: A new and empty #PhotosPrintPreview widget.
**/
GtkWidget *
photos_print_preview_new (void)
{
PhotosPrintPreview *preview;
GtkWidget *area;
preview = g_object_new (PHOTOS_TYPE_PRINT_PREVIEW, NULL);
area = preview->area;
gtk_widget_set_events (area,
GDK_EXPOSURE_MASK |
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_SCROLL_MASK |
GDK_KEY_PRESS_MASK);
g_object_set (G_OBJECT (area),
"can-focus", TRUE,
NULL);
/* update_relative_sizes (preview); */
g_signal_connect (G_OBJECT (area), "draw",
G_CALLBACK (draw_cb), preview);
g_signal_connect (G_OBJECT (area), "motion-notify-event",
G_CALLBACK (motion_notify_event_cb), preview);
g_signal_connect (G_OBJECT (area), "button-press-event",
G_CALLBACK (button_press_event_cb), preview);
g_signal_connect (G_OBJECT (area), "button-release-event",
G_CALLBACK (button_release_event_cb), preview);
g_signal_connect (G_OBJECT (area), "key-press-event",
G_CALLBACK (key_press_event_cb), preview);
g_signal_connect (area, "size-allocate",
G_CALLBACK (size_allocate_cb), preview);
return GTK_WIDGET (preview);
}
static gboolean
draw_cb (GtkDrawingArea *drawing_area,
cairo_t *cr,
gpointer user_data)
{
update_relative_sizes (PHOTOS_PRINT_PREVIEW (user_data));
photos_print_preview_draw (PHOTOS_PRINT_PREVIEW (user_data), cr);
if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) {
fprintf (stderr, "Cairo is unhappy: %s\n",
cairo_status_to_string (cairo_status (cr)));
}
return TRUE;
}
/**
* get_current_image_coordinates:
* @preview: an #PhotosPrintPreview
* @x0: A pointer where to store the x coordinate.
* @y0: A pointer where to store the y coordinate.
*
* This function returns the current image coordinates, according
* with the properties of the given @preview widget.
**/
static void
get_current_image_coordinates (PhotosPrintPreview *preview, gint *x0, gint *y0)
{
GtkAllocation allocation;
gtk_widget_get_allocation (GTK_WIDGET (preview->area), &allocation);
*x0 = (gint) ((1 - preview->pixbuf_x_align) * preview->l_rmargin + preview->pixbuf_x_align * (allocation.width - preview->r_rmargin - preview->r_width));
*y0 = (gint) ((1 - preview->pixbuf_y_align) * preview->t_rmargin + preview->pixbuf_y_align * (allocation.height - preview->b_rmargin - preview->r_height));
}
/**
* press_inside_image_area:
* @preview: an #PhotosPrintPreview
* @x: the points x coordinate
* @y: the points y coordinate
*
* Returns whether the given point is inside the image area.
*
* Returns: %TRUE if the given point is inside of the image area,
* %FALSE otherwise.
**/
static gboolean
press_inside_image_area (PhotosPrintPreview *preview, guint x, guint y)
{
const gint xs = (gint) x;
const gint ys = (gint) y;
gint x0;
gint y0;
get_current_image_coordinates (preview, &x0, &y0);
if (xs >= x0 && ys >= y0 && xs <= x0 + preview->r_width && ys <= y0 + preview->r_height)
return TRUE;
return FALSE;
}
gboolean
photos_print_preview_point_in_image_area (PhotosPrintPreview *preview, guint x, guint y)
{
g_return_val_if_fail (PHOTOS_IS_PRINT_PREVIEW (preview), FALSE);
return press_inside_image_area (preview, x, y);
}
static void
create_image_scaled (PhotosPrintPreview *preview)
{
if (preview->pixbuf_scaled == NULL)
{
gint i_height;
gint i_width;
GtkAllocation allocation;
gtk_widget_get_allocation (preview->area, &allocation);
i_width = gdk_pixbuf_get_width (preview->pixbuf);
i_height = gdk_pixbuf_get_height (preview->pixbuf);
if ((i_width > allocation.width) || (i_height > allocation.height))
{
gdouble scale;
scale = MIN ((gdouble) allocation.width/i_width, (gdouble) allocation.height/i_height);
preview->pixbuf_scaled = gdk_pixbuf_scale_simple (preview->pixbuf,
i_width * scale,
i_height * scale,
GDK_INTERP_TILES);
}
else
{
preview->pixbuf_scaled = preview->pixbuf;
g_object_ref (preview->pixbuf_scaled);
}
}
}
static GdkPixbuf *
create_preview_buffer (PhotosPrintPreview *preview)
{
GdkPixbuf *pixbuf;
gint width, height;
GdkInterpType type = GDK_INTERP_TILES;
if (preview->pixbuf == NULL) {
return NULL;
}
create_image_scaled (preview);
width = gdk_pixbuf_get_width (preview->pixbuf);
height = gdk_pixbuf_get_height (preview->pixbuf);
width *= preview->i_scale * preview->p_scale;
height *= preview->i_scale * preview->p_scale;
if (width < 1 || height < 1)
return NULL;
/* to use GDK_INTERP_TILES for small pixbufs is expensive and unnecessary */
if (width < 25 || height < 25)
type = GDK_INTERP_NEAREST;
if (preview->pixbuf_scaled) {
pixbuf = gdk_pixbuf_scale_simple (preview->pixbuf_scaled,
width, height, type);
} else {
pixbuf = gdk_pixbuf_scale_simple (preview->pixbuf,
width, height, type);
}
return pixbuf;
}
static void
create_surface (PhotosPrintPreview *preview)
{
GdkPixbuf *pixbuf;
if (preview->surface != NULL)
{
cairo_surface_destroy (preview->surface);
preview->surface = NULL;
}
pixbuf = create_preview_buffer (preview);
if (pixbuf != NULL)
{
preview->surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, 0,
gtk_widget_get_window (GTK_WIDGET (preview)));
g_object_unref (pixbuf);
}
preview->flag_create_surface = FALSE;
}
static gboolean
create_surface_when_idle (PhotosPrintPreview *preview)
{
create_surface (preview);
return FALSE;
}
static gboolean
button_press_event_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
PhotosPrintPreview *preview = PHOTOS_PRINT_PREVIEW (user_data);
preview->cursorx = event->x;
preview->cursory = event->y;
switch (event->button)
{
case 1:
preview->grabbed = press_inside_image_area (preview, event->x, event->y);
break;
default:
break;
}
if (preview->grabbed)
{
gtk_widget_queue_draw (GTK_WIDGET (preview));
}
gtk_widget_grab_focus (preview->area);
return FALSE;
}
static gboolean
button_release_event_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
PhotosPrintPreview *preview = PHOTOS_PRINT_PREVIEW (user_data);
switch (event->button)
{
case 1:
preview->grabbed = FALSE;
preview->r_dx = 0;
preview->r_dy = 0;
gtk_widget_queue_draw (GTK_WIDGET (preview));
break;
default:
break;
}
return FALSE;
}
static gboolean
key_press_event_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
{
gboolean stop_emission = FALSE;
const gchar *property;
gfloat align;
gfloat delta;
delta = 0;
switch (event->keyval)
{
case GDK_KEY_Left:
property = "pixbuf-x-align";
delta = -0.01;
break;
case GDK_KEY_Right:
property = "pixbuf-x-align";
delta = 0.01;
break;
case GDK_KEY_Up:
property = "pixbuf-y-align";
delta = -0.01;
break;
case GDK_KEY_Down:
property = "pixbuf-y-align";
delta = 0.01;
break;
default:
break;
}
if (delta != 0)
{
g_object_get (G_OBJECT (user_data), property, &align, NULL);
align += delta;
align = CLAMP (align, 0, 1);
g_object_set (G_OBJECT (user_data), property, align, NULL);
stop_emission = TRUE;
g_signal_emit (G_OBJECT (user_data), preview_signals[SIGNAL_PIXBUF_MOVED], 0);
}
return stop_emission;
}
static gboolean
motion_notify_event_cb (GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
{
PhotosPrintPreview *self = PHOTOS_PRINT_PREVIEW (user_data);
GtkAllocation allocation;
gdouble dx, dy;
if (self->grabbed)
{
dx = event->x - self->cursorx;
dy = event->y - self->cursory;
gtk_widget_get_allocation (widget, &allocation);
/* Make sure the image stays inside the margins */
self->pixbuf_x_align += (dx + self->r_dx) / (allocation.width - self->r_width - self->l_rmargin - self->r_rmargin);
if (self->pixbuf_x_align < 0. || self->pixbuf_x_align > 1.)
{
self->pixbuf_x_align = CLAMP (self->pixbuf_x_align, 0., 1.);
self->r_dx += dx;
}
else
self->r_dx = 0;
self->pixbuf_y_align += (dy + self->r_dy) / (allocation.height - self->r_height - self->t_rmargin - self->b_rmargin);
if (self->pixbuf_y_align < 0. || self->pixbuf_y_align > 1.)
{
self->pixbuf_y_align = CLAMP (self->pixbuf_y_align, 0., 1.);
self->r_dy += dy;
}
else
self->r_dy = 0;
/* we do this to correctly change the property values */
g_object_set (PHOTOS_PRINT_PREVIEW (user_data),
"pixbuf-x-align", self->pixbuf_x_align,
"pixbuf-y-align", self->pixbuf_y_align,
NULL);
self->cursorx = event->x;
self->cursory = event->y;
g_signal_emit (G_OBJECT (user_data), preview_signals[SIGNAL_PIXBUF_MOVED], 0);
}
else
{
if (press_inside_image_area (PHOTOS_PRINT_PREVIEW (user_data), event->x, event->y))
{
GdkCursor *cursor;
cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), GDK_FLEUR);
gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
g_object_unref (cursor);
}
else
{
gdk_window_set_cursor (gtk_widget_get_window (widget), NULL);
}
}
return FALSE;
}
static void
size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation, gpointer user_data)
{
PhotosPrintPreview *preview;
preview = PHOTOS_PRINT_PREVIEW (user_data);
update_relative_sizes (preview);
preview->flag_create_surface = TRUE;
if (preview->pixbuf_scaled != NULL)
{
g_object_unref (preview->pixbuf_scaled);
preview->pixbuf_scaled = NULL;
}
g_idle_add ((GSourceFunc) create_surface_when_idle, preview);
}
static void
photos_print_preview_draw (PhotosPrintPreview *preview, cairo_t *cr)
{
GtkWidget *area;
GtkAllocation allocation;
gint x0, y0;
gboolean has_focus;
area = preview->area;
has_focus = gtk_widget_has_focus (area);
gtk_widget_get_allocation (area, &allocation);
/* draw the page */
cairo_set_source_rgb (cr, 1., 1., 1.);
cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
cairo_fill (cr);
/* draw the page margins */
cairo_set_source_rgb (cr, 0., 0., 0.);
cairo_set_line_width (cr, 0.1);
cairo_rectangle (cr,
preview->l_rmargin, preview->t_rmargin,
allocation.width - preview->l_rmargin - preview->r_rmargin,
allocation.height - preview->t_rmargin - preview->b_rmargin);
cairo_stroke (cr);
get_current_image_coordinates (preview, &x0, &y0);
if (preview->flag_create_surface) {
create_surface (preview);
}
if (preview->surface) {
cairo_set_source_surface (cr, preview->surface, x0, y0);
cairo_paint (cr);
} else if (preview->pixbuf_scaled) {
/* just in the remote case we don't have the surface */
/* adjust (x0, y0) to the new scale */
gdouble scale = preview->i_scale * preview->p_scale *
gdk_pixbuf_get_width (preview->pixbuf) / gdk_pixbuf_get_width (preview->pixbuf_scaled);
x0 /= scale;
y0 /= scale;
cairo_scale (cr, scale, scale);
gdk_cairo_set_source_pixbuf (cr, preview->pixbuf_scaled, x0, y0);
cairo_paint (cr);
} else if (preview->pixbuf) {
/* just in the remote case we don't have the surface */
/* adjust (x0, y0) to the new scale */
x0 /= preview->i_scale * preview->p_scale;
y0 /= preview->i_scale * preview->p_scale;
cairo_scale (cr, preview->i_scale*preview->p_scale, preview->i_scale*preview->p_scale);
gdk_cairo_set_source_pixbuf (cr, preview->pixbuf, x0, y0);
cairo_paint (cr);
}
if (has_focus) {
GtkStyleContext *ctx;
ctx = gtk_widget_get_style_context (area);
gtk_render_focus (ctx, cr, x0, y0,
preview->r_width, preview->r_height);
}
}
static void
update_relative_sizes (PhotosPrintPreview *preview)
{
GtkAllocation allocation;
gint i_width, i_height;
if (preview->pixbuf != NULL) {
i_width = gdk_pixbuf_get_width (preview->pixbuf);
i_height = gdk_pixbuf_get_height (preview->pixbuf);
} else {
i_width = i_height = 0;
}
gtk_widget_get_allocation (preview->area, &allocation);
preview->p_scale = (gfloat) allocation.width / (preview->p_width * 72.0);
preview->r_width = (gint) i_width * preview->i_scale * preview->p_scale;
preview->r_height = (gint) i_height * preview->i_scale * preview->p_scale;
preview->l_rmargin = (gint) (72. * preview->l_margin * preview->p_scale);
preview->r_rmargin = (gint) (72. * preview->r_margin * preview->p_scale);
preview->t_rmargin = (gint) (72. * preview->t_margin * preview->p_scale);
preview->b_rmargin = (gint) (72. * preview->b_margin * preview->p_scale);
}
/**
* photos_print_preview_set_page_margins:
* @preview: a #PhotosPrintPreview
* @l_margin: Left margin.
* @r_margin: Right margin.
* @t_margin: Top margin.
* @b_margin: Bottom margin.
*
* Manually set the margins, in inches.
**/
void
photos_print_preview_set_page_margins (PhotosPrintPreview *preview,
gfloat l_margin,
gfloat r_margin,
gfloat t_margin,
gfloat b_margin)
{
g_return_if_fail (PHOTOS_IS_PRINT_PREVIEW (preview));
g_object_set (G_OBJECT(preview),
"page-left-margin", l_margin,
"page-right-margin", r_margin,
"page-top-margin", t_margin,
"page-bottom-margin", r_margin,
NULL);
}
/**
* photos_print_preview_set_from_page_setup:
* @preview: a #PhotosPrintPreview
* @setup: a #GtkPageSetup to set the properties from
*
* Sets up the page properties from a #GtkPageSetup. Useful when using the
* widget with the GtkPrint API.
**/
void
photos_print_preview_set_from_page_setup (PhotosPrintPreview *preview,
GtkPageSetup *setup)
{
g_return_if_fail (PHOTOS_IS_PRINT_PREVIEW (preview));
g_return_if_fail (GTK_IS_PAGE_SETUP (setup));
g_object_set (G_OBJECT (preview),
"page-left-margin", gtk_page_setup_get_left_margin (setup, GTK_UNIT_INCH),
"page-right-margin", gtk_page_setup_get_right_margin (setup, GTK_UNIT_INCH),
"page-top-margin", gtk_page_setup_get_top_margin (setup, GTK_UNIT_INCH),
"page-bottom-margin", gtk_page_setup_get_bottom_margin (setup, GTK_UNIT_INCH),
"paper-width", gtk_page_setup_get_paper_width (setup, GTK_UNIT_INCH),
"paper-height", gtk_page_setup_get_paper_height (setup, GTK_UNIT_INCH),
NULL);
}
/**
* photos_print_preview_get_image_position:
* @preview: a #PhotosPrintPreview
* @x: a pointer to a #gdouble, or %NULL to ignore it
* @y: a pointer to a #gdouble, or %NULL to ignore it
*
* Gets current image position in inches, relative to the margins. A
* (0, 0) position is the intersection between the left and top margins.
**/
void
photos_print_preview_get_image_position (PhotosPrintPreview *preview,
gdouble *x,
gdouble *y)
{
gdouble width, height;
g_return_if_fail (PHOTOS_IS_PRINT_PREVIEW (preview));
if (x != NULL) {
width = gdk_pixbuf_get_width (preview->pixbuf) * preview->i_scale / 72.;
*x = preview->pixbuf_x_align * (preview->p_width - preview->l_margin - preview->r_margin - width);
}
if (y != NULL) {
height = gdk_pixbuf_get_height (preview->pixbuf) * preview->i_scale / 72.;
*y = preview->pixbuf_y_align * (preview->p_height - preview->t_margin - preview->b_margin - height);
}
}
/**
* photos_print_preview_set_image_position:
* @preview: a #PhotosPrintPreview
* @x: The X coordinate, in inches, or -1 to ignore it.
* @y: The Y coordinate, in inches, or -1 to ignore it.
*
* Sets the image position. You can pass -1 to one of the coordinates if you
* only want to set the other.
**/
void
photos_print_preview_set_image_position (PhotosPrintPreview *preview,
gdouble x,
gdouble y)
{
gfloat x_align, y_align;
gdouble width, height;
g_return_if_fail (PHOTOS_IS_PRINT_PREVIEW (preview));
if (x != -1) {
width = gdk_pixbuf_get_width (preview->pixbuf) * preview->i_scale / 72.;
x_align = CLAMP (x/(preview->p_width - preview->l_margin - preview->r_margin - width), 0, 1);
g_object_set (preview, "pixbuf-x-align", x_align, NULL);
}
if (y != -1) {
height = gdk_pixbuf_get_height (preview->pixbuf) * preview->i_scale / 72.;
y_align = CLAMP (y/(preview->p_height - preview->t_margin - preview->b_margin - height), 0, 1);
g_object_set (preview, "pixbuf-y-align", y_align, NULL);
}
}
/**
* photos_print_preview_set_scale:
* @preview: a #PhotosPrintPreview
* @scale: a scale value, between 0 and 1.
*
* Sets the scale for the image.
**/
void
photos_print_preview_set_scale (PhotosPrintPreview *preview, gfloat scale)
{
g_return_if_fail (PHOTOS_IS_PRINT_PREVIEW (preview));
g_object_set (preview,
"pixbuf-scale", scale,
NULL);
g_signal_emit (G_OBJECT (preview),
preview_signals
[SIGNAL_PIXBUF_SCALED], 0);
}
/**
* photos_print_preview_get_scale:
* @preview: A #PhotosPrintPreview.
*
* Gets the scale for the image.
*
* Returns: The scale for the image.
**/
gfloat
photos_print_preview_get_scale (PhotosPrintPreview *preview)
{
gfloat scale;
g_return_val_if_fail (PHOTOS_IS_PRINT_PREVIEW (preview), 0);
g_object_get (preview,
"pixbuf-scale", &scale,
NULL);
return scale;
}