1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 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/>. */
19 #include "ui/gui/psppire-output-window.h"
25 #include <sys/types.h>
28 #include "libpspp/cast.h"
29 #include "libpspp/message.h"
30 #include "libpspp/string-map.h"
31 #include "output/cairo.h"
32 #include "output/chart-item.h"
33 #include "output/driver-provider.h"
34 #include "output/message-item.h"
35 #include "output/output-item.h"
36 #include "output/tab.h"
37 #include "output/table-item.h"
38 #include "output/text-item.h"
39 #include "ui/gui/help-menu.h"
40 #include "ui/gui/builder-wrapper.h"
41 #include "ui/gui/psppire-output-view.h"
43 #include "gl/xalloc.h"
48 #define _(msgid) gettext (msgid)
49 #define N_(msgid) msgid
51 static void psppire_output_window_class_init (PsppireOutputWindowClass *class);
52 static void psppire_output_window_init (PsppireOutputWindow *window);
55 psppire_output_window_get_type (void)
57 static GType psppire_output_window_type = 0;
59 if (!psppire_output_window_type)
61 static const GTypeInfo psppire_output_window_info =
63 sizeof (PsppireOutputWindowClass),
65 (GBaseFinalizeFunc) NULL,
66 (GClassInitFunc)psppire_output_window_class_init,
67 (GClassFinalizeFunc) NULL,
69 sizeof (PsppireOutputWindow),
71 (GInstanceInitFunc) psppire_output_window_init,
74 psppire_output_window_type =
75 g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireOutputWindow",
76 &psppire_output_window_info, 0);
79 return psppire_output_window_type;
82 static GObjectClass *parent_class;
85 psppire_output_window_finalize (GObject *object)
87 if (G_OBJECT_CLASS (parent_class)->finalize)
88 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
93 psppire_output_window_dispose (GObject *obj)
95 PsppireOutputWindow *window = PSPPIRE_OUTPUT_WINDOW (obj);
97 if (window->dispose_has_run)
100 window->dispose_has_run = TRUE;
101 psppire_output_view_destroy (window->view);
104 /* Chain up to the parent class */
105 G_OBJECT_CLASS (parent_class)->dispose (obj);
109 psppire_output_window_class_init (PsppireOutputWindowClass *class)
111 GObjectClass *object_class = G_OBJECT_CLASS (class);
113 parent_class = g_type_class_peek_parent (class);
114 object_class->dispose = psppire_output_window_dispose;
116 object_class->finalize = psppire_output_window_finalize;
119 /* Output driver class. */
121 struct psppire_output_driver
123 struct output_driver driver;
124 PsppireOutputWindow *window;
127 static struct output_driver_class psppire_output_class;
129 static struct psppire_output_driver *
130 psppire_output_cast (struct output_driver *driver)
132 assert (driver->class == &psppire_output_class);
133 return UP_CAST (driver, struct psppire_output_driver, driver);
137 psppire_output_submit (struct output_driver *this,
138 const struct output_item *item)
140 struct psppire_output_driver *pod = psppire_output_cast (this);
141 PsppireOutputWindow *window;
143 if (pod->window == NULL)
145 pod->window = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
146 gtk_widget_show_all (GTK_WIDGET (pod->window));
147 pod->window->driver = pod;
149 window = pod->window;
151 psppire_output_view_put (window->view, item);
153 gtk_window_set_urgency_hint (GTK_WINDOW (pod->window), TRUE);
156 static struct output_driver_class psppire_output_class =
158 "PSPPIRE", /* name */
160 psppire_output_submit, /* submit */
165 psppire_output_window_setup (void)
167 struct psppire_output_driver *pod;
168 struct output_driver *d;
170 pod = xzalloc (sizeof *pod);
172 output_driver_init (d, &psppire_output_class, "PSPPIRE",
173 SETTINGS_DEVICE_UNFILTERED);
174 output_driver_register (d);
179 /* Callback for the "delete" action (clicking the x on the top right
180 hand corner of the window) */
182 on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
184 PsppireOutputWindow *ow = PSPPIRE_OUTPUT_WINDOW (user_data);
186 gtk_widget_destroy (GTK_WIDGET (ow));
188 ow->driver->window = NULL;
196 cancel_urgency (GtkWindow *window, gpointer data)
198 gtk_window_set_urgency_hint (window, FALSE);
201 static void psppire_output_window_print (PsppireOutputWindow *window);
205 export_output (PsppireOutputWindow *window, struct string_map *options,
208 string_map_insert (options, "format", format);
209 psppire_output_view_export (window->view, options);
231 #define N_EXTENSIONS (n_FT - 1)
233 struct file_types ft[n_FT] = {
234 {N_("Infer file type from extension"), NULL},
235 {N_("PDF (*.pdf)"), ".pdf"},
236 {N_("HTML (*.html)"), ".html"},
237 {N_("OpenDocument (*.odt)"), ".odt"},
238 {N_("Text (*.txt)"), ".txt"},
239 {N_("PostScript (*.ps)"), ".ps"},
240 {N_("Comma-Separated Values (*.csv)"), ".csv"}
245 on_combo_change (GtkFileChooser *chooser)
247 gboolean sensitive = FALSE;
248 GtkWidget *combo = gtk_file_chooser_get_extra_widget (chooser);
251 gchar *fn = gtk_file_chooser_get_filename (chooser);
253 if (combo && gtk_widget_get_realized (combo))
254 x = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
266 for (i = 1 ; i < N_EXTENSIONS ; ++i)
268 if ( g_str_has_suffix (fn, ft[i].ext))
278 gtk_dialog_set_response_sensitive (GTK_DIALOG (chooser), GTK_RESPONSE_ACCEPT, sensitive);
283 on_file_chooser_change (GObject *w, GParamSpec *pspec, gpointer data)
286 GtkFileChooser *chooser = data;
287 const gchar *name = g_param_spec_get_name (pspec);
289 if ( ! gtk_widget_get_realized (GTK_WIDGET (chooser)))
292 /* Ignore this one. It causes recursion. */
293 if ( 0 == strcmp ("tooltip-text", name))
296 on_combo_change (chooser);
300 /* Recursively descend all the children of W, connecting
301 to their "notify" signal */
303 iterate_widgets (GtkWidget *w, gpointer data)
305 if ( GTK_IS_CONTAINER (w))
306 gtk_container_forall (GTK_CONTAINER (w), iterate_widgets, data);
308 g_signal_connect (w, "notify", G_CALLBACK (on_file_chooser_change), data);
313 static GtkListStore *
314 create_file_type_list (void)
318 GtkListStore *list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
320 for (i = 0 ; i < n_FT ; ++i)
322 gtk_list_store_append (list, &iter);
323 gtk_list_store_set (list, &iter,
324 0, gettext (ft[i].label),
333 psppire_output_window_export (PsppireOutputWindow *window)
339 GtkFileChooser *chooser;
341 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Export Output"),
343 GTK_FILE_CHOOSER_ACTION_SAVE,
344 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
345 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
348 g_object_set (dialog, "local-only", FALSE, NULL);
350 chooser = GTK_FILE_CHOOSER (dialog);
352 list = create_file_type_list ();
354 combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list));
358 /* Create text cell renderer */
359 GtkCellRenderer *cell = gtk_cell_renderer_text_new();
360 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, FALSE );
362 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo), cell, "text", 0);
365 g_signal_connect_swapped (combo, "changed", G_CALLBACK (on_combo_change), chooser);
367 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
369 gtk_file_chooser_set_extra_widget (chooser, combo);
371 /* This kludge is necessary because there is no signal to tell us
372 when the candidate filename of a GtkFileChooser has changed */
373 gtk_container_forall (GTK_CONTAINER (dialog), iterate_widgets, dialog);
376 gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);
378 response = gtk_dialog_run (GTK_DIALOG (dialog));
380 if ( response == GTK_RESPONSE_ACCEPT )
382 gint file_type = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
383 gchar *filename = gtk_file_chooser_get_filename (chooser);
384 struct string_map options;
386 g_return_if_fail (filename);
388 if (file_type == FT_AUTO)
390 /* If the "Infer file type from extension" option was chosen,
391 search for the respective type in the list.
392 (It's a O(n) search, but fortunately n is small). */
394 for (i = 1 ; i < N_EXTENSIONS ; ++i)
396 if ( g_str_has_suffix (filename, ft[i].ext))
403 else if (! g_str_has_suffix (filename, ft[file_type].ext))
405 /* If an explicit document format was chosen, and if the chosen
406 filename does not already have that particular "extension",
410 gchar *of = filename;
411 filename = g_strconcat (filename, ft[file_type].ext, NULL);
415 string_map_init (&options);
416 string_map_insert (&options, "output-file", filename);
421 export_output (window, &options, "pdf");
424 export_output (window, &options, "html");
427 export_output (window, &options, "odt");
430 export_output (window, &options, "ps");
433 export_output (window, &options, "csv");
437 string_map_insert (&options, "headers", "false");
438 string_map_insert (&options, "paginate", "false");
439 string_map_insert (&options, "squeeze", "true");
440 string_map_insert (&options, "emphasis", "none");
441 string_map_insert (&options, "charts", "none");
442 string_map_insert (&options, "top-margin", "0");
443 string_map_insert (&options, "bottom-margin", "0");
444 export_output (window, &options, "txt");
447 g_assert_not_reached ();
450 string_map_destroy (&options);
455 gtk_widget_destroy (dialog);
459 psppire_output_window_init (PsppireOutputWindow *window)
463 xml = builder_new ("output-window.ui");
465 gtk_widget_reparent (get_widget_assert (xml, "vbox1"), GTK_WIDGET (window));
467 window->dispose_has_run = FALSE;
469 window->view = psppire_output_view_new (
470 GTK_LAYOUT (get_widget_assert (xml, "output")),
471 GTK_TREE_VIEW (get_widget_assert (xml, "overview")),
472 get_action_assert (xml, "edit_copy"),
473 get_action_assert (xml, "edit_select-all"));
478 g_signal_connect (window,
480 G_CALLBACK (cancel_urgency),
483 g_signal_connect (get_action_assert (xml,"windows_minimise-all"),
485 G_CALLBACK (psppire_window_minimise_all),
489 GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (xml, "uimanager1", GTK_TYPE_UI_MANAGER));
490 merge_help_menu (uim);
492 PSPPIRE_WINDOW (window)->menu =
493 GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows_menuitem/windows_minimise-all")->parent);
496 g_signal_connect_swapped (get_action_assert (xml, "file_export"), "activate",
497 G_CALLBACK (psppire_output_window_export), window);
500 g_signal_connect_swapped (get_action_assert (xml, "file_print"), "activate",
501 G_CALLBACK (psppire_output_window_print), window);
503 g_object_unref (xml);
505 g_signal_connect (window, "delete-event",
506 G_CALLBACK (on_delete), window);
511 psppire_output_window_new (void)
513 return GTK_WIDGET (g_object_new (psppire_output_window_get_type (),
514 /* TRANSLATORS: This will form a filename. Please avoid whitespace. */
515 "filename", _("Output"),
516 "description", _("Output Viewer"),
521 psppire_output_window_print (PsppireOutputWindow *window)
523 psppire_output_view_print (window->view, GTK_WINDOW (window));