diff options
Diffstat (limited to 'gedit/gedit-print-preview.c')
-rw-r--r-- | gedit/gedit-print-preview.c | 1158 |
1 files changed, 1158 insertions, 0 deletions
diff --git a/gedit/gedit-print-preview.c b/gedit/gedit-print-preview.c new file mode 100644 index 0000000..0ef4a9e --- /dev/null +++ b/gedit/gedit-print-preview.c @@ -0,0 +1,1158 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "gedit-print-preview.h" + +#include <math.h> +#include <stdlib.h> +#include <glib/gi18n.h> +#include <gdk/gdkkeysyms.h> +#include <cairo-pdf.h> + +#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: */ |