1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011, 2012 Free Software Foundation
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
23 #include <sys/types.h>
26 #include "libpspp/cast.h"
27 #include "libpspp/message.h"
28 #include "libpspp/string-map.h"
29 #include "output/cairo.h"
30 #include "output/chart-item.h"
31 #include "output/driver-provider.h"
32 #include "output/message-item.h"
33 #include "output/output-item.h"
34 #include "output/tab.h"
35 #include "output/table-item.h"
36 #include "output/text-item.h"
37 #include "ui/gui/help-menu.h"
38 #include "ui/gui/builder-wrapper.h"
39 #include "ui/gui/psppire-output-window.h"
42 #include "gl/tmpdir.h"
43 #include "gl/xalloc.h"
44 #include "gl/c-xvasprintf.h"
49 #define _(msgid) gettext (msgid)
50 #define N_(msgid) msgid
54 COL_TITLE, /* Table title. */
55 COL_ADDR, /* Pointer to the table */
56 COL_Y, /* Y position of top of title. */
60 static void psppire_output_window_base_finalize (PsppireOutputWindowClass *, gpointer);
61 static void psppire_output_window_base_init (PsppireOutputWindowClass *class);
62 static void psppire_output_window_class_init (PsppireOutputWindowClass *class);
63 static void psppire_output_window_init (PsppireOutputWindow *window);
65 static void psppire_output_window_style_set (GtkWidget *window, GtkStyle *prev);
69 psppire_output_window_get_type (void)
71 static GType psppire_output_window_type = 0;
73 if (!psppire_output_window_type)
75 static const GTypeInfo psppire_output_window_info =
77 sizeof (PsppireOutputWindowClass),
78 (GBaseInitFunc) psppire_output_window_base_init,
79 (GBaseFinalizeFunc) psppire_output_window_base_finalize,
80 (GClassInitFunc)psppire_output_window_class_init,
81 (GClassFinalizeFunc) NULL,
83 sizeof (PsppireOutputWindow),
85 (GInstanceInitFunc) psppire_output_window_init,
88 psppire_output_window_type =
89 g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireOutputWindow",
90 &psppire_output_window_info, 0);
93 return psppire_output_window_type;
96 static GObjectClass *parent_class;
99 psppire_output_window_finalize (GObject *object)
101 string_map_destroy (&PSPPIRE_OUTPUT_WINDOW(object)->render_opts);
104 if (G_OBJECT_CLASS (parent_class)->finalize)
105 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
110 psppire_output_window_dispose (GObject *obj)
112 PsppireOutputWindow *viewer = PSPPIRE_OUTPUT_WINDOW (obj);
115 for (i = 0; i < viewer->n_items; i++)
116 output_item_unref (viewer->items[i]);
117 free (viewer->items);
118 viewer->items = NULL;
119 viewer->n_items = viewer->allocated_items = 0;
121 if (viewer->print_settings != NULL)
122 g_object_unref (viewer->print_settings);
124 /* Chain up to the parent class */
125 G_OBJECT_CLASS (parent_class)->dispose (obj);
129 psppire_output_window_class_init (PsppireOutputWindowClass *class)
131 GObjectClass *object_class = G_OBJECT_CLASS (class);
133 parent_class = g_type_class_peek_parent (class);
134 object_class->dispose = psppire_output_window_dispose;
136 GTK_WIDGET_CLASS (object_class)->style_set = psppire_output_window_style_set;
141 psppire_output_window_base_init (PsppireOutputWindowClass *class)
143 GObjectClass *object_class = G_OBJECT_CLASS (class);
145 object_class->finalize = psppire_output_window_finalize;
151 psppire_output_window_base_finalize (PsppireOutputWindowClass *class,
156 /* Output driver class. */
158 struct psppire_output_driver
160 struct output_driver driver;
161 PsppireOutputWindow *viewer;
162 struct xr_driver *xr;
166 static struct output_driver_class psppire_output_class;
168 static struct psppire_output_driver *
169 psppire_output_cast (struct output_driver *driver)
171 assert (driver->class == &psppire_output_class);
172 return UP_CAST (driver, struct psppire_output_driver, driver);
175 static void on_dwgarea_realize (GtkWidget *widget, gpointer data);
178 expose_event_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
180 PsppireOutputWindow *viewer = PSPPIRE_OUTPUT_WINDOW (data);
181 struct xr_rendering *r = g_object_get_data (G_OBJECT (widget), "rendering");
182 cairo_t *cr = gdk_cairo_create (widget->window);
184 const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (viewer));
186 PangoFontDescription *font_desc;
190 gdk_color_to_string (&style->text[gtk_widget_get_state (GTK_WIDGET (widget))]);
192 string_map_replace (&viewer->render_opts, "foreground-color", fgc);
196 /* Use GTK+ default font as proportional font. */
197 font_name = pango_font_description_to_string (style->font_desc);
198 string_map_replace (&viewer->render_opts, "prop-font", font_name);
201 /* Derived emphasized font from proportional font. */
202 font_desc = pango_font_description_copy (style->font_desc);
203 pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
204 font_name = pango_font_description_to_string (font_desc);
205 string_map_replace (&viewer->render_opts, "emph-font", font_name);
207 pango_font_description_free (font_desc);
209 xr_rendering_apply_options (r, &viewer->render_opts);
211 xr_rendering_draw (r, cr, event->area.x, event->area.y,
212 event->area.width, event->area.height);
219 psppire_output_submit (struct output_driver *this,
220 const struct output_item *item)
222 struct psppire_output_driver *pod = psppire_output_cast (this);
223 PsppireOutputWindow *viewer;
224 GtkWidget *drawing_area;
225 struct xr_rendering *r;
233 if (pod->viewer == NULL)
235 pod->viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
236 gtk_widget_show_all (GTK_WIDGET (pod->viewer));
237 pod->viewer->driver = pod;
239 viewer = pod->viewer;
241 if (viewer->n_items >= viewer->allocated_items)
242 viewer->items = x2nrealloc (viewer->items, &viewer->allocated_items,
243 sizeof *viewer->items);
244 viewer->items[viewer->n_items++] = output_item_ref (item);
246 if (is_text_item (item))
248 const struct text_item *text_item = to_text_item (item);
249 enum text_item_type type = text_item_get_type (text_item);
250 const char *text = text_item_get_text (text_item);
252 if (type == TEXT_ITEM_COMMAND_CLOSE)
254 viewer->in_command = false;
257 else if (text[0] == '\0')
261 cr = gdk_cairo_create (GTK_WIDGET (pod->viewer)->window);
264 const GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (viewer));
265 struct text_item *text_item;
266 PangoFontDescription *font_desc;
270 /* Set the widget's text color as the foreground color for the output driver */
271 gchar *fgc = gdk_color_to_string (&style->text[gtk_widget_get_state (GTK_WIDGET (viewer))]);
273 string_map_insert (&pod->viewer->render_opts, "foreground-color", fgc);
276 /* Use GTK+ default font as proportional font. */
277 font_name = pango_font_description_to_string (style->font_desc);
278 string_map_insert (&pod->viewer->render_opts, "prop-font", font_name);
281 /* Derived emphasized font from proportional font. */
282 font_desc = pango_font_description_copy (style->font_desc);
283 pango_font_description_set_style (font_desc, PANGO_STYLE_ITALIC);
284 font_name = pango_font_description_to_string (font_desc);
285 string_map_insert (&pod->viewer->render_opts, "emph-font", font_name);
287 pango_font_description_free (font_desc);
289 /* Pretend that the "page" has a reasonable width and a very big length,
290 so that most tables can be conveniently viewed on-screen with vertical
291 scrolling only. (The length should not be increased very much because
292 it is already close enough to INT_MAX when expressed as thousands of a
294 string_map_insert (&pod->viewer->render_opts, "paper-size", "300x200000mm");
295 string_map_insert (&pod->viewer->render_opts, "left-margin", "0");
296 string_map_insert (&pod->viewer->render_opts, "right-margin", "0");
297 string_map_insert (&pod->viewer->render_opts, "top-margin", "0");
298 string_map_insert (&pod->viewer->render_opts, "bottom-margin", "0");
300 pod->xr = xr_driver_create (cr, &pod->viewer->render_opts);
303 text_item = text_item_create (TEXT_ITEM_PARAGRAPH, "X");
304 r = xr_rendering_create (pod->xr, text_item_super (text_item), cr);
305 xr_rendering_measure (r, &font_width, &pod->font_height);
306 /* xr_rendering_destroy (r); */
307 text_item_unref (text_item);
310 pod->viewer->y += pod->font_height / 2;
312 r = xr_rendering_create (pod->xr, item, cr);
316 xr_rendering_measure (r, &tw, &th);
318 drawing_area = gtk_drawing_area_new ();
320 g_object_set_data (G_OBJECT (drawing_area), "rendering", r);
321 g_signal_connect (drawing_area, "realize",
322 G_CALLBACK (on_dwgarea_realize), pod->viewer);
324 g_signal_connect (drawing_area, "expose_event",
325 G_CALLBACK (expose_event_callback), pod->viewer);
327 gtk_widget_set_size_request (drawing_area, tw, th);
328 gtk_layout_put (pod->viewer->output, drawing_area, 0, pod->viewer->y);
330 gtk_widget_show (drawing_area);
332 if (!is_text_item (item)
333 || text_item_get_type (to_text_item (item)) != TEXT_ITEM_SYNTAX
334 || !viewer->in_command)
336 store = GTK_TREE_STORE (gtk_tree_view_get_model (viewer->overview));
338 ds_init_empty (&title);
339 if (is_text_item (item)
340 && text_item_get_type (to_text_item (item)) == TEXT_ITEM_COMMAND_OPEN)
342 gtk_tree_store_append (store, &iter, NULL);
343 viewer->cur_command = iter; /* XXX shouldn't save a GtkTreeIter */
344 viewer->in_command = true;
348 GtkTreeIter *p = viewer->in_command ? &viewer->cur_command : NULL;
349 gtk_tree_store_append (store, &iter, p);
353 if (is_text_item (item))
354 ds_put_cstr (&title, text_item_get_text (to_text_item (item)));
355 else if (is_message_item (item))
357 const struct message_item *msg_item = to_message_item (item);
358 const struct msg *msg = message_item_get_msg (msg_item);
359 ds_put_format (&title, "%s: %s", _("Message"),
360 msg_severity_to_string (msg->severity));
362 else if (is_table_item (item))
364 const char *caption = table_item_get_caption (to_table_item (item));
366 ds_put_format (&title, "Table: %s", caption);
368 ds_put_cstr (&title, "Table");
370 else if (is_chart_item (item))
372 const char *s = chart_item_get_title (to_chart_item (item));
374 ds_put_format (&title, "Chart: %s", s);
376 ds_put_cstr (&title, "Chart");
378 gtk_tree_store_set (store, &iter,
379 COL_TITLE, ds_cstr (&title),
385 path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
386 gtk_tree_view_expand_row (viewer->overview, path, TRUE);
387 gtk_tree_path_free (path);
390 if (pod->viewer->max_width < tw)
391 pod->viewer->max_width = tw;
392 pod->viewer->y += th;
394 gtk_layout_set_size (pod->viewer->output,
395 pod->viewer->max_width, pod->viewer->y);
397 gtk_window_set_urgency_hint (GTK_WINDOW (pod->viewer), TRUE);
403 static struct output_driver_class psppire_output_class =
405 "PSPPIRE", /* name */
407 psppire_output_submit, /* submit */
412 psppire_output_window_setup (void)
414 struct psppire_output_driver *pod;
415 struct output_driver *d;
417 pod = xzalloc (sizeof *pod);
419 output_driver_init (d, &psppire_output_class, "PSPPIRE",
420 SETTINGS_DEVICE_UNFILTERED);
421 output_driver_register (d);
426 /* Callback for the "delete" action (clicking the x on the top right
427 hand corner of the window) */
429 on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
431 PsppireOutputWindow *ow = PSPPIRE_OUTPUT_WINDOW (user_data);
433 gtk_widget_destroy (GTK_WIDGET (ow));
435 ow->driver->viewer = NULL;
443 cancel_urgency (GtkWindow *window, gpointer data)
445 gtk_window_set_urgency_hint (window, FALSE);
449 on_row_activate (GtkTreeView *overview,
451 GtkTreeViewColumn *column,
452 PsppireOutputWindow *window)
460 model = gtk_tree_view_get_model (overview);
461 if (!gtk_tree_model_get_iter (model, &iter, path))
464 gtk_tree_model_get_value (model, &iter, COL_Y, &value);
465 y = g_value_get_long (&value);
466 g_value_unset (&value);
468 vadj = gtk_layout_get_vadjustment (window->output);
470 max = vadj->upper - vadj->page_size;
475 gtk_adjustment_set_value (vadj, y);
478 static void psppire_output_window_print (PsppireOutputWindow *window);
482 export_output (PsppireOutputWindow *window, struct string_map *options,
485 struct output_driver *driver;
488 string_map_insert (options, "format", format);
489 driver = output_driver_create (options);
493 for (i = 0; i < window->n_items; i++)
494 driver->class->submit (driver, window->items[i]);
495 output_driver_destroy (driver);
517 #define N_EXTENSIONS (n_FT - 1)
519 struct file_types ft[n_FT] = {
520 {N_("Infer file type from extension"), NULL},
521 {N_("PDF (*.pdf)"), ".pdf"},
522 {N_("HTML (*.html)"), ".html"},
523 {N_("OpenDocument (*.odt)"), ".odt"},
524 {N_("Text (*.txt)"), ".txt"},
525 {N_("PostScript (*.ps)"), ".ps"},
526 {N_("Comma-Separated Values (*.csv)"), ".csv"}
531 on_combo_change (GtkFileChooser *chooser)
533 gboolean sensitive = FALSE;
534 GtkWidget *combo = gtk_file_chooser_get_extra_widget (chooser);
537 gchar *fn = gtk_file_chooser_get_filename (chooser);
539 if (combo && gtk_widget_get_realized (combo))
540 x = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
552 for (i = 1 ; i < N_EXTENSIONS ; ++i)
554 if ( g_str_has_suffix (fn, ft[i].ext))
564 gtk_dialog_set_response_sensitive (GTK_DIALOG (chooser), GTK_RESPONSE_ACCEPT, sensitive);
569 on_file_chooser_change (GObject *w, GParamSpec *pspec, gpointer data)
572 GtkFileChooser *chooser = data;
573 const gchar *name = g_param_spec_get_name (pspec);
575 if ( ! gtk_widget_get_realized (GTK_WIDGET (chooser)))
578 /* Ignore this one. It causes recursion. */
579 if ( 0 == strcmp ("tooltip-text", name))
582 on_combo_change (chooser);
586 /* Recursively descend all the children of W, connecting
587 to their "notify" signal */
589 iterate_widgets (GtkWidget *w, gpointer data)
591 if ( GTK_IS_CONTAINER (w))
592 gtk_container_forall (GTK_CONTAINER (w), iterate_widgets, data);
594 g_signal_connect (w, "notify", G_CALLBACK (on_file_chooser_change), data);
599 static GtkListStore *
600 create_file_type_list (void)
604 GtkListStore *list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
606 for (i = 0 ; i < n_FT ; ++i)
608 gtk_list_store_append (list, &iter);
609 gtk_list_store_set (list, &iter,
610 0, gettext (ft[i].label),
619 psppire_output_window_export (PsppireOutputWindow *window)
625 GtkFileChooser *chooser;
627 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Export Output"),
629 GTK_FILE_CHOOSER_ACTION_SAVE,
630 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
631 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
634 g_object_set (dialog, "local-only", FALSE, NULL);
636 chooser = GTK_FILE_CHOOSER (dialog);
638 list = create_file_type_list ();
640 combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list));
644 /* Create text cell renderer */
645 GtkCellRenderer *cell = gtk_cell_renderer_text_new();
646 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE );
648 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cell, "text", 0);
651 g_signal_connect_swapped (combo, "changed", G_CALLBACK (on_combo_change), chooser);
653 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
655 gtk_file_chooser_set_extra_widget (chooser, combo);
657 /* This kludge is necessary because there is no signal to tell us
658 when the candidate filename of a GtkFileChooser has changed */
659 gtk_container_forall (GTK_CONTAINER (dialog), iterate_widgets, dialog);
662 gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);
664 response = gtk_dialog_run (GTK_DIALOG (dialog));
666 if ( response == GTK_RESPONSE_ACCEPT )
668 int file_type = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
669 char *filename = gtk_file_chooser_get_filename (chooser);
670 struct string_map options;
672 g_return_if_fail (filename);
674 if (file_type == FT_AUTO)
677 for (i = 1 ; i < N_EXTENSIONS ; ++i)
679 if ( g_str_has_suffix (filename, ft[i].ext))
688 string_map_init (&options);
689 string_map_insert (&options, "output-file", filename);
694 export_output (window, &options, "pdf");
697 export_output (window, &options, "html");
700 export_output (window, &options, "odt");
703 export_output (window, &options, "ps");
706 export_output (window, &options, "csv");
710 string_map_insert (&options, "headers", "false");
711 string_map_insert (&options, "paginate", "false");
712 string_map_insert (&options, "squeeze", "true");
713 string_map_insert (&options, "emphasis", "none");
714 string_map_insert (&options, "charts", "none");
715 string_map_insert (&options, "top-margin", "0");
716 string_map_insert (&options, "bottom-margin", "0");
717 export_output (window, &options, "txt");
720 g_assert_not_reached ();
723 string_map_destroy (&options);
728 gtk_widget_destroy (dialog);
740 /* GNU Hurd doesn't have PATH_MAX. Use a fallback.
741 Temporary directory names are usually not that long. */
743 # define PATH_MAX 1024
747 clipboard_get_cb (GtkClipboard *clipboard,
748 GtkSelectionData *selection_data,
752 PsppireOutputWindow *window = data;
756 struct output_driver *driver = NULL;
757 char dirname[PATH_MAX], *filename;
758 struct string_map options;
760 GtkTreeSelection *sel = gtk_tree_view_get_selection (window->overview);
761 GtkTreeModel *model = gtk_tree_view_get_model (window->overview);
763 GList *rows = gtk_tree_selection_get_selected_rows (sel, &model);
769 if (path_search (dirname, sizeof dirname, NULL, NULL, true)
770 || mkdtemp (dirname) == NULL)
772 error (0, errno, _("failed to create temporary directory"));
775 filename = xasprintf ("%s/clip.tmp", dirname);
777 string_map_init (&options);
778 string_map_insert (&options, "output-file", filename);
782 case SELECT_FMT_UTF8:
783 string_map_insert (&options, "box", "unicode");
786 case SELECT_FMT_TEXT:
787 string_map_insert (&options, "format", "txt");
790 case SELECT_FMT_HTML:
791 string_map_insert (&options, "format", "html");
792 string_map_insert (&options, "borders", "false");
793 string_map_insert (&options, "css", "false");
797 string_map_insert (&options, "format", "odt");
801 g_warning ("unsupported clip target\n");
806 driver = output_driver_create (&options);
812 GtkTreePath *path = n->data ;
814 struct output_item *item ;
816 gtk_tree_model_get_iter (model, &iter, path);
817 gtk_tree_model_get (model, &iter, COL_ADDR, &item, -1);
819 driver->class->submit (driver, item);
824 if ( driver->class->flush)
825 driver->class->flush (driver);
828 /* Some drivers (eg: the odt one) don't write anything until they
830 output_driver_destroy (driver);
833 if ( g_file_get_contents (filename, &text, &length, NULL) )
835 gtk_selection_data_set (selection_data, selection_data->target,
837 (const guchar *) text, length);
843 output_driver_destroy (driver);
855 clipboard_clear_cb (GtkClipboard *clipboard,
860 static const GtkTargetEntry targets[] = {
862 { "STRING", 0, SELECT_FMT_TEXT },
863 { "TEXT", 0, SELECT_FMT_TEXT },
864 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
865 { "text/plain", 0, SELECT_FMT_TEXT },
867 { "UTF8_STRING", 0, SELECT_FMT_UTF8 },
868 { "text/plain;charset=utf-8", 0, SELECT_FMT_UTF8 },
870 { "text/html", 0, SELECT_FMT_HTML },
872 { "application/vnd.oasis.opendocument.text", 0, SELECT_FMT_ODT }
876 on_copy (PsppireOutputWindow *window)
879 GtkClipboard *clipboard =
880 gtk_widget_get_clipboard (GTK_WIDGET (window),
881 GDK_SELECTION_CLIPBOARD);
883 if (!gtk_clipboard_set_with_data (clipboard, targets,
884 G_N_ELEMENTS (targets),
885 clipboard_get_cb, clipboard_clear_cb,
888 clipboard_clear_cb (clipboard, window);
893 on_selection_change (GtkTreeSelection *sel, GtkAction *copy_action)
895 /* The Copy action is available only if there is something selected */
896 gtk_action_set_sensitive (copy_action, gtk_tree_selection_count_selected_rows (sel) > 0);
900 on_select_all (PsppireOutputWindow *window)
902 GtkTreeSelection *sel = gtk_tree_view_get_selection (window->overview);
903 gtk_tree_view_expand_all (window->overview);
904 gtk_tree_selection_select_all (sel);
909 copy_base_to_bg (GtkWidget *dest, GtkWidget *src)
912 for (i = 0; i < 5; ++i)
914 GdkColor *col = >k_widget_get_style (src)->base[i];
915 gtk_widget_modify_bg (dest, i, col);
917 col = >k_widget_get_style (src)->text[i];
918 gtk_widget_modify_fg (dest, i, col);
923 on_dwgarea_realize (GtkWidget *dwg_area, gpointer data)
925 GtkWidget *viewer = GTK_WIDGET (data);
927 copy_base_to_bg (dwg_area, viewer);
932 psppire_output_window_style_set (GtkWidget *w, GtkStyle *prev)
934 GtkWidget *op = GTK_WIDGET (PSPPIRE_OUTPUT_WINDOW (w)->output);
936 /* Copy the base style from the parent widget to the container and
938 We do this, because the container's primary purpose is to
939 display text. This way psppire appears to follow the chosen
942 copy_base_to_bg (op, w);
943 gtk_container_foreach (GTK_CONTAINER (op), (GtkCallback) copy_base_to_bg,
944 PSPPIRE_OUTPUT_WINDOW (w)->output);
946 /* Chain up to the parent class */
947 GTK_WIDGET_CLASS (parent_class)->style_set (w, prev);
951 psppire_output_window_init (PsppireOutputWindow *window)
953 GtkTreeViewColumn *column;
954 GtkCellRenderer *renderer;
956 GtkAction *copy_action;
957 GtkAction *select_all_action;
958 GtkTreeSelection *sel;
961 string_map_init (&window->render_opts);
963 xml = builder_new ("output-viewer.ui");
965 copy_action = get_action_assert (xml, "edit_copy");
966 select_all_action = get_action_assert (xml, "edit_select-all");
968 gtk_action_set_sensitive (copy_action, FALSE);
970 g_signal_connect_swapped (copy_action, "activate", G_CALLBACK (on_copy), window);
972 g_signal_connect_swapped (select_all_action, "activate", G_CALLBACK (on_select_all), window);
974 gtk_widget_reparent (get_widget_assert (xml, "vbox1"), GTK_WIDGET (window));
976 window->output = GTK_LAYOUT (get_widget_assert (xml, "output"));
979 window->overview = GTK_TREE_VIEW (get_widget_assert (xml, "overview"));
981 sel = gtk_tree_view_get_selection (window->overview);
983 gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE);
985 g_signal_connect (sel, "changed", G_CALLBACK (on_selection_change), copy_action);
987 model = GTK_TREE_MODEL (gtk_tree_store_new (
989 G_TYPE_STRING, /* COL_TITLE */
990 G_TYPE_POINTER, /* COL_ADDR */
991 G_TYPE_LONG)); /* COL_Y */
992 gtk_tree_view_set_model (window->overview, model);
993 g_object_unref (model);
995 window->in_command = false;
997 window->items = NULL;
998 window->n_items = window->allocated_items = 0;
1000 column = gtk_tree_view_column_new ();
1001 gtk_tree_view_append_column (GTK_TREE_VIEW (window->overview), column);
1002 renderer = gtk_cell_renderer_text_new ();
1003 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1004 gtk_tree_view_column_add_attribute (column, renderer, "text", COL_TITLE);
1006 g_signal_connect (GTK_TREE_VIEW (window->overview),
1007 "row-activated", G_CALLBACK (on_row_activate), window);
1011 g_signal_connect (window,
1013 G_CALLBACK (cancel_urgency),
1016 g_signal_connect (get_action_assert (xml,"windows_minimise-all"),
1018 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 PSPPIRE_WINDOW (window)->menu =
1026 GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows_menuitem/windows_minimise-all")->parent);
1029 g_signal_connect_swapped (get_action_assert (xml, "file_export"), "activate",
1030 G_CALLBACK (psppire_output_window_export), window);
1033 g_signal_connect_swapped (get_action_assert (xml, "file_print"), "activate",
1034 G_CALLBACK (psppire_output_window_print), window);
1036 g_object_unref (xml);
1038 g_signal_connect (window, "delete-event",
1039 G_CALLBACK (on_delete), window);
1044 psppire_output_window_new (void)
1046 return GTK_WIDGET (g_object_new (psppire_output_window_get_type (),
1047 /* TRANSLATORS: This will form a filename. Please avoid whitespace. */
1048 "filename", _("Output"),
1049 "description", _("Output Viewer"),
1055 create_xr_print_driver (GtkPrintContext *context, PsppireOutputWindow *window)
1057 struct string_map options;
1058 GtkPageSetup *page_setup;
1059 double width, height;
1061 double right_margin;
1063 double bottom_margin;
1065 page_setup = gtk_print_context_get_page_setup (context);
1066 width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_MM);
1067 height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_MM);
1068 left_margin = gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_MM);
1069 right_margin = gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_MM);
1070 top_margin = gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_MM);
1071 bottom_margin = gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_MM);
1073 string_map_init (&options);
1074 string_map_insert_nocopy (&options, xstrdup ("paper-size"),
1075 c_xasprintf("%.2fx%.2fmm", width, height));
1076 string_map_insert_nocopy (&options, xstrdup ("left-margin"),
1077 c_xasprintf ("%.2fmm", left_margin));
1078 string_map_insert_nocopy (&options, xstrdup ("right-margin"),
1079 c_xasprintf ("%.2fmm", right_margin));
1080 string_map_insert_nocopy (&options, xstrdup ("top-margin"),
1081 c_xasprintf ("%.2fmm", top_margin));
1082 string_map_insert_nocopy (&options, xstrdup ("bottom-margin"),
1083 c_xasprintf ("%.2fmm", bottom_margin));
1086 xr_driver_create (gtk_print_context_get_cairo_context (context), &options);
1088 string_map_destroy (&options);
1092 paginate (GtkPrintOperation *operation,
1093 GtkPrintContext *context,
1094 PsppireOutputWindow *window)
1096 if (window->paginated)
1098 /* Sometimes GTK+ emits this signal again even after pagination is
1099 complete. Don't let that screw up printing. */
1102 else if ( window->print_item < window->n_items )
1104 xr_driver_output_item (window->print_xrd, window->items[window->print_item++]);
1105 while (xr_driver_need_new_page (window->print_xrd))
1107 xr_driver_next_page (window->print_xrd, NULL);
1108 window->print_n_pages ++;
1114 gtk_print_operation_set_n_pages (operation, window->print_n_pages);
1116 /* Re-create the driver to do the real printing. */
1117 xr_driver_destroy (window->print_xrd);
1118 create_xr_print_driver (context, window);
1119 window->print_item = 0;
1120 window->paginated = TRUE;
1127 begin_print (GtkPrintOperation *operation,
1128 GtkPrintContext *context,
1129 PsppireOutputWindow *window)
1131 create_xr_print_driver (context, window);
1133 window->print_item = 0;
1134 window->print_n_pages = 1;
1135 window->paginated = FALSE;
1139 end_print (GtkPrintOperation *operation,
1140 GtkPrintContext *context,
1141 PsppireOutputWindow *window)
1143 xr_driver_destroy (window->print_xrd);
1148 draw_page (GtkPrintOperation *operation,
1149 GtkPrintContext *context,
1151 PsppireOutputWindow *window)
1153 xr_driver_next_page (window->print_xrd, gtk_print_context_get_cairo_context (context));
1154 while (!xr_driver_need_new_page (window->print_xrd)
1155 && window->print_item < window->n_items)
1156 xr_driver_output_item (window->print_xrd, window->items [window->print_item++]);
1161 psppire_output_window_print (PsppireOutputWindow *window)
1163 GtkPrintOperationResult res;
1165 GtkPrintOperation *print = gtk_print_operation_new ();
1167 if (window->print_settings != NULL)
1168 gtk_print_operation_set_print_settings (print, window->print_settings);
1170 g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), window);
1171 g_signal_connect (print, "end_print", G_CALLBACK (end_print), window);
1172 g_signal_connect (print, "paginate", G_CALLBACK (paginate), window);
1173 g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), window);
1175 res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
1176 GTK_WINDOW (window), NULL);
1178 if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
1180 if (window->print_settings != NULL)
1181 g_object_unref (window->print_settings);
1182 window->print_settings = g_object_ref (gtk_print_operation_get_print_settings (print));
1186 g_object_unref (print);