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"
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 draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data)
168 PsppireOutputWindow *viewer = PSPPIRE_OUTPUT_WINDOW (data);
169 struct xr_rendering *r = g_object_get_data (G_OBJECT (widget), "rendering");
170 const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (viewer));
172 PangoFontDescription *font_desc;
176 gdk_color_to_string (&style->text[gtk_widget_get_state (GTK_WIDGET (widget))]);
178 string_map_replace (&viewer->render_opts, "foreground-color", fgc);
182 /* Use GTK+ default font as proportional font. */
183 font_name = pango_font_description_to_string (style->font_desc);
184 string_map_replace (&viewer->render_opts, "prop-font", font_name);
187 /* Derived emphasized font from proportional font. */
188 font_desc = pango_font_description_copy (style->font_desc);
189 pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
190 font_name = pango_font_description_to_string (font_desc);
191 string_map_replace (&viewer->render_opts, "emph-font", font_name);
193 pango_font_description_free (font_desc);
195 xr_rendering_apply_options (r, &viewer->render_opts);
196 xr_rendering_draw_all (r, cr);
203 psppire_output_submit (struct output_driver *this,
204 const struct output_item *item)
206 struct psppire_output_driver *pod = psppire_output_cast (this);
207 PsppireOutputWindow *viewer;
208 GtkWidget *drawing_area;
209 struct xr_rendering *r;
217 if (pod->viewer == NULL)
219 pod->viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
220 gtk_widget_show_all (GTK_WIDGET (pod->viewer));
221 pod->viewer->driver = pod;
223 viewer = pod->viewer;
225 if (viewer->n_items >= viewer->allocated_items)
226 viewer->items = x2nrealloc (viewer->items, &viewer->allocated_items,
227 sizeof *viewer->items);
228 viewer->items[viewer->n_items++] = output_item_ref (item);
230 if (is_text_item (item))
232 const struct text_item *text_item = to_text_item (item);
233 enum text_item_type type = text_item_get_type (text_item);
234 const char *text = text_item_get_text (text_item);
236 if (type == TEXT_ITEM_COMMAND_CLOSE)
238 viewer->in_command = false;
241 else if (text[0] == '\0')
245 cr = gdk_cairo_create (gtk_widget_get_window (GTK_WIDGET (pod->viewer)));
248 const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (viewer));
249 struct text_item *text_item;
250 PangoFontDescription *font_desc;
254 /* Set the widget's text color as the foreground color for the output driver */
255 gchar *fgc = gdk_color_to_string (&style->text[gtk_widget_get_state (GTK_WIDGET (viewer))]);
257 string_map_insert (&pod->viewer->render_opts, "foreground-color", fgc);
260 /* Use GTK+ default font as proportional font. */
261 font_name = pango_font_description_to_string (style->font_desc);
262 string_map_insert (&pod->viewer->render_opts, "prop-font", font_name);
265 /* Derived emphasized font from proportional font. */
266 font_desc = pango_font_description_copy (style->font_desc);
267 pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
268 font_name = pango_font_description_to_string (font_desc);
269 string_map_insert (&pod->viewer->render_opts, "emph-font", font_name);
271 pango_font_description_free (font_desc);
273 /* Pretend that the "page" has a reasonable width and a very big length,
274 so that most tables can be conveniently viewed on-screen with vertical
275 scrolling only. (The length should not be increased very much because
276 it is already close enough to INT_MAX when expressed as thousands of a
278 string_map_insert (&pod->viewer->render_opts, "paper-size", "300x200000mm");
279 string_map_insert (&pod->viewer->render_opts, "left-margin", "0");
280 string_map_insert (&pod->viewer->render_opts, "right-margin", "0");
281 string_map_insert (&pod->viewer->render_opts, "top-margin", "0");
282 string_map_insert (&pod->viewer->render_opts, "bottom-margin", "0");
284 pod->xr = xr_driver_create (cr, &pod->viewer->render_opts);
287 text_item = text_item_create (TEXT_ITEM_PARAGRAPH, "X");
288 r = xr_rendering_create (pod->xr, text_item_super (text_item), cr);
289 xr_rendering_measure (r, &font_width, &pod->font_height);
290 /* xr_rendering_destroy (r); */
291 text_item_unref (text_item);
294 pod->viewer->y += pod->font_height / 2;
296 r = xr_rendering_create (pod->xr, item, cr);
300 xr_rendering_measure (r, &tw, &th);
302 drawing_area = gtk_drawing_area_new ();
304 g_object_set_data (G_OBJECT (drawing_area), "rendering", r);
305 g_signal_connect (drawing_area, "realize",
306 G_CALLBACK (on_dwgarea_realize), pod->viewer);
308 g_signal_connect (drawing_area, "draw",
309 G_CALLBACK (draw_callback), pod->viewer);
311 gtk_widget_set_size_request (drawing_area, tw, th);
312 gtk_layout_put (pod->viewer->output, drawing_area, 0, pod->viewer->y);
314 gtk_widget_show (drawing_area);
316 if (!is_text_item (item)
317 || text_item_get_type (to_text_item (item)) != TEXT_ITEM_SYNTAX
318 || !viewer->in_command)
320 store = GTK_TREE_STORE (gtk_tree_view_get_model (viewer->overview));
322 ds_init_empty (&title);
323 if (is_text_item (item)
324 && text_item_get_type (to_text_item (item)) == TEXT_ITEM_COMMAND_OPEN)
326 gtk_tree_store_append (store, &iter, NULL);
327 viewer->cur_command = iter; /* XXX shouldn't save a GtkTreeIter */
328 viewer->in_command = true;
332 GtkTreeIter *p = viewer->in_command ? &viewer->cur_command : NULL;
333 gtk_tree_store_append (store, &iter, p);
337 if (is_text_item (item))
338 ds_put_cstr (&title, text_item_get_text (to_text_item (item)));
339 else if (is_message_item (item))
341 const struct message_item *msg_item = to_message_item (item);
342 const struct msg *msg = message_item_get_msg (msg_item);
343 ds_put_format (&title, "%s: %s", _("Message"),
344 msg_severity_to_string (msg->severity));
346 else if (is_table_item (item))
348 const char *caption = table_item_get_caption (to_table_item (item));
350 ds_put_format (&title, "Table: %s", caption);
352 ds_put_cstr (&title, "Table");
354 else if (is_chart_item (item))
356 const char *s = chart_item_get_title (to_chart_item (item));
358 ds_put_format (&title, "Chart: %s", s);
360 ds_put_cstr (&title, "Chart");
362 gtk_tree_store_set (store, &iter,
363 COL_TITLE, ds_cstr (&title),
369 path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
370 gtk_tree_view_expand_row (viewer->overview, path, TRUE);
371 gtk_tree_path_free (path);
374 if (pod->viewer->max_width < tw)
375 pod->viewer->max_width = tw;
376 pod->viewer->y += th;
378 gtk_layout_set_size (pod->viewer->output,
379 pod->viewer->max_width, pod->viewer->y);
381 gtk_window_set_urgency_hint (GTK_WINDOW (pod->viewer), TRUE);
387 static struct output_driver_class psppire_output_class =
389 "PSPPIRE", /* name */
391 psppire_output_submit, /* submit */
396 psppire_output_window_setup (void)
398 struct psppire_output_driver *pod;
399 struct output_driver *d;
401 pod = xzalloc (sizeof *pod);
403 output_driver_init (d, &psppire_output_class, "PSPPIRE",
404 SETTINGS_DEVICE_UNFILTERED);
405 output_driver_register (d);
410 /* Callback for the "delete" action (clicking the x on the top right
411 hand corner of the window) */
413 on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
415 PsppireOutputWindow *ow = PSPPIRE_OUTPUT_WINDOW (user_data);
417 gtk_widget_destroy (GTK_WIDGET (ow));
419 ow->driver->viewer = NULL;
427 cancel_urgency (GtkWindow *window, gpointer data)
429 gtk_window_set_urgency_hint (window, FALSE);
433 on_row_activate (GtkTreeView *overview,
435 GtkTreeViewColumn *column,
436 PsppireOutputWindow *window)
444 model = gtk_tree_view_get_model (overview);
445 if (!gtk_tree_model_get_iter (model, &iter, path))
448 gtk_tree_model_get_value (model, &iter, COL_Y, &value);
449 y = g_value_get_long (&value);
450 g_value_unset (&value);
452 vadj = gtk_layout_get_vadjustment (window->output);
453 min = gtk_adjustment_get_lower (vadj);
454 max = gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj);
459 gtk_adjustment_set_value (vadj, y);
462 static void psppire_output_window_print (PsppireOutputWindow *window);
466 export_output (PsppireOutputWindow *window, struct string_map *options,
469 struct output_driver *driver;
472 string_map_insert (options, "format", format);
473 driver = output_driver_create (options);
477 for (i = 0; i < window->n_items; i++)
478 driver->class->submit (driver, window->items[i]);
479 output_driver_destroy (driver);
501 #define N_EXTENSIONS (n_FT - 1)
503 struct file_types ft[n_FT] = {
504 {N_("Infer file type from extension"), NULL},
505 {N_("PDF (*.pdf)"), ".pdf"},
506 {N_("HTML (*.html)"), ".html"},
507 {N_("OpenDocument (*.odt)"), ".odt"},
508 {N_("Text (*.txt)"), ".txt"},
509 {N_("PostScript (*.ps)"), ".ps"},
510 {N_("Comma-Separated Values (*.csv)"), ".csv"}
515 on_combo_change (GtkFileChooser *chooser)
517 gboolean sensitive = FALSE;
518 GtkWidget *combo = gtk_file_chooser_get_extra_widget (chooser);
521 gchar *fn = gtk_file_chooser_get_filename (chooser);
523 if (combo && gtk_widget_get_realized (combo))
524 x = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
536 for (i = 1 ; i < N_EXTENSIONS ; ++i)
538 if ( g_str_has_suffix (fn, ft[i].ext))
548 gtk_dialog_set_response_sensitive (GTK_DIALOG (chooser), GTK_RESPONSE_ACCEPT, sensitive);
553 on_file_chooser_change (GObject *w, GParamSpec *pspec, gpointer data)
556 GtkFileChooser *chooser = data;
557 const gchar *name = g_param_spec_get_name (pspec);
559 if ( ! gtk_widget_get_realized (GTK_WIDGET (chooser)))
562 /* Ignore this one. It causes recursion. */
563 if ( 0 == strcmp ("tooltip-text", name))
566 on_combo_change (chooser);
570 /* Recursively descend all the children of W, connecting
571 to their "notify" signal */
573 iterate_widgets (GtkWidget *w, gpointer data)
575 if ( GTK_IS_CONTAINER (w))
576 gtk_container_forall (GTK_CONTAINER (w), iterate_widgets, data);
578 g_signal_connect (w, "notify", G_CALLBACK (on_file_chooser_change), data);
583 static GtkListStore *
584 create_file_type_list (void)
588 GtkListStore *list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
590 for (i = 0 ; i < n_FT ; ++i)
592 gtk_list_store_append (list, &iter);
593 gtk_list_store_set (list, &iter,
594 0, gettext (ft[i].label),
603 psppire_output_window_export (PsppireOutputWindow *window)
609 GtkFileChooser *chooser;
611 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Export Output"),
613 GTK_FILE_CHOOSER_ACTION_SAVE,
614 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
615 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
618 g_object_set (dialog, "local-only", FALSE, NULL);
620 chooser = GTK_FILE_CHOOSER (dialog);
622 list = create_file_type_list ();
624 combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list));
628 /* Create text cell renderer */
629 GtkCellRenderer *cell = gtk_cell_renderer_text_new();
630 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE );
632 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cell, "text", 0);
635 g_signal_connect_swapped (combo, "changed", G_CALLBACK (on_combo_change), chooser);
637 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
639 gtk_file_chooser_set_extra_widget (chooser, combo);
641 /* This kludge is necessary because there is no signal to tell us
642 when the candidate filename of a GtkFileChooser has changed */
643 gtk_container_forall (GTK_CONTAINER (dialog), iterate_widgets, dialog);
646 gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);
648 response = gtk_dialog_run (GTK_DIALOG (dialog));
650 if ( response == GTK_RESPONSE_ACCEPT )
652 gint file_type = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
653 gchar *filename = gtk_file_chooser_get_filename (chooser);
654 struct string_map options;
656 g_return_if_fail (filename);
658 if (file_type == FT_AUTO)
660 /* If the "Infer file type from extension" option was chosen,
661 search for the respective type in the list.
662 (It's a O(n) search, but fortunately n is small). */
664 for (i = 1 ; i < N_EXTENSIONS ; ++i)
666 if ( g_str_has_suffix (filename, ft[i].ext))
673 else if (! g_str_has_suffix (filename, ft[file_type].ext))
675 /* If an explicit document format was chosen, and if the chosen
676 filename does not already have that particular "extension",
680 gchar *of = filename;
681 filename = g_strconcat (filename, ft[file_type].ext, NULL);
685 string_map_init (&options);
686 string_map_insert (&options, "output-file", filename);
691 export_output (window, &options, "pdf");
694 export_output (window, &options, "html");
697 export_output (window, &options, "odt");
700 export_output (window, &options, "ps");
703 export_output (window, &options, "csv");
707 string_map_insert (&options, "headers", "false");
708 string_map_insert (&options, "paginate", "false");
709 string_map_insert (&options, "squeeze", "true");
710 string_map_insert (&options, "emphasis", "none");
711 string_map_insert (&options, "charts", "none");
712 string_map_insert (&options, "top-margin", "0");
713 string_map_insert (&options, "bottom-margin", "0");
714 export_output (window, &options, "txt");
717 g_assert_not_reached ();
720 string_map_destroy (&options);
725 gtk_widget_destroy (dialog);
737 /* GNU Hurd doesn't have PATH_MAX. Use a fallback.
738 Temporary directory names are usually not that long. */
740 # define PATH_MAX 1024
744 clipboard_get_cb (GtkClipboard *clipboard,
745 GtkSelectionData *selection_data,
749 PsppireOutputWindow *window = data;
753 struct output_driver *driver = NULL;
754 char dirname[PATH_MAX], *filename;
755 struct string_map options;
757 GtkTreeSelection *sel = gtk_tree_view_get_selection (window->overview);
758 GtkTreeModel *model = gtk_tree_view_get_model (window->overview);
760 GList *rows = gtk_tree_selection_get_selected_rows (sel, &model);
766 if (path_search (dirname, sizeof dirname, NULL, NULL, true)
767 || mkdtemp (dirname) == NULL)
769 error (0, errno, _("failed to create temporary directory"));
772 filename = xasprintf ("%s/clip.tmp", dirname);
774 string_map_init (&options);
775 string_map_insert (&options, "output-file", filename);
779 case SELECT_FMT_UTF8:
780 string_map_insert (&options, "box", "unicode");
783 case SELECT_FMT_TEXT:
784 string_map_insert (&options, "format", "txt");
787 case SELECT_FMT_HTML:
788 string_map_insert (&options, "format", "html");
789 string_map_insert (&options, "borders", "false");
790 string_map_insert (&options, "css", "false");
794 string_map_insert (&options, "format", "odt");
798 g_warning ("unsupported clip target\n");
803 driver = output_driver_create (&options);
809 GtkTreePath *path = n->data ;
811 struct output_item *item ;
813 gtk_tree_model_get_iter (model, &iter, path);
814 gtk_tree_model_get (model, &iter, COL_ADDR, &item, -1);
816 driver->class->submit (driver, item);
821 if ( driver->class->flush)
822 driver->class->flush (driver);
825 /* Some drivers (eg: the odt one) don't write anything until they
827 output_driver_destroy (driver);
830 if ( g_file_get_contents (filename, &text, &length, NULL) )
832 gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data),
834 (const guchar *) text, length);
840 output_driver_destroy (driver);
852 clipboard_clear_cb (GtkClipboard *clipboard,
857 static const GtkTargetEntry targets[] = {
859 { "STRING", 0, SELECT_FMT_TEXT },
860 { "TEXT", 0, SELECT_FMT_TEXT },
861 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
862 { "text/plain", 0, SELECT_FMT_TEXT },
864 { "UTF8_STRING", 0, SELECT_FMT_UTF8 },
865 { "text/plain;charset=utf-8", 0, SELECT_FMT_UTF8 },
867 { "text/html", 0, SELECT_FMT_HTML },
869 { "application/vnd.oasis.opendocument.text", 0, SELECT_FMT_ODT }
873 on_copy (PsppireOutputWindow *window)
876 GtkClipboard *clipboard =
877 gtk_widget_get_clipboard (GTK_WIDGET (window),
878 GDK_SELECTION_CLIPBOARD);
880 if (!gtk_clipboard_set_with_data (clipboard, targets,
881 G_N_ELEMENTS (targets),
882 clipboard_get_cb, clipboard_clear_cb,
885 clipboard_clear_cb (clipboard, window);
890 on_selection_change (GtkTreeSelection *sel, GtkAction *copy_action)
892 /* The Copy action is available only if there is something selected */
893 gtk_action_set_sensitive (copy_action, gtk_tree_selection_count_selected_rows (sel) > 0);
897 on_select_all (PsppireOutputWindow *window)
899 GtkTreeSelection *sel = gtk_tree_view_get_selection (window->overview);
900 gtk_tree_view_expand_all (window->overview);
901 gtk_tree_selection_select_all (sel);
906 copy_base_to_bg (GtkWidget *dest, GtkWidget *src)
909 for (i = 0; i < 5; ++i)
911 GdkColor *col = >k_widget_get_style (src)->base[i];
912 gtk_widget_modify_bg (dest, i, col);
914 col = >k_widget_get_style (src)->text[i];
915 gtk_widget_modify_fg (dest, i, col);
920 on_dwgarea_realize (GtkWidget *dwg_area, gpointer data)
922 GtkWidget *viewer = GTK_WIDGET (data);
924 copy_base_to_bg (dwg_area, viewer);
929 psppire_output_window_style_set (GtkWidget *w, GtkStyle *prev)
931 GtkWidget *op = GTK_WIDGET (PSPPIRE_OUTPUT_WINDOW (w)->output);
933 /* Copy the base style from the parent widget to the container and
935 We do this, because the container's primary purpose is to
936 display text. This way psppire appears to follow the chosen
939 copy_base_to_bg (op, w);
940 gtk_container_foreach (GTK_CONTAINER (op), (GtkCallback) copy_base_to_bg,
941 PSPPIRE_OUTPUT_WINDOW (w)->output);
943 /* Chain up to the parent class */
944 GTK_WIDGET_CLASS (parent_class)->style_set (w, prev);
948 psppire_output_window_init (PsppireOutputWindow *window)
950 GtkTreeViewColumn *column;
951 GtkCellRenderer *renderer;
953 GtkAction *copy_action;
954 GtkAction *select_all_action;
955 GtkTreeSelection *sel;
958 string_map_init (&window->render_opts);
960 xml = builder_new ("output-viewer.ui");
962 copy_action = get_action_assert (xml, "edit_copy");
963 select_all_action = get_action_assert (xml, "edit_select-all");
965 gtk_action_set_sensitive (copy_action, FALSE);
967 g_signal_connect_swapped (copy_action, "activate", G_CALLBACK (on_copy), window);
969 g_signal_connect_swapped (select_all_action, "activate", G_CALLBACK (on_select_all), window);
971 gtk_widget_reparent (get_widget_assert (xml, "vbox1"), GTK_WIDGET (window));
973 window->output = GTK_LAYOUT (get_widget_assert (xml, "output"));
975 window->print_settings = NULL;
976 window->dispose_has_run = FALSE;
978 window->overview = GTK_TREE_VIEW (get_widget_assert (xml, "overview"));
980 sel = gtk_tree_view_get_selection (window->overview);
982 gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE);
984 g_signal_connect (sel, "changed", G_CALLBACK (on_selection_change), copy_action);
986 model = GTK_TREE_MODEL (gtk_tree_store_new (
988 G_TYPE_STRING, /* COL_TITLE */
989 G_TYPE_POINTER, /* COL_ADDR */
990 G_TYPE_LONG)); /* COL_Y */
991 gtk_tree_view_set_model (window->overview, model);
992 g_object_unref (model);
994 window->in_command = false;
996 window->items = NULL;
997 window->n_items = window->allocated_items = 0;
999 column = gtk_tree_view_column_new ();
1000 gtk_tree_view_append_column (GTK_TREE_VIEW (window->overview), column);
1001 renderer = gtk_cell_renderer_text_new ();
1002 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1003 gtk_tree_view_column_add_attribute (column, renderer, "text", COL_TITLE);
1005 g_signal_connect (GTK_TREE_VIEW (window->overview),
1006 "row-activated", G_CALLBACK (on_row_activate), window);
1010 g_signal_connect (window,
1012 G_CALLBACK (cancel_urgency),
1015 g_signal_connect (get_action_assert (xml,"windows_minimise-all"),
1017 G_CALLBACK (psppire_window_minimise_all),
1022 GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (xml, "uimanager1", GTK_TYPE_UI_MANAGER));
1023 merge_help_menu (uim);
1025 w = gtk_ui_manager_get_widget (uim,"/ui/menubar/windows_menuitem/windows_minimise-all");
1027 PSPPIRE_WINDOW (window)->menu =
1028 GTK_MENU_SHELL (gtk_widget_get_parent (w));
1031 g_signal_connect_swapped (get_action_assert (xml, "file_export"), "activate",
1032 G_CALLBACK (psppire_output_window_export), window);
1035 g_signal_connect_swapped (get_action_assert (xml, "file_print"), "activate",
1036 G_CALLBACK (psppire_output_window_print), window);
1038 g_object_unref (xml);
1040 g_signal_connect (window, "delete-event",
1041 G_CALLBACK (on_delete), window);
1046 psppire_output_window_new (void)
1048 return GTK_WIDGET (g_object_new (psppire_output_window_get_type (),
1049 /* TRANSLATORS: This will form a filename. Please avoid whitespace. */
1050 "filename", _("Output"),
1051 "description", _("Output Viewer"),
1058 get_cairo_context_from_print_context (GtkPrintContext *context)
1060 cairo_t *cr = gtk_print_context_get_cairo_context (context);
1063 For all platforms except windows, gtk_print_context_get_dpi_[xy] returns 72.
1064 Windows returns 600.
1066 double xres = gtk_print_context_get_dpi_x (context);
1067 double yres = gtk_print_context_get_dpi_y (context);
1069 /* This means that the cairo context now has its dimensions in Points */
1070 cairo_scale (cr, xres / 72.0, yres / 72.0);
1077 create_xr_print_driver (GtkPrintContext *context, PsppireOutputWindow *window)
1079 struct string_map options;
1080 GtkPageSetup *page_setup;
1081 double width, height;
1083 double right_margin;
1085 double bottom_margin;
1087 page_setup = gtk_print_context_get_page_setup (context);
1088 width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_MM);
1089 height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_MM);
1090 left_margin = gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_MM);
1091 right_margin = gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_MM);
1092 top_margin = gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_MM);
1093 bottom_margin = gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_MM);
1095 string_map_init (&options);
1096 string_map_insert_nocopy (&options, xstrdup ("paper-size"),
1097 c_xasprintf("%.2fx%.2fmm", width, height));
1098 string_map_insert_nocopy (&options, xstrdup ("left-margin"),
1099 c_xasprintf ("%.2fmm", left_margin));
1100 string_map_insert_nocopy (&options, xstrdup ("right-margin"),
1101 c_xasprintf ("%.2fmm", right_margin));
1102 string_map_insert_nocopy (&options, xstrdup ("top-margin"),
1103 c_xasprintf ("%.2fmm", top_margin));
1104 string_map_insert_nocopy (&options, xstrdup ("bottom-margin"),
1105 c_xasprintf ("%.2fmm", bottom_margin));
1107 window->print_xrd = xr_driver_create (get_cairo_context_from_print_context (context), &options);
1109 string_map_destroy (&options);
1113 paginate (GtkPrintOperation *operation,
1114 GtkPrintContext *context,
1115 PsppireOutputWindow *window)
1117 if (window->paginated)
1119 /* Sometimes GTK+ emits this signal again even after pagination is
1120 complete. Don't let that screw up printing. */
1123 else if ( window->print_item < window->n_items )
1125 xr_driver_output_item (window->print_xrd, window->items[window->print_item++]);
1126 while (xr_driver_need_new_page (window->print_xrd))
1128 xr_driver_next_page (window->print_xrd, NULL);
1129 window->print_n_pages ++;
1135 gtk_print_operation_set_n_pages (operation, window->print_n_pages);
1137 /* Re-create the driver to do the real printing. */
1138 xr_driver_destroy (window->print_xrd);
1139 create_xr_print_driver (context, window);
1140 window->print_item = 0;
1141 window->paginated = TRUE;
1148 begin_print (GtkPrintOperation *operation,
1149 GtkPrintContext *context,
1150 PsppireOutputWindow *window)
1152 create_xr_print_driver (context, window);
1154 window->print_item = 0;
1155 window->print_n_pages = 1;
1156 window->paginated = FALSE;
1160 end_print (GtkPrintOperation *operation,
1161 GtkPrintContext *context,
1162 PsppireOutputWindow *window)
1164 xr_driver_destroy (window->print_xrd);
1169 draw_page (GtkPrintOperation *operation,
1170 GtkPrintContext *context,
1172 PsppireOutputWindow *window)
1174 xr_driver_next_page (window->print_xrd, get_cairo_context_from_print_context (context));
1175 while (!xr_driver_need_new_page (window->print_xrd)
1176 && window->print_item < window->n_items)
1177 xr_driver_output_item (window->print_xrd, window->items [window->print_item++]);
1182 psppire_output_window_print (PsppireOutputWindow *window)
1184 GtkPrintOperationResult res;
1186 GtkPrintOperation *print = gtk_print_operation_new ();
1188 if (window->print_settings != NULL)
1189 gtk_print_operation_set_print_settings (print, window->print_settings);
1191 g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), window);
1192 g_signal_connect (print, "end_print", G_CALLBACK (end_print), window);
1193 g_signal_connect (print, "paginate", G_CALLBACK (paginate), window);
1194 g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), window);
1196 res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
1197 GTK_WINDOW (window), NULL);
1199 if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
1201 if (window->print_settings != NULL)
1202 g_object_unref (window->print_settings);
1203 window->print_settings = g_object_ref (gtk_print_operation_get_print_settings (print));
1206 g_object_unref (print);