1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011, 2012 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/>. */
23 #include <sys/types.h>
26 #include "libpspp/cast.h"
27 #include "libpspp/message.h"
28 #include "libpspp/string-map.h"
29 #include "output/cairo.h"
30 #include "output/chart-item.h"
31 #include "output/driver-provider.h"
32 #include "output/message-item.h"
33 #include "output/output-item.h"
34 #include "output/tab.h"
35 #include "output/table-item.h"
36 #include "output/text-item.h"
37 #include "ui/gui/help-menu.h"
38 #include "ui/gui/builder-wrapper.h"
39 #include "ui/gui/psppire-output-window.h"
42 #include "gl/tmpdir.h"
43 #include "gl/xalloc.h"
44 #include "gl/c-xvasprintf.h"
49 #define _(msgid) gettext (msgid)
50 #define N_(msgid) msgid
54 COL_TITLE, /* Table title. */
55 COL_ADDR, /* Pointer to the table */
56 COL_Y, /* Y position of top of title. */
60 static void psppire_output_window_class_init (PsppireOutputWindowClass *class);
61 static void psppire_output_window_init (PsppireOutputWindow *window);
63 static void psppire_output_window_style_set (GtkWidget *window, GtkStyle *prev);
67 psppire_output_window_get_type (void)
69 static GType psppire_output_window_type = 0;
71 if (!psppire_output_window_type)
73 static const GTypeInfo psppire_output_window_info =
75 sizeof (PsppireOutputWindowClass),
77 (GBaseFinalizeFunc) NULL,
78 (GClassInitFunc)psppire_output_window_class_init,
79 (GClassFinalizeFunc) NULL,
81 sizeof (PsppireOutputWindow),
83 (GInstanceInitFunc) psppire_output_window_init,
86 psppire_output_window_type =
87 g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireOutputWindow",
88 &psppire_output_window_info, 0);
91 return psppire_output_window_type;
94 static GObjectClass *parent_class;
97 psppire_output_window_finalize (GObject *object)
99 string_map_destroy (&PSPPIRE_OUTPUT_WINDOW(object)->render_opts);
102 if (G_OBJECT_CLASS (parent_class)->finalize)
103 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
108 psppire_output_window_dispose (GObject *obj)
110 PsppireOutputWindow *viewer = PSPPIRE_OUTPUT_WINDOW (obj);
113 if (viewer->dispose_has_run)
116 viewer->dispose_has_run = TRUE;
117 for (i = 0; i < viewer->n_items; i++)
118 output_item_unref (viewer->items[i]);
119 free (viewer->items);
120 viewer->items = NULL;
121 viewer->n_items = viewer->allocated_items = 0;
123 if (viewer->print_settings != NULL)
124 g_object_unref (viewer->print_settings);
126 /* Chain up to the parent class */
127 G_OBJECT_CLASS (parent_class)->dispose (obj);
131 psppire_output_window_class_init (PsppireOutputWindowClass *class)
133 GObjectClass *object_class = G_OBJECT_CLASS (class);
135 parent_class = g_type_class_peek_parent (class);
136 object_class->dispose = psppire_output_window_dispose;
138 GTK_WIDGET_CLASS (object_class)->style_set = psppire_output_window_style_set;
139 object_class->finalize = psppire_output_window_finalize;
144 /* Output driver class. */
146 struct psppire_output_driver
148 struct output_driver driver;
149 PsppireOutputWindow *viewer;
150 struct xr_driver *xr;
154 static struct output_driver_class psppire_output_class;
156 static struct psppire_output_driver *
157 psppire_output_cast (struct output_driver *driver)
159 assert (driver->class == &psppire_output_class);
160 return UP_CAST (driver, struct psppire_output_driver, driver);
163 static void on_dwgarea_realize (GtkWidget *widget, gpointer data);
166 expose_event_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
168 PsppireOutputWindow *viewer = PSPPIRE_OUTPUT_WINDOW (data);
169 struct xr_rendering *r = g_object_get_data (G_OBJECT (widget), "rendering");
170 cairo_t *cr = gdk_cairo_create (widget->window);
172 const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (viewer));
174 PangoFontDescription *font_desc;
178 gdk_color_to_string (&style->text[gtk_widget_get_state (GTK_WIDGET (widget))]);
180 string_map_replace (&viewer->render_opts, "foreground-color", fgc);
184 /* Use GTK+ default font as proportional font. */
185 font_name = pango_font_description_to_string (style->font_desc);
186 string_map_replace (&viewer->render_opts, "prop-font", font_name);
189 /* Derived emphasized font from proportional font. */
190 font_desc = pango_font_description_copy (style->font_desc);
191 pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
192 font_name = pango_font_description_to_string (font_desc);
193 string_map_replace (&viewer->render_opts, "emph-font", font_name);
195 pango_font_description_free (font_desc);
197 xr_rendering_apply_options (r, &viewer->render_opts);
199 xr_rendering_draw (r, cr, event->area.x, event->area.y,
200 event->area.width, event->area.height);
207 psppire_output_submit (struct output_driver *this,
208 const struct output_item *item)
210 struct psppire_output_driver *pod = psppire_output_cast (this);
211 PsppireOutputWindow *viewer;
212 GtkWidget *drawing_area;
213 struct xr_rendering *r;
221 if (pod->viewer == NULL)
223 pod->viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
224 gtk_widget_show_all (GTK_WIDGET (pod->viewer));
225 pod->viewer->driver = pod;
227 viewer = pod->viewer;
229 if (viewer->n_items >= viewer->allocated_items)
230 viewer->items = x2nrealloc (viewer->items, &viewer->allocated_items,
231 sizeof *viewer->items);
232 viewer->items[viewer->n_items++] = output_item_ref (item);
234 if (is_text_item (item))
236 const struct text_item *text_item = to_text_item (item);
237 enum text_item_type type = text_item_get_type (text_item);
238 const char *text = text_item_get_text (text_item);
240 if (type == TEXT_ITEM_COMMAND_CLOSE)
242 viewer->in_command = false;
245 else if (text[0] == '\0')
249 cr = gdk_cairo_create (GTK_WIDGET (pod->viewer)->window);
252 const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (viewer));
253 struct text_item *text_item;
254 PangoFontDescription *font_desc;
258 /* Set the widget's text color as the foreground color for the output driver */
259 gchar *fgc = gdk_color_to_string (&style->text[gtk_widget_get_state (GTK_WIDGET (viewer))]);
261 string_map_insert (&pod->viewer->render_opts, "foreground-color", fgc);
264 /* Use GTK+ default font as proportional font. */
265 font_name = pango_font_description_to_string (style->font_desc);
266 string_map_insert (&pod->viewer->render_opts, "prop-font", font_name);
269 /* Derived emphasized font from proportional font. */
270 font_desc = pango_font_description_copy (style->font_desc);
271 pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
272 font_name = pango_font_description_to_string (font_desc);
273 string_map_insert (&pod->viewer->render_opts, "emph-font", font_name);
275 pango_font_description_free (font_desc);
277 /* Pretend that the "page" has a reasonable width and a very big length,
278 so that most tables can be conveniently viewed on-screen with vertical
279 scrolling only. (The length should not be increased very much because
280 it is already close enough to INT_MAX when expressed as thousands of a
282 string_map_insert (&pod->viewer->render_opts, "paper-size", "300x200000mm");
283 string_map_insert (&pod->viewer->render_opts, "left-margin", "0");
284 string_map_insert (&pod->viewer->render_opts, "right-margin", "0");
285 string_map_insert (&pod->viewer->render_opts, "top-margin", "0");
286 string_map_insert (&pod->viewer->render_opts, "bottom-margin", "0");
288 pod->xr = xr_driver_create (cr, &pod->viewer->render_opts);
291 text_item = text_item_create (TEXT_ITEM_PARAGRAPH, "X");
292 r = xr_rendering_create (pod->xr, text_item_super (text_item), cr);
293 xr_rendering_measure (r, &font_width, &pod->font_height);
294 /* xr_rendering_destroy (r); */
295 text_item_unref (text_item);
298 pod->viewer->y += pod->font_height / 2;
300 r = xr_rendering_create (pod->xr, item, cr);
304 xr_rendering_measure (r, &tw, &th);
306 drawing_area = gtk_drawing_area_new ();
308 g_object_set_data (G_OBJECT (drawing_area), "rendering", r);
309 g_signal_connect (drawing_area, "realize",
310 G_CALLBACK (on_dwgarea_realize), pod->viewer);
312 g_signal_connect (drawing_area, "expose_event",
313 G_CALLBACK (expose_event_callback), pod->viewer);
315 gtk_widget_set_size_request (drawing_area, tw, th);
316 gtk_layout_put (pod->viewer->output, drawing_area, 0, pod->viewer->y);
318 gtk_widget_show (drawing_area);
320 if (!is_text_item (item)
321 || text_item_get_type (to_text_item (item)) != TEXT_ITEM_SYNTAX
322 || !viewer->in_command)
324 store = GTK_TREE_STORE (gtk_tree_view_get_model (viewer->overview));
326 ds_init_empty (&title);
327 if (is_text_item (item)
328 && text_item_get_type (to_text_item (item)) == TEXT_ITEM_COMMAND_OPEN)
330 gtk_tree_store_append (store, &iter, NULL);
331 viewer->cur_command = iter; /* XXX shouldn't save a GtkTreeIter */
332 viewer->in_command = true;
336 GtkTreeIter *p = viewer->in_command ? &viewer->cur_command : NULL;
337 gtk_tree_store_append (store, &iter, p);
341 if (is_text_item (item))
342 ds_put_cstr (&title, text_item_get_text (to_text_item (item)));
343 else if (is_message_item (item))
345 const struct message_item *msg_item = to_message_item (item);
346 const struct msg *msg = message_item_get_msg (msg_item);
347 ds_put_format (&title, "%s: %s", _("Message"),
348 msg_severity_to_string (msg->severity));
350 else if (is_table_item (item))
352 const char *caption = table_item_get_caption (to_table_item (item));
354 ds_put_format (&title, "Table: %s", caption);
356 ds_put_cstr (&title, "Table");
358 else if (is_chart_item (item))
360 const char *s = chart_item_get_title (to_chart_item (item));
362 ds_put_format (&title, "Chart: %s", s);
364 ds_put_cstr (&title, "Chart");
366 gtk_tree_store_set (store, &iter,
367 COL_TITLE, ds_cstr (&title),
373 path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
374 gtk_tree_view_expand_row (viewer->overview, path, TRUE);
375 gtk_tree_path_free (path);
378 if (pod->viewer->max_width < tw)
379 pod->viewer->max_width = tw;
380 pod->viewer->y += th;
382 gtk_layout_set_size (pod->viewer->output,
383 pod->viewer->max_width, pod->viewer->y);
385 gtk_window_set_urgency_hint (GTK_WINDOW (pod->viewer), TRUE);
391 static struct output_driver_class psppire_output_class =
393 "PSPPIRE", /* name */
395 psppire_output_submit, /* submit */
400 psppire_output_window_setup (void)
402 struct psppire_output_driver *pod;
403 struct output_driver *d;
405 pod = xzalloc (sizeof *pod);
407 output_driver_init (d, &psppire_output_class, "PSPPIRE",
408 SETTINGS_DEVICE_UNFILTERED);
409 output_driver_register (d);
414 /* Callback for the "delete" action (clicking the x on the top right
415 hand corner of the window) */
417 on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
419 PsppireOutputWindow *ow = PSPPIRE_OUTPUT_WINDOW (user_data);
421 gtk_widget_destroy (GTK_WIDGET (ow));
423 ow->driver->viewer = NULL;
431 cancel_urgency (GtkWindow *window, gpointer data)
433 gtk_window_set_urgency_hint (window, FALSE);
437 on_row_activate (GtkTreeView *overview,
439 GtkTreeViewColumn *column,
440 PsppireOutputWindow *window)
448 model = gtk_tree_view_get_model (overview);
449 if (!gtk_tree_model_get_iter (model, &iter, path))
452 gtk_tree_model_get_value (model, &iter, COL_Y, &value);
453 y = g_value_get_long (&value);
454 g_value_unset (&value);
456 vadj = gtk_layout_get_vadjustment (window->output);
458 max = vadj->upper - vadj->page_size;
463 gtk_adjustment_set_value (vadj, y);
466 static void psppire_output_window_print (PsppireOutputWindow *window);
470 export_output (PsppireOutputWindow *window, struct string_map *options,
473 struct output_driver *driver;
476 string_map_insert (options, "format", format);
477 driver = output_driver_create (options);
481 for (i = 0; i < window->n_items; i++)
482 driver->class->submit (driver, window->items[i]);
483 output_driver_destroy (driver);
505 #define N_EXTENSIONS (n_FT - 1)
507 struct file_types ft[n_FT] = {
508 {N_("Infer file type from extension"), NULL},
509 {N_("PDF (*.pdf)"), ".pdf"},
510 {N_("HTML (*.html)"), ".html"},
511 {N_("OpenDocument (*.odt)"), ".odt"},
512 {N_("Text (*.txt)"), ".txt"},
513 {N_("PostScript (*.ps)"), ".ps"},
514 {N_("Comma-Separated Values (*.csv)"), ".csv"}
519 on_combo_change (GtkFileChooser *chooser)
521 gboolean sensitive = FALSE;
522 GtkWidget *combo = gtk_file_chooser_get_extra_widget (chooser);
525 gchar *fn = gtk_file_chooser_get_filename (chooser);
527 if (combo && gtk_widget_get_realized (combo))
528 x = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
540 for (i = 1 ; i < N_EXTENSIONS ; ++i)
542 if ( g_str_has_suffix (fn, ft[i].ext))
552 gtk_dialog_set_response_sensitive (GTK_DIALOG (chooser), GTK_RESPONSE_ACCEPT, sensitive);
557 on_file_chooser_change (GObject *w, GParamSpec *pspec, gpointer data)
560 GtkFileChooser *chooser = data;
561 const gchar *name = g_param_spec_get_name (pspec);
563 if ( ! gtk_widget_get_realized (GTK_WIDGET (chooser)))
566 /* Ignore this one. It causes recursion. */
567 if ( 0 == strcmp ("tooltip-text", name))
570 on_combo_change (chooser);
574 /* Recursively descend all the children of W, connecting
575 to their "notify" signal */
577 iterate_widgets (GtkWidget *w, gpointer data)
579 if ( GTK_IS_CONTAINER (w))
580 gtk_container_forall (GTK_CONTAINER (w), iterate_widgets, data);
582 g_signal_connect (w, "notify", G_CALLBACK (on_file_chooser_change), data);
587 static GtkListStore *
588 create_file_type_list (void)
592 GtkListStore *list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
594 for (i = 0 ; i < n_FT ; ++i)
596 gtk_list_store_append (list, &iter);
597 gtk_list_store_set (list, &iter,
598 0, gettext (ft[i].label),
607 psppire_output_window_export (PsppireOutputWindow *window)
613 GtkFileChooser *chooser;
615 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Export Output"),
617 GTK_FILE_CHOOSER_ACTION_SAVE,
618 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
619 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
622 g_object_set (dialog, "local-only", FALSE, NULL);
624 chooser = GTK_FILE_CHOOSER (dialog);
626 list = create_file_type_list ();
628 combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list));
632 /* Create text cell renderer */
633 GtkCellRenderer *cell = gtk_cell_renderer_text_new();
634 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE );
636 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cell, "text", 0);
639 g_signal_connect_swapped (combo, "changed", G_CALLBACK (on_combo_change), chooser);
641 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
643 gtk_file_chooser_set_extra_widget (chooser, combo);
645 /* This kludge is necessary because there is no signal to tell us
646 when the candidate filename of a GtkFileChooser has changed */
647 gtk_container_forall (GTK_CONTAINER (dialog), iterate_widgets, dialog);
650 gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);
652 response = gtk_dialog_run (GTK_DIALOG (dialog));
654 if ( response == GTK_RESPONSE_ACCEPT )
656 int file_type = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
657 char *filename = gtk_file_chooser_get_filename (chooser);
658 struct string_map options;
660 g_return_if_fail (filename);
662 if (file_type == FT_AUTO)
665 for (i = 1 ; i < N_EXTENSIONS ; ++i)
667 if ( g_str_has_suffix (filename, ft[i].ext))
676 string_map_init (&options);
677 string_map_insert (&options, "output-file", filename);
682 export_output (window, &options, "pdf");
685 export_output (window, &options, "html");
688 export_output (window, &options, "odt");
691 export_output (window, &options, "ps");
694 export_output (window, &options, "csv");
698 string_map_insert (&options, "headers", "false");
699 string_map_insert (&options, "paginate", "false");
700 string_map_insert (&options, "squeeze", "true");
701 string_map_insert (&options, "emphasis", "none");
702 string_map_insert (&options, "charts", "none");
703 string_map_insert (&options, "top-margin", "0");
704 string_map_insert (&options, "bottom-margin", "0");
705 export_output (window, &options, "txt");
708 g_assert_not_reached ();
711 string_map_destroy (&options);
716 gtk_widget_destroy (dialog);
728 /* GNU Hurd doesn't have PATH_MAX. Use a fallback.
729 Temporary directory names are usually not that long. */
731 # define PATH_MAX 1024
735 clipboard_get_cb (GtkClipboard *clipboard,
736 GtkSelectionData *selection_data,
740 PsppireOutputWindow *window = data;
744 struct output_driver *driver = NULL;
745 char dirname[PATH_MAX], *filename;
746 struct string_map options;
748 GtkTreeSelection *sel = gtk_tree_view_get_selection (window->overview);
749 GtkTreeModel *model = gtk_tree_view_get_model (window->overview);
751 GList *rows = gtk_tree_selection_get_selected_rows (sel, &model);
757 if (path_search (dirname, sizeof dirname, NULL, NULL, true)
758 || mkdtemp (dirname) == NULL)
760 error (0, errno, _("failed to create temporary directory"));
763 filename = xasprintf ("%s/clip.tmp", dirname);
765 string_map_init (&options);
766 string_map_insert (&options, "output-file", filename);
770 case SELECT_FMT_UTF8:
771 string_map_insert (&options, "box", "unicode");
774 case SELECT_FMT_TEXT:
775 string_map_insert (&options, "format", "txt");
778 case SELECT_FMT_HTML:
779 string_map_insert (&options, "format", "html");
780 string_map_insert (&options, "borders", "false");
781 string_map_insert (&options, "css", "false");
785 string_map_insert (&options, "format", "odt");
789 g_warning ("unsupported clip target\n");
794 driver = output_driver_create (&options);
800 GtkTreePath *path = n->data ;
802 struct output_item *item ;
804 gtk_tree_model_get_iter (model, &iter, path);
805 gtk_tree_model_get (model, &iter, COL_ADDR, &item, -1);
807 driver->class->submit (driver, item);
812 if ( driver->class->flush)
813 driver->class->flush (driver);
816 /* Some drivers (eg: the odt one) don't write anything until they
818 output_driver_destroy (driver);
821 if ( g_file_get_contents (filename, &text, &length, NULL) )
823 gtk_selection_data_set (selection_data, selection_data->target,
825 (const guchar *) text, length);
831 output_driver_destroy (driver);
843 clipboard_clear_cb (GtkClipboard *clipboard,
848 static const GtkTargetEntry targets[] = {
850 { "STRING", 0, SELECT_FMT_TEXT },
851 { "TEXT", 0, SELECT_FMT_TEXT },
852 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
853 { "text/plain", 0, SELECT_FMT_TEXT },
855 { "UTF8_STRING", 0, SELECT_FMT_UTF8 },
856 { "text/plain;charset=utf-8", 0, SELECT_FMT_UTF8 },
858 { "text/html", 0, SELECT_FMT_HTML },
860 { "application/vnd.oasis.opendocument.text", 0, SELECT_FMT_ODT }
864 on_copy (PsppireOutputWindow *window)
867 GtkClipboard *clipboard =
868 gtk_widget_get_clipboard (GTK_WIDGET (window),
869 GDK_SELECTION_CLIPBOARD);
871 if (!gtk_clipboard_set_with_data (clipboard, targets,
872 G_N_ELEMENTS (targets),
873 clipboard_get_cb, clipboard_clear_cb,
876 clipboard_clear_cb (clipboard, window);
881 on_selection_change (GtkTreeSelection *sel, GtkAction *copy_action)
883 /* The Copy action is available only if there is something selected */
884 gtk_action_set_sensitive (copy_action, gtk_tree_selection_count_selected_rows (sel) > 0);
888 on_select_all (PsppireOutputWindow *window)
890 GtkTreeSelection *sel = gtk_tree_view_get_selection (window->overview);
891 gtk_tree_view_expand_all (window->overview);
892 gtk_tree_selection_select_all (sel);
897 copy_base_to_bg (GtkWidget *dest, GtkWidget *src)
900 for (i = 0; i < 5; ++i)
902 GdkColor *col = >k_widget_get_style (src)->base[i];
903 gtk_widget_modify_bg (dest, i, col);
905 col = >k_widget_get_style (src)->text[i];
906 gtk_widget_modify_fg (dest, i, col);
911 on_dwgarea_realize (GtkWidget *dwg_area, gpointer data)
913 GtkWidget *viewer = GTK_WIDGET (data);
915 copy_base_to_bg (dwg_area, viewer);
920 psppire_output_window_style_set (GtkWidget *w, GtkStyle *prev)
922 GtkWidget *op = GTK_WIDGET (PSPPIRE_OUTPUT_WINDOW (w)->output);
924 /* Copy the base style from the parent widget to the container and
926 We do this, because the container's primary purpose is to
927 display text. This way psppire appears to follow the chosen
930 copy_base_to_bg (op, w);
931 gtk_container_foreach (GTK_CONTAINER (op), (GtkCallback) copy_base_to_bg,
932 PSPPIRE_OUTPUT_WINDOW (w)->output);
934 /* Chain up to the parent class */
935 GTK_WIDGET_CLASS (parent_class)->style_set (w, prev);
939 psppire_output_window_init (PsppireOutputWindow *window)
941 GtkTreeViewColumn *column;
942 GtkCellRenderer *renderer;
944 GtkAction *copy_action;
945 GtkAction *select_all_action;
946 GtkTreeSelection *sel;
949 string_map_init (&window->render_opts);
951 xml = builder_new ("output-viewer.ui");
953 copy_action = get_action_assert (xml, "edit_copy");
954 select_all_action = get_action_assert (xml, "edit_select-all");
956 gtk_action_set_sensitive (copy_action, FALSE);
958 g_signal_connect_swapped (copy_action, "activate", G_CALLBACK (on_copy), window);
960 g_signal_connect_swapped (select_all_action, "activate", G_CALLBACK (on_select_all), window);
962 gtk_widget_reparent (get_widget_assert (xml, "vbox1"), GTK_WIDGET (window));
964 window->output = GTK_LAYOUT (get_widget_assert (xml, "output"));
966 window->print_settings = NULL;
967 window->dispose_has_run = FALSE;
969 window->overview = GTK_TREE_VIEW (get_widget_assert (xml, "overview"));
971 sel = gtk_tree_view_get_selection (window->overview);
973 gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE);
975 g_signal_connect (sel, "changed", G_CALLBACK (on_selection_change), copy_action);
977 model = GTK_TREE_MODEL (gtk_tree_store_new (
979 G_TYPE_STRING, /* COL_TITLE */
980 G_TYPE_POINTER, /* COL_ADDR */
981 G_TYPE_LONG)); /* COL_Y */
982 gtk_tree_view_set_model (window->overview, model);
983 g_object_unref (model);
985 window->in_command = false;
987 window->items = NULL;
988 window->n_items = window->allocated_items = 0;
990 column = gtk_tree_view_column_new ();
991 gtk_tree_view_append_column (GTK_TREE_VIEW (window->overview), column);
992 renderer = gtk_cell_renderer_text_new ();
993 gtk_tree_view_column_pack_start (column, renderer, TRUE);
994 gtk_tree_view_column_add_attribute (column, renderer, "text", COL_TITLE);
996 g_signal_connect (GTK_TREE_VIEW (window->overview),
997 "row-activated", G_CALLBACK (on_row_activate), window);
1001 g_signal_connect (window,
1003 G_CALLBACK (cancel_urgency),
1006 g_signal_connect (get_action_assert (xml,"windows_minimise-all"),
1008 G_CALLBACK (psppire_window_minimise_all),
1012 GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (xml, "uimanager1", GTK_TYPE_UI_MANAGER));
1013 merge_help_menu (uim);
1015 PSPPIRE_WINDOW (window)->menu =
1016 GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows_menuitem/windows_minimise-all")->parent);
1019 g_signal_connect_swapped (get_action_assert (xml, "file_export"), "activate",
1020 G_CALLBACK (psppire_output_window_export), window);
1023 g_signal_connect_swapped (get_action_assert (xml, "file_print"), "activate",
1024 G_CALLBACK (psppire_output_window_print), window);
1026 g_object_unref (xml);
1028 g_signal_connect (window, "delete-event",
1029 G_CALLBACK (on_delete), window);
1034 psppire_output_window_new (void)
1036 return GTK_WIDGET (g_object_new (psppire_output_window_get_type (),
1037 /* TRANSLATORS: This will form a filename. Please avoid whitespace. */
1038 "filename", _("Output"),
1039 "description", _("Output Viewer"),
1046 get_cairo_context_from_print_context (GtkPrintContext *context)
1048 cairo_t *cr = gtk_print_context_get_cairo_context (context);
1051 For all platforms except windows, gtk_print_context_get_dpi_[xy] returns 72.
1052 Windows returns 600.
1054 double xres = gtk_print_context_get_dpi_x (context);
1055 double yres = gtk_print_context_get_dpi_y (context);
1057 /* This means that the cairo context now has its dimensions in Points */
1058 cairo_scale (cr, xres / 72.0, yres / 72.0);
1065 create_xr_print_driver (GtkPrintContext *context, PsppireOutputWindow *window)
1067 struct string_map options;
1068 GtkPageSetup *page_setup;
1069 double width, height;
1071 double right_margin;
1073 double bottom_margin;
1075 page_setup = gtk_print_context_get_page_setup (context);
1076 width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_MM);
1077 height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_MM);
1078 left_margin = gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_MM);
1079 right_margin = gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_MM);
1080 top_margin = gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_MM);
1081 bottom_margin = gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_MM);
1083 string_map_init (&options);
1084 string_map_insert_nocopy (&options, xstrdup ("paper-size"),
1085 c_xasprintf("%.2fx%.2fmm", width, height));
1086 string_map_insert_nocopy (&options, xstrdup ("left-margin"),
1087 c_xasprintf ("%.2fmm", left_margin));
1088 string_map_insert_nocopy (&options, xstrdup ("right-margin"),
1089 c_xasprintf ("%.2fmm", right_margin));
1090 string_map_insert_nocopy (&options, xstrdup ("top-margin"),
1091 c_xasprintf ("%.2fmm", top_margin));
1092 string_map_insert_nocopy (&options, xstrdup ("bottom-margin"),
1093 c_xasprintf ("%.2fmm", bottom_margin));
1095 window->print_xrd = xr_driver_create (get_cairo_context_from_print_context (context), &options);
1097 string_map_destroy (&options);
1101 paginate (GtkPrintOperation *operation,
1102 GtkPrintContext *context,
1103 PsppireOutputWindow *window)
1105 if (window->paginated)
1107 /* Sometimes GTK+ emits this signal again even after pagination is
1108 complete. Don't let that screw up printing. */
1111 else if ( window->print_item < window->n_items )
1113 xr_driver_output_item (window->print_xrd, window->items[window->print_item++]);
1114 while (xr_driver_need_new_page (window->print_xrd))
1116 xr_driver_next_page (window->print_xrd, NULL);
1117 window->print_n_pages ++;
1123 gtk_print_operation_set_n_pages (operation, window->print_n_pages);
1125 /* Re-create the driver to do the real printing. */
1126 xr_driver_destroy (window->print_xrd);
1127 create_xr_print_driver (context, window);
1128 window->print_item = 0;
1129 window->paginated = TRUE;
1136 begin_print (GtkPrintOperation *operation,
1137 GtkPrintContext *context,
1138 PsppireOutputWindow *window)
1140 create_xr_print_driver (context, window);
1142 window->print_item = 0;
1143 window->print_n_pages = 1;
1144 window->paginated = FALSE;
1148 end_print (GtkPrintOperation *operation,
1149 GtkPrintContext *context,
1150 PsppireOutputWindow *window)
1152 xr_driver_destroy (window->print_xrd);
1157 draw_page (GtkPrintOperation *operation,
1158 GtkPrintContext *context,
1160 PsppireOutputWindow *window)
1162 xr_driver_next_page (window->print_xrd, get_cairo_context_from_print_context (context));
1163 while (!xr_driver_need_new_page (window->print_xrd)
1164 && window->print_item < window->n_items)
1165 xr_driver_output_item (window->print_xrd, window->items [window->print_item++]);
1170 psppire_output_window_print (PsppireOutputWindow *window)
1172 GtkPrintOperationResult res;
1174 GtkPrintOperation *print = gtk_print_operation_new ();
1176 if (window->print_settings != NULL)
1177 gtk_print_operation_set_print_settings (print, window->print_settings);
1179 g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), window);
1180 g_signal_connect (print, "end_print", G_CALLBACK (end_print), window);
1181 g_signal_connect (print, "paginate", G_CALLBACK (paginate), window);
1182 g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), window);
1184 res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
1185 GTK_WINDOW (window), NULL);
1187 if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
1189 if (window->print_settings != NULL)
1190 g_object_unref (window->print_settings);
1191 window->print_settings = g_object_ref (gtk_print_operation_get_print_settings (print));
1194 g_object_unref (print);