Added (source) configurable default output directory.
[pspp-builds.git] / src / ui / gui / output-viewer.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18 #include <gtk/gtk.h>
19 #include <data/file-name.h>
20 #include "window-manager.h"
21 #include "output-viewer.h"
22 #include "helper.h"
23 #include "about.h"
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28
29 #include <glade/glade.h>
30 #include <ctype.h>
31
32 #include "xalloc.h"
33
34 struct output_viewer
35 {
36   struct editor_window parent;
37   GtkTextBuffer *buffer;  /* The buffer which contains the text */
38   GtkWidget *textview ;
39   FILE *fp;               /* The file it's viewing */
40 };
41
42
43 static void
44 cancel_urgency (GtkWindow *window,  gpointer data)
45 {
46   gtk_window_set_urgency_hint (window, FALSE);
47 }
48
49
50 static struct output_viewer *the_output_viewer = NULL;
51
52 int viewer_length = 16;
53 int viewer_width = 59;
54
55 /* Callback for the "delete" action (clicking the x on the top right
56    hand corner of the window) */
57 static gboolean
58 on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
59 {
60   struct output_viewer *ov = user_data;
61
62   g_free (ov);
63
64   the_output_viewer = NULL;
65
66   unlink (output_file_name ());
67
68   return FALSE;
69 }
70
71
72 /* Sets width and length according to the new size
73    of the output window */
74 static void
75 on_textview_resize (GtkWidget     *widget,
76                     GtkAllocation *allocation,
77                     gpointer       user_data)
78 {
79   PangoContext * context ;
80   PangoLayout *layout ;
81   PangoRectangle logical;
82   GtkStyle *style;
83   gint right_margin, left_margin;
84   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
85
86   context = gtk_widget_create_pango_context (widget);
87   layout = pango_layout_new (context);
88
89   style = gtk_widget_get_style (widget);
90
91   pango_layout_set_font_description (layout, style->font_desc);
92
93   /* Find the width of one character.  We can use any character, because
94      the textview has a monospaced font */
95   pango_layout_set_text (layout, "M", 1);
96
97   pango_layout_get_extents (layout,  NULL, &logical);
98
99   left_margin = gtk_text_view_get_left_margin (text_view);
100   right_margin = gtk_text_view_get_right_margin (text_view);
101
102   viewer_length = allocation->height / PANGO_PIXELS (logical.height);
103   viewer_width = (allocation->width - right_margin - left_margin)
104     / PANGO_PIXELS (logical.width);
105
106   g_object_unref (G_OBJECT (layout));
107   g_object_unref (G_OBJECT (context));
108 }
109
110
111
112 /*
113   Create a new output viewer
114 */
115 struct output_viewer *
116 new_output_viewer (void)
117 {
118   GladeXML *xml = XML_NEW ("output-viewer.glade");
119
120   struct output_viewer *ov ;
121   struct editor_window *e;
122
123   connect_help (xml);
124
125   ov = g_malloc (sizeof (*ov));
126
127   e = (struct editor_window *)ov;
128
129
130   e->window = GTK_WINDOW (get_widget_assert (xml, "output-viewer-window"));
131   ov->textview = get_widget_assert (xml, "output-viewer-textview");
132   ov->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (ov->textview));
133
134   g_signal_connect (e->window,
135                     "focus-in-event",
136                     G_CALLBACK (cancel_urgency),
137                     NULL);
138
139   {
140     /* Output uses ascii characters for tabular material.
141        So we need a monospaced font otherwise it'll look silly */
142     PangoFontDescription *font_desc =
143       pango_font_description_from_string ("monospace");
144
145     gtk_widget_modify_font (ov->textview, font_desc);
146     pango_font_description_free (font_desc);
147   }
148
149   g_signal_connect (ov->textview, "size-allocate",
150                     G_CALLBACK (on_textview_resize), NULL);
151
152   ov->fp = NULL;
153
154   g_signal_connect (get_widget_assert (xml,"help_about"),
155                     "activate",
156                     G_CALLBACK (about_new),
157                     e->window);
158
159   g_signal_connect (get_widget_assert (xml,"help_reference"),
160                     "activate",
161                     G_CALLBACK (reference_manual),
162                     NULL);
163
164   g_signal_connect (get_widget_assert (xml,"windows_minimise-all"),
165                     "activate",
166                     G_CALLBACK (minimise_all_windows),
167                     NULL);
168
169   g_object_unref (xml);
170
171
172   g_signal_connect (e->window, "delete-event",
173                     G_CALLBACK (on_delete), ov);
174
175   return ov;
176 }
177
178
179 void
180 reload_the_viewer (void)
181 {
182   struct stat buf;
183
184   /* If there is no output, then don't do anything */
185   if (0 != stat (output_file_name (), &buf))
186     return ;
187
188   if ( NULL == the_output_viewer )
189     {
190       the_output_viewer =
191         (struct output_viewer *) window_create (WINDOW_OUTPUT, NULL);
192     }
193
194   reload_viewer (the_output_viewer);
195 }
196
197
198 void
199 reload_viewer (struct output_viewer *ov)
200 {
201   GtkTextIter end_iter;
202   GtkTextMark *mark ;
203
204   static char *line = NULL;
205
206   gboolean chars_inserted = FALSE;
207
208   gtk_text_buffer_get_end_iter (ov->buffer, &end_iter);
209
210   line = xrealloc (line, sizeof (char) * (viewer_width + 1));
211
212
213   mark = gtk_text_buffer_create_mark (ov->buffer, NULL, &end_iter, TRUE);
214
215 #ifdef __CYGWIN__
216   /*
217     Apparently Windoze is not capabale of writing to a file whilst
218     another (or the same) process is reading from it.   Therefore, we
219     must close the file after reading it, and clear the entire buffer
220     before writing to it.
221     This will be slower for large buffers, but should work
222     (in so far as anything ever works on windows).
223   */
224   {
225     GtkTextIter start_iter;
226     FILE *fp = fopen (OUTPUT_FILE_NAME, "r");
227     if ( !fp)
228       {
229         g_print ("Cannot open %s\n", OUTPUT_FILE_NAME);
230         return;
231       }
232
233     /* Delete all the entire buffer */
234     gtk_text_buffer_get_start_iter (ov->buffer, &start_iter);
235     gtk_text_buffer_delete (ov->buffer, &start_iter, &end_iter);
236
237
238     gtk_text_buffer_get_start_iter (ov->buffer, &start_iter);
239     /* Read in the next lot of text */
240     while (fgets (line, viewer_width + 1, fp) != NULL)
241       {
242         chars_inserted = TRUE;
243         gtk_text_buffer_insert (ov->buffer, &start_iter, line, -1);
244       }
245
246     fclose (fp);
247   }
248 #else
249   {
250     if ( ov->fp == NULL)
251       {
252         ov->fp = fopen (output_file_name (), "r");
253         if ( ov->fp == NULL)
254           {
255             g_print ("Cannot open %s\n", output_file_name ());
256             return;
257           }
258       }
259
260     /* Read in the next lot of text */
261     while (fgets (line, viewer_width + 1, ov->fp) != NULL)
262       {
263         chars_inserted = TRUE;
264         gtk_text_buffer_insert (ov->buffer, &end_iter, line, -1);
265       }
266   }
267 #endif
268
269   /* Scroll to where the start of this lot of text begins */
270   gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (ov->textview),
271                                 mark,
272                                 0.1, TRUE, 0.0, 0.0);
273
274
275   if ( chars_inserted )
276     gtk_window_set_urgency_hint ( ((struct editor_window *)ov)->window, TRUE);
277 }
278
279
280 #define OUTPUT_FILE_NAME "psppire.txt"
281
282 const char *
283 output_file_name (void)
284 {
285   const char *dir = default_output_path ();
286   static char *filename = NULL;
287
288   if ( NULL == filename )
289     filename = xasprintf ("%s%s", dir, OUTPUT_FILE_NAME);
290
291
292   return filename;
293 }