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);
739 /* GNU Hurd doesn't have PATH_MAX. Use a fallback.
740 Temporary directory names are usually not that long. */
742 # define PATH_MAX 1024
746 clipboard_get_cb (GtkClipboard *clipboard,
747 GtkSelectionData *selection_data,
751 PsppireOutputWindow *window = data;
755 struct output_driver *driver = NULL;
756 char dirname[PATH_MAX], *filename;
757 struct string_map options;
759 GtkTreeSelection *sel = gtk_tree_view_get_selection (window->overview);
760 GtkTreeModel *model = gtk_tree_view_get_model (window->overview);
762 GList *rows = gtk_tree_selection_get_selected_rows (sel, &model);
768 if (path_search (dirname, sizeof dirname, NULL, NULL, true)
769 || mkdtemp (dirname) == NULL)
771 error (0, errno, _("failed to create temporary directory"));
774 filename = xasprintf ("%s/clip.tmp", dirname);
776 string_map_init (&options);
777 string_map_insert (&options, "output-file", filename);
781 case SELECT_FMT_UTF8:
782 string_map_insert (&options, "box", "unicode");
785 case SELECT_FMT_TEXT:
786 string_map_insert (&options, "format", "txt");
789 case SELECT_FMT_HTML:
790 string_map_insert (&options, "format", "html");
791 string_map_insert (&options, "borders", "false");
792 string_map_insert (&options, "css", "false");
796 string_map_insert (&options, "format", "odt");
800 g_warning ("unsupported clip target\n");
805 driver = output_driver_create (&options);
811 GtkTreePath *path = n->data ;
813 struct output_item *item ;
815 gtk_tree_model_get_iter (model, &iter, path);
816 gtk_tree_model_get (model, &iter, COL_ADDR, &item, -1);
818 driver->class->submit (driver, item);
823 if ( driver->class->flush)
824 driver->class->flush (driver);
827 /* Some drivers (eg: the odt one) don't write anything until they
829 output_driver_destroy (driver);
832 if ( g_file_get_contents (filename, &text, &length, NULL) )
834 gtk_selection_data_set (selection_data, selection_data->target,
836 (const guchar *) text, length);
842 output_driver_destroy (driver);
854 clipboard_clear_cb (GtkClipboard *clipboard,
859 static const GtkTargetEntry targets[] = {
861 { "STRING", 0, SELECT_FMT_TEXT },
862 { "TEXT", 0, SELECT_FMT_TEXT },
863 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
864 { "text/plain", 0, SELECT_FMT_TEXT },
866 { "UTF8_STRING", 0, SELECT_FMT_UTF8 },
867 { "text/plain;charset=utf-8", 0, SELECT_FMT_UTF8 },
869 { "text/html", 0, SELECT_FMT_HTML },
871 { "application/vnd.oasis.opendocument.text", 0, SELECT_FMT_ODT }
875 on_copy (PsppireOutputWindow *window)
878 GtkClipboard *clipboard =
879 gtk_widget_get_clipboard (GTK_WIDGET (window),
880 GDK_SELECTION_CLIPBOARD);
882 if (!gtk_clipboard_set_with_data (clipboard, targets,
883 G_N_ELEMENTS (targets),
884 clipboard_get_cb, clipboard_clear_cb,
887 clipboard_clear_cb (clipboard, window);
892 on_selection_change (GtkTreeSelection *sel, GtkAction *copy_action)
894 /* The Copy action is available only if there is something selected */
895 gtk_action_set_sensitive (copy_action, gtk_tree_selection_count_selected_rows (sel) > 0);
899 on_select_all (PsppireOutputWindow *window)
901 GtkTreeSelection *sel = gtk_tree_view_get_selection (window->overview);
902 gtk_tree_view_expand_all (window->overview);
903 gtk_tree_selection_select_all (sel);
908 copy_base_to_bg (GtkWidget *dest, GtkWidget *src)
911 for (i = 0; i < 5; ++i)
913 GdkColor *col = >k_widget_get_style (src)->base[i];
914 gtk_widget_modify_bg (dest, i, col);
916 col = >k_widget_get_style (src)->text[i];
917 gtk_widget_modify_fg (dest, i, col);
922 on_dwgarea_realize (GtkWidget *dwg_area, gpointer data)
924 GtkWidget *viewer = GTK_WIDGET (data);
926 copy_base_to_bg (dwg_area, viewer);
931 psppire_output_window_style_set (GtkWidget *w, GtkStyle *prev)
933 GtkWidget *op = GTK_WIDGET (PSPPIRE_OUTPUT_WINDOW (w)->output);
935 /* Copy the base style from the parent widget to the container and
937 We do this, because the container's primary purpose is to
938 display text. This way psppire appears to follow the chosen
941 copy_base_to_bg (op, w);
942 gtk_container_foreach (GTK_CONTAINER (op), (GtkCallback) copy_base_to_bg,
943 PSPPIRE_OUTPUT_WINDOW (w)->output);
945 /* Chain up to the parent class */
946 GTK_WIDGET_CLASS (parent_class)->style_set (w, prev);
950 psppire_output_window_init (PsppireOutputWindow *window)
952 GtkTreeViewColumn *column;
953 GtkCellRenderer *renderer;
955 GtkAction *copy_action;
956 GtkAction *select_all_action;
957 GtkTreeSelection *sel;
960 string_map_init (&window->render_opts);
962 xml = builder_new ("output-viewer.ui");
964 copy_action = get_action_assert (xml, "edit_copy");
965 select_all_action = get_action_assert (xml, "edit_select-all");
967 gtk_action_set_sensitive (copy_action, FALSE);
969 g_signal_connect_swapped (copy_action, "activate", G_CALLBACK (on_copy), window);
971 g_signal_connect_swapped (select_all_action, "activate", G_CALLBACK (on_select_all), window);
973 gtk_widget_reparent (get_widget_assert (xml, "vbox1"), GTK_WIDGET (window));
975 window->output = GTK_LAYOUT (get_widget_assert (xml, "output"));
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),
1021 GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (xml, "uimanager1", GTK_TYPE_UI_MANAGER));
1022 merge_help_menu (uim);
1024 PSPPIRE_WINDOW (window)->menu =
1025 GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows_menuitem/windows_minimise-all")->parent);
1028 g_signal_connect_swapped (get_action_assert (xml, "file_export"), "activate",
1029 G_CALLBACK (psppire_output_window_export), window);
1032 g_signal_connect_swapped (get_action_assert (xml, "file_print"), "activate",
1033 G_CALLBACK (psppire_output_window_print), window);
1035 g_object_unref (xml);
1037 g_signal_connect (window, "delete-event",
1038 G_CALLBACK (on_delete), window);
1043 psppire_output_window_new (void)
1045 return GTK_WIDGET (g_object_new (psppire_output_window_get_type (),
1046 /* TRANSLATORS: This will form a filename. Please avoid whitespace. */
1047 "filename", _("Output"),
1048 "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 xasprintf("%.2fx%.2fmm", width, height));
1076 string_map_insert_nocopy (&options, xstrdup ("left-margin"),
1077 xasprintf ("%.2fmm", left_margin));
1078 string_map_insert_nocopy (&options, xstrdup ("right-margin"),
1079 xasprintf ("%.2fmm", right_margin));
1080 string_map_insert_nocopy (&options, xstrdup ("top-margin"),
1081 xasprintf ("%.2fmm", top_margin));
1082 string_map_insert_nocopy (&options, xstrdup ("bottom-margin"),
1083 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);