psppire-output-view: Fix memory leaks.
[pspp] / src / ui / gui / psppire-output-view.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008-2014 Free Software Foundation.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "ui/gui/psppire-output-view.h"
20
21 #include <errno.h>
22 #include <stdbool.h>
23
24 #include "libpspp/assertion.h"
25 #include "libpspp/string-map.h"
26 #include "output/cairo.h"
27 #include "output/driver-provider.h"
28 #include "output/driver.h"
29 #include "output/chart-item.h"
30 #include "output/message-item.h"
31 #include "output/output-item.h"
32 #include "output/table-item.h"
33 #include "output/text-item.h"
34
35 #include "gl/c-xvasprintf.h"
36 #include "gl/tmpdir.h"
37 #include "gl/xalloc.h"
38
39 #include <gettext.h>
40 #define _(msgid) gettext (msgid)
41
42 struct psppire_output_view
43   {
44     struct xr_driver *xr;
45     int font_height;
46
47     GtkLayout *output;
48     int max_width;
49     int y;
50
51     struct string_map render_opts;
52     GtkTreeView *overview;
53     GtkTreeIter cur_command;
54     bool in_command;
55
56     GtkWidget *toplevel;
57
58     struct output_item **items;
59     size_t n_items, allocated_items;
60
61     /* Variables pertaining to printing */
62     GtkPrintSettings *print_settings;
63     struct xr_driver *print_xrd;
64     int print_item;
65     int print_n_pages;
66     gboolean paginated;
67   };
68
69 enum
70   {
71     COL_TITLE,                  /* Table title. */
72     COL_ADDR,                   /* Pointer to the table */
73     COL_Y,                      /* Y position of top of title. */
74     N_COLS
75   };
76
77 static void on_dwgarea_realize (GtkWidget *widget, gpointer data);
78
79 static gboolean
80 expose_event_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
81 {
82   struct psppire_output_view *view = data;
83   struct xr_rendering *r = g_object_get_data (G_OBJECT (widget), "rendering");
84   cairo_t *cr = gdk_cairo_create (widget->window);
85
86   const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (view->output));
87
88   PangoFontDescription *font_desc;
89   char *font_name;
90
91   gchar *fgc =
92     gdk_color_to_string (&style->text[gtk_widget_get_state (GTK_WIDGET (view->output))]);
93
94   string_map_replace (&view->render_opts, "foreground-color", fgc);
95
96   free (fgc);
97
98   /* Use GTK+ default font as proportional font. */
99   font_name = pango_font_description_to_string (style->font_desc);
100   string_map_replace (&view->render_opts, "prop-font", font_name);
101   g_free (font_name);
102
103   /* Derived emphasized font from proportional font. */
104   font_desc = pango_font_description_copy (style->font_desc);
105   pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
106   font_name = pango_font_description_to_string (font_desc);
107   string_map_replace (&view->render_opts, "emph-font", font_name);
108   g_free (font_name);
109   pango_font_description_free (font_desc);
110
111   xr_rendering_apply_options (r, &view->render_opts);
112
113   xr_rendering_draw (r, cr, event->area.x, event->area.y,
114                      event->area.width, event->area.height);
115   cairo_destroy (cr);
116
117   return TRUE;
118 }
119
120 static void
121 free_rendering (gpointer rendering_)
122 {
123   struct xr_rendering *rendering = rendering_;
124   xr_rendering_destroy (rendering);
125 }
126
127 void
128 psppire_output_view_put (struct psppire_output_view *view,
129                          const struct output_item *item)
130 {
131   GtkWidget *drawing_area;
132   struct xr_rendering *r;
133   struct string title;
134   GtkTreeStore *store;
135   GtkTreePath *path;
136   GtkTreeIter iter;
137   cairo_t *cr;
138   int tw, th;
139
140   if (view->n_items >= view->allocated_items)
141     view->items = x2nrealloc (view->items, &view->allocated_items,
142                                 sizeof *view->items);
143   view->items[view->n_items++] = output_item_ref (item);
144
145   if (is_text_item (item))
146     {
147       const struct text_item *text_item = to_text_item (item);
148       enum text_item_type type = text_item_get_type (text_item);
149       const char *text = text_item_get_text (text_item);
150
151       if (type == TEXT_ITEM_COMMAND_CLOSE)
152         {
153           view->in_command = false;
154           return;
155         }
156       else if (text[0] == '\0')
157         return;
158     }
159
160   cr = gdk_cairo_create (GTK_WIDGET (view->output)->window);
161   if (view->xr == NULL)
162     {
163       const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (view->output));
164       struct text_item *text_item;
165       PangoFontDescription *font_desc;
166       char *font_name;
167       int font_width;
168
169       /* Set the widget's text color as the foreground color for the output driver */
170       gchar *fgc = gdk_color_to_string (&style->text[gtk_widget_get_state (GTK_WIDGET (view->output))]);
171
172       string_map_insert (&view->render_opts, "foreground-color", fgc);
173       g_free (fgc);
174
175       /* Use GTK+ default font as proportional font. */
176       font_name = pango_font_description_to_string (style->font_desc);
177       string_map_insert (&view->render_opts, "prop-font", font_name);
178       g_free (font_name);
179
180       /* Derived emphasized font from proportional font. */
181       font_desc = pango_font_description_copy (style->font_desc);
182       pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
183       font_name = pango_font_description_to_string (font_desc);
184       string_map_insert (&view->render_opts, "emph-font", font_name);
185       g_free (font_name);
186       pango_font_description_free (font_desc);
187
188       /* Pretend that the "page" has a reasonable width and a very big length,
189          so that most tables can be conveniently viewed on-screen with vertical
190          scrolling only.  (The length should not be increased very much because
191          it is already close enough to INT_MAX when expressed as thousands of a
192          point.) */
193       string_map_insert (&view->render_opts, "paper-size", "300x200000mm");
194       string_map_insert (&view->render_opts, "left-margin", "0");
195       string_map_insert (&view->render_opts, "right-margin", "0");
196       string_map_insert (&view->render_opts, "top-margin", "0");
197       string_map_insert (&view->render_opts, "bottom-margin", "0");
198
199       view->xr = xr_driver_create (cr, &view->render_opts);
200
201       text_item = text_item_create (TEXT_ITEM_PARAGRAPH, "X");
202       r = xr_rendering_create (view->xr, text_item_super (text_item), cr);
203       xr_rendering_measure (r, &font_width, &view->font_height);
204       /* xr_rendering_destroy (r); */
205       text_item_unref (text_item);
206     }
207
208   if (view->y > 0)
209     view->y += view->font_height / 2;
210
211   r = xr_rendering_create (view->xr, item, cr);
212   if (r == NULL)
213     goto done;
214
215   xr_rendering_measure (r, &tw, &th);
216
217   drawing_area = gtk_drawing_area_new ();
218
219   g_object_set_data_full (G_OBJECT (drawing_area), "rendering", r, free_rendering);
220   g_signal_connect (drawing_area, "realize",
221                     G_CALLBACK (on_dwgarea_realize), view);
222   g_signal_connect (drawing_area, "expose_event",
223                     G_CALLBACK (expose_event_callback), view);
224
225   gtk_widget_set_size_request (drawing_area, tw, th);
226   gtk_layout_put (view->output, drawing_area, 0, view->y);
227
228   gtk_widget_show (drawing_area);
229
230   if (view->overview
231       && (!is_text_item (item)
232           || text_item_get_type (to_text_item (item)) != TEXT_ITEM_SYNTAX
233           || !view->in_command))
234     {
235       store = GTK_TREE_STORE (gtk_tree_view_get_model (view->overview));
236
237       ds_init_empty (&title);
238       if (is_text_item (item)
239           && text_item_get_type (to_text_item (item)) == TEXT_ITEM_COMMAND_OPEN)
240         {
241           gtk_tree_store_append (store, &iter, NULL);
242           view->cur_command = iter; /* XXX shouldn't save a GtkTreeIter */
243           view->in_command = true;
244         }
245       else
246         {
247           GtkTreeIter *p = view->in_command ? &view->cur_command : NULL;
248           gtk_tree_store_append (store, &iter, p);
249         }
250
251       ds_clear (&title);
252       if (is_text_item (item))
253         ds_put_cstr (&title, text_item_get_text (to_text_item (item)));
254       else if (is_message_item (item))
255         {
256           const struct message_item *msg_item = to_message_item (item);
257           const struct msg *msg = message_item_get_msg (msg_item);
258           ds_put_format (&title, "%s: %s", _("Message"),
259                          msg_severity_to_string (msg->severity));
260         }
261       else if (is_table_item (item))
262         {
263           const char *caption = table_item_get_caption (to_table_item (item));
264           if (caption != NULL)
265             ds_put_format (&title, "Table: %s", caption);
266           else
267             ds_put_cstr (&title, "Table");
268         }
269       else if (is_chart_item (item))
270         {
271           const char *s = chart_item_get_title (to_chart_item (item));
272           if (s != NULL)
273             ds_put_format (&title, "Chart: %s", s);
274           else
275             ds_put_cstr (&title, "Chart");
276         }
277       gtk_tree_store_set (store, &iter,
278                           COL_TITLE, ds_cstr (&title),
279                           COL_ADDR, item,
280                           COL_Y, view->y,
281                           -1);
282       ds_destroy (&title);
283
284       path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
285       gtk_tree_view_expand_row (view->overview, path, TRUE);
286       gtk_tree_path_free (path);
287     }
288
289   if (view->max_width < tw)
290     view->max_width = tw;
291   view->y += th;
292
293   gtk_layout_set_size (view->output, view->max_width, view->y);
294
295 done:
296   cairo_destroy (cr);
297 }
298
299 static void
300 on_row_activate (GtkTreeView *overview,
301                  GtkTreePath *path,
302                  GtkTreeViewColumn *column,
303                  struct psppire_output_view *view)
304 {
305   GtkTreeModel *model;
306   GtkTreeIter iter;
307   GtkAdjustment *vadj;
308   GValue value = {0};
309   double y, min, max;
310
311   model = gtk_tree_view_get_model (overview);
312   if (!gtk_tree_model_get_iter (model, &iter, path))
313     return;
314
315   gtk_tree_model_get_value (model, &iter, COL_Y, &value);
316   y = g_value_get_long (&value);
317   g_value_unset (&value);
318
319   vadj = gtk_layout_get_vadjustment (view->output);
320   min = vadj->lower;
321   max = vadj->upper - vadj->page_size;
322   if (y < min)
323     y = min;
324   else if (y > max)
325     y = max;
326   gtk_adjustment_set_value (vadj, y);
327 }
328
329 static void
330 copy_base_to_bg (GtkWidget *dest, GtkWidget *src)
331 {
332   int i;
333   for (i = 0; i < 5; ++i)
334     {
335       gtk_widget_modify_bg (dest, i, &gtk_widget_get_style (src)->base[i]);
336       gtk_widget_modify_fg (dest, i, &gtk_widget_get_style (src)->text[i]);
337     }
338 }
339
340 /* Copy the base style from the parent widget to the container and all its
341    children.  We do this because the container's primary purpose is to display
342    text.  This way psppire appears to follow the chosen gnome theme. */
343 static void
344 on_style_set (GtkWidget *toplevel, GtkStyle *prev,
345               struct psppire_output_view *view)
346 {
347   copy_base_to_bg (GTK_WIDGET (view->output), toplevel);
348   gtk_container_foreach (GTK_CONTAINER (view->output),
349                          (GtkCallback) copy_base_to_bg, view->output);
350 }
351
352 static void
353 on_dwgarea_realize (GtkWidget *dwg_area, gpointer data)
354 {
355   copy_base_to_bg (dwg_area, gtk_widget_get_toplevel (dwg_area));
356 }
357
358 enum {
359   SELECT_FMT_NULL,
360   SELECT_FMT_TEXT,
361   SELECT_FMT_UTF8,
362   SELECT_FMT_HTML,
363   SELECT_FMT_ODT
364 };
365
366 /* GNU Hurd doesn't have PATH_MAX.  Use a fallback.
367    Temporary directory names are usually not that long.  */
368 #ifndef PATH_MAX
369 # define PATH_MAX 1024
370 #endif
371
372 static void
373 clipboard_get_cb (GtkClipboard     *clipboard,
374                   GtkSelectionData *selection_data,
375                   guint             info,
376                   gpointer          data)
377 {
378   struct psppire_output_view *view = data;
379
380   gsize length;
381   gchar *text = NULL;
382   struct output_driver *driver = NULL;
383   char dirname[PATH_MAX], *filename;
384   struct string_map options;
385
386   GtkTreeSelection *sel = gtk_tree_view_get_selection (view->overview);
387   GtkTreeModel *model = gtk_tree_view_get_model (view->overview);
388
389   GList *rows = gtk_tree_selection_get_selected_rows (sel, &model);
390   GList *n = rows;
391
392   if ( n == NULL)
393     return;
394
395   if (path_search (dirname, sizeof dirname, NULL, NULL, true)
396       || mkdtemp (dirname) == NULL)
397     {
398       msg_error (errno, _("failed to create temporary directory during clipboard operation"));
399       return;
400     }
401   filename = xasprintf ("%s/clip.tmp", dirname);
402
403   string_map_init (&options);
404   string_map_insert (&options, "output-file", filename);
405
406   switch (info)
407     {
408     case SELECT_FMT_UTF8:
409       string_map_insert (&options, "box", "unicode");
410       /* fall-through */
411
412     case SELECT_FMT_TEXT:
413       string_map_insert (&options, "format", "txt");
414       break;
415
416     case SELECT_FMT_HTML:
417       string_map_insert (&options, "format", "html");
418       string_map_insert (&options, "borders", "false");
419       string_map_insert (&options, "css", "false");
420       break;
421
422     case SELECT_FMT_ODT:
423       string_map_insert (&options, "format", "odt");
424       break;
425
426     default:
427       g_warning ("unsupported clip target\n");
428       goto finish;
429       break;
430     }
431
432   driver = output_driver_create (&options);
433   if (driver == NULL)
434     goto finish;
435
436   while (n)
437     {
438       GtkTreePath *path = n->data ;
439       GtkTreeIter iter;
440       struct output_item *item ;
441
442       gtk_tree_model_get_iter (model, &iter, path);
443       gtk_tree_model_get (model, &iter, COL_ADDR, &item, -1);
444
445       driver->class->submit (driver, item);
446
447       n = n->next;
448     }
449
450   if ( driver->class->flush)
451     driver->class->flush (driver);
452
453
454   /* Some drivers (eg: the odt one) don't write anything until they
455      are closed */
456   output_driver_destroy (driver);
457   driver = NULL;
458
459   if ( g_file_get_contents (filename, &text, &length, NULL) )
460     {
461       gtk_selection_data_set (selection_data, selection_data->target,
462                               8,
463                               (const guchar *) text, length);
464     }
465
466  finish:
467
468   if (driver != NULL)
469     output_driver_destroy (driver);
470
471   g_free (text);
472
473   unlink (filename);
474   free (filename);
475   rmdir (dirname);
476
477   g_list_free (rows);
478 }
479
480 static void
481 clipboard_clear_cb (GtkClipboard *clipboard,
482                     gpointer data)
483 {
484 }
485
486 static const GtkTargetEntry targets[] = {
487
488   { "STRING",        0, SELECT_FMT_TEXT },
489   { "TEXT",          0, SELECT_FMT_TEXT },
490   { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
491   { "text/plain",    0, SELECT_FMT_TEXT },
492
493   { "UTF8_STRING",   0, SELECT_FMT_UTF8 },
494   { "text/plain;charset=utf-8", 0, SELECT_FMT_UTF8 },
495
496   { "text/html",     0, SELECT_FMT_HTML },
497
498   { "application/vnd.oasis.opendocument.text", 0, SELECT_FMT_ODT }
499 };
500
501 static void
502 on_copy (struct psppire_output_view *view)
503 {
504   GtkWidget *widget = GTK_WIDGET (view->overview);
505   GtkClipboard *cb = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
506
507   if (!gtk_clipboard_set_with_data (cb, targets, G_N_ELEMENTS (targets),
508                                     clipboard_get_cb, clipboard_clear_cb,
509                                     view))
510     clipboard_clear_cb (cb, view);
511 }
512
513 static void
514 on_selection_change (GtkTreeSelection *sel, GtkAction *copy_action)
515 {
516   /* The Copy action is available only if there is something selected */
517   gtk_action_set_sensitive (copy_action, gtk_tree_selection_count_selected_rows (sel) > 0);
518 }
519
520 static void
521 on_select_all (struct psppire_output_view *view)
522 {
523   GtkTreeSelection *sel = gtk_tree_view_get_selection (view->overview);
524   gtk_tree_view_expand_all (view->overview);
525   gtk_tree_selection_select_all (sel);
526 }
527
528 struct psppire_output_view *
529 psppire_output_view_new (GtkLayout *output, GtkTreeView *overview,
530                          GtkAction *copy_action, GtkAction *select_all_action)
531 {
532   struct psppire_output_view *view;
533   GtkTreeViewColumn *column;
534   GtkCellRenderer *renderer;
535   GtkTreeSelection *sel;
536   GtkTreeModel *model;
537
538   view = xmalloc (sizeof *view);
539   view->xr = NULL;
540   view->font_height = 0;
541   view->output = output;
542   view->max_width = 0;
543   view->y = 0;
544   string_map_init (&view->render_opts);
545   view->overview = overview;
546   memset (&view->cur_command, 0, sizeof view->cur_command);
547   view->in_command = false;
548   view->toplevel = gtk_widget_get_toplevel (GTK_WIDGET (output));
549   view->items = NULL;
550   view->n_items = view->allocated_items = 0;
551   view->print_settings = NULL;
552   view->print_xrd = NULL;
553   view->print_item = 0;
554   view->print_n_pages = 0;
555   view->paginated = FALSE;
556
557   g_signal_connect (view->toplevel, "style-set", G_CALLBACK (on_style_set), view);
558
559
560   if (overview)
561     {
562       model = GTK_TREE_MODEL (gtk_tree_store_new (
563                                 N_COLS,
564                                 G_TYPE_STRING,  /* COL_TITLE */
565                                 G_TYPE_POINTER, /* COL_ADDR */
566                                 G_TYPE_LONG));  /* COL_Y */
567       gtk_tree_view_set_model (overview, model);
568       g_object_unref (model);
569
570       sel = gtk_tree_view_get_selection (overview);
571       gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE);
572       g_signal_connect (sel, "changed", G_CALLBACK (on_selection_change),
573                         copy_action);
574
575       column = gtk_tree_view_column_new ();
576       gtk_tree_view_append_column (GTK_TREE_VIEW (overview), column);
577       renderer = gtk_cell_renderer_text_new ();
578       gtk_tree_view_column_pack_start (column, renderer, TRUE);
579       gtk_tree_view_column_add_attribute (column, renderer, "text", COL_TITLE);
580
581       g_signal_connect (GTK_TREE_VIEW (overview),
582                         "row-activated", G_CALLBACK (on_row_activate), view);
583
584       gtk_action_set_sensitive (copy_action, FALSE);
585       g_signal_connect_swapped (copy_action, "activate",
586                                 G_CALLBACK (on_copy), view);
587       g_signal_connect_swapped (select_all_action, "activate",
588                                 G_CALLBACK (on_select_all), view);
589     }
590
591   return view;
592 }
593
594 void
595 psppire_output_view_destroy (struct psppire_output_view *view)
596 {
597   size_t i;
598
599   if (!view)
600     return;
601
602   g_signal_handlers_disconnect_by_func (view->toplevel,
603                                         G_CALLBACK (on_style_set), view);
604
605   string_map_destroy (&view->render_opts);
606
607   for (i = 0; i < view->n_items; i++)
608     output_item_unref (view->items[i]);
609   free (view->items);
610   view->items = NULL;
611   view->n_items = view->allocated_items = 0;
612
613   if (view->print_settings != NULL)
614     g_object_unref (view->print_settings);
615
616   xr_driver_destroy (view->xr);
617
618   free (view);
619 }
620 \f
621 /* Export. */
622
623 void
624 psppire_output_view_export (struct psppire_output_view *view,
625                             struct string_map *options)
626 {
627   struct output_driver *driver;
628
629   driver = output_driver_create (options);
630   if (driver)
631     {
632       size_t i;
633
634       for (i = 0; i < view->n_items; i++)
635         driver->class->submit (driver, view->items[i]);
636       output_driver_destroy (driver);
637     }
638 }
639 \f
640 /* Print. */
641
642 static cairo_t *
643 get_cairo_context_from_print_context (GtkPrintContext *context)
644 {
645   cairo_t *cr = gtk_print_context_get_cairo_context (context);
646   
647   /*
648     For all platforms except windows, gtk_print_context_get_dpi_[xy] returns 72.
649     Windows returns 600.
650   */
651   double xres = gtk_print_context_get_dpi_x (context);
652   double yres = gtk_print_context_get_dpi_y (context);
653   
654   /* This means that the cairo context now has its dimensions in Points */
655   cairo_scale (cr, xres / 72.0, yres / 72.0);
656   
657   return cr;
658 }
659
660
661 static void
662 create_xr_print_driver (GtkPrintContext *context, struct psppire_output_view *view)
663 {
664   struct string_map options;
665   GtkPageSetup *page_setup;
666   double width, height;
667   double left_margin;
668   double right_margin;
669   double top_margin;
670   double bottom_margin;
671
672   page_setup = gtk_print_context_get_page_setup (context);
673   width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_MM);
674   height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_MM);
675   left_margin = gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_MM);
676   right_margin = gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_MM);
677   top_margin = gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_MM);
678   bottom_margin = gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_MM);
679
680   string_map_init (&options);
681   string_map_insert_nocopy (&options, xstrdup ("paper-size"),
682                             c_xasprintf("%.2fx%.2fmm", width, height));
683   string_map_insert_nocopy (&options, xstrdup ("left-margin"),
684                             c_xasprintf ("%.2fmm", left_margin));
685   string_map_insert_nocopy (&options, xstrdup ("right-margin"),
686                             c_xasprintf ("%.2fmm", right_margin));
687   string_map_insert_nocopy (&options, xstrdup ("top-margin"),
688                             c_xasprintf ("%.2fmm", top_margin));
689   string_map_insert_nocopy (&options, xstrdup ("bottom-margin"),
690                             c_xasprintf ("%.2fmm", bottom_margin));
691
692   view->print_xrd = xr_driver_create (get_cairo_context_from_print_context (context), &options);
693
694   string_map_destroy (&options);
695 }
696
697 static gboolean
698 paginate (GtkPrintOperation *operation,
699           GtkPrintContext   *context,
700           struct psppire_output_view *view)
701 {
702   if (view->paginated)
703     {
704       /* Sometimes GTK+ emits this signal again even after pagination is
705          complete.  Don't let that screw up printing. */
706       return TRUE;
707     }
708   else if ( view->print_item < view->n_items )
709     {
710       xr_driver_output_item (view->print_xrd, view->items[view->print_item++]);
711       while (xr_driver_need_new_page (view->print_xrd))
712         {
713           xr_driver_next_page (view->print_xrd, NULL);
714           view->print_n_pages ++;
715         }
716       return FALSE;
717     }
718   else
719     {
720       gtk_print_operation_set_n_pages (operation, view->print_n_pages);
721
722       /* Re-create the driver to do the real printing. */
723       xr_driver_destroy (view->print_xrd);
724       create_xr_print_driver (context, view);
725       view->print_item = 0;
726       view->paginated = TRUE;
727
728       return TRUE;
729     }
730 }
731
732 static void
733 begin_print (GtkPrintOperation *operation,
734              GtkPrintContext   *context,
735              struct psppire_output_view *view)
736 {
737   create_xr_print_driver (context, view);
738
739   view->print_item = 0;
740   view->print_n_pages = 1;
741   view->paginated = FALSE;
742 }
743
744 static void
745 end_print (GtkPrintOperation *operation,
746            GtkPrintContext   *context,
747            struct psppire_output_view *view)
748 {
749   xr_driver_destroy (view->print_xrd);
750 }
751
752
753 static void
754 draw_page (GtkPrintOperation *operation,
755            GtkPrintContext   *context,
756            gint               page_number,
757            struct psppire_output_view *view)
758 {
759   xr_driver_next_page (view->print_xrd, get_cairo_context_from_print_context (context));
760   while (!xr_driver_need_new_page (view->print_xrd)
761          && view->print_item < view->n_items)
762     xr_driver_output_item (view->print_xrd, view->items [view->print_item++]);
763 }
764
765
766 void
767 psppire_output_view_print (struct psppire_output_view *view,
768                            GtkWindow *parent_window)
769 {
770   GtkPrintOperationResult res;
771
772   GtkPrintOperation *print = gtk_print_operation_new ();
773
774   if (view->print_settings != NULL) 
775     gtk_print_operation_set_print_settings (print, view->print_settings);
776
777   g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), view);
778   g_signal_connect (print, "end_print",   G_CALLBACK (end_print),   view);
779   g_signal_connect (print, "paginate",    G_CALLBACK (paginate),    view);
780   g_signal_connect (print, "draw_page",   G_CALLBACK (draw_page),   view);
781
782   res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
783                                  parent_window, NULL);
784
785   if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
786     {
787       if (view->print_settings != NULL)
788         g_object_unref (view->print_settings);
789       view->print_settings = g_object_ref (gtk_print_operation_get_print_settings (print));
790     }
791
792   g_object_unref (print);
793 }