918 lines
27 KiB
C
918 lines
27 KiB
C
/* PIKA - Photo and Image Kooker Application
|
|
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
|
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
|
*
|
|
* Original copyright, applying to most contents (license remains unchanged):
|
|
* 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 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <libpika/pika.h>
|
|
#include <libpika/pikaui.h>
|
|
|
|
#include "print-preview.h"
|
|
|
|
|
|
enum
|
|
{
|
|
OFFSETS_CHANGED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
|
|
#define SIZE_REQUEST 200
|
|
|
|
|
|
struct _PrintPreview
|
|
{
|
|
GtkEventBox parent_instance;
|
|
|
|
GdkCursor *cursor;
|
|
|
|
GtkPageSetup *page;
|
|
cairo_surface_t *thumbnail;
|
|
gboolean dragging;
|
|
gboolean inside;
|
|
|
|
PikaDrawable *drawable;
|
|
|
|
gdouble image_offset_x;
|
|
gdouble image_offset_y;
|
|
gdouble image_offset_x_max;
|
|
gdouble image_offset_y_max;
|
|
gdouble image_width;
|
|
gdouble image_height;
|
|
|
|
gboolean use_full_page;
|
|
|
|
/* for mouse drags */
|
|
gdouble orig_offset_x;
|
|
gdouble orig_offset_y;
|
|
gint start_x;
|
|
gint start_y;
|
|
};
|
|
|
|
struct _PrintPreviewClass
|
|
{
|
|
GtkEventBoxClass parent_class;
|
|
|
|
void (* offsets_changed) (PrintPreview *print_preview,
|
|
gint offset_x,
|
|
gint offset_y);
|
|
};
|
|
|
|
|
|
static void print_preview_finalize (GObject *object);
|
|
|
|
static void print_preview_realize (GtkWidget *widget);
|
|
static void print_preview_unrealize (GtkWidget *widget);
|
|
static void print_preview_get_preferred_width (GtkWidget *widget,
|
|
gint *minimum_width,
|
|
gint *natural_width);
|
|
static void print_preview_get_preferred_height (GtkWidget *widget,
|
|
gint *minimum_height,
|
|
gint *natural_height);
|
|
static void print_preview_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation);
|
|
static gboolean print_preview_draw (GtkWidget *widget,
|
|
cairo_t *cr);
|
|
static gboolean print_preview_button_press_event (GtkWidget *widget,
|
|
GdkEventButton *event);
|
|
static gboolean print_preview_button_release_event (GtkWidget *widget,
|
|
GdkEventButton *event);
|
|
static gboolean print_preview_motion_notify_event (GtkWidget *widget,
|
|
GdkEventMotion *event);
|
|
static gboolean print_preview_leave_notify_event (GtkWidget *widget,
|
|
GdkEventCrossing *event);
|
|
|
|
static gboolean print_preview_is_inside (PrintPreview *preview,
|
|
gdouble x,
|
|
gdouble y);
|
|
static void print_preview_set_inside (PrintPreview *preview,
|
|
gboolean inside);
|
|
|
|
static gdouble print_preview_get_scale (PrintPreview *preview);
|
|
|
|
static void print_preview_get_page_size (PrintPreview *preview,
|
|
gdouble *paper_width,
|
|
gdouble *paper_height);
|
|
static void print_preview_get_page_margins (PrintPreview *preview,
|
|
gdouble *left_margin,
|
|
gdouble *right_margin,
|
|
gdouble *top_margin,
|
|
gdouble *bottom_margin);
|
|
static cairo_surface_t * print_preview_get_thumbnail (PikaDrawable *drawable,
|
|
gint width,
|
|
gint height);
|
|
|
|
|
|
G_DEFINE_TYPE (PrintPreview, print_preview, GTK_TYPE_EVENT_BOX)
|
|
|
|
#define parent_class print_preview_parent_class
|
|
|
|
static guint print_preview_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
|
|
#define g_marshal_value_peek_double(v) (v)->data[0].v_double
|
|
|
|
static void
|
|
marshal_VOID__DOUBLE_DOUBLE (GClosure *closure,
|
|
GValue *return_value,
|
|
guint n_param_values,
|
|
const GValue *param_values,
|
|
gpointer invocation_hint,
|
|
gpointer marshal_data)
|
|
{
|
|
typedef void (*GMarshalFunc_VOID__DOUBLE_DOUBLE) (gpointer data1,
|
|
gdouble arg_1,
|
|
gdouble arg_2,
|
|
gpointer data2);
|
|
register GMarshalFunc_VOID__DOUBLE_DOUBLE callback;
|
|
register GCClosure *cc = (GCClosure*) closure;
|
|
register gpointer data1, data2;
|
|
|
|
g_return_if_fail (n_param_values == 3);
|
|
|
|
if (G_CCLOSURE_SWAP_DATA (closure))
|
|
{
|
|
data1 = closure->data;
|
|
data2 = g_value_peek_pointer (param_values + 0);
|
|
}
|
|
else
|
|
{
|
|
data1 = g_value_peek_pointer (param_values + 0);
|
|
data2 = closure->data;
|
|
}
|
|
|
|
callback = (GMarshalFunc_VOID__DOUBLE_DOUBLE) (marshal_data ?
|
|
marshal_data : cc->callback);
|
|
|
|
callback (data1,
|
|
g_marshal_value_peek_double (param_values + 1),
|
|
g_marshal_value_peek_double (param_values + 2),
|
|
data2);
|
|
}
|
|
|
|
static void
|
|
print_preview_class_init (PrintPreviewClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
print_preview_signals[OFFSETS_CHANGED] =
|
|
g_signal_new ("offsets-changed",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (PrintPreviewClass, offsets_changed),
|
|
NULL, NULL,
|
|
marshal_VOID__DOUBLE_DOUBLE,
|
|
G_TYPE_NONE, 2,
|
|
G_TYPE_DOUBLE,
|
|
G_TYPE_DOUBLE);
|
|
|
|
object_class->finalize = print_preview_finalize;
|
|
|
|
widget_class->realize = print_preview_realize;
|
|
widget_class->unrealize = print_preview_unrealize;
|
|
widget_class->get_preferred_width = print_preview_get_preferred_width;
|
|
widget_class->get_preferred_height = print_preview_get_preferred_height;
|
|
widget_class->size_allocate = print_preview_size_allocate;
|
|
widget_class->draw = print_preview_draw;
|
|
widget_class->button_press_event = print_preview_button_press_event;
|
|
widget_class->button_release_event = print_preview_button_release_event;
|
|
widget_class->motion_notify_event = print_preview_motion_notify_event;
|
|
widget_class->leave_notify_event = print_preview_leave_notify_event;
|
|
|
|
klass->offsets_changed = NULL;
|
|
}
|
|
|
|
static void
|
|
print_preview_init (PrintPreview *preview)
|
|
{
|
|
gtk_event_box_set_visible_window (GTK_EVENT_BOX (preview), FALSE);
|
|
|
|
gtk_widget_add_events (GTK_WIDGET (preview),
|
|
GDK_BUTTON_PRESS_MASK |
|
|
GDK_BUTTON_RELEASE_MASK |
|
|
GDK_POINTER_MOTION_MASK);
|
|
}
|
|
|
|
|
|
static void
|
|
print_preview_finalize (GObject *object)
|
|
{
|
|
PrintPreview *preview = PRINT_PREVIEW (object);
|
|
|
|
if (preview->thumbnail)
|
|
{
|
|
cairo_surface_destroy (preview->thumbnail);
|
|
preview->thumbnail = NULL;
|
|
}
|
|
|
|
if (preview->page)
|
|
{
|
|
g_object_unref (preview->page);
|
|
preview->page = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (print_preview_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
print_preview_realize (GtkWidget *widget)
|
|
{
|
|
PrintPreview *preview = PRINT_PREVIEW (widget);
|
|
|
|
GTK_WIDGET_CLASS (print_preview_parent_class)->realize (widget);
|
|
|
|
preview->cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
|
|
GDK_HAND1);
|
|
}
|
|
|
|
static void
|
|
print_preview_unrealize (GtkWidget *widget)
|
|
{
|
|
PrintPreview *preview = PRINT_PREVIEW (widget);
|
|
|
|
if (preview->cursor)
|
|
g_object_unref (preview->cursor);
|
|
|
|
GTK_WIDGET_CLASS (print_preview_parent_class)->unrealize (widget);
|
|
}
|
|
|
|
static void
|
|
print_preview_size_request (GtkWidget *widget,
|
|
GtkRequisition *requisition)
|
|
{
|
|
PrintPreview *preview = PRINT_PREVIEW (widget);
|
|
gdouble paper_width;
|
|
gdouble paper_height;
|
|
gint border;
|
|
|
|
border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
|
|
|
|
print_preview_get_page_size (preview, &paper_width, &paper_height);
|
|
|
|
if (paper_width > paper_height)
|
|
{
|
|
requisition->height = SIZE_REQUEST;
|
|
requisition->width = paper_width * SIZE_REQUEST / paper_height;
|
|
requisition->width = MIN (requisition->width, 2 * SIZE_REQUEST);
|
|
}
|
|
else
|
|
{
|
|
requisition->width = SIZE_REQUEST;
|
|
requisition->height = paper_height * SIZE_REQUEST / paper_width;
|
|
requisition->height = MIN (requisition->height, 2 * SIZE_REQUEST);
|
|
}
|
|
|
|
requisition->width += 2 * border;
|
|
requisition->height += 2 * border;
|
|
}
|
|
|
|
static void
|
|
print_preview_get_preferred_width (GtkWidget *widget,
|
|
gint *minimum_width,
|
|
gint *natural_width)
|
|
{
|
|
GtkRequisition requisition;
|
|
|
|
print_preview_size_request (widget, &requisition);
|
|
|
|
*minimum_width = *natural_width = requisition.width;
|
|
}
|
|
|
|
static void
|
|
print_preview_get_preferred_height (GtkWidget *widget,
|
|
gint *minimum_height,
|
|
gint *natural_height)
|
|
{
|
|
GtkRequisition requisition;
|
|
|
|
print_preview_size_request (widget, &requisition);
|
|
|
|
*minimum_height = *natural_height = requisition.height;
|
|
}
|
|
|
|
static void
|
|
print_preview_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation)
|
|
{
|
|
PrintPreview *preview = PRINT_PREVIEW (widget);
|
|
|
|
GTK_WIDGET_CLASS (print_preview_parent_class)->size_allocate (widget,
|
|
allocation);
|
|
|
|
if (preview->thumbnail)
|
|
{
|
|
cairo_surface_destroy (preview->thumbnail);
|
|
preview->thumbnail = NULL;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
print_preview_button_press_event (GtkWidget *widget,
|
|
GdkEventButton *event)
|
|
{
|
|
PrintPreview *preview = PRINT_PREVIEW (widget);
|
|
|
|
if (event->type == GDK_BUTTON_PRESS && event->button == 1 && preview->inside)
|
|
{
|
|
GdkDisplay *display = gtk_widget_get_display (widget);
|
|
GdkSeat *seat = gdk_display_get_default_seat (display);
|
|
GdkCursor *cursor;
|
|
|
|
cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
|
|
GDK_FLEUR);
|
|
|
|
if (gdk_seat_grab (seat, gdk_event_get_window ((GdkEvent *) event),
|
|
GDK_SEAT_CAPABILITY_ALL_POINTING, FALSE,
|
|
cursor,
|
|
(GdkEvent *) event,
|
|
NULL, NULL) == GDK_GRAB_SUCCESS)
|
|
{
|
|
preview->orig_offset_x = preview->image_offset_x;
|
|
preview->orig_offset_y = preview->image_offset_y;
|
|
|
|
preview->start_x = event->x;
|
|
preview->start_y = event->y;
|
|
|
|
preview->dragging = TRUE;
|
|
}
|
|
|
|
g_object_unref (cursor);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
print_preview_button_release_event (GtkWidget *widget,
|
|
GdkEventButton *event)
|
|
{
|
|
PrintPreview *preview = PRINT_PREVIEW (widget);
|
|
|
|
if (preview->dragging)
|
|
{
|
|
GdkDisplay *display = gtk_widget_get_display (widget);
|
|
GdkSeat *seat = gdk_display_get_default_seat (display);
|
|
|
|
gdk_seat_ungrab (seat);
|
|
|
|
preview->dragging = FALSE;
|
|
|
|
print_preview_set_inside (preview,
|
|
print_preview_is_inside (preview,
|
|
event->x, event->y));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
print_preview_motion_notify_event (GtkWidget *widget,
|
|
GdkEventMotion *event)
|
|
{
|
|
PrintPreview *preview = PRINT_PREVIEW (widget);
|
|
|
|
if (preview->dragging)
|
|
{
|
|
gdouble scale = print_preview_get_scale (preview);
|
|
gdouble offset_x;
|
|
gdouble offset_y;
|
|
|
|
offset_x = (preview->orig_offset_x +
|
|
(event->x - preview->start_x) / scale);
|
|
offset_y = (preview->orig_offset_y +
|
|
(event->y - preview->start_y) / scale);
|
|
|
|
offset_x = CLAMP (offset_x, 0, preview->image_offset_x_max);
|
|
offset_y = CLAMP (offset_y, 0, preview->image_offset_y_max);
|
|
|
|
if (preview->image_offset_x != offset_x ||
|
|
preview->image_offset_y != offset_y)
|
|
{
|
|
print_preview_set_image_offsets (preview, offset_x, offset_y);
|
|
|
|
g_signal_emit (preview,
|
|
print_preview_signals[OFFSETS_CHANGED], 0,
|
|
preview->image_offset_x,
|
|
preview->image_offset_y);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
print_preview_set_inside (preview,
|
|
print_preview_is_inside (preview,
|
|
event->x, event->y));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
print_preview_leave_notify_event (GtkWidget *widget,
|
|
GdkEventCrossing *event)
|
|
{
|
|
PrintPreview *preview = PRINT_PREVIEW (widget);
|
|
|
|
if (event->mode == GDK_CROSSING_NORMAL)
|
|
print_preview_set_inside (preview, FALSE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
print_preview_draw (GtkWidget *widget,
|
|
cairo_t *cr)
|
|
{
|
|
PrintPreview *preview = PRINT_PREVIEW (widget);
|
|
GtkAllocation allocation;
|
|
GdkRGBA color;
|
|
gdouble paper_width;
|
|
gdouble paper_height;
|
|
gdouble left_margin;
|
|
gdouble right_margin;
|
|
gdouble top_margin;
|
|
gdouble bottom_margin;
|
|
gdouble scale;
|
|
gint border;
|
|
|
|
gtk_widget_get_allocation (widget, &allocation);
|
|
|
|
border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
|
|
|
|
print_preview_get_page_size (preview, &paper_width, &paper_height);
|
|
print_preview_get_page_margins (preview,
|
|
&left_margin, &right_margin,
|
|
&top_margin, &bottom_margin);
|
|
|
|
scale = print_preview_get_scale (preview);
|
|
|
|
cairo_translate (cr, border, border);
|
|
|
|
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
|
|
{
|
|
gint width = allocation.width - 2 * border;
|
|
|
|
cairo_translate (cr, width - scale * paper_width, 0);
|
|
}
|
|
|
|
cairo_set_line_width (cr, 1.0);
|
|
|
|
/* draw page background */
|
|
cairo_rectangle (cr, 0, 0, scale * paper_width, scale * paper_height);
|
|
|
|
color = (GdkRGBA) { 0.0, 0.0, 0.0, 1.0 };
|
|
gdk_cairo_set_source_rgba (cr, &color);
|
|
cairo_stroke_preserve (cr);
|
|
|
|
color = (GdkRGBA) { 1.0, 1.0, 1.0, 1.0 };
|
|
gdk_cairo_set_source_rgba (cr, &color);
|
|
cairo_fill (cr);
|
|
|
|
/* draw page_margins */
|
|
cairo_rectangle (cr,
|
|
scale * left_margin,
|
|
scale * top_margin,
|
|
scale * (paper_width - left_margin - right_margin),
|
|
scale * (paper_height - top_margin - bottom_margin));
|
|
|
|
color = (GdkRGBA) { 0.0, 0.0, 0.0, 0.3 };
|
|
gdk_cairo_set_source_rgba (cr, &color);
|
|
cairo_stroke (cr);
|
|
|
|
cairo_translate (cr,
|
|
scale * (left_margin + preview->image_offset_x),
|
|
scale * (top_margin + preview->image_offset_y));
|
|
|
|
if (preview->dragging || preview->inside)
|
|
{
|
|
cairo_rectangle (cr,
|
|
0, 0,
|
|
scale * preview->image_width,
|
|
scale * preview->image_height);
|
|
|
|
color = (GdkRGBA) { 0.0, 0.0, 0.0, 1.0 };
|
|
gdk_cairo_set_source_rgba (cr, &color);
|
|
cairo_stroke (cr);
|
|
}
|
|
|
|
if (preview->thumbnail == NULL &&
|
|
pika_item_is_valid (PIKA_ITEM (preview->drawable)))
|
|
{
|
|
preview->thumbnail =
|
|
print_preview_get_thumbnail (preview->drawable,
|
|
MIN (allocation.width, 1024),
|
|
MIN (allocation.height, 1024));
|
|
}
|
|
|
|
if (preview->thumbnail != NULL)
|
|
{
|
|
gdouble scale_x;
|
|
gdouble scale_y;
|
|
|
|
scale_x = (preview->image_width /
|
|
cairo_image_surface_get_width (preview->thumbnail));
|
|
scale_y = (preview->image_height /
|
|
cairo_image_surface_get_height (preview->thumbnail));
|
|
|
|
cairo_rectangle (cr, 0, 0, preview->image_width, preview->image_height);
|
|
|
|
cairo_scale (cr, scale_x * scale, scale_y * scale);
|
|
|
|
cairo_set_source_surface (cr, preview->thumbnail, 0, 0);
|
|
cairo_fill (cr);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* print_preview_new:
|
|
* @page: page setup
|
|
* @drawable: the drawable to print
|
|
*
|
|
* Creates a new #PrintPreview widget.
|
|
*
|
|
* Returns: the new #PrintPreview widget.
|
|
**/
|
|
GtkWidget *
|
|
print_preview_new (GtkPageSetup *page,
|
|
PikaDrawable *drawable)
|
|
{
|
|
PrintPreview *preview;
|
|
|
|
g_return_val_if_fail (GTK_IS_PAGE_SETUP (page), NULL);
|
|
|
|
preview = g_object_new (PRINT_TYPE_PREVIEW, NULL);
|
|
|
|
preview->drawable = drawable;
|
|
|
|
print_preview_set_page_setup (preview, page);
|
|
|
|
return GTK_WIDGET (preview);
|
|
}
|
|
|
|
/**
|
|
* print_preview_set_image_dpi:
|
|
* @preview: a #PrintPreview.
|
|
* @xres: the X resolution
|
|
* @yres: the Y resolution
|
|
*
|
|
* Sets the resolution of the image/drawable displayed by the
|
|
* #PrintPreview.
|
|
**/
|
|
void
|
|
print_preview_set_image_dpi (PrintPreview *preview,
|
|
gdouble xres,
|
|
gdouble yres)
|
|
{
|
|
gdouble width;
|
|
gdouble height;
|
|
|
|
g_return_if_fail (PRINT_IS_PREVIEW (preview));
|
|
g_return_if_fail (xres > 0.0 && yres > 0.0);
|
|
|
|
width = pika_drawable_get_width (preview->drawable) * 72.0 / xres;
|
|
height = pika_drawable_get_height (preview->drawable) * 72.0 / yres;
|
|
|
|
if (width != preview->image_width || height != preview->image_height)
|
|
{
|
|
preview->image_width = width;
|
|
preview->image_height = height;
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (preview));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* print_preview_set_page_setup:
|
|
* @preview: a #PrintPreview.
|
|
* @page: the page setup to use
|
|
*
|
|
* Sets the page setup to use by the #PrintPreview.
|
|
**/
|
|
void
|
|
print_preview_set_page_setup (PrintPreview *preview,
|
|
GtkPageSetup *page)
|
|
{
|
|
g_return_if_fail (PRINT_IS_PREVIEW (preview));
|
|
g_return_if_fail (GTK_IS_PAGE_SETUP (page));
|
|
|
|
if (preview->page)
|
|
g_object_unref (preview->page);
|
|
|
|
preview->page = gtk_page_setup_copy (page);
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (preview));
|
|
}
|
|
|
|
/**
|
|
* print_preview_set_image_offsets:
|
|
* @preview: a #PrintPreview.
|
|
* @offset_x: the X offset
|
|
* @offset_y: the Y offset
|
|
*
|
|
* Sets the offsets of the image/drawable displayed by the #PrintPreview.
|
|
* It does not emit the "offsets-changed" signal.
|
|
**/
|
|
void
|
|
print_preview_set_image_offsets (PrintPreview *preview,
|
|
gdouble offset_x,
|
|
gdouble offset_y)
|
|
{
|
|
g_return_if_fail (PRINT_IS_PREVIEW (preview));
|
|
|
|
preview->image_offset_x = offset_x;
|
|
preview->image_offset_y = offset_y;
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (preview));
|
|
}
|
|
|
|
/**
|
|
* print_preview_set_image_offsets_max:
|
|
* @preview: a #PrintPreview.
|
|
* @offset_x_max: the maximum X offset allowed
|
|
* @offset_y_max: the maximum Y offset allowed
|
|
*
|
|
* Sets the maximum offsets of the image/drawable displayed by the
|
|
* #PrintPreview. It does not emit the "offsets-changed" signal.
|
|
**/
|
|
void
|
|
print_preview_set_image_offsets_max (PrintPreview *preview,
|
|
gdouble offset_x_max,
|
|
gdouble offset_y_max)
|
|
{
|
|
g_return_if_fail (PRINT_IS_PREVIEW (preview));
|
|
|
|
preview->image_offset_x_max = offset_x_max;
|
|
preview->image_offset_y_max = offset_y_max;
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (preview));
|
|
}
|
|
|
|
/**
|
|
* print_preview_set_use_full_page:
|
|
* @preview: a #PrintPreview.
|
|
* @full_page: TRUE to ignore the page margins
|
|
*
|
|
* If @full_page is TRUE, the page margins are ignored and the full page
|
|
* can be used to setup printing.
|
|
**/
|
|
void
|
|
print_preview_set_use_full_page (PrintPreview *preview,
|
|
gboolean full_page)
|
|
{
|
|
g_return_if_fail (PRINT_IS_PREVIEW (preview));
|
|
|
|
preview->use_full_page = full_page;
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (preview));
|
|
}
|
|
|
|
static gboolean
|
|
print_preview_is_inside (PrintPreview *preview,
|
|
gdouble x,
|
|
gdouble y)
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (preview);
|
|
GtkAllocation allocation;
|
|
gdouble left_margin;
|
|
gdouble right_margin;
|
|
gdouble top_margin;
|
|
gdouble bottom_margin;
|
|
gdouble scale;
|
|
gint border;
|
|
|
|
gtk_widget_get_allocation (widget, &allocation);
|
|
|
|
border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
|
|
|
|
x -= border;
|
|
|
|
scale = print_preview_get_scale (preview);
|
|
|
|
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
|
|
{
|
|
gdouble paper_width;
|
|
gdouble paper_height;
|
|
gint width = allocation.width - 2 * border;
|
|
|
|
print_preview_get_page_size (preview, &paper_width, &paper_height);
|
|
|
|
x -= width - scale * paper_width;
|
|
}
|
|
|
|
print_preview_get_page_margins (preview,
|
|
&left_margin, &right_margin,
|
|
&top_margin, &bottom_margin);
|
|
|
|
x = x / scale - left_margin;
|
|
y = y / scale - top_margin;
|
|
|
|
return (x > preview->image_offset_x &&
|
|
x < preview->image_offset_x + preview->image_width &&
|
|
y > preview->image_offset_y &&
|
|
y < preview->image_offset_y + preview->image_height);
|
|
}
|
|
|
|
static void
|
|
print_preview_set_inside (PrintPreview *preview,
|
|
gboolean inside)
|
|
{
|
|
if (inside != preview->inside)
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (preview);
|
|
|
|
preview->inside = inside;
|
|
|
|
if (gtk_widget_is_drawable (widget))
|
|
gdk_window_set_cursor (gtk_widget_get_window (widget),
|
|
inside ? preview->cursor : NULL);
|
|
|
|
gtk_widget_queue_draw (widget);
|
|
}
|
|
}
|
|
|
|
static gdouble
|
|
print_preview_get_scale (PrintPreview *preview)
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (preview);
|
|
GtkAllocation allocation;
|
|
gdouble paper_width;
|
|
gdouble paper_height;
|
|
gdouble scale_x;
|
|
gdouble scale_y;
|
|
gint border;
|
|
|
|
gtk_widget_get_allocation (widget, &allocation);
|
|
|
|
border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
|
|
|
|
print_preview_get_page_size (preview, &paper_width, &paper_height);
|
|
|
|
scale_x = (gdouble) (allocation.width - 2 * border) / paper_width;
|
|
scale_y = (gdouble) (allocation.height - 2 * border) / paper_height;
|
|
|
|
return MIN (scale_x, scale_y);
|
|
}
|
|
|
|
static void
|
|
print_preview_get_page_size (PrintPreview *preview,
|
|
gdouble *paper_width,
|
|
gdouble *paper_height)
|
|
{
|
|
*paper_width = gtk_page_setup_get_paper_width (preview->page,
|
|
GTK_UNIT_POINTS);
|
|
*paper_height = gtk_page_setup_get_paper_height (preview->page,
|
|
GTK_UNIT_POINTS);
|
|
}
|
|
|
|
static void
|
|
print_preview_get_page_margins (PrintPreview *preview,
|
|
gdouble *left_margin,
|
|
gdouble *right_margin,
|
|
gdouble *top_margin,
|
|
gdouble *bottom_margin)
|
|
{
|
|
if (preview->use_full_page)
|
|
{
|
|
*left_margin = 0.0;
|
|
*right_margin = 0.0;
|
|
*top_margin = 0.0;
|
|
*bottom_margin = 0.0;
|
|
}
|
|
else
|
|
{
|
|
*left_margin = gtk_page_setup_get_left_margin (preview->page,
|
|
GTK_UNIT_POINTS);
|
|
*right_margin = gtk_page_setup_get_right_margin (preview->page,
|
|
GTK_UNIT_POINTS);
|
|
*top_margin = gtk_page_setup_get_top_margin (preview->page,
|
|
GTK_UNIT_POINTS);
|
|
*bottom_margin = gtk_page_setup_get_bottom_margin (preview->page,
|
|
GTK_UNIT_POINTS);
|
|
}
|
|
}
|
|
|
|
|
|
/* This thumbnail code should eventually end up in libpikaui. */
|
|
|
|
static cairo_surface_t *
|
|
print_preview_get_thumbnail (PikaDrawable *drawable,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
cairo_surface_t *surface;
|
|
cairo_format_t format;
|
|
GBytes *data;
|
|
guchar *dest;
|
|
const guchar *src;
|
|
gint src_stride;
|
|
gint dest_stride;
|
|
gint y;
|
|
gint bpp;
|
|
|
|
g_return_val_if_fail (width > 0 && width <= 1024, NULL);
|
|
g_return_val_if_fail (height > 0 && height <= 1024, NULL);
|
|
|
|
data = pika_drawable_get_thumbnail_data (drawable,
|
|
width, height,
|
|
&width, &height, &bpp);
|
|
|
|
switch (bpp)
|
|
{
|
|
case 1:
|
|
case 3:
|
|
format = CAIRO_FORMAT_RGB24;
|
|
break;
|
|
|
|
case 2:
|
|
case 4:
|
|
format = CAIRO_FORMAT_ARGB32;
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
surface = cairo_image_surface_create (format, width, height);
|
|
|
|
src = g_bytes_get_data (data, NULL);
|
|
src_stride = width * bpp;
|
|
|
|
dest = cairo_image_surface_get_data (surface);
|
|
dest_stride = cairo_image_surface_get_stride (surface);
|
|
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
const guchar *s = src;
|
|
guchar *d = dest;
|
|
gint w = width;
|
|
|
|
switch (bpp)
|
|
{
|
|
case 1:
|
|
while (w--)
|
|
{
|
|
PIKA_CAIRO_RGB24_SET_PIXEL (d, s[0], s[0], s[0]);
|
|
s += 1;
|
|
d += 4;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
while (w--)
|
|
{
|
|
PIKA_CAIRO_ARGB32_SET_PIXEL (d, s[0], s[0], s[0], s[1]);
|
|
s += 2;
|
|
d += 4;
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
while (w--)
|
|
{
|
|
PIKA_CAIRO_RGB24_SET_PIXEL (d, s[0], s[1], s[2]);
|
|
s += 3;
|
|
d += 4;
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
while (w--)
|
|
{
|
|
PIKA_CAIRO_ARGB32_SET_PIXEL (d, s[0], s[1], s[2], s[3]);
|
|
s += 4;
|
|
d += 4;
|
|
}
|
|
break;
|
|
}
|
|
|
|
src += src_stride;
|
|
dest += dest_stride;
|
|
}
|
|
|
|
g_bytes_unref (data);
|
|
|
|
cairo_surface_mark_dirty (surface);
|
|
|
|
return surface;
|
|
}
|