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 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 (gtk_widget_get_window (widget));
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_get_window (GTK_WIDGET (pod->viewer)));
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);
457 min = gtk_adjustment_get_lower (vadj);
458 max = gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj);
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 gint file_type = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
657 gchar *filename = gtk_file_chooser_get_filename (chooser);
658 struct string_map options;
660 g_return_if_fail (filename);
662 if (file_type == FT_AUTO)
664 /* If the "Infer file type from extension" option was chosen,
665 search for the respective type in the list.
666 (It's a O(n) search, but fortunately n is small). */
668 for (i = 1 ; i < N_EXTENSIONS ; ++i)
670 if ( g_str_has_suffix (filename, ft[i].ext))
677 else if (! g_str_has_suffix (filename, ft[file_type].ext))
679 /* If an explicit document format was chosen, and if the chosen
680 filename does not already have that particular "extension",
684 gchar *of = filename;
685 filename = g_strconcat (filename, ft[file_type].ext, NULL);
689 string_map_init (&options);
690 string_map_insert (&options, "output-file", filename);
695 export_output (window, &options, "pdf");
698 export_output (window, &options, "html");
701 export_output (window, &options, "odt");
704 export_output (window, &options, "ps");
707 export_output (window, &options, "csv");
711 string_map_insert (&options, "headers", "false");
712 string_map_insert (&options, "paginate", "false");
713 string_map_insert (&options, "squeeze", "true");
714 string_map_insert (&options, "emphasis", "none");
715 string_map_insert (&options, "charts", "none");
716 string_map_insert (&options, "top-margin", "0");
717 string_map_insert (&options, "bottom-margin", "0");
718 export_output (window, &options, "txt");
721 g_assert_not_reached ();
724 string_map_destroy (&options);
729 gtk_widget_destroy (dialog);
741 /* GNU Hurd doesn't have PATH_MAX. Use a fallback.
742 Temporary directory names are usually not that long. */
744 # define PATH_MAX 1024
748 clipboard_get_cb (GtkClipboard *clipboard,
749 GtkSelectionData *selection_data,
753 PsppireOutputWindow *window = data;
757 struct output_driver *driver = NULL;
758 char dirname[PATH_MAX], *filename;
759 struct string_map options;
761 GtkTreeSelection *sel = gtk_tree_view_get_selection (window->overview);
762 GtkTreeModel *model = gtk_tree_view_get_model (window->overview);
764 GList *rows = gtk_tree_selection_get_selected_rows (sel, &model);
770 if (path_search (dirname, sizeof dirname, NULL, NULL, true)
771 || mkdtemp (dirname) == NULL)
773 error (0, errno, _("failed to create temporary directory"));
776 filename = xasprintf ("%s/clip.tmp", dirname);
778 string_map_init (&options);
779 string_map_insert (&options, "output-file", filename);
783 case SELECT_FMT_UTF8:
784 string_map_insert (&options, "box", "unicode");
787 case SELECT_FMT_TEXT:
788 string_map_insert (&options, "format", "txt");
791 case SELECT_FMT_HTML:
792 string_map_insert (&options, "format", "html");
793 string_map_insert (&options, "borders", "false");
794 string_map_insert (&options, "css", "false");
798 string_map_insert (&options, "format", "odt");
802 g_warning ("unsupported clip target\n");
807 driver = output_driver_create (&options);
813 GtkTreePath *path = n->data ;
815 struct output_item *item ;
817 gtk_tree_model_get_iter (model, &iter, path);
818 gtk_tree_model_get (model, &iter, COL_ADDR, &item, -1);
820 driver->class->submit (driver, item);
825 if ( driver->class->flush)
826 driver->class->flush (driver);
829 /* Some drivers (eg: the odt one) don't write anything until they
831 output_driver_destroy (driver);
834 if ( g_file_get_contents (filename, &text, &length, NULL) )
836 gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data),
838 (const guchar *) text, length);
844 output_driver_destroy (driver);
856 clipboard_clear_cb (GtkClipboard *clipboard,
861 static const GtkTargetEntry targets[] = {
863 { "STRING", 0, SELECT_FMT_TEXT },
864 { "TEXT", 0, SELECT_FMT_TEXT },
865 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
866 { "text/plain", 0, SELECT_FMT_TEXT },
868 { "UTF8_STRING", 0, SELECT_FMT_UTF8 },
869 { "text/plain;charset=utf-8", 0, SELECT_FMT_UTF8 },
871 { "text/html", 0, SELECT_FMT_HTML },
873 { "application/vnd.oasis.opendocument.text", 0, SELECT_FMT_ODT }
877 on_copy (PsppireOutputWindow *window)
880 GtkClipboard *clipboard =
881 gtk_widget_get_clipboard (GTK_WIDGET (window),
882 GDK_SELECTION_CLIPBOARD);
884 if (!gtk_clipboard_set_with_data (clipboard, targets,
885 G_N_ELEMENTS (targets),
886 clipboard_get_cb, clipboard_clear_cb,
889 clipboard_clear_cb (clipboard, window);
894 on_selection_change (GtkTreeSelection *sel, GtkAction *copy_action)
896 /* The Copy action is available only if there is something selected */
897 gtk_action_set_sensitive (copy_action, gtk_tree_selection_count_selected_rows (sel) > 0);
901 on_select_all (PsppireOutputWindow *window)
903 GtkTreeSelection *sel = gtk_tree_view_get_selection (window->overview);
904 gtk_tree_view_expand_all (window->overview);
905 gtk_tree_selection_select_all (sel);
910 copy_base_to_bg (GtkWidget *dest, GtkWidget *src)
913 for (i = 0; i < 5; ++i)
915 GdkColor *col = >k_widget_get_style (src)->base[i];
916 gtk_widget_modify_bg (dest, i, col);
918 col = >k_widget_get_style (src)->text[i];
919 gtk_widget_modify_fg (dest, i, col);
924 on_dwgarea_realize (GtkWidget *dwg_area, gpointer data)
926 GtkWidget *viewer = GTK_WIDGET (data);
928 copy_base_to_bg (dwg_area, viewer);
933 psppire_output_window_style_set (GtkWidget *w, GtkStyle *prev)
935 GtkWidget *op = GTK_WIDGET (PSPPIRE_OUTPUT_WINDOW (w)->output);
937 /* Copy the base style from the parent widget to the container and
939 We do this, because the container's primary purpose is to
940 display text. This way psppire appears to follow the chosen
943 copy_base_to_bg (op, w);
944 gtk_container_foreach (GTK_CONTAINER (op), (GtkCallback) copy_base_to_bg,
945 PSPPIRE_OUTPUT_WINDOW (w)->output);
947 /* Chain up to the parent class */
948 GTK_WIDGET_CLASS (parent_class)->style_set (w, prev);
952 psppire_output_window_init (PsppireOutputWindow *window)
954 GtkTreeViewColumn *column;
955 GtkCellRenderer *renderer;
957 GtkAction *copy_action;
958 GtkAction *select_all_action;
959 GtkTreeSelection *sel;
962 string_map_init (&window->render_opts);
964 xml = builder_new ("output-viewer.ui");
966 copy_action = get_action_assert (xml, "edit_copy");
967 select_all_action = get_action_assert (xml, "edit_select-all");
969 gtk_action_set_sensitive (copy_action, FALSE);
971 g_signal_connect_swapped (copy_action, "activate", G_CALLBACK (on_copy), window);
973 g_signal_connect_swapped (select_all_action, "activate", G_CALLBACK (on_select_all), window);
975 gtk_widget_reparent (get_widget_assert (xml, "vbox1"), GTK_WIDGET (window));
977 window->output = GTK_LAYOUT (get_widget_assert (xml, "output"));
979 window->print_settings = NULL;
980 window->dispose_has_run = FALSE;
982 window->overview = GTK_TREE_VIEW (get_widget_assert (xml, "overview"));
984 sel = gtk_tree_view_get_selection (window->overview);
986 gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE);
988 g_signal_connect (sel, "changed", G_CALLBACK (on_selection_change), copy_action);
990 model = GTK_TREE_MODEL (gtk_tree_store_new (
992 G_TYPE_STRING, /* COL_TITLE */
993 G_TYPE_POINTER, /* COL_ADDR */
994 G_TYPE_LONG)); /* COL_Y */
995 gtk_tree_view_set_model (window->overview, model);
996 g_object_unref (model);
998 window->in_command = false;
1000 window->items = NULL;
1001 window->n_items = window->allocated_items = 0;
1003 column = gtk_tree_view_column_new ();
1004 gtk_tree_view_append_column (GTK_TREE_VIEW (window->overview), column);
1005 renderer = gtk_cell_renderer_text_new ();
1006 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1007 gtk_tree_view_column_add_attribute (column, renderer, "text", COL_TITLE);
1009 g_signal_connect (GTK_TREE_VIEW (window->overview),
1010 "row-activated", G_CALLBACK (on_row_activate), window);
1014 g_signal_connect (window,
1016 G_CALLBACK (cancel_urgency),
1019 g_signal_connect (get_action_assert (xml,"windows_minimise-all"),
1021 G_CALLBACK (psppire_window_minimise_all),
1026 GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (xml, "uimanager1", GTK_TYPE_UI_MANAGER));
1027 merge_help_menu (uim);
1029 w = gtk_ui_manager_get_widget (uim,"/ui/menubar/windows_menuitem/windows_minimise-all");
1031 PSPPIRE_WINDOW (window)->menu =
1032 GTK_MENU_SHELL (gtk_widget_get_parent (w));
1035 g_signal_connect_swapped (get_action_assert (xml, "file_export"), "activate",
1036 G_CALLBACK (psppire_output_window_export), window);
1039 g_signal_connect_swapped (get_action_assert (xml, "file_print"), "activate",
1040 G_CALLBACK (psppire_output_window_print), window);
1042 g_object_unref (xml);
1044 g_signal_connect (window, "delete-event",
1045 G_CALLBACK (on_delete), window);
1050 psppire_output_window_new (void)
1052 return GTK_WIDGET (g_object_new (psppire_output_window_get_type (),
1053 /* TRANSLATORS: This will form a filename. Please avoid whitespace. */
1054 "filename", _("Output"),
1055 "description", _("Output Viewer"),
1062 get_cairo_context_from_print_context (GtkPrintContext *context)
1064 cairo_t *cr = gtk_print_context_get_cairo_context (context);
1067 For all platforms except windows, gtk_print_context_get_dpi_[xy] returns 72.
1068 Windows returns 600.
1070 double xres = gtk_print_context_get_dpi_x (context);
1071 double yres = gtk_print_context_get_dpi_y (context);
1073 /* This means that the cairo context now has its dimensions in Points */
1074 cairo_scale (cr, xres / 72.0, yres / 72.0);
1081 create_xr_print_driver (GtkPrintContext *context, PsppireOutputWindow *window)
1083 struct string_map options;
1084 GtkPageSetup *page_setup;
1085 double width, height;
1087 double right_margin;
1089 double bottom_margin;
1091 page_setup = gtk_print_context_get_page_setup (context);
1092 width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_MM);
1093 height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_MM);
1094 left_margin = gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_MM);
1095 right_margin = gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_MM);
1096 top_margin = gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_MM);
1097 bottom_margin = gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_MM);
1099 string_map_init (&options);
1100 string_map_insert_nocopy (&options, xstrdup ("paper-size"),
1101 c_xasprintf("%.2fx%.2fmm", width, height));
1102 string_map_insert_nocopy (&options, xstrdup ("left-margin"),
1103 c_xasprintf ("%.2fmm", left_margin));
1104 string_map_insert_nocopy (&options, xstrdup ("right-margin"),
1105 c_xasprintf ("%.2fmm", right_margin));
1106 string_map_insert_nocopy (&options, xstrdup ("top-margin"),
1107 c_xasprintf ("%.2fmm", top_margin));
1108 string_map_insert_nocopy (&options, xstrdup ("bottom-margin"),
1109 c_xasprintf ("%.2fmm", bottom_margin));
1111 window->print_xrd = xr_driver_create (get_cairo_context_from_print_context (context), &options);
1113 string_map_destroy (&options);
1117 paginate (GtkPrintOperation *operation,
1118 GtkPrintContext *context,
1119 PsppireOutputWindow *window)
1121 if (window->paginated)
1123 /* Sometimes GTK+ emits this signal again even after pagination is
1124 complete. Don't let that screw up printing. */
1127 else if ( window->print_item < window->n_items )
1129 xr_driver_output_item (window->print_xrd, window->items[window->print_item++]);
1130 while (xr_driver_need_new_page (window->print_xrd))
1132 xr_driver_next_page (window->print_xrd, NULL);
1133 window->print_n_pages ++;
1139 gtk_print_operation_set_n_pages (operation, window->print_n_pages);
1141 /* Re-create the driver to do the real printing. */
1142 xr_driver_destroy (window->print_xrd);
1143 create_xr_print_driver (context, window);
1144 window->print_item = 0;
1145 window->paginated = TRUE;
1152 begin_print (GtkPrintOperation *operation,
1153 GtkPrintContext *context,
1154 PsppireOutputWindow *window)
1156 create_xr_print_driver (context, window);
1158 window->print_item = 0;
1159 window->print_n_pages = 1;
1160 window->paginated = FALSE;
1164 end_print (GtkPrintOperation *operation,
1165 GtkPrintContext *context,
1166 PsppireOutputWindow *window)
1168 xr_driver_destroy (window->print_xrd);
1173 draw_page (GtkPrintOperation *operation,
1174 GtkPrintContext *context,
1176 PsppireOutputWindow *window)
1178 xr_driver_next_page (window->print_xrd, get_cairo_context_from_print_context (context));
1179 while (!xr_driver_need_new_page (window->print_xrd)
1180 && window->print_item < window->n_items)
1181 xr_driver_output_item (window->print_xrd, window->items [window->print_item++]);
1186 psppire_output_window_print (PsppireOutputWindow *window)
1188 GtkPrintOperationResult res;
1190 GtkPrintOperation *print = gtk_print_operation_new ();
1192 if (window->print_settings != NULL)
1193 gtk_print_operation_set_print_settings (print, window->print_settings);
1195 g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), window);
1196 g_signal_connect (print, "end_print", G_CALLBACK (end_print), window);
1197 g_signal_connect (print, "paginate", G_CALLBACK (paginate), window);
1198 g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), window);
1200 res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
1201 GTK_WINDOW (window), NULL);
1203 if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
1205 if (window->print_settings != NULL)
1206 g_object_unref (window->print_settings);
1207 window->print_settings = g_object_ref (gtk_print_operation_get_print_settings (print));
1210 g_object_unref (print);