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