1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008-2015, 2016 Free Software Foundation.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "ui/gui/psppire-output-view.h"
25 #include "librsvg/rsvg.h"
27 #include "libpspp/assertion.h"
28 #include "libpspp/string-map.h"
29 #include "output/cairo.h"
30 #include "output/driver-provider.h"
31 #include "output/driver.h"
32 #include "output/chart-item.h"
33 #include "output/group-item.h"
34 #include "output/message-item.h"
35 #include "output/output-item.h"
36 #include "output/output-item-provider.h"
37 #include "output/table-item.h"
38 #include "output/text-item.h"
40 #include "gl/c-xvasprintf.h"
41 #include "gl/minmax.h"
42 #include "gl/tmpdir.h"
43 #include "gl/xalloc.h"
46 #define _(msgid) gettext (msgid)
48 struct output_view_item
50 struct output_item *item;
51 GtkWidget *drawing_area;
54 struct psppire_output_view
64 struct string_map render_opts;
65 GtkTreeView *overview;
66 GtkTreePath *cur_group;
70 guint buttontime; /* Time of the button event */
72 struct output_view_item *items;
73 size_t n_items, allocated_items;
74 struct output_view_item *selected_item;
76 /* Variables pertaining to printing */
77 GtkPrintSettings *print_settings;
78 struct xr_driver *print_xrd;
86 COL_NAME, /* Table name. */
87 COL_ADDR, /* Pointer to the table */
88 COL_Y, /* Y position of top of name. */
92 static GtkTargetList *build_target_list (const struct output_item *item);
93 static void clipboard_get_cb (GtkClipboard *clipboard,
94 GtkSelectionData *selection_data,
98 /* Draws a white background on the GtkLayout to match the white background of
99 each of the output items. */
101 layout_draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data)
103 int width = gtk_widget_get_allocated_width (widget);
104 int height = gtk_widget_get_allocated_height (widget);
105 GtkStyleContext *context = gtk_widget_get_style_context (widget);
106 gtk_render_background (context, cr, 0, 0, width, height);
107 return FALSE; /* Continue drawing the GtkDrawingAreas. */
111 draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data)
114 if (!gdk_cairo_get_clip_rectangle (cr, &clip))
117 struct xr_rendering *r = g_object_get_data (G_OBJECT (widget), "rendering");
119 /* Draw the background based on the state of the widget
120 which can be selected or not selected */
121 GtkStyleContext *context = gtk_widget_get_style_context (widget);
122 gtk_render_background (context, cr, clip.x, clip.y,
123 clip.x + clip.width, clip.y + clip.height);
124 /* Select the default foreground color based on current style
125 and state of the widget */
126 GtkStateFlags state = gtk_widget_get_state_flags (widget);
128 gtk_style_context_get_color (context, state, &color);
129 cairo_set_source_rgba (cr, color.red, color.green, color.blue, color.alpha);
130 xr_rendering_draw (r, cr, clip.x, clip.y,
131 clip.x + clip.width, clip.y + clip.height);
137 free_rendering (gpointer rendering_)
139 struct xr_rendering *rendering = rendering_;
140 xr_rendering_destroy (rendering);
144 get_xr_options (struct psppire_output_view *view, struct string_map *options)
146 string_map_clear (options);
148 GtkStyleContext *context
149 = gtk_widget_get_style_context (GTK_WIDGET (view->output));
150 GtkStateFlags state = gtk_widget_get_state_flags (GTK_WIDGET (view->output));
152 /* Use GTK+ default font as proportional font. */
153 PangoFontDescription *font_desc;
154 gtk_style_context_get (context, state, "font", &font_desc, NULL);
155 char *font_name = pango_font_description_to_string (font_desc);
156 string_map_insert (options, "prop-font", font_name);
159 /* Derived emphasized font from proportional font. */
160 pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
161 font_name = pango_font_description_to_string (font_desc);
162 string_map_insert (options, "emph-font", font_name);
164 pango_font_description_free (font_desc);
166 /* Pretend that the "page" has a reasonable width and a very big length,
167 so that most tables can be conveniently viewed on-screen with vertical
168 scrolling only. (The length should not be increased very much because
169 it is already close enough to INT_MAX when expressed as thousands of a
171 string_map_insert_nocopy (options, xstrdup ("paper-size"),
172 xasprintf ("%dx1000000pt", view->render_width));
173 string_map_insert (options, "left-margin", "0");
174 string_map_insert (options, "right-margin", "0");
175 string_map_insert (options, "top-margin", "0");
176 string_map_insert (options, "bottom-margin", "0");
180 create_xr (struct psppire_output_view *view)
182 get_xr_options (view, &view->render_opts);
184 struct string_map options;
185 string_map_clone (&options, &view->render_opts);
187 GdkWindow *win = gtk_layout_get_bin_window (view->output);
188 cairo_region_t *region = gdk_window_get_visible_region (win);
189 GdkDrawingContext *ctx = gdk_window_begin_draw_frame (win, region);
190 cairo_t *cr = gdk_drawing_context_get_cairo_context (ctx);
192 view->xr = xr_driver_create (cr, &options);
193 string_map_destroy (&options);
195 struct text_item *text_item = text_item_create (TEXT_ITEM_LOG, "X");
196 struct xr_rendering *r
197 = xr_rendering_create (view->xr, text_item_super (text_item), cr);
198 xr_rendering_measure (r, NULL, &view->font_height);
199 xr_rendering_destroy (r);
200 text_item_unref (text_item);
202 gdk_window_end_draw_frame (win, ctx);
203 cairo_region_destroy (region);
206 /* Return the horizontal position to place a widget whose
207 width is CHILD_WIDTH */
209 get_xpos (const struct psppire_output_view *view, gint child_width)
211 GdkWindow *gdkw = gtk_widget_get_window (GTK_WIDGET (view->output));
212 guint w = gdk_window_get_width (gdkw);
214 g_object_get (view->output, "border-width", &gutter, NULL);
215 return (gtk_widget_get_direction (GTK_WIDGET (view->output)) == GTK_TEXT_DIR_RTL) ? w - child_width - gutter: gutter;
218 static struct output_view_item *
219 find_selected_item (struct psppire_output_view *view)
221 struct output_view_item *item = NULL;
224 if (view->items == NULL)
227 for (item = view->items; item < &view->items[view->n_items]; item++)
229 GtkWidget *widget = GTK_WIDGET (item->drawing_area);
230 if GTK_IS_WIDGET (widget)
232 GtkStateFlags state = gtk_widget_get_state_flags (widget);
233 if (state & GTK_STATE_FLAG_SELECTED)
242 set_copy_action (struct psppire_output_view *view,
245 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view->output));
246 GAction *copy_action = g_action_map_lookup_action (G_ACTION_MAP (toplevel),
248 g_object_set (copy_action,
254 clear_selection (struct psppire_output_view *view)
258 struct output_view_item *item = find_selected_item (view);
261 set_copy_action (view, FALSE);
262 GtkWidget *widget = GTK_WIDGET (item->drawing_area);
263 if (GTK_IS_WIDGET (widget))
265 gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_SELECTED);
266 gtk_widget_queue_draw (widget);
271 off_item_button_press_event_cb (GtkWidget *widget,
272 GdkEventButton *event,
273 struct psppire_output_view *view)
275 /* buttontime is set by button_press_event_cb
276 If our event->time is equal to the time from the
277 button_press_event_cb, then we handle the same event.
278 In that case we must not clear the selection because
279 it was just set by button_press_event_cb from the item */
280 if (event->time != view->buttontime)
281 clear_selection (view);
282 return FALSE; /* Forward the event -> DragNDrop */
286 button_press_event_cb (GtkWidget *widget,
287 GdkEventButton *event,
288 struct psppire_output_view *view)
290 view->buttontime = event->time;
291 clear_selection (view);
292 set_copy_action (view, TRUE);
293 gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
294 gtk_widget_queue_draw (widget);
295 return FALSE; /* Forward Event -> off_item will trigger */
299 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
300 GtkSelectionData *selection_data,
301 guint target_type, guint time,
302 struct psppire_output_view *view)
304 view->selected_item = find_selected_item (view);
305 clipboard_get_cb (NULL, selection_data, target_type, view);
309 create_drawing_area (struct psppire_output_view *view,
310 GtkWidget *drawing_area, struct xr_rendering *r,
311 int tw, int th, const struct output_item *item)
313 struct string_map options = STRING_MAP_INITIALIZER (options);
314 string_map_insert (&options, "transparent", "true");
315 string_map_insert (&options, "systemcolors", "true");
316 xr_rendering_apply_options (r, &options);
318 g_object_set_data_full (G_OBJECT (drawing_area),
319 "rendering", r, free_rendering);
320 g_signal_connect (drawing_area, "button-press-event",
321 G_CALLBACK (button_press_event_cb), view);
322 gtk_widget_add_events (drawing_area, GDK_BUTTON_PRESS_MASK);
324 { /* Drag and Drop */
325 GtkTargetList *tl = build_target_list (item);
327 gtk_drag_source_set (drawing_area, GDK_BUTTON1_MASK, NULL, 0, GDK_ACTION_COPY);
328 gtk_drag_source_set_target_list (drawing_area, tl);
329 gtk_target_list_unref (tl);
330 g_signal_connect (drawing_area, "drag-data-get",
331 G_CALLBACK (drag_data_get_cb), view);
333 GtkStyleContext *context = gtk_widget_get_style_context (drawing_area);
334 gtk_style_context_add_class (context,
335 GTK_STYLE_CLASS_VIEW);
336 g_signal_connect (drawing_area, "draw",
337 G_CALLBACK (draw_callback), view);
339 gtk_widget_set_size_request (drawing_area, tw, th);
340 gint xpos = get_xpos (view, tw);
342 gtk_layout_put (view->output, drawing_area, xpos, view->y);
344 gtk_widget_show (drawing_area);
348 rerender (struct psppire_output_view *view)
350 struct output_view_item *item;
351 GdkWindow *gdkw = gtk_widget_get_window (GTK_WIDGET (view->output));
353 if (!view->n_items || ! gdkw)
356 if (view->xr == NULL)
359 GdkWindow *win = gtk_layout_get_bin_window (view->output);
360 cairo_region_t *region = gdk_window_get_visible_region (win);
361 GdkDrawingContext *ctx = gdk_window_begin_draw_frame (win, region);
362 cairo_t *cr = gdk_drawing_context_get_cairo_context (ctx);
366 for (item = view->items; item < &view->items[view->n_items]; item++)
368 struct xr_rendering *r;
373 view->y += view->font_height / 2;
375 if (is_group_open_item (item->item))
378 r = xr_rendering_create (view->xr, item->item, cr);
381 g_warn_if_reached ();
385 xr_rendering_measure (r, &tw, &th);
387 gint xpos = get_xpos (view, tw);
389 if (!item->drawing_area)
391 item->drawing_area = gtk_drawing_area_new ();
392 create_drawing_area (view, item->drawing_area, r, tw, th, item->item);
396 g_object_set_data_full (G_OBJECT (item->drawing_area),
397 "rendering", r, free_rendering);
398 gtk_widget_set_size_request (item->drawing_area, tw, th);
399 gtk_layout_move (view->output, item->drawing_area, xpos, view->y);
405 /* This code probably doesn't bring us anthing, but Gtk
406 shows warnings if get_preferred_width/height is not
407 called before the size_allocate below is called. */
408 gtk_widget_get_preferred_width (item->drawing_area, &minw, NULL);
409 gtk_widget_get_preferred_height (item->drawing_area, &minh, NULL);
410 if (th > minh) th = minh;
411 if (tw > minw) tw = minw;
418 gtk_widget_size_allocate (item->drawing_area, &alloc);
420 if (view->max_width < tw)
421 view->max_width = tw;
425 gtk_layout_set_size (view->output,
426 view->max_width + view->font_height,
427 view->y + view->font_height);
429 gdk_window_end_draw_frame (win, ctx);
430 cairo_region_destroy (region);
435 psppire_output_view_put (struct psppire_output_view *view,
436 const struct output_item *item)
438 struct output_view_item *view_item;
439 GtkWidget *drawing_area;
443 if (is_group_close_item (item))
447 if (!gtk_tree_path_up (view->cur_group))
449 gtk_tree_path_free (view->cur_group);
450 view->cur_group = NULL;
455 else if (is_text_item (item))
457 const struct text_item *text_item = to_text_item (item);
458 const char *text = text_item_get_text (text_item);
463 if (view->n_items >= view->allocated_items)
464 view->items = x2nrealloc (view->items, &view->allocated_items,
465 sizeof *view->items);
466 view_item = &view->items[view->n_items++];
467 view_item->item = output_item_ref (item);
468 view_item->drawing_area = NULL;
470 GdkWindow *win = gtk_widget_get_window (GTK_WIDGET (view->output));
471 if (is_group_open_item (item))
475 view_item->drawing_area = drawing_area = gtk_drawing_area_new ();
477 if (view->xr == NULL)
480 cairo_region_t *region = gdk_window_get_visible_region (win);
481 GdkDrawingContext *ctx = gdk_window_begin_draw_frame (win, region);
482 cairo_t *cr = gdk_drawing_context_get_cairo_context (ctx);
485 view->y += view->font_height / 2;
487 struct xr_rendering *r = xr_rendering_create (view->xr, item, cr);
490 gdk_window_end_draw_frame (win, ctx);
491 cairo_region_destroy (region);
495 xr_rendering_measure (r, &tw, &th);
496 create_drawing_area (view, drawing_area, r, tw, th, item);
497 gdk_window_end_draw_frame (win, ctx);
498 cairo_region_destroy (region);
505 GtkTreeStore *store = GTK_TREE_STORE (
506 gtk_tree_view_get_model (view->overview));
508 ds_init_empty (&name);
510 /* Create a new node in the tree and puts a reference to it in 'iter'. */
514 && gtk_tree_path_get_depth (view->cur_group) > 0
515 && gtk_tree_model_get_iter (GTK_TREE_MODEL (store),
516 &parent, view->cur_group))
517 gtk_tree_store_append (store, &iter, &parent);
519 gtk_tree_store_append (store, &iter, NULL);
521 if (is_group_open_item (item))
523 gtk_tree_path_free (view->cur_group);
524 view->cur_group = gtk_tree_model_get_path (GTK_TREE_MODEL (store),
529 if (is_text_item (item))
531 const struct text_item *text_item = to_text_item (item);
532 ds_put_cstr (&name, text_item_type_to_string (
533 text_item_get_type (text_item)));
535 else if (is_message_item (item))
537 const struct message_item *msg_item = to_message_item (item);
538 const struct msg *msg = message_item_get_msg (msg_item);
539 ds_put_format (&name, "%s: %s", _("Message"),
540 msg_severity_to_string (msg->severity));
542 else if (is_table_item (item))
544 const struct table_item_text *title
545 = table_item_get_title (to_table_item (item));
547 ds_put_format (&name, "Table: %s", title->content);
549 ds_put_cstr (&name, "Table");
551 else if (is_chart_item (item))
553 const char *s = chart_item_get_title (to_chart_item (item));
555 ds_put_format (&name, "Chart: %s", s);
557 ds_put_cstr (&name, "Chart");
559 else if (is_group_open_item (item))
560 ds_put_cstr (&name, to_group_open_item (item)->command_name);
561 gtk_tree_store_set (store, &iter,
562 COL_NAME, ds_cstr (&name),
568 GtkTreePath *path = gtk_tree_model_get_path (
569 GTK_TREE_MODEL (store), &iter);
570 gtk_tree_view_expand_row (view->overview, path, TRUE);
571 gtk_tree_path_free (path);
574 if (view->max_width < tw)
575 view->max_width = tw;
578 gtk_layout_set_size (view->output, view->max_width, view->y);
582 on_row_activate (GtkTreeView *overview,
584 GtkTreeViewColumn *column,
585 struct psppire_output_view *view)
593 model = gtk_tree_view_get_model (overview);
594 if (!gtk_tree_model_get_iter (model, &iter, path))
597 gtk_tree_model_get_value (model, &iter, COL_Y, &value);
598 y = g_value_get_long (&value);
599 g_value_unset (&value);
601 vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (view->output));
602 min = gtk_adjustment_get_lower (vadj);
603 max = gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj);
608 gtk_adjustment_set_value (vadj, y);
612 on_style_updated (GtkWidget *toplevel, struct psppire_output_view *view)
614 if (!view->n_items || !gtk_widget_get_window (GTK_WIDGET (view->output)))
617 /* GTK+ fires this signal for trivial changes like the mouse moving in or out
618 of the window. Check whether the actual rendering options changed and
619 re-render only if they did. */
620 struct string_map options = STRING_MAP_INITIALIZER (options);
621 get_xr_options (view, &options);
622 if (!string_map_equals (&options, &view->render_opts))
624 xr_driver_destroy (view->xr);
629 string_map_destroy (&options);
642 /* GNU Hurd doesn't have PATH_MAX. Use a fallback.
643 Temporary directory names are usually not that long. */
645 # define PATH_MAX 1024
648 /* Returns a pixbuf from a svg file */
649 /* You must unref the pixbuf after usage */
651 derive_pixbuf_from_svg (const char *filename)
654 GdkPixbuf *pixbuf = NULL;
656 RsvgHandle *handle = rsvg_handle_new_from_file (filename, &err);
659 rsvg_handle_set_dpi (handle, 300.0);
660 pixbuf = rsvg_handle_get_pixbuf (handle);
661 g_object_unref (handle);
664 pixbuf = gdk_pixbuf_new_from_file (filename, &err);
668 msg (ME, _("Could not open file %s during copy operation: %s"),
669 filename, err->message);
677 clipboard_get_cb (GtkClipboard *clipboard,
678 GtkSelectionData *selection_data,
682 struct psppire_output_view *view = data;
686 struct output_driver *driver = NULL;
687 char dirname[PATH_MAX], *filename;
688 struct string_map options;
690 if (view->selected_item == NULL)
693 if (path_search (dirname, sizeof dirname, NULL, NULL, true)
694 || mkdtemp (dirname) == NULL)
696 msg_error (errno, _("failed to create temporary directory during clipboard operation"));
699 filename = xasprintf ("%s/clip.tmp", dirname);
701 string_map_init (&options);
702 string_map_insert (&options, "output-file", filename);
706 case SELECT_FMT_UTF8:
707 string_map_insert (&options, "box", "unicode");
710 case SELECT_FMT_TEXT:
711 string_map_insert (&options, "format", "txt");
714 case SELECT_FMT_HTML:
715 string_map_insert (&options, "format", "html");
716 string_map_insert (&options, "borders", "false");
717 string_map_insert (&options, "css", "false");
726 string_map_insert (&options, "format", "odt");
730 g_warning ("unsupported clip target\n");
735 if ((info == SELECT_FMT_IMG) ||
736 (info == SELECT_FMT_SVG) )
738 GtkWidget *widget = view->selected_item->drawing_area;
739 struct xr_rendering *r = g_object_get_data (G_OBJECT (widget), "rendering");
740 xr_draw_svg_file (r, filename);
744 driver = output_driver_create (&options);
748 driver->class->submit (driver, view->selected_item->item);
750 if (driver->class->flush)
751 driver->class->flush (driver);
753 /* Some drivers (eg: the odt one) don't write anything until they
755 output_driver_destroy (driver);
759 if (info == SELECT_FMT_IMG)
761 GdkPixbuf *pixbuf = derive_pixbuf_from_svg (filename);
764 gtk_selection_data_set_pixbuf (selection_data, pixbuf);
765 g_object_unref (pixbuf);
768 else if (g_file_get_contents (filename, &text, &length, NULL))
769 gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data),
771 (const guchar *) text, length);
776 output_driver_destroy (driver);
786 clipboard_clear_cb (GtkClipboard *clipboard,
792 CT ( ctn1, "STRING", 0, SELECT_FMT_TEXT ) \
793 CT ( ctn2, "TEXT", 0, SELECT_FMT_TEXT ) \
794 CT ( ctn3, "COMPOUND_TEXT", 0, SELECT_FMT_TEXT ) \
795 CT ( ctn4, "text/plain", 0, SELECT_FMT_TEXT ) \
796 CT ( ctn5, "UTF8_STRING", 0, SELECT_FMT_UTF8 ) \
797 CT ( ctn6, "text/plain;charset=utf-8", 0, SELECT_FMT_UTF8 ) \
798 CT ( ctn7, "text/html", 0, SELECT_FMT_HTML ) \
799 CT ( ctn8, "image/svg+xml", 0, SELECT_FMT_SVG )
801 #define CT(ID, TARGET, FLAGS, INFO) static gchar ID[] = TARGET;
804 static gchar ctnlast[] = "application/vnd.oasis.opendocument.text";
806 static const GtkTargetEntry targets[] = {
807 #define CT(ID, TARGET, FLAGS, INFO) { ID, FLAGS, INFO },
810 { ctnlast, 0, SELECT_FMT_ODT }
813 static GtkTargetList *
814 build_target_list (const struct output_item *item)
816 GtkTargetList *tl = gtk_target_list_new (targets, G_N_ELEMENTS (targets));
817 g_return_val_if_fail (tl, NULL);
818 if (is_table_item (item) ||
819 is_chart_item (item))
820 gtk_target_list_add_image_targets (tl, SELECT_FMT_IMG, TRUE);
825 on_copy (struct psppire_output_view *view)
827 GtkWidget *widget = GTK_WIDGET (view->overview);
828 GtkClipboard *cb = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
830 struct output_view_item *ov_item = find_selected_item (view);
833 view->selected_item = ov_item;
834 GtkTargetList *tl = build_target_list (ov_item->item);
835 g_return_if_fail (tl);
836 gint no_of_targets = 0;
837 GtkTargetEntry *ta = gtk_target_table_new_from_list (tl, &no_of_targets);
838 g_return_if_fail (ta);
839 if (!gtk_clipboard_set_with_data (cb, ta, no_of_targets,
840 clipboard_get_cb, clipboard_clear_cb,
842 clipboard_clear_cb (cb, view);
844 gtk_target_list_unref (tl);
845 gtk_target_table_free (ta,no_of_targets);
849 on_size_allocate (GtkWidget *widget,
850 GdkRectangle *allocation,
851 struct psppire_output_view *view)
853 view->render_width = MAX (300, allocation->width);
858 on_realize (GtkWidget *overview, GObject *view)
860 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (overview));
862 GAction *copy_action = g_action_map_lookup_action (G_ACTION_MAP (toplevel),
865 GAction *select_all_action = g_action_map_lookup_action (G_ACTION_MAP (toplevel),
868 g_object_set (copy_action, "enabled", FALSE, NULL);
869 g_object_set (select_all_action, "enabled", FALSE, NULL);
871 g_signal_connect_swapped (copy_action, "activate",
872 G_CALLBACK (on_copy), view);
876 struct psppire_output_view *
877 psppire_output_view_new (GtkLayout *output, GtkTreeView *overview)
879 struct psppire_output_view *view;
880 GtkTreeViewColumn *column;
881 GtkCellRenderer *renderer;
885 view = xmalloc (sizeof *view);
887 view->font_height = 0;
888 view->output = output;
889 view->render_width = 0;
892 string_map_init (&view->render_opts);
893 view->overview = overview;
894 view->cur_group = NULL;
895 view->toplevel = gtk_widget_get_toplevel (GTK_WIDGET (output));
896 view->buttontime = 0;
898 view->n_items = view->allocated_items = 0;
899 view->selected_item = NULL;
900 view->print_settings = NULL;
901 view->print_xrd = NULL;
902 view->print_item = 0;
903 view->print_n_pages = 0;
904 view->paginated = FALSE;
906 g_signal_connect (output, "draw", G_CALLBACK (layout_draw_callback), NULL);
908 g_signal_connect (output, "style-updated", G_CALLBACK (on_style_updated), view);
910 g_signal_connect (output, "size-allocate", G_CALLBACK (on_size_allocate), view);
912 gtk_widget_add_events (GTK_WIDGET (output), GDK_BUTTON_PRESS_MASK);
913 g_signal_connect (output, "button-press-event",
914 G_CALLBACK (off_item_button_press_event_cb), view);
916 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (output)),
917 GTK_STYLE_CLASS_VIEW);
921 g_signal_connect (overview, "realize", G_CALLBACK (on_realize), view);
923 model = GTK_TREE_MODEL (gtk_tree_store_new (
925 G_TYPE_STRING, /* COL_NAME */
926 G_TYPE_POINTER, /* COL_ADDR */
927 G_TYPE_LONG)); /* COL_Y */
928 gtk_tree_view_set_model (overview, model);
929 g_object_unref (model);
931 column = gtk_tree_view_column_new ();
932 gtk_tree_view_append_column (GTK_TREE_VIEW (overview), column);
933 renderer = gtk_cell_renderer_text_new ();
934 gtk_tree_view_column_pack_start (column, renderer, TRUE);
935 gtk_tree_view_column_add_attribute (column, renderer, "text", COL_NAME);
937 g_signal_connect (GTK_TREE_VIEW (overview),
938 "row-activated", G_CALLBACK (on_row_activate), view);
945 psppire_output_view_destroy (struct psppire_output_view *view)
952 g_signal_handlers_disconnect_by_func (view->output,
953 G_CALLBACK (on_style_updated), view);
955 string_map_destroy (&view->render_opts);
957 for (i = 0; i < view->n_items; i++)
958 output_item_unref (view->items[i].item);
961 view->n_items = view->allocated_items = 0;
963 if (view->print_settings != NULL)
964 g_object_unref (view->print_settings);
966 xr_driver_destroy (view->xr);
969 gtk_tree_path_free (view->cur_group);
975 psppire_output_view_clear (struct psppire_output_view *view)
982 for (i = 0; i < view->n_items; i++)
984 gtk_container_remove (GTK_CONTAINER (view->output),
985 view->items[i].drawing_area);
986 output_item_unref (view->items[i].item);
990 view->n_items = view->allocated_items = 0;
996 psppire_output_view_export (struct psppire_output_view *view,
997 struct string_map *options)
999 struct output_driver *driver;
1001 driver = output_driver_create (options);
1006 for (i = 0; i < view->n_items; i++)
1007 driver->class->submit (driver, view->items[i].item);
1008 output_driver_destroy (driver);
1015 get_cairo_context_from_print_context (GtkPrintContext *context)
1017 cairo_t *cr = gtk_print_context_get_cairo_context (context);
1020 For all platforms except windows, gtk_print_context_get_dpi_[xy] returns 72.
1021 Windows returns 600.
1023 double xres = gtk_print_context_get_dpi_x (context);
1024 double yres = gtk_print_context_get_dpi_y (context);
1026 /* This means that the cairo context now has its dimensions in Points */
1027 cairo_scale (cr, xres / 72.0, yres / 72.0);
1034 create_xr_print_driver (GtkPrintContext *context, struct psppire_output_view *view)
1036 struct string_map options;
1037 GtkPageSetup *page_setup;
1038 double width, height;
1040 double right_margin;
1042 double bottom_margin;
1044 page_setup = gtk_print_context_get_page_setup (context);
1045 width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_MM);
1046 height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_MM);
1047 left_margin = gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_MM);
1048 right_margin = gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_MM);
1049 top_margin = gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_MM);
1050 bottom_margin = gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_MM);
1052 string_map_init (&options);
1053 string_map_insert_nocopy (&options, xstrdup ("paper-size"),
1054 c_xasprintf("%.2fx%.2fmm", width, height));
1055 string_map_insert_nocopy (&options, xstrdup ("left-margin"),
1056 c_xasprintf ("%.2fmm", left_margin));
1057 string_map_insert_nocopy (&options, xstrdup ("right-margin"),
1058 c_xasprintf ("%.2fmm", right_margin));
1059 string_map_insert_nocopy (&options, xstrdup ("top-margin"),
1060 c_xasprintf ("%.2fmm", top_margin));
1061 string_map_insert_nocopy (&options, xstrdup ("bottom-margin"),
1062 c_xasprintf ("%.2fmm", bottom_margin));
1064 view->print_xrd = xr_driver_create (get_cairo_context_from_print_context (context), &options);
1066 string_map_destroy (&options);
1070 paginate (GtkPrintOperation *operation,
1071 GtkPrintContext *context,
1072 struct psppire_output_view *view)
1074 if (view->paginated)
1076 /* Sometimes GTK+ emits this signal again even after pagination is
1077 complete. Don't let that screw up printing. */
1080 else if (view->print_item < view->n_items)
1082 xr_driver_output_item (view->print_xrd,
1083 view->items[view->print_item++].item);
1084 while (xr_driver_need_new_page (view->print_xrd))
1086 xr_driver_next_page (view->print_xrd, get_cairo_context_from_print_context (context));
1087 view->print_n_pages ++;
1093 gtk_print_operation_set_n_pages (operation, view->print_n_pages);
1095 /* Re-create the driver to do the real printing. */
1096 xr_driver_destroy (view->print_xrd);
1097 create_xr_print_driver (context, view);
1098 view->print_item = 0;
1099 view->paginated = TRUE;
1106 begin_print (GtkPrintOperation *operation,
1107 GtkPrintContext *context,
1108 struct psppire_output_view *view)
1110 create_xr_print_driver (context, view);
1112 view->print_item = 0;
1113 view->print_n_pages = 1;
1114 view->paginated = FALSE;
1118 end_print (GtkPrintOperation *operation,
1119 GtkPrintContext *context,
1120 struct psppire_output_view *view)
1122 xr_driver_destroy (view->print_xrd);
1127 draw_page (GtkPrintOperation *operation,
1128 GtkPrintContext *context,
1130 struct psppire_output_view *view)
1132 xr_driver_next_page (view->print_xrd, get_cairo_context_from_print_context (context));
1133 while (!xr_driver_need_new_page (view->print_xrd)
1134 && view->print_item < view->n_items)
1135 xr_driver_output_item (view->print_xrd, view->items [view->print_item++].item);
1140 psppire_output_view_print (struct psppire_output_view *view,
1141 GtkWindow *parent_window)
1143 GtkPrintOperationResult res;
1145 GtkPrintOperation *print = gtk_print_operation_new ();
1147 if (view->print_settings != NULL)
1148 gtk_print_operation_set_print_settings (print, view->print_settings);
1150 g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), view);
1151 g_signal_connect (print, "end_print", G_CALLBACK (end_print), view);
1152 g_signal_connect (print, "paginate", G_CALLBACK (paginate), view);
1153 g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), view);
1155 res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
1156 parent_window, NULL);
1158 if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
1160 if (view->print_settings != NULL)
1161 g_object_unref (view->print_settings);
1162 view->print_settings = g_object_ref (gtk_print_operation_get_print_settings (print));
1165 g_object_unref (print);
1168 struct psppire_output_view_driver
1170 struct output_driver driver;
1171 struct psppire_output_view *view;
1174 static struct psppire_output_view_driver *
1175 psppire_output_view_driver_cast (struct output_driver *driver)
1177 return UP_CAST (driver, struct psppire_output_view_driver, driver);
1181 psppire_output_view_submit (struct output_driver *this,
1182 const struct output_item *item)
1184 struct psppire_output_view_driver *povd = psppire_output_view_driver_cast (this);
1186 if (is_table_item (item))
1187 psppire_output_view_put (povd->view, item);
1190 static struct output_driver_class psppire_output_view_driver_class =
1192 "PSPPIRE Output View", /* name */
1194 psppire_output_view_submit, /* submit */
1199 psppire_output_view_register_driver (struct psppire_output_view *view)
1201 struct psppire_output_view_driver *povd;
1202 struct output_driver *d;
1204 povd = xzalloc (sizeof *povd);
1207 output_driver_init (d, &psppire_output_view_driver_class, "PSPPIRE Output View",
1208 SETTINGS_DEVICE_UNFILTERED);
1209 output_driver_register (d);