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"
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_base_finalize (PsppireOutputWindowClass *, gpointer);
60 static void psppire_output_window_base_init (PsppireOutputWindowClass *class);
61 static void psppire_output_window_class_init (PsppireOutputWindowClass *class);
62 static void psppire_output_window_init (PsppireOutputWindow *window);
64 static void psppire_output_window_style_set (GtkWidget *window, GtkStyle *prev);
68 psppire_output_window_get_type (void)
70 static GType psppire_output_window_type = 0;
72 if (!psppire_output_window_type)
74 static const GTypeInfo psppire_output_window_info =
76 sizeof (PsppireOutputWindowClass),
77 (GBaseInitFunc) psppire_output_window_base_init,
78 (GBaseFinalizeFunc) psppire_output_window_base_finalize,
79 (GClassInitFunc)psppire_output_window_class_init,
80 (GClassFinalizeFunc) NULL,
82 sizeof (PsppireOutputWindow),
84 (GInstanceInitFunc) psppire_output_window_init,
87 psppire_output_window_type =
88 g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireOutputWindow",
89 &psppire_output_window_info, 0);
92 return psppire_output_window_type;
95 static GObjectClass *parent_class;
98 psppire_output_window_finalize (GObject *object)
100 string_map_destroy (&PSPPIRE_OUTPUT_WINDOW(object)->render_opts);
103 if (G_OBJECT_CLASS (parent_class)->finalize)
104 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
109 psppire_output_window_dispose (GObject *obj)
111 PsppireOutputWindow *viewer = PSPPIRE_OUTPUT_WINDOW (obj);
114 for (i = 0; i < viewer->n_items; i++)
115 output_item_unref (viewer->items[i]);
116 free (viewer->items);
117 viewer->items = NULL;
118 viewer->n_items = viewer->allocated_items = 0;
120 if (viewer->print_settings != NULL)
121 g_object_unref (viewer->print_settings);
123 /* Chain up to the parent class */
124 G_OBJECT_CLASS (parent_class)->dispose (obj);
128 psppire_output_window_class_init (PsppireOutputWindowClass *class)
130 GObjectClass *object_class = G_OBJECT_CLASS (class);
132 parent_class = g_type_class_peek_parent (class);
133 object_class->dispose = psppire_output_window_dispose;
135 GTK_WIDGET_CLASS (object_class)->style_set = psppire_output_window_style_set;
140 psppire_output_window_base_init (PsppireOutputWindowClass *class)
142 GObjectClass *object_class = G_OBJECT_CLASS (class);
144 object_class->finalize = psppire_output_window_finalize;
150 psppire_output_window_base_finalize (PsppireOutputWindowClass *class,
155 /* Output driver class. */
157 struct psppire_output_driver
159 struct output_driver driver;
160 PsppireOutputWindow *viewer;
161 struct xr_driver *xr;
165 static struct output_driver_class psppire_output_class;
167 static struct psppire_output_driver *
168 psppire_output_cast (struct output_driver *driver)
170 assert (driver->class == &psppire_output_class);
171 return UP_CAST (driver, struct psppire_output_driver, driver);
174 static void on_dwgarea_realize (GtkWidget *widget, gpointer data);
177 expose_event_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
179 PsppireOutputWindow *viewer = PSPPIRE_OUTPUT_WINDOW (data);
180 struct xr_rendering *r = g_object_get_data (G_OBJECT (widget), "rendering");
181 cairo_t *cr = gdk_cairo_create (widget->window);
183 const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (viewer));
185 PangoFontDescription *font_desc;
189 gdk_color_to_string (&style->text[gtk_widget_get_state (GTK_WIDGET (widget))]);
191 string_map_replace (&viewer->render_opts, "foreground-color", fgc);
195 /* Use GTK+ default font as proportional font. */
196 font_name = pango_font_description_to_string (style->font_desc);
197 string_map_replace (&viewer->render_opts, "prop-font", font_name);
200 /* Derived emphasized font from proportional font. */
201 font_desc = pango_font_description_copy (style->font_desc);
202 pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
203 font_name = pango_font_description_to_string (font_desc);
204 string_map_replace (&viewer->render_opts, "emph-font", font_name);
206 pango_font_description_free (font_desc);
208 xr_rendering_apply_options (r, &viewer->render_opts);
210 xr_rendering_draw (r, cr, event->area.x, event->area.y,
211 event->area.width, event->area.height);
218 psppire_output_submit (struct output_driver *this,
219 const struct output_item *item)
221 struct psppire_output_driver *pod = psppire_output_cast (this);
222 PsppireOutputWindow *viewer;
223 GtkWidget *drawing_area;
224 struct xr_rendering *r;
232 if (pod->viewer == NULL)
234 pod->viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
235 gtk_widget_show_all (GTK_WIDGET (pod->viewer));
236 pod->viewer->driver = pod;
238 viewer = pod->viewer;
240 if (viewer->n_items >= viewer->allocated_items)
241 viewer->items = x2nrealloc (viewer->items, &viewer->allocated_items,
242 sizeof *viewer->items);
243 viewer->items[viewer->n_items++] = output_item_ref (item);
245 if (is_text_item (item))
247 const struct text_item *text_item = to_text_item (item);
248 enum text_item_type type = text_item_get_type (text_item);
249 const char *text = text_item_get_text (text_item);
251 if (type == TEXT_ITEM_COMMAND_CLOSE)
253 viewer->in_command = false;
256 else if (text[0] == '\0')
260 cr = gdk_cairo_create (GTK_WIDGET (pod->viewer)->window);
263 const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (viewer));
264 struct text_item *text_item;
265 PangoFontDescription *font_desc;
269 /* Set the widget's text color as the foreground color for the output driver */
270 gchar *fgc = gdk_color_to_string (&style->text[gtk_widget_get_state (GTK_WIDGET (viewer))]);
272 string_map_insert (&pod->viewer->render_opts, "foreground-color", fgc);
275 /* Use GTK+ default font as proportional font. */
276 font_name = pango_font_description_to_string (style->font_desc);
277 string_map_insert (&pod->viewer->render_opts, "prop-font", font_name);
280 /* Derived emphasized font from proportional font. */
281 font_desc = pango_font_description_copy (style->font_desc);
282 pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
283 font_name = pango_font_description_to_string (font_desc);
284 string_map_insert (&pod->viewer->render_opts, "emph-font", font_name);
286 pango_font_description_free (font_desc);
288 /* Pretend that the "page" has a reasonable width and a very big length,
289 so that most tables can be conveniently viewed on-screen with vertical
290 scrolling only. (The length should not be increased very much because
291 it is already close enough to INT_MAX when expressed as thousands of a
293 string_map_insert (&pod->viewer->render_opts, "paper-size", "300x200000mm");
294 string_map_insert (&pod->viewer->render_opts, "left-margin", "0");
295 string_map_insert (&pod->viewer->render_opts, "right-margin", "0");
296 string_map_insert (&pod->viewer->render_opts, "top-margin", "0");
297 string_map_insert (&pod->viewer->render_opts, "bottom-margin", "0");
299 pod->xr = xr_driver_create (cr, &pod->viewer->render_opts);
302 text_item = text_item_create (TEXT_ITEM_PARAGRAPH, "X");
303 r = xr_rendering_create (pod->xr, text_item_super (text_item), cr);
304 xr_rendering_measure (r, &font_width, &pod->font_height);
305 /* xr_rendering_destroy (r); */
306 text_item_unref (text_item);
309 pod->viewer->y += pod->font_height / 2;
311 r = xr_rendering_create (pod->xr, item, cr);
315 xr_rendering_measure (r, &tw, &th);
317 drawing_area = gtk_drawing_area_new ();
319 g_object_set_data (G_OBJECT (drawing_area), "rendering", r);
320 g_signal_connect (drawing_area, "realize",
321 G_CALLBACK (on_dwgarea_realize), pod->viewer);
323 g_signal_connect (drawing_area, "expose_event",
324 G_CALLBACK (expose_event_callback), pod->viewer);
326 gtk_widget_set_size_request (drawing_area, tw, th);
327 gtk_layout_put (pod->viewer->output, drawing_area, 0, pod->viewer->y);
329 gtk_widget_show (drawing_area);
331 if (!is_text_item (item)
332 || text_item_get_type (to_text_item (item)) != TEXT_ITEM_SYNTAX
333 || !viewer->in_command)
335 store = GTK_TREE_STORE (gtk_tree_view_get_model (viewer->overview));
337 ds_init_empty (&title);
338 if (is_text_item (item)
339 && text_item_get_type (to_text_item (item)) == TEXT_ITEM_COMMAND_OPEN)
341 gtk_tree_store_append (store, &iter, NULL);
342 viewer->cur_command = iter; /* XXX shouldn't save a GtkTreeIter */
343 viewer->in_command = true;
347 GtkTreeIter *p = viewer->in_command ? &viewer->cur_command : NULL;
348 gtk_tree_store_append (store, &iter, p);
352 if (is_text_item (item))
353 ds_put_cstr (&title, text_item_get_text (to_text_item (item)));
354 else if (is_message_item (item))
356 const struct message_item *msg_item = to_message_item (item);
357 const struct msg *msg = message_item_get_msg (msg_item);
358 ds_put_format (&title, "%s: %s", _("Message"),
359 msg_severity_to_string (msg->severity));
361 else if (is_table_item (item))
363 const char *caption = table_item_get_caption (to_table_item (item));
365 ds_put_format (&title, "Table: %s", caption);
367 ds_put_cstr (&title, "Table");
369 else if (is_chart_item (item))
371 const char *s = chart_item_get_title (to_chart_item (item));
373 ds_put_format (&title, "Chart: %s", s);
375 ds_put_cstr (&title, "Chart");
377 gtk_tree_store_set (store, &iter,
378 COL_TITLE, ds_cstr (&title),
384 path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
385 gtk_tree_view_expand_row (viewer->overview, path, TRUE);
386 gtk_tree_path_free (path);
389 if (pod->viewer->max_width < tw)
390 pod->viewer->max_width = tw;
391 pod->viewer->y += th;
393 gtk_layout_set_size (pod->viewer->output,
394 pod->viewer->max_width, pod->viewer->y);
396 gtk_window_set_urgency_hint (GTK_WINDOW (pod->viewer), TRUE);
402 static struct output_driver_class psppire_output_class =
404 "PSPPIRE", /* name */
406 psppire_output_submit, /* submit */
411 psppire_output_window_setup (void)
413 struct psppire_output_driver *pod;
414 struct output_driver *d;
416 pod = xzalloc (sizeof *pod);
418 output_driver_init (d, &psppire_output_class, "PSPPIRE",
419 SETTINGS_DEVICE_UNFILTERED);
420 output_driver_register (d);
425 /* Callback for the "delete" action (clicking the x on the top right
426 hand corner of the window) */
428 on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
430 PsppireOutputWindow *ow = PSPPIRE_OUTPUT_WINDOW (user_data);
432 gtk_widget_destroy (GTK_WIDGET (ow));
434 ow->driver->viewer = NULL;
442 cancel_urgency (GtkWindow *window, gpointer data)
444 gtk_window_set_urgency_hint (window, FALSE);
448 on_row_activate (GtkTreeView *overview,
450 GtkTreeViewColumn *column,
451 PsppireOutputWindow *window)
459 model = gtk_tree_view_get_model (overview);
460 if (!gtk_tree_model_get_iter (model, &iter, path))
463 gtk_tree_model_get_value (model, &iter, COL_Y, &value);
464 y = g_value_get_long (&value);
465 g_value_unset (&value);
467 vadj = gtk_layout_get_vadjustment (window->output);
469 max = vadj->upper - vadj->page_size;
474 gtk_adjustment_set_value (vadj, y);
477 static void psppire_output_window_print (PsppireOutputWindow *window);
481 export_output (PsppireOutputWindow *window, struct string_map *options,
484 struct output_driver *driver;
487 string_map_insert (options, "format", format);
488 driver = output_driver_create (options);
492 for (i = 0; i < window->n_items; i++)
493 driver->class->submit (driver, window->items[i]);
494 output_driver_destroy (driver);
516 #define N_EXTENSIONS (n_FT - 1)
518 struct file_types ft[n_FT] = {
519 {N_("Infer file type from extension"), NULL},
520 {N_("PDF (*.pdf)"), ".pdf"},
521 {N_("HTML (*.html)"), ".html"},
522 {N_("OpenDocument (*.odt)"), ".odt"},
523 {N_("Text (*.txt)"), ".txt"},
524 {N_("PostScript (*.ps)"), ".ps"},
525 {N_("Comma-Separated Values (*.csv)"), ".csv"}
530 on_combo_change (GtkFileChooser *chooser)
532 gboolean sensitive = FALSE;
533 GtkWidget *combo = gtk_file_chooser_get_extra_widget (chooser);
536 gchar *fn = gtk_file_chooser_get_filename (chooser);
538 if (combo && gtk_widget_get_realized (combo))
539 x = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
551 for (i = 1 ; i < N_EXTENSIONS ; ++i)
553 if ( g_str_has_suffix (fn, ft[i].ext))
563 gtk_dialog_set_response_sensitive (GTK_DIALOG (chooser), GTK_RESPONSE_ACCEPT, sensitive);
568 on_file_chooser_change (GObject *w, GParamSpec *pspec, gpointer data)
571 GtkFileChooser *chooser = data;
572 const gchar *name = g_param_spec_get_name (pspec);
574 if ( ! gtk_widget_get_realized (GTK_WIDGET (chooser)))
577 /* Ignore this one. It causes recursion. */
578 if ( 0 == strcmp ("tooltip-text", name))
581 on_combo_change (chooser);
585 /* Recursively descend all the children of W, connecting
586 to their "notify" signal */
588 iterate_widgets (GtkWidget *w, gpointer data)
590 if ( GTK_IS_CONTAINER (w))
591 gtk_container_forall (GTK_CONTAINER (w), iterate_widgets, data);
593 g_signal_connect (w, "notify", G_CALLBACK (on_file_chooser_change), data);
598 static GtkListStore *
599 create_file_type_list (void)
603 GtkListStore *list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
605 for (i = 0 ; i < n_FT ; ++i)
607 gtk_list_store_append (list, &iter);
608 gtk_list_store_set (list, &iter,
609 0, gettext (ft[i].label),
618 psppire_output_window_export (PsppireOutputWindow *window)
624 GtkFileChooser *chooser;
626 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Export Output"),
628 GTK_FILE_CHOOSER_ACTION_SAVE,
629 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
630 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
633 g_object_set (dialog, "local-only", FALSE, NULL);
635 chooser = GTK_FILE_CHOOSER (dialog);
637 list = create_file_type_list ();
639 combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list));
643 /* Create text cell renderer */
644 GtkCellRenderer *cell = gtk_cell_renderer_text_new();
645 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE );
647 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cell, "text", 0);
650 g_signal_connect_swapped (combo, "changed", G_CALLBACK (on_combo_change), chooser);
652 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
654 gtk_file_chooser_set_extra_widget (chooser, combo);
656 /* This kludge is necessary because there is no signal to tell us
657 when the candidate filename of a GtkFileChooser has changed */
658 gtk_container_forall (GTK_CONTAINER (dialog), iterate_widgets, dialog);
661 gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);
663 response = gtk_dialog_run (GTK_DIALOG (dialog));
665 if ( response == GTK_RESPONSE_ACCEPT )
667 int file_type = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
668 char *filename = gtk_file_chooser_get_filename (chooser);
669 struct string_map options;
671 g_return_if_fail (filename);
673 if (file_type == FT_AUTO)
676 for (i = 1 ; i < N_EXTENSIONS ; ++i)
678 if ( g_str_has_suffix (filename, ft[i].ext))
687 string_map_init (&options);
688 string_map_insert (&options, "output-file", filename);
693 export_output (window, &options, "pdf");
696 export_output (window, &options, "html");
699 export_output (window, &options, "odt");
702 export_output (window, &options, "ps");
705 export_output (window, &options, "csv");
709 string_map_insert (&options, "headers", "false");
710 string_map_insert (&options, "paginate", "false");
711 string_map_insert (&options, "squeeze", "true");
712 string_map_insert (&options, "emphasis", "none");
713 string_map_insert (&options, "charts", "none");
714 string_map_insert (&options, "top-margin", "0");
715 string_map_insert (&options, "bottom-margin", "0");
716 export_output (window, &options, "txt");
719 g_assert_not_reached ();
722 string_map_destroy (&options);
727 gtk_widget_destroy (dialog);
740 clipboard_get_cb (GtkClipboard *clipboard,
741 GtkSelectionData *selection_data,
745 PsppireOutputWindow *window = data;
749 struct output_driver *driver = NULL;
750 char dirname[PATH_MAX], *filename;
751 struct string_map options;
753 GtkTreeSelection *sel = gtk_tree_view_get_selection (window->overview);
754 GtkTreeModel *model = gtk_tree_view_get_model (window->overview);
756 GList *rows = gtk_tree_selection_get_selected_rows (sel, &model);
762 if (path_search (dirname, sizeof dirname, NULL, NULL, true)
763 || mkdtemp (dirname) == NULL)
765 error (0, errno, _("failed to create temporary directory"));
768 filename = xasprintf ("%s/clip.tmp", dirname);
770 string_map_init (&options);
771 string_map_insert (&options, "output-file", filename);
775 case SELECT_FMT_UTF8:
776 string_map_insert (&options, "box", "unicode");
779 case SELECT_FMT_TEXT:
780 string_map_insert (&options, "format", "txt");
783 case SELECT_FMT_HTML:
784 string_map_insert (&options, "format", "html");
785 string_map_insert (&options, "borders", "false");
786 string_map_insert (&options, "css", "false");
790 string_map_insert (&options, "format", "odt");
794 g_warning ("unsupported clip target\n");
799 driver = output_driver_create (&options);
805 GtkTreePath *path = n->data ;
807 struct output_item *item ;
809 gtk_tree_model_get_iter (model, &iter, path);
810 gtk_tree_model_get (model, &iter, COL_ADDR, &item, -1);
812 driver->class->submit (driver, item);
817 if ( driver->class->flush)
818 driver->class->flush (driver);
821 /* Some drivers (eg: the odt one) don't write anything until they
823 output_driver_destroy (driver);
826 if ( g_file_get_contents (filename, &text, &length, NULL) )
828 gtk_selection_data_set (selection_data, selection_data->target,
830 (const guchar *) text, length);
836 output_driver_destroy (driver);
848 clipboard_clear_cb (GtkClipboard *clipboard,
853 static const GtkTargetEntry targets[] = {
855 { "STRING", 0, SELECT_FMT_TEXT },
856 { "TEXT", 0, SELECT_FMT_TEXT },
857 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
858 { "text/plain", 0, SELECT_FMT_TEXT },
860 { "UTF8_STRING", 0, SELECT_FMT_UTF8 },
861 { "text/plain;charset=utf-8", 0, SELECT_FMT_UTF8 },
863 { "text/html", 0, SELECT_FMT_HTML },
865 { "application/vnd.oasis.opendocument.text", 0, SELECT_FMT_ODT }
869 on_copy (PsppireOutputWindow *window)
872 GtkClipboard *clipboard =
873 gtk_widget_get_clipboard (GTK_WIDGET (window),
874 GDK_SELECTION_CLIPBOARD);
876 if (!gtk_clipboard_set_with_data (clipboard, targets,
877 G_N_ELEMENTS (targets),
878 clipboard_get_cb, clipboard_clear_cb,
881 clipboard_clear_cb (clipboard, window);
886 on_selection_change (GtkTreeSelection *sel, GtkAction *copy_action)
888 /* The Copy action is available only if there is something selected */
889 gtk_action_set_sensitive (copy_action, gtk_tree_selection_count_selected_rows (sel) > 0);
893 on_select_all (PsppireOutputWindow *window)
895 GtkTreeSelection *sel = gtk_tree_view_get_selection (window->overview);
896 gtk_tree_view_expand_all (window->overview);
897 gtk_tree_selection_select_all (sel);
902 copy_base_to_bg (GtkWidget *dest, GtkWidget *src)
905 for (i = 0; i < 5; ++i)
907 GdkColor *col = >k_widget_get_style (src)->base[i];
908 gtk_widget_modify_bg (dest, i, col);
910 col = >k_widget_get_style (src)->text[i];
911 gtk_widget_modify_fg (dest, i, col);
916 on_dwgarea_realize (GtkWidget *dwg_area, gpointer data)
918 GtkWidget *viewer = GTK_WIDGET (data);
920 copy_base_to_bg (dwg_area, viewer);
925 psppire_output_window_style_set (GtkWidget *w, GtkStyle *prev)
927 GtkWidget *op = GTK_WIDGET (PSPPIRE_OUTPUT_WINDOW (w)->output);
929 /* Copy the base style from the parent widget to the container and
931 We do this, because the container's primary purpose is to
932 display text. This way psppire appears to follow the chosen
935 copy_base_to_bg (op, w);
936 gtk_container_foreach (GTK_CONTAINER (op), (GtkCallback) copy_base_to_bg,
937 PSPPIRE_OUTPUT_WINDOW (w)->output);
939 /* Chain up to the parent class */
940 GTK_WIDGET_CLASS (parent_class)->style_set (w, prev);
944 psppire_output_window_init (PsppireOutputWindow *window)
946 GtkTreeViewColumn *column;
947 GtkCellRenderer *renderer;
949 GtkAction *copy_action;
950 GtkAction *select_all_action;
951 GtkTreeSelection *sel;
953 string_map_init (&window->render_opts);
955 xml = builder_new ("output-viewer.ui");
957 copy_action = get_action_assert (xml, "edit_copy");
958 select_all_action = get_action_assert (xml, "edit_select-all");
960 gtk_action_set_sensitive (copy_action, FALSE);
962 g_signal_connect_swapped (copy_action, "activate", G_CALLBACK (on_copy), window);
964 g_signal_connect_swapped (select_all_action, "activate", G_CALLBACK (on_select_all), window);
966 gtk_widget_reparent (get_widget_assert (xml, "vbox1"), GTK_WIDGET (window));
968 window->output = GTK_LAYOUT (get_widget_assert (xml, "output"));
971 window->overview = GTK_TREE_VIEW (get_widget_assert (xml, "overview"));
973 sel = gtk_tree_view_get_selection (window->overview);
975 gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE);
977 g_signal_connect (sel, "changed", G_CALLBACK (on_selection_change), copy_action);
979 gtk_tree_view_set_model (window->overview,
980 GTK_TREE_MODEL (gtk_tree_store_new (
982 G_TYPE_STRING, /* COL_TITLE */
983 G_TYPE_POINTER, /* COL_ADDR */
984 G_TYPE_LONG))); /* COL_Y */
986 window->in_command = false;
988 window->items = NULL;
989 window->n_items = window->allocated_items = 0;
991 column = gtk_tree_view_column_new ();
992 gtk_tree_view_append_column (GTK_TREE_VIEW (window->overview), column);
993 renderer = gtk_cell_renderer_text_new ();
994 gtk_tree_view_column_pack_start (column, renderer, TRUE);
995 gtk_tree_view_column_add_attribute (column, renderer, "text", COL_TITLE);
997 g_signal_connect (GTK_TREE_VIEW (window->overview),
998 "row-activated", G_CALLBACK (on_row_activate), window);
1002 g_signal_connect (window,
1004 G_CALLBACK (cancel_urgency),
1007 g_signal_connect (get_action_assert (xml,"windows_minimise-all"),
1009 G_CALLBACK (psppire_window_minimise_all),
1013 GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (xml, "uimanager1", GTK_TYPE_UI_MANAGER));
1014 merge_help_menu (uim);
1016 PSPPIRE_WINDOW (window)->menu =
1017 GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows_menuitem/windows_minimise-all")->parent);
1020 g_signal_connect_swapped (get_action_assert (xml, "file_export"), "activate",
1021 G_CALLBACK (psppire_output_window_export), window);
1024 g_signal_connect_swapped (get_action_assert (xml, "file_print"), "activate",
1025 G_CALLBACK (psppire_output_window_print), window);
1027 g_object_unref (xml);
1029 g_signal_connect (window, "delete-event",
1030 G_CALLBACK (on_delete), window);
1035 psppire_output_window_new (void)
1037 return GTK_WIDGET (g_object_new (psppire_output_window_get_type (),
1038 /* TRANSLATORS: This will form a filename. Please avoid whitespace. */
1039 "filename", _("Output"),
1040 "description", _("Output Viewer"),
1047 create_xr_print_driver (GtkPrintContext *context, PsppireOutputWindow *window)
1049 struct string_map options;
1050 GtkPageSetup *page_setup;
1051 double width, height;
1053 double right_margin;
1055 double bottom_margin;
1057 page_setup = gtk_print_context_get_page_setup (context);
1058 width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_MM);
1059 height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_MM);
1060 left_margin = gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_MM);
1061 right_margin = gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_MM);
1062 top_margin = gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_MM);
1063 bottom_margin = gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_MM);
1065 string_map_init (&options);
1066 string_map_insert_nocopy (&options, xstrdup ("paper-size"),
1067 xasprintf("%.2fx%.2fmm", width, height));
1068 string_map_insert_nocopy (&options, xstrdup ("left-margin"),
1069 xasprintf ("%.2fmm", left_margin));
1070 string_map_insert_nocopy (&options, xstrdup ("right-margin"),
1071 xasprintf ("%.2fmm", right_margin));
1072 string_map_insert_nocopy (&options, xstrdup ("top-margin"),
1073 xasprintf ("%.2fmm", top_margin));
1074 string_map_insert_nocopy (&options, xstrdup ("bottom-margin"),
1075 xasprintf ("%.2fmm", bottom_margin));
1078 xr_driver_create (gtk_print_context_get_cairo_context (context), &options);
1080 string_map_destroy (&options);
1084 paginate (GtkPrintOperation *operation,
1085 GtkPrintContext *context,
1086 PsppireOutputWindow *window)
1088 if (window->paginated)
1090 /* Sometimes GTK+ emits this signal again even after pagination is
1091 complete. Don't let that screw up printing. */
1094 else if ( window->print_item < window->n_items )
1096 xr_driver_output_item (window->print_xrd, window->items[window->print_item++]);
1097 while (xr_driver_need_new_page (window->print_xrd))
1099 xr_driver_next_page (window->print_xrd, NULL);
1100 window->print_n_pages ++;
1106 gtk_print_operation_set_n_pages (operation, window->print_n_pages);
1108 /* Re-create the driver to do the real printing. */
1109 xr_driver_destroy (window->print_xrd);
1110 create_xr_print_driver (context, window);
1111 window->print_item = 0;
1112 window->paginated = TRUE;
1119 begin_print (GtkPrintOperation *operation,
1120 GtkPrintContext *context,
1121 PsppireOutputWindow *window)
1123 create_xr_print_driver (context, window);
1125 window->print_item = 0;
1126 window->print_n_pages = 1;
1127 window->paginated = FALSE;
1131 end_print (GtkPrintOperation *operation,
1132 GtkPrintContext *context,
1133 PsppireOutputWindow *window)
1135 xr_driver_destroy (window->print_xrd);
1140 draw_page (GtkPrintOperation *operation,
1141 GtkPrintContext *context,
1143 PsppireOutputWindow *window)
1145 xr_driver_next_page (window->print_xrd, gtk_print_context_get_cairo_context (context));
1146 while (!xr_driver_need_new_page (window->print_xrd)
1147 && window->print_item < window->n_items)
1148 xr_driver_output_item (window->print_xrd, window->items [window->print_item++]);
1153 psppire_output_window_print (PsppireOutputWindow *window)
1155 GtkPrintOperationResult res;
1157 GtkPrintOperation *print = gtk_print_operation_new ();
1159 if (window->print_settings != NULL)
1160 gtk_print_operation_set_print_settings (print, window->print_settings);
1162 g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), window);
1163 g_signal_connect (print, "end_print", G_CALLBACK (end_print), window);
1164 g_signal_connect (print, "paginate", G_CALLBACK (paginate), window);
1165 g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), window);
1167 res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
1168 GTK_WINDOW (window), NULL);
1170 if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
1172 if (window->print_settings != NULL)
1173 g_object_unref (window->print_settings);
1174 window->print_settings = g_object_ref (gtk_print_operation_get_print_settings (print));
1178 g_object_unref (print);