/*
* gedit-print-preview.c
*
* Copyright (C) 2008 Paolo Borelli
* Copyright (C) 2015 Sébastien Wilmet
*
* 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, see .
*/
#include "gedit-print-preview.h"
#include
#include
#include
#include
#include
#define PRINTER_DPI (72.0)
#define TOOLTIP_THRESHOLD 20
#define PAGE_PAD 12
#define PAGE_SHADOW_OFFSET 5
#define ZOOM_IN_FACTOR (1.2)
#define ZOOM_OUT_FACTOR (1.0 / ZOOM_IN_FACTOR)
struct _GeditPrintPreview
{
GtkGrid parent_instance;
GtkPrintOperation *operation;
GtkPrintContext *context;
GtkPrintOperationPreview *gtk_preview;
GtkButton *prev_button;
GtkButton *next_button;
GtkEntry *page_entry;
GtkLabel *last_page_label;
GtkButton *multi_pages_button;
GtkButton *zoom_one_button;
GtkButton *zoom_fit_button;
GtkButton *zoom_in_button;
GtkButton *zoom_out_button;
GtkButton *close_button;
/* The GtkLayout is where the pages are drawn. The layout should have
* the focus, because key-press-events and scroll-events are handled on
* the layout. It is AFAIK not easily possible to handle those events on
* the GeditPrintPreview itself because when a toolbar item has the
* focus, some key presses (like the arrows) moves the focus to a
* sibling toolbar item instead.
*/
GtkLayout *layout;
gdouble scale;
/* multipage support */
gint n_columns;
/* FIXME: handle correctly page selection (e.g. print only
* page 1-3, 7 and 12.
*/
guint cur_page; /* starts at 0 */
gint cursor_x;
gint cursor_y;
guint has_tooltip : 1;
};
G_DEFINE_TYPE (GeditPrintPreview, gedit_print_preview, GTK_TYPE_GRID)
static void
gedit_print_preview_dispose (GObject *object)
{
GeditPrintPreview *preview = GEDIT_PRINT_PREVIEW (object);
if (preview->gtk_preview != NULL)
{
GtkPrintOperationPreview *gtk_preview;
/* Set preview->gtk_preview to NULL because when calling
* end_preview() this dispose() function can be run a second
* time.
*/
gtk_preview = preview->gtk_preview;
preview->gtk_preview = NULL;
gtk_print_operation_preview_end_preview (gtk_preview);
g_object_unref (gtk_preview);
}
g_clear_object (&preview->operation);
g_clear_object (&preview->context);
G_OBJECT_CLASS (gedit_print_preview_parent_class)->dispose (object);
}
static void
gedit_print_preview_grab_focus (GtkWidget *widget)
{
GeditPrintPreview *preview = GEDIT_PRINT_PREVIEW (widget);
gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
}
static void
gedit_print_preview_class_init (GeditPrintPreviewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = gedit_print_preview_dispose;
widget_class->grab_focus = gedit_print_preview_grab_focus;
/* Bind class to template */
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/gedit/ui/gedit-print-preview.ui");
gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, prev_button);
gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, next_button);
gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, page_entry);
gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, last_page_label);
gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, multi_pages_button);
gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, zoom_one_button);
gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, zoom_fit_button);
gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, zoom_in_button);
gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, zoom_out_button);
gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, close_button);
gtk_widget_class_bind_template_child (widget_class, GeditPrintPreview, layout);
}
static gint
get_n_pages (GeditPrintPreview *preview)
{
gint n_pages;
g_object_get (preview->operation, "n-pages", &n_pages, NULL);
return n_pages;
}
static gdouble
get_screen_dpi (GeditPrintPreview *preview)
{
GdkScreen *screen;
gdouble dpi;
static gboolean warning_shown = FALSE;
screen = gtk_widget_get_screen (GTK_WIDGET (preview));
if (screen == NULL)
{
return PRINTER_DPI;
}
dpi = gdk_screen_get_resolution (screen);
if (dpi < 30.0 || 600.0 < dpi)
{
if (!warning_shown)
{
g_warning ("Invalid the x-resolution for the screen, assuming 96dpi");
warning_shown = TRUE;
}
dpi = 96.0;
}
return dpi;
}
/* Get the paper size in points: these must be used only
* after the widget has been mapped and the dpi is known.
*/
static gdouble
get_paper_width (GeditPrintPreview *preview)
{
GtkPageSetup *page_setup;
gdouble paper_width;
page_setup = gtk_print_context_get_page_setup (preview->context);
paper_width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_INCH);
return paper_width * get_screen_dpi (preview);
}
static gdouble
get_paper_height (GeditPrintPreview *preview)
{
GtkPageSetup *page_setup;
gdouble paper_height;
page_setup = gtk_print_context_get_page_setup (preview->context);
paper_height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_INCH);
return paper_height * get_screen_dpi (preview);
}
/* The tile size is the size in pixels of the area where a page will be
* drawn, including the padding. The size is independent of the
* orientation.
*/
static void
get_tile_size (GeditPrintPreview *preview,
gint *tile_width,
gint *tile_height)
{
if (tile_width != NULL)
{
*tile_width = 2 * PAGE_PAD + round (preview->scale * get_paper_width (preview));
}
if (tile_height != NULL)
{
*tile_height = 2 * PAGE_PAD + round (preview->scale * get_paper_height (preview));
}
}
static void
get_adjustments (GeditPrintPreview *preview,
GtkAdjustment **hadj,
GtkAdjustment **vadj)
{
*hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (preview->layout));
*vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (preview->layout));
}
static void
update_layout_size (GeditPrintPreview *preview)
{
gint tile_width;
gint tile_height;
get_tile_size (preview, &tile_width, &tile_height);
/* force size of the drawing area to make the scrolled window work */
gtk_layout_set_size (preview->layout,
tile_width * preview->n_columns,
tile_height);
gtk_widget_queue_draw (GTK_WIDGET (preview->layout));
}
/* Zoom should always be set with one of these two function
* so that the tile size is properly updated.
*/
static void
set_zoom_factor (GeditPrintPreview *preview,
gdouble zoom)
{
preview->scale = zoom;
update_layout_size (preview);
}
static void
set_zoom_fit_to_size (GeditPrintPreview *preview)
{
GtkAdjustment *hadj, *vadj;
gdouble width, height;
gdouble paper_width, paper_height;
gdouble zoomx, zoomy;
get_adjustments (preview, &hadj, &vadj);
width = gtk_adjustment_get_page_size (hadj);
height = gtk_adjustment_get_page_size (vadj);
width /= preview->n_columns;
paper_width = get_paper_width (preview);
paper_height = get_paper_height (preview);
zoomx = MAX (1, width - 2 * PAGE_PAD) / paper_width;
zoomy = MAX (1, height - 2 * PAGE_PAD) / paper_height;
set_zoom_factor (preview, zoomx <= zoomy ? zoomx : zoomy);
}
static void
zoom_in (GeditPrintPreview *preview)
{
set_zoom_factor (preview, preview->scale * ZOOM_IN_FACTOR);
}
static void
zoom_out (GeditPrintPreview *preview)
{
set_zoom_factor (preview, preview->scale * ZOOM_OUT_FACTOR);
}
static void
goto_page (GeditPrintPreview *preview,
gint page)
{
gchar *page_str;
gint n_pages;
page_str = g_strdup_printf ("%d", page + 1);
gtk_entry_set_text (preview->page_entry, page_str);
g_free (page_str);
n_pages = get_n_pages (preview);
gtk_widget_set_sensitive (GTK_WIDGET (preview->prev_button),
page > 0 &&
n_pages > 1);
gtk_widget_set_sensitive (GTK_WIDGET (preview->next_button),
page < (n_pages - 1) &&
n_pages > 1);
if (page != preview->cur_page)
{
preview->cur_page = page;
if (n_pages > 0)
{
gtk_widget_queue_draw (GTK_WIDGET (preview->layout));
}
}
}
static void
prev_button_clicked (GtkWidget *button,
GeditPrintPreview *preview)
{
GdkEvent *event;
gint page;
event = gtk_get_current_event ();
if (event->button.state & GDK_SHIFT_MASK)
{
page = 0;
}
else
{
page = preview->cur_page - preview->n_columns;
}
goto_page (preview, MAX (page, 0));
gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
gdk_event_free (event);
}
static void
next_button_clicked (GtkWidget *button,
GeditPrintPreview *preview)
{
GdkEvent *event;
gint page;
gint n_pages = get_n_pages (preview);
event = gtk_get_current_event ();
if (event->button.state & GDK_SHIFT_MASK)
{
page = n_pages - 1;
}
else
{
page = preview->cur_page + preview->n_columns;
}
goto_page (preview, MIN (page, n_pages - 1));
gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
gdk_event_free (event);
}
static void
page_entry_activated (GtkEntry *entry,
GeditPrintPreview *preview)
{
const gchar *text;
gint page;
gint n_pages = get_n_pages (preview);
text = gtk_entry_get_text (entry);
page = CLAMP (atoi (text), 1, n_pages) - 1;
goto_page (preview, page);
gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
}
static void
page_entry_insert_text (GtkEditable *editable,
const gchar *text,
gint length,
gint *position)
{
const gchar *end;
const gchar *p;
end = text + length;
for (p = text; p < end; p = g_utf8_next_char (p))
{
if (!g_unichar_isdigit (g_utf8_get_char (p)))
{
g_signal_stop_emission_by_name (editable, "insert-text");
break;
}
}
}
static gboolean
page_entry_focus_out (GtkEntry *entry,
GdkEventFocus *event,
GeditPrintPreview *preview)
{
const gchar *text;
gint page;
text = gtk_entry_get_text (entry);
page = atoi (text) - 1;
/* Reset the page number only if really needed */
if (page != preview->cur_page)
{
gchar *str;
str = g_strdup_printf ("%d", preview->cur_page + 1);
gtk_entry_set_text (entry, str);
g_free (str);
}
return GDK_EVENT_PROPAGATE;
}
static void
on_1x1_clicked (GtkMenuItem *item,
GeditPrintPreview *preview)
{
preview->n_columns = 1;
update_layout_size (preview);
gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
}
static void
on_1x2_clicked (GtkMenuItem *item,
GeditPrintPreview *preview)
{
preview->n_columns = 2;
set_zoom_fit_to_size (preview);
gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
}
static void
multi_pages_button_clicked (GtkWidget *button,
GeditPrintPreview *preview)
{
GtkWidget *menu;
GtkWidget *item;
menu = gtk_menu_new ();
gtk_widget_show (menu);
g_signal_connect (menu,
"selection-done",
G_CALLBACK (gtk_widget_destroy),
NULL);
item = gtk_menu_item_new_with_label ("1x1");
gtk_widget_show (item);
gtk_menu_attach (GTK_MENU (menu), item, 0, 1, 0, 1);
g_signal_connect (item, "activate", G_CALLBACK (on_1x1_clicked), preview);
item = gtk_menu_item_new_with_label ("1x2");
gtk_widget_show (item);
gtk_menu_attach (GTK_MENU (menu), item, 1, 2, 0, 1);
g_signal_connect (item, "activate", G_CALLBACK (on_1x2_clicked), preview);
gtk_menu_popup_at_pointer (GTK_MENU (menu), NULL);
}
static void
zoom_one_button_clicked (GtkWidget *button,
GeditPrintPreview *preview)
{
set_zoom_factor (preview, 1);
gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
}
static void
zoom_fit_button_clicked (GtkWidget *button,
GeditPrintPreview *preview)
{
set_zoom_fit_to_size (preview);
gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
}
static void
zoom_in_button_clicked (GtkWidget *button,
GeditPrintPreview *preview)
{
zoom_in (preview);
gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
}
static void
zoom_out_button_clicked (GtkWidget *button,
GeditPrintPreview *preview)
{
zoom_out (preview);
gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
}
static void
close_button_clicked (GtkWidget *button,
GeditPrintPreview *preview)
{
gtk_widget_destroy (GTK_WIDGET (preview));
}
static gboolean
scroll_event_activated (GtkWidget *widget,
GdkEventScroll *event,
GeditPrintPreview *preview)
{
if (event->state & GDK_CONTROL_MASK)
{
if ((event->direction == GDK_SCROLL_UP) ||
(event->direction == GDK_SCROLL_SMOOTH &&
event->delta_y < 0))
{
zoom_in (preview);
}
else if ((event->direction == GDK_SCROLL_DOWN) ||
(event->direction == GDK_SCROLL_SMOOTH &&
event->delta_y > 0))
{
zoom_out (preview);
}
return GDK_EVENT_STOP;
}
return GDK_EVENT_PROPAGATE;
}
static gint
get_first_page_displayed (GeditPrintPreview *preview)
{
return preview->cur_page - (preview->cur_page % preview->n_columns);
}
/* Returns the page number (starting from 0) or -1 if no page. */
static gint
get_page_at_coords (GeditPrintPreview *preview,
gint x,
gint y)
{
gint tile_width, tile_height;
GtkAdjustment *hadj, *vadj;
gint col, page;
get_tile_size (preview, &tile_width, &tile_height);
if (tile_height <= 0 || tile_width <= 0)
{
return -1;
}
get_adjustments (preview, &hadj, &vadj);
x += gtk_adjustment_get_value (hadj);
y += gtk_adjustment_get_value (vadj);
col = x / tile_width;
if (col >= preview->n_columns || y > tile_height)
{
return -1;
}
page = get_first_page_displayed (preview) + col;
if (page >= get_n_pages (preview))
{
return -1;
}
/* FIXME: we could try to be picky and check if we actually are inside
* the page (i.e. not in the padding or shadow).
*/
return page;
}
static gboolean
on_preview_layout_motion_notify (GtkWidget *widget,
GdkEvent *event,
GeditPrintPreview *preview)
{
gint temp_x;
gint temp_y;
gint diff_x;
gint diff_y;
temp_x = ((GdkEventMotion*)event)->x;
temp_y = ((GdkEventMotion*)event)->y;
diff_x = abs (temp_x - preview->cursor_x);
diff_y = abs (temp_y - preview->cursor_y);
if ((diff_x >= TOOLTIP_THRESHOLD) || (diff_y >= TOOLTIP_THRESHOLD))
{
preview->has_tooltip = FALSE;
preview->cursor_x = temp_x;
preview->cursor_y = temp_y;
}
else
{
preview->has_tooltip = TRUE;
}
return GDK_EVENT_STOP;
}
static gboolean
preview_layout_query_tooltip (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_tip,
GtkTooltip *tooltip,
GeditPrintPreview *preview)
{
if (preview->has_tooltip)
{
gint page;
gchar *tip;
page = get_page_at_coords (preview, x, y);
if (page < 0)
{
return FALSE;
}
tip = g_strdup_printf (_("Page %d of %d"),
page + 1,
get_n_pages (preview));
gtk_tooltip_set_text (tooltip, tip);
g_free (tip);
return TRUE;
}
else
{
preview->has_tooltip = TRUE;
return FALSE;
}
}
static gint
preview_layout_key_press (GtkWidget *widget,
GdkEventKey *event,
GeditPrintPreview *preview)
{
GtkAdjustment *hadj, *vadj;
gdouble x, y;
gdouble hlower, vlower;
gdouble hupper, vupper;
gdouble visible_width, visible_height;
gdouble hstep, vstep;
gint n_pages;
gboolean do_move = FALSE;
get_adjustments (preview, &hadj, &vadj);
x = gtk_adjustment_get_value (hadj);
y = gtk_adjustment_get_value (vadj);
hlower = gtk_adjustment_get_lower (hadj);
vlower = gtk_adjustment_get_lower (vadj);
hupper = gtk_adjustment_get_upper (hadj);
vupper = gtk_adjustment_get_upper (vadj);
visible_width = gtk_adjustment_get_page_size (hadj);
visible_height = gtk_adjustment_get_page_size (vadj);
hstep = 10;
vstep = 10;
n_pages = get_n_pages (preview);
switch (event->keyval)
{
case '1':
set_zoom_fit_to_size (preview);
break;
case '+':
case '=':
case GDK_KEY_KP_Add:
zoom_in (preview);
break;
case '-':
case '_':
case GDK_KEY_KP_Subtract:
zoom_out (preview);
break;
case GDK_KEY_KP_Right:
case GDK_KEY_Right:
if (event->state & GDK_SHIFT_MASK)
x = hupper - visible_width;
else
x = MIN (hupper - visible_width, x + hstep);
do_move = TRUE;
break;
case GDK_KEY_KP_Left:
case GDK_KEY_Left:
if (event->state & GDK_SHIFT_MASK)
x = hlower;
else
x = MAX (hlower, x - hstep);
do_move = TRUE;
break;
case GDK_KEY_KP_Up:
case GDK_KEY_Up:
if (event->state & GDK_SHIFT_MASK)
goto page_up;
y = MAX (vlower, y - vstep);
do_move = TRUE;
break;
case GDK_KEY_KP_Down:
case GDK_KEY_Down:
if (event->state & GDK_SHIFT_MASK)
goto page_down;
y = MIN (vupper - visible_height, y + vstep);
do_move = TRUE;
break;
case GDK_KEY_KP_Page_Up:
case GDK_KEY_Page_Up:
case GDK_KEY_Delete:
case GDK_KEY_KP_Delete:
case GDK_KEY_BackSpace:
page_up:
if (y <= vlower)
{
if (preview->cur_page > 0)
{
goto_page (preview, preview->cur_page - 1);
y = (vupper - visible_height);
}
}
else
{
y = vlower;
}
do_move = TRUE;
break;
case GDK_KEY_KP_Page_Down:
case GDK_KEY_Page_Down:
case ' ':
page_down:
if (y >= (vupper - visible_height))
{
if (preview->cur_page < n_pages - 1)
{
goto_page (preview, preview->cur_page + 1);
y = vlower;
}
}
else
{
y = (vupper - visible_height);
}
do_move = TRUE;
break;
case GDK_KEY_KP_Home:
case GDK_KEY_Home:
goto_page (preview, 0);
y = vlower;
do_move = TRUE;
break;
case GDK_KEY_KP_End:
case GDK_KEY_End:
goto_page (preview, n_pages - 1);
y = vlower;
do_move = TRUE;
break;
case GDK_KEY_Escape:
gtk_widget_destroy (GTK_WIDGET (preview));
break;
case 'p':
if (event->state & GDK_MOD1_MASK)
{
gtk_widget_grab_focus (GTK_WIDGET (preview->page_entry));
}
break;
default:
return GDK_EVENT_PROPAGATE;
}
if (do_move)
{
gtk_adjustment_set_value (hadj, x);
gtk_adjustment_set_value (vadj, y);
}
return GDK_EVENT_STOP;
}
static void
gedit_print_preview_init (GeditPrintPreview *preview)
{
preview->cur_page = 0;
preview->scale = 1.0;
preview->n_columns = 1;
preview->cursor_x = 0;
preview->cursor_y = 0;
preview->has_tooltip = TRUE;
gtk_widget_init_template (GTK_WIDGET (preview));
g_signal_connect (preview->prev_button,
"clicked",
G_CALLBACK (prev_button_clicked),
preview);
g_signal_connect (preview->next_button,
"clicked",
G_CALLBACK (next_button_clicked),
preview);
g_signal_connect (preview->page_entry,
"activate",
G_CALLBACK (page_entry_activated),
preview);
g_signal_connect (preview->page_entry,
"insert-text",
G_CALLBACK (page_entry_insert_text),
NULL);
g_signal_connect (preview->page_entry,
"focus-out-event",
G_CALLBACK (page_entry_focus_out),
preview);
g_signal_connect (preview->multi_pages_button,
"clicked",
G_CALLBACK (multi_pages_button_clicked),
preview);
g_signal_connect (preview->zoom_one_button,
"clicked",
G_CALLBACK (zoom_one_button_clicked),
preview);
g_signal_connect (preview->zoom_fit_button,
"clicked",
G_CALLBACK (zoom_fit_button_clicked),
preview);
g_signal_connect (preview->zoom_in_button,
"clicked",
G_CALLBACK (zoom_in_button_clicked),
preview);
g_signal_connect (preview->zoom_out_button,
"clicked",
G_CALLBACK (zoom_out_button_clicked),
preview);
g_signal_connect (preview->close_button,
"clicked",
G_CALLBACK (close_button_clicked),
preview);
g_signal_connect (preview->layout,
"query-tooltip",
G_CALLBACK (preview_layout_query_tooltip),
preview);
g_signal_connect (preview->layout,
"key-press-event",
G_CALLBACK (preview_layout_key_press),
preview);
g_signal_connect (preview->layout,
"scroll-event",
G_CALLBACK (scroll_event_activated),
preview);
/* hide the tooltip once we move the cursor, since gtk does not do it for us */
g_signal_connect (preview->layout,
"motion-notify-event",
G_CALLBACK (on_preview_layout_motion_notify),
preview);
gtk_widget_grab_focus (GTK_WIDGET (preview->layout));
}
static void
draw_page_content (cairo_t *cr,
gint page_number,
GeditPrintPreview *preview)
{
gdouble dpi;
/* scale to the desired size */
cairo_scale (cr, preview->scale, preview->scale);
dpi = get_screen_dpi (preview);
gtk_print_context_set_cairo_context (preview->context, cr, dpi, dpi);
gtk_print_operation_preview_render_page (preview->gtk_preview,
page_number);
}
/* For the frame, we scale and rotate manually, since
* the line width should not depend on the zoom and
* the drop shadow should be on the bottom right no matter
* the orientation.
*/
static void
draw_page_frame (cairo_t *cr,
GeditPrintPreview *preview)
{
gdouble width;
gdouble height;
width = get_paper_width (preview) * preview->scale;
height = get_paper_height (preview) * preview->scale;
/* drop shadow */
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_rectangle (cr,
PAGE_SHADOW_OFFSET, PAGE_SHADOW_OFFSET,
width, height);
cairo_fill (cr);
/* page frame */
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_rectangle (cr,
0, 0,
width, height);
cairo_fill_preserve (cr);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_set_line_width (cr, 1);
cairo_stroke (cr);
}
static void
draw_page (cairo_t *cr,
gdouble x,
gdouble y,
gint page_number,
GeditPrintPreview *preview)
{
cairo_save (cr);
/* move to the page top left corner */
cairo_translate (cr, x + PAGE_PAD, y + PAGE_PAD);
draw_page_frame (cr, preview);
draw_page_content (cr, page_number, preview);
cairo_restore (cr);
}
static gboolean
preview_draw (GtkWidget *widget,
cairo_t *cr,
GeditPrintPreview *preview)
{
GdkWindow *bin_window;
gint tile_width;
gint page_num;
gint n_pages;
gint col;
bin_window = gtk_layout_get_bin_window (preview->layout);
if (!gtk_cairo_should_draw_window (cr, bin_window))
{
return GDK_EVENT_STOP;
}
cairo_save (cr);
gtk_cairo_transform_to_window (cr, widget, bin_window);
get_tile_size (preview, &tile_width, NULL);
n_pages = get_n_pages (preview);
col = 0;
page_num = get_first_page_displayed (preview);
while (col < preview->n_columns && page_num < n_pages)
{
if (!gtk_print_operation_preview_is_selected (preview->gtk_preview, page_num))
{
page_num++;
continue;
}
draw_page (cr,
col * tile_width,
0,
page_num,
preview);
col++;
page_num++;
}
cairo_restore (cr);
return GDK_EVENT_STOP;
}
static void
init_last_page_label (GeditPrintPreview *preview)
{
gchar *str;
str = g_strdup_printf ("%d", get_n_pages (preview));
gtk_label_set_text (preview->last_page_label, str);
g_free (str);
}
static void
preview_ready (GtkPrintOperationPreview *gtk_preview,
GtkPrintContext *context,
GeditPrintPreview *preview)
{
init_last_page_label (preview);
goto_page (preview, 0);
set_zoom_factor (preview, 1.0);
/* let the default gtklayout handler clear the background */
g_signal_connect_after (preview->layout,
"draw",
G_CALLBACK (preview_draw),
preview);
gtk_widget_queue_draw (GTK_WIDGET (preview->layout));
}
/* HACK: we need a dummy surface to paginate... can we use something simpler? */
static cairo_status_t
dummy_write_func (G_GNUC_UNUSED gpointer closure,
G_GNUC_UNUSED const guchar *data,
G_GNUC_UNUSED guint length)
{
return CAIRO_STATUS_SUCCESS;
}
static cairo_surface_t *
create_preview_surface_platform (GtkPaperSize *paper_size,
gdouble *dpi_x,
gdouble *dpi_y)
{
gdouble width, height;
width = gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS);
height = gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS);
*dpi_x = *dpi_y = PRINTER_DPI;
return cairo_pdf_surface_create_for_stream (dummy_write_func, NULL,
width, height);
}
static cairo_surface_t *
create_preview_surface (GeditPrintPreview *preview,
gdouble *dpi_x,
gdouble *dpi_y)
{
GtkPageSetup *page_setup;
GtkPaperSize *paper_size;
page_setup = gtk_print_context_get_page_setup (preview->context);
/* Note: gtk_page_setup_get_paper_size() swaps width and height for
* landscape.
*/
paper_size = gtk_page_setup_get_paper_size (page_setup);
return create_preview_surface_platform (paper_size, dpi_x, dpi_y);
}
GtkWidget *
gedit_print_preview_new (GtkPrintOperation *operation,
GtkPrintOperationPreview *gtk_preview,
GtkPrintContext *context)
{
GeditPrintPreview *preview;
cairo_surface_t *surface;
cairo_t *cr;
gdouble dpi_x, dpi_y;
g_return_val_if_fail (GTK_IS_PRINT_OPERATION (operation), NULL);
g_return_val_if_fail (GTK_IS_PRINT_OPERATION_PREVIEW (gtk_preview), NULL);
preview = g_object_new (GEDIT_TYPE_PRINT_PREVIEW, NULL);
preview->operation = g_object_ref (operation);
preview->gtk_preview = g_object_ref (gtk_preview);
preview->context = g_object_ref (context);
/* FIXME: is this legal?? */
gtk_print_operation_set_unit (operation, GTK_UNIT_POINTS);
g_signal_connect_object (gtk_preview,
"ready",
G_CALLBACK (preview_ready),
preview,
0);
/* FIXME: we need a cr to paginate... but we can't get the drawing
* area surface because it's not there yet... for now I create
* a dummy pdf surface.
* gtk_print_context_set_cairo_context() should be called in the
* got-page-size handler.
*/
surface = create_preview_surface (preview, &dpi_x, &dpi_y);
cr = cairo_create (surface);
gtk_print_context_set_cairo_context (context, cr, dpi_x, dpi_y);
cairo_destroy (cr);
cairo_surface_destroy (surface);
return GTK_WIDGET (preview);
}
/* ex:set ts=8 noet: */