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