1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 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"
41 #include "gl/tmpdir.h"
42 #include "gl/xalloc.h"
43 #include "gl/c-xvasprintf.h"
48 #define _(msgid) gettext (msgid)
49 #define N_(msgid) msgid
53 COL_TITLE, /* Table title. */
54 COL_ADDR, /* Pointer to the table */
55 COL_Y, /* Y position of top of title. */
59 static void psppire_output_window_class_init (PsppireOutputWindowClass *class);
60 static void psppire_output_window_init (PsppireOutputWindow *window);
62 static void psppire_output_window_style_set (GtkWidget *window, GtkStyle *prev);
66 psppire_output_window_get_type (void)
68 static GType psppire_output_window_type = 0;
70 if (!psppire_output_window_type)
72 static const GTypeInfo psppire_output_window_info =
74 sizeof (PsppireOutputWindowClass),
76 (GBaseFinalizeFunc) NULL,
77 (GClassInitFunc)psppire_output_window_class_init,
78 (GClassFinalizeFunc) NULL,
80 sizeof (PsppireOutputWindow),
82 (GInstanceInitFunc) psppire_output_window_init,
85 psppire_output_window_type =
86 g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireOutputWindow",
87 &psppire_output_window_info, 0);
90 return psppire_output_window_type;
93 static GObjectClass *parent_class;
96 psppire_output_window_finalize (GObject *object)
98 string_map_destroy (&PSPPIRE_OUTPUT_WINDOW(object)->render_opts);
101 if (G_OBJECT_CLASS (parent_class)->finalize)
102 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
107 psppire_output_window_dispose (GObject *obj)
109 PsppireOutputWindow *viewer = PSPPIRE_OUTPUT_WINDOW (obj);
112 if (viewer->dispose_has_run)
115 viewer->dispose_has_run = TRUE;
116 for (i = 0; i < viewer->n_items; i++)
117 output_item_unref (viewer->items[i]);
118 free (viewer->items);
119 viewer->items = NULL;
120 viewer->n_items = viewer->allocated_items = 0;
122 if (viewer->print_settings != NULL)
123 g_object_unref (viewer->print_settings);
125 /* Chain up to the parent class */
126 G_OBJECT_CLASS (parent_class)->dispose (obj);
130 psppire_output_window_class_init (PsppireOutputWindowClass *class)
132 GObjectClass *object_class = G_OBJECT_CLASS (class);
134 parent_class = g_type_class_peek_parent (class);
135 object_class->dispose = psppire_output_window_dispose;
137 GTK_WIDGET_CLASS (object_class)->style_set = psppire_output_window_style_set;
138 object_class->finalize = psppire_output_window_finalize;
143 /* Output driver class. */
145 struct psppire_output_driver
147 struct output_driver driver;
148 PsppireOutputWindow *viewer;
149 struct xr_driver *xr;
153 static struct output_driver_class psppire_output_class;
155 static struct psppire_output_driver *
156 psppire_output_cast (struct output_driver *driver)
158 assert (driver->class == &psppire_output_class);
159 return UP_CAST (driver, struct psppire_output_driver, driver);
162 static void on_dwgarea_realize (GtkWidget *widget, gpointer data);
165 draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data)
167 PsppireOutputWindow *viewer = PSPPIRE_OUTPUT_WINDOW (data);
168 struct xr_rendering *r = g_object_get_data (G_OBJECT (widget), "rendering");
169 const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (viewer));
171 PangoFontDescription *font_desc;
175 gdk_color_to_string (&style->text[gtk_widget_get_state (GTK_WIDGET (widget))]);
177 string_map_replace (&viewer->render_opts, "foreground-color", fgc);
181 /* Use GTK+ default font as proportional font. */
182 font_name = pango_font_description_to_string (style->font_desc);
183 string_map_replace (&viewer->render_opts, "prop-font", font_name);
186 /* Derived emphasized font from proportional font. */
187 font_desc = pango_font_description_copy (style->font_desc);
188 pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
189 font_name = pango_font_description_to_string (font_desc);
190 string_map_replace (&viewer->render_opts, "emph-font", font_name);
192 pango_font_description_free (font_desc);
194 xr_rendering_apply_options (r, &viewer->render_opts);
195 xr_rendering_draw_all (r, cr);
202 psppire_output_submit (struct output_driver *this,
203 const struct output_item *item)
205 struct psppire_output_driver *pod = psppire_output_cast (this);
206 PsppireOutputWindow *viewer;
207 GtkWidget *drawing_area;
208 struct xr_rendering *r;
216 if (pod->viewer == NULL)
218 pod->viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
219 gtk_widget_show_all (GTK_WIDGET (pod->viewer));
220 pod->viewer->driver = pod;
222 viewer = pod->viewer;
224 if (viewer->n_items >= viewer->allocated_items)
225 viewer->items = x2nrealloc (viewer->items, &viewer->allocated_items,
226 sizeof *viewer->items);
227 viewer->items[viewer->n_items++] = output_item_ref (item);
229 if (is_text_item (item))
231 const struct text_item *text_item = to_text_item (item);
232 enum text_item_type type = text_item_get_type (text_item);
233 const char *text = text_item_get_text (text_item);
235 if (type == TEXT_ITEM_COMMAND_CLOSE)
237 viewer->in_command = false;
240 else if (text[0] == '\0')
244 cr = gdk_cairo_create (gtk_widget_get_window (GTK_WIDGET (pod->viewer)));
247 const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (viewer));
248 struct text_item *text_item;
249 PangoFontDescription *font_desc;
253 /* Set the widget's text color as the foreground color for the output driver */
254 gchar *fgc = gdk_color_to_string (&style->text[gtk_widget_get_state (GTK_WIDGET (viewer))]);
256 string_map_insert (&pod->viewer->render_opts, "foreground-color", fgc);
259 /* Use GTK+ default font as proportional font. */
260 font_name = pango_font_description_to_string (style->font_desc);
261 string_map_insert (&pod->viewer->render_opts, "prop-font", font_name);
264 /* Derived emphasized font from proportional font. */
265 font_desc = pango_font_description_copy (style->font_desc);
266 pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
267 font_name = pango_font_description_to_string (font_desc);
268 string_map_insert (&pod->viewer->render_opts, "emph-font", font_name);
270 pango_font_description_free (font_desc);
272 /* Pretend that the "page" has a reasonable width and a very big length,
273 so that most tables can be conveniently viewed on-screen with vertical
274 scrolling only. (The length should not be increased very much because
275 it is already close enough to INT_MAX when expressed as thousands of a
277 string_map_insert (&pod->viewer->render_opts, "paper-size", "300x200000mm");
278 string_map_insert (&pod->viewer->render_opts, "left-margin", "0");
279 string_map_insert (&pod->viewer->render_opts, "right-margin", "0");
280 string_map_insert (&pod->viewer->render_opts, "top-margin", "0");
281 string_map_insert (&pod->viewer->render_opts, "bottom-margin", "0");
283 pod->xr = xr_driver_create (cr, &pod->viewer->render_opts);
286 text_item = text_item_create (TEXT_ITEM_PARAGRAPH, "X");
287 r = xr_rendering_create (pod->xr, text_item_super (text_item), cr);
288 xr_rendering_measure (r, &font_width, &pod->font_height);
289 /* xr_rendering_destroy (r); */
290 text_item_unref (text_item);
293 pod->viewer->y += pod->font_height / 2;
295 r = xr_rendering_create (pod->xr, item, cr);
299 xr_rendering_measure (r, &tw, &th);
301 drawing_area = gtk_drawing_area_new ();
303 g_object_set_data (G_OBJECT (drawing_area), "rendering", r);
304 g_signal_connect (drawing_area, "realize",
305 G_CALLBACK (on_dwgarea_realize), pod->viewer);
307 g_signal_connect (drawing_area, "draw",
308 G_CALLBACK (draw_callback), pod->viewer);
310 gtk_widget_set_size_request (drawing_area, tw, th);
311 gtk_layout_put (pod->viewer->output, drawing_area, 0, pod->viewer->y);
313 gtk_widget_show (drawing_area);
315 if (!is_text_item (item)
316 || text_item_get_type (to_text_item (item)) != TEXT_ITEM_SYNTAX
317 || !viewer->in_command)
319 store = GTK_TREE_STORE (gtk_tree_view_get_model (viewer->overview));
321 ds_init_empty (&title);
322 if (is_text_item (item)
323 && text_item_get_type (to_text_item (item)) == TEXT_ITEM_COMMAND_OPEN)
325 gtk_tree_store_append (store, &iter, NULL);
326 viewer->cur_command = iter; /* XXX shouldn't save a GtkTreeIter */
327 viewer->in_command = true;
331 GtkTreeIter *p = viewer->in_command ? &viewer->cur_command : NULL;
332 gtk_tree_store_append (store, &iter, p);
336 if (is_text_item (item))
337 ds_put_cstr (&title, text_item_get_text (to_text_item (item)));
338 else if (is_message_item (item))
340 const struct message_item *msg_item = to_message_item (item);
341 const struct msg *msg = message_item_get_msg (msg_item);
342 ds_put_format (&title, "%s: %s", _("Message"),
343 msg_severity_to_string (msg->severity));
345 else if (is_table_item (item))
347 const char *caption = table_item_get_caption (to_table_item (item));
349 ds_put_format (&title, "Table: %s", caption);
351 ds_put_cstr (&title, "Table");
353 else if (is_chart_item (item))
355 const char *s = chart_item_get_title (to_chart_item (item));
357 ds_put_format (&title, "Chart: %s", s);
359 ds_put_cstr (&title, "Chart");
361 gtk_tree_store_set (store, &iter,
362 COL_TITLE, ds_cstr (&title),
368 path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
369 gtk_tree_view_expand_row (viewer->overview, path, TRUE);
370 gtk_tree_path_free (path);
373 if (pod->viewer->max_width < tw)
374 pod->viewer->max_width = tw;
375 pod->viewer->y += th;
377 gtk_layout_set_size (pod->viewer->output,
378 pod->viewer->max_width, pod->viewer->y);
380 gtk_window_set_urgency_hint (GTK_WINDOW (pod->viewer), TRUE);
386 static struct output_driver_class psppire_output_class =
388 "PSPPIRE", /* name */
390 psppire_output_submit, /* submit */
395 psppire_output_window_setup (void)
397 struct psppire_output_driver *pod;
398 struct output_driver *d;
400 pod = xzalloc (sizeof *pod);
402 output_driver_init (d, &psppire_output_class, "PSPPIRE",
403 SETTINGS_DEVICE_UNFILTERED);
404 output_driver_register (d);
409 /* Callback for the "delete" action (clicking the x on the top right
410 hand corner of the window) */
412 on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
414 PsppireOutputWindow *ow = PSPPIRE_OUTPUT_WINDOW (user_data);
416 gtk_widget_destroy (GTK_WIDGET (ow));
418 ow->driver->viewer = NULL;
426 cancel_urgency (GtkWindow *window, gpointer data)
428 gtk_window_set_urgency_hint (window, FALSE);
432 on_row_activate (GtkTreeView *overview,
434 GtkTreeViewColumn *column,
435 PsppireOutputWindow *window)
443 model = gtk_tree_view_get_model (overview);
444 if (!gtk_tree_model_get_iter (model, &iter, path))
447 gtk_tree_model_get_value (model, &iter, COL_Y, &value);
448 y = g_value_get_long (&value);
449 g_value_unset (&value);
451 vadj = gtk_layout_get_vadjustment (window->output);
452 min = gtk_adjustment_get_lower (vadj);
453 max = gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj);
458 gtk_adjustment_set_value (vadj, y);
461 static void psppire_output_window_print (PsppireOutputWindow *window);
465 export_output (PsppireOutputWindow *window, struct string_map *options,
468 struct output_driver *driver;
471 string_map_insert (options, "format", format);
472 driver = output_driver_create (options);
476 for (i = 0; i < window->n_items; i++)
477 driver->class->submit (driver, window->items[i]);
478 output_driver_destroy (driver);
500 #define N_EXTENSIONS (n_FT - 1)
502 struct file_types ft[n_FT] = {
503 {N_("Infer file type from extension"), NULL},
504 {N_("PDF (*.pdf)"), ".pdf"},
505 {N_("HTML (*.html)"), ".html"},
506 {N_("OpenDocument (*.odt)"), ".odt"},
507 {N_("Text (*.txt)"), ".txt"},
508 {N_("PostScript (*.ps)"), ".ps"},
509 {N_("Comma-Separated Values (*.csv)"), ".csv"}
514 on_combo_change (GtkFileChooser *chooser)
516 gboolean sensitive = FALSE;
517 GtkWidget *combo = gtk_file_chooser_get_extra_widget (chooser);
520 gchar *fn = gtk_file_chooser_get_filename (chooser);
522 if (combo && gtk_widget_get_realized (combo))
523 x = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
535 for (i = 1 ; i < N_EXTENSIONS ; ++i)
537 if ( g_str_has_suffix (fn, ft[i].ext))
547 gtk_dialog_set_response_sensitive (GTK_DIALOG (chooser), GTK_RESPONSE_ACCEPT, sensitive);
552 on_file_chooser_change (GObject *w, GParamSpec *pspec, gpointer data)
555 GtkFileChooser *chooser = data;
556 const gchar *name = g_param_spec_get_name (pspec);
558 if ( ! gtk_widget_get_realized (GTK_WIDGET (chooser)))
561 /* Ignore this one. It causes recursion. */
562 if ( 0 == strcmp ("tooltip-text", name))
565 on_combo_change (chooser);
569 /* Recursively descend all the children of W, connecting
570 to their "notify" signal */
572 iterate_widgets (GtkWidget *w, gpointer data)
574 if ( GTK_IS_CONTAINER (w))
575 gtk_container_forall (GTK_CONTAINER (w), iterate_widgets, data);
577 g_signal_connect (w, "notify", G_CALLBACK (on_file_chooser_change), data);
582 static GtkListStore *
583 create_file_type_list (void)
587 GtkListStore *list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
589 for (i = 0 ; i < n_FT ; ++i)
591 gtk_list_store_append (list, &iter);
592 gtk_list_store_set (list, &iter,
593 0, gettext (ft[i].label),
602 psppire_output_window_export (PsppireOutputWindow *window)
608 GtkFileChooser *chooser;
610 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Export Output"),
612 GTK_FILE_CHOOSER_ACTION_SAVE,
613 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
614 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
617 g_object_set (dialog, "local-only", FALSE, NULL);
619 chooser = GTK_FILE_CHOOSER (dialog);
621 list = create_file_type_list ();
623 combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list));
627 /* Create text cell renderer */
628 GtkCellRenderer *cell = gtk_cell_renderer_text_new();
629 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE );
631 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cell, "text", 0);
634 g_signal_connect_swapped (combo, "changed", G_CALLBACK (on_combo_change), chooser);
636 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
638 gtk_file_chooser_set_extra_widget (chooser, combo);
640 /* This kludge is necessary because there is no signal to tell us
641 when the candidate filename of a GtkFileChooser has changed */
642 gtk_container_forall (GTK_CONTAINER (dialog), iterate_widgets, dialog);
645 gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);
647 response = gtk_dialog_run (GTK_DIALOG (dialog));
649 if ( response == GTK_RESPONSE_ACCEPT )
651 gint file_type = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
652 gchar *filename = gtk_file_chooser_get_filename (chooser);
653 struct string_map options;
655 g_return_if_fail (filename);
657 if (file_type == FT_AUTO)
659 /* If the "Infer file type from extension" option was chosen,
660 search for the respective type in the list.
661 (It's a O(n) search, but fortunately n is small). */
663 for (i = 1 ; i < N_EXTENSIONS ; ++i)
665 if ( g_str_has_suffix (filename, ft[i].ext))
672 else if (! g_str_has_suffix (filename, ft[file_type].ext))
674 /* If an explicit document format was chosen, and if the chosen
675 filename does not already have that particular "extension",
679 gchar *of = filename;
680 filename = g_strconcat (filename, ft[file_type].ext, NULL);
684 string_map_init (&options);
685 string_map_insert (&options, "output-file", filename);
690 export_output (window, &options, "pdf");
693 export_output (window, &options, "html");
696 export_output (window, &options, "odt");
699 export_output (window, &options, "ps");
702 export_output (window, &options, "csv");
706 string_map_insert (&options, "headers", "false");
707 string_map_insert (&options, "paginate", "false");
708 string_map_insert (&options, "squeeze", "true");
709 string_map_insert (&options, "emphasis", "none");
710 string_map_insert (&options, "charts", "none");
711 string_map_insert (&options, "top-margin", "0");
712 string_map_insert (&options, "bottom-margin", "0");
713 export_output (window, &options, "txt");
716 g_assert_not_reached ();
719 string_map_destroy (&options);
724 gtk_widget_destroy (dialog);
736 /* GNU Hurd doesn't have PATH_MAX. Use a fallback.
737 Temporary directory names are usually not that long. */
739 # define PATH_MAX 1024
743 clipboard_get_cb (GtkClipboard *clipboard,
744 GtkSelectionData *selection_data,
748 PsppireOutputWindow *window = data;
752 struct output_driver *driver = NULL;
753 char dirname[PATH_MAX], *filename;
754 struct string_map options;
756 GtkTreeSelection *sel = gtk_tree_view_get_selection (window->overview);
757 GtkTreeModel *model = gtk_tree_view_get_model (window->overview);
759 GList *rows = gtk_tree_selection_get_selected_rows (sel, &model);
765 if (path_search (dirname, sizeof dirname, NULL, NULL, true)
766 || mkdtemp (dirname) == NULL)
768 msg_error (errno, _("failed to create temporary directory during clipboard operation"));
771 filename = xasprintf ("%s/clip.tmp", dirname);
773 string_map_init (&options);
774 string_map_insert (&options, "output-file", filename);
778 case SELECT_FMT_UTF8:
779 string_map_insert (&options, "box", "unicode");
782 case SELECT_FMT_TEXT:
783 string_map_insert (&options, "format", "txt");
786 case SELECT_FMT_HTML:
787 string_map_insert (&options, "format", "html");
788 string_map_insert (&options, "borders", "false");
789 string_map_insert (&options, "css", "false");
793 string_map_insert (&options, "format", "odt");
797 g_warning ("unsupported clip target\n");
802 driver = output_driver_create (&options);
808 GtkTreePath *path = n->data ;
810 struct output_item *item ;
812 gtk_tree_model_get_iter (model, &iter, path);
813 gtk_tree_model_get (model, &iter, COL_ADDR, &item, -1);
815 driver->class->submit (driver, item);
820 if ( driver->class->flush)
821 driver->class->flush (driver);
824 /* Some drivers (eg: the odt one) don't write anything until they
826 output_driver_destroy (driver);
829 if ( g_file_get_contents (filename, &text, &length, NULL) )
831 gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data),
833 (const guchar *) text, length);
839 output_driver_destroy (driver);
851 clipboard_clear_cb (GtkClipboard *clipboard,
856 static const GtkTargetEntry targets[] = {
858 { "STRING", 0, SELECT_FMT_TEXT },
859 { "TEXT", 0, SELECT_FMT_TEXT },
860 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
861 { "text/plain", 0, SELECT_FMT_TEXT },
863 { "UTF8_STRING", 0, SELECT_FMT_UTF8 },
864 { "text/plain;charset=utf-8", 0, SELECT_FMT_UTF8 },
866 { "text/html", 0, SELECT_FMT_HTML },
868 { "application/vnd.oasis.opendocument.text", 0, SELECT_FMT_ODT }
872 on_copy (PsppireOutputWindow *window)
875 GtkClipboard *clipboard =
876 gtk_widget_get_clipboard (GTK_WIDGET (window),
877 GDK_SELECTION_CLIPBOARD);
879 if (!gtk_clipboard_set_with_data (clipboard, targets,
880 G_N_ELEMENTS (targets),
881 clipboard_get_cb, clipboard_clear_cb,
884 clipboard_clear_cb (clipboard, window);
889 on_selection_change (GtkTreeSelection *sel, GtkAction *copy_action)
891 /* The Copy action is available only if there is something selected */
892 gtk_action_set_sensitive (copy_action, gtk_tree_selection_count_selected_rows (sel) > 0);
896 on_select_all (PsppireOutputWindow *window)
898 GtkTreeSelection *sel = gtk_tree_view_get_selection (window->overview);
899 gtk_tree_view_expand_all (window->overview);
900 gtk_tree_selection_select_all (sel);
905 copy_base_to_bg (GtkWidget *dest, GtkWidget *src)
908 for (i = 0; i < 5; ++i)
910 GdkColor *col = >k_widget_get_style (src)->base[i];
911 gtk_widget_modify_bg (dest, i, col);
913 col = >k_widget_get_style (src)->text[i];
914 gtk_widget_modify_fg (dest, i, col);
919 on_dwgarea_realize (GtkWidget *dwg_area, gpointer data)
921 GtkWidget *viewer = GTK_WIDGET (data);
923 copy_base_to_bg (dwg_area, viewer);
928 psppire_output_window_style_set (GtkWidget *w, GtkStyle *prev)
930 GtkWidget *op = GTK_WIDGET (PSPPIRE_OUTPUT_WINDOW (w)->output);
932 /* Copy the base style from the parent widget to the container and
934 We do this, because the container's primary purpose is to
935 display text. This way psppire appears to follow the chosen
938 copy_base_to_bg (op, w);
939 gtk_container_foreach (GTK_CONTAINER (op), (GtkCallback) copy_base_to_bg,
940 PSPPIRE_OUTPUT_WINDOW (w)->output);
942 /* Chain up to the parent class */
943 GTK_WIDGET_CLASS (parent_class)->style_set (w, prev);
947 psppire_output_window_init (PsppireOutputWindow *window)
949 GtkTreeViewColumn *column;
950 GtkCellRenderer *renderer;
952 GtkAction *copy_action;
953 GtkAction *select_all_action;
954 GtkTreeSelection *sel;
957 string_map_init (&window->render_opts);
959 xml = builder_new ("output-viewer.ui");
961 copy_action = get_action_assert (xml, "edit_copy");
962 select_all_action = get_action_assert (xml, "edit_select-all");
964 gtk_action_set_sensitive (copy_action, FALSE);
966 g_signal_connect_swapped (copy_action, "activate", G_CALLBACK (on_copy), window);
968 g_signal_connect_swapped (select_all_action, "activate", G_CALLBACK (on_select_all), window);
970 gtk_widget_reparent (get_widget_assert (xml, "vbox1"), GTK_WIDGET (window));
972 window->output = GTK_LAYOUT (get_widget_assert (xml, "output"));
974 window->print_settings = NULL;
975 window->dispose_has_run = FALSE;
977 window->overview = GTK_TREE_VIEW (get_widget_assert (xml, "overview"));
979 sel = gtk_tree_view_get_selection (window->overview);
981 gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE);
983 g_signal_connect (sel, "changed", G_CALLBACK (on_selection_change), copy_action);
985 model = GTK_TREE_MODEL (gtk_tree_store_new (
987 G_TYPE_STRING, /* COL_TITLE */
988 G_TYPE_POINTER, /* COL_ADDR */
989 G_TYPE_LONG)); /* COL_Y */
990 gtk_tree_view_set_model (window->overview, model);
991 g_object_unref (model);
993 window->in_command = false;
995 window->items = NULL;
996 window->n_items = window->allocated_items = 0;
998 column = gtk_tree_view_column_new ();
999 gtk_tree_view_append_column (GTK_TREE_VIEW (window->overview), column);
1000 renderer = gtk_cell_renderer_text_new ();
1001 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1002 gtk_tree_view_column_add_attribute (column, renderer, "text", COL_TITLE);
1004 g_signal_connect (GTK_TREE_VIEW (window->overview),
1005 "row-activated", G_CALLBACK (on_row_activate), window);
1009 g_signal_connect (window,
1011 G_CALLBACK (cancel_urgency),
1014 g_signal_connect (get_action_assert (xml,"windows_minimise-all"),
1016 G_CALLBACK (psppire_window_minimise_all),
1021 GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (xml, "uimanager1", GTK_TYPE_UI_MANAGER));
1022 merge_help_menu (uim);
1024 w = gtk_ui_manager_get_widget (uim,"/ui/menubar/windows_menuitem/windows_minimise-all");
1026 PSPPIRE_WINDOW (window)->menu =
1027 GTK_MENU_SHELL (gtk_widget_get_parent (w));
1030 g_signal_connect_swapped (get_action_assert (xml, "file_export"), "activate",
1031 G_CALLBACK (psppire_output_window_export), window);
1034 g_signal_connect_swapped (get_action_assert (xml, "file_print"), "activate",
1035 G_CALLBACK (psppire_output_window_print), window);
1037 g_object_unref (xml);
1039 g_signal_connect (window, "delete-event",
1040 G_CALLBACK (on_delete), window);
1045 psppire_output_window_new (void)
1047 return GTK_WIDGET (g_object_new (psppire_output_window_get_type (),
1048 /* TRANSLATORS: This will form a filename. Please avoid whitespace. */
1049 "filename", _("Output"),
1050 "description", _("Output Viewer"),
1057 get_cairo_context_from_print_context (GtkPrintContext *context)
1059 cairo_t *cr = gtk_print_context_get_cairo_context (context);
1062 For all platforms except windows, gtk_print_context_get_dpi_[xy] returns 72.
1063 Windows returns 600.
1065 double xres = gtk_print_context_get_dpi_x (context);
1066 double yres = gtk_print_context_get_dpi_y (context);
1068 /* This means that the cairo context now has its dimensions in Points */
1069 cairo_scale (cr, xres / 72.0, yres / 72.0);
1076 create_xr_print_driver (GtkPrintContext *context, PsppireOutputWindow *window)
1078 struct string_map options;
1079 GtkPageSetup *page_setup;
1080 double width, height;
1082 double right_margin;
1084 double bottom_margin;
1086 page_setup = gtk_print_context_get_page_setup (context);
1087 width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_MM);
1088 height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_MM);
1089 left_margin = gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_MM);
1090 right_margin = gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_MM);
1091 top_margin = gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_MM);
1092 bottom_margin = gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_MM);
1094 string_map_init (&options);
1095 string_map_insert_nocopy (&options, xstrdup ("paper-size"),
1096 c_xasprintf("%.2fx%.2fmm", width, height));
1097 string_map_insert_nocopy (&options, xstrdup ("left-margin"),
1098 c_xasprintf ("%.2fmm", left_margin));
1099 string_map_insert_nocopy (&options, xstrdup ("right-margin"),
1100 c_xasprintf ("%.2fmm", right_margin));
1101 string_map_insert_nocopy (&options, xstrdup ("top-margin"),
1102 c_xasprintf ("%.2fmm", top_margin));
1103 string_map_insert_nocopy (&options, xstrdup ("bottom-margin"),
1104 c_xasprintf ("%.2fmm", bottom_margin));
1106 window->print_xrd = xr_driver_create (get_cairo_context_from_print_context (context), &options);
1108 string_map_destroy (&options);
1112 paginate (GtkPrintOperation *operation,
1113 GtkPrintContext *context,
1114 PsppireOutputWindow *window)
1116 if (window->paginated)
1118 /* Sometimes GTK+ emits this signal again even after pagination is
1119 complete. Don't let that screw up printing. */
1122 else if ( window->print_item < window->n_items )
1124 xr_driver_output_item (window->print_xrd, window->items[window->print_item++]);
1125 while (xr_driver_need_new_page (window->print_xrd))
1127 xr_driver_next_page (window->print_xrd, NULL);
1128 window->print_n_pages ++;
1134 gtk_print_operation_set_n_pages (operation, window->print_n_pages);
1136 /* Re-create the driver to do the real printing. */
1137 xr_driver_destroy (window->print_xrd);
1138 create_xr_print_driver (context, window);
1139 window->print_item = 0;
1140 window->paginated = TRUE;
1147 begin_print (GtkPrintOperation *operation,
1148 GtkPrintContext *context,
1149 PsppireOutputWindow *window)
1151 create_xr_print_driver (context, window);
1153 window->print_item = 0;
1154 window->print_n_pages = 1;
1155 window->paginated = FALSE;
1159 end_print (GtkPrintOperation *operation,
1160 GtkPrintContext *context,
1161 PsppireOutputWindow *window)
1163 xr_driver_destroy (window->print_xrd);
1168 draw_page (GtkPrintOperation *operation,
1169 GtkPrintContext *context,
1171 PsppireOutputWindow *window)
1173 xr_driver_next_page (window->print_xrd, get_cairo_context_from_print_context (context));
1174 while (!xr_driver_need_new_page (window->print_xrd)
1175 && window->print_item < window->n_items)
1176 xr_driver_output_item (window->print_xrd, window->items [window->print_item++]);
1181 psppire_output_window_print (PsppireOutputWindow *window)
1183 GtkPrintOperationResult res;
1185 GtkPrintOperation *print = gtk_print_operation_new ();
1187 if (window->print_settings != NULL)
1188 gtk_print_operation_set_print_settings (print, window->print_settings);
1190 g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), window);
1191 g_signal_connect (print, "end_print", G_CALLBACK (end_print), window);
1192 g_signal_connect (print, "paginate", G_CALLBACK (paginate), window);
1193 g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), window);
1195 res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
1196 GTK_WINDOW (window), NULL);
1198 if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
1200 if (window->print_settings != NULL)
1201 g_object_unref (window->print_settings);
1202 window->print_settings = g_object_ref (gtk_print_operation_get_print_settings (print));
1205 g_object_unref (print);