Merge commit 'HEAD'; commit 'savannah/master'
[pspp] / src / ui / gui / psppire-output-window.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008  Free Software Foundation
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
19 #include <gtk/gtksignal.h>
20 #include <gtk/gtkbox.h>
21 #include <glade/glade.h>
22 #include "helper.h"
23
24 #include <libpspp/message.h>
25 #include <stdlib.h>
26
27 #include "about.h"
28
29 #include "psppire-output-window.h"
30
31
32 #include "xalloc.h"
33
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37
38 #include <gettext.h>
39 #define _(msgid) gettext (msgid)
40 #define N_(msgid) msgid
41
42
43
44 static void psppire_output_window_base_finalize (PsppireOutputWindowClass *, gpointer);
45 static void psppire_output_window_base_init     (PsppireOutputWindowClass *class);
46 static void psppire_output_window_class_init    (PsppireOutputWindowClass *class);
47 static void psppire_output_window_init          (PsppireOutputWindow      *window);
48
49
50 GType
51 psppire_output_window_get_type (void)
52 {
53   static GType psppire_output_window_type = 0;
54
55   if (!psppire_output_window_type)
56     {
57       static const GTypeInfo psppire_output_window_info =
58       {
59         sizeof (PsppireOutputWindowClass),
60         (GBaseInitFunc) psppire_output_window_base_init,
61         (GBaseFinalizeFunc) psppire_output_window_base_finalize,
62         (GClassInitFunc)psppire_output_window_class_init,
63         (GClassFinalizeFunc) NULL,
64         NULL,
65         sizeof (PsppireOutputWindow),
66         0,
67         (GInstanceInitFunc) psppire_output_window_init,
68       };
69
70       psppire_output_window_type =
71         g_type_register_static (PSPPIRE_WINDOW_TYPE, "PsppireOutputWindow",
72                                 &psppire_output_window_info, 0);
73     }
74
75   return psppire_output_window_type;
76 }
77
78
79 static void
80 psppire_output_window_finalize (GObject *object)
81 {
82   GObjectClass *class = G_OBJECT_GET_CLASS (object);
83
84   GObjectClass *parent_class = g_type_class_peek_parent (class);
85
86
87   if (G_OBJECT_CLASS (parent_class)->finalize)
88     (*G_OBJECT_CLASS (parent_class)->finalize) (object);
89
90 }
91
92
93 static void
94 psppire_output_window_class_init (PsppireOutputWindowClass *class)
95 {
96 }
97
98
99 static void
100 psppire_output_window_base_init (PsppireOutputWindowClass *class)
101 {
102   GObjectClass *object_class = G_OBJECT_CLASS (class);
103
104   object_class->finalize = psppire_output_window_finalize;
105 }
106
107
108
109 static void
110 psppire_output_window_base_finalize (PsppireOutputWindowClass *class,
111                                      gpointer class_data)
112 {
113 }
114
115
116 \f
117
118 static PsppireOutputWindow *the_output_viewer = NULL;
119
120
121 int viewer_length = 16;
122 int viewer_width = 59;
123
124 /* Callback for the "delete" action (clicking the x on the top right
125    hand corner of the window) */
126 static gboolean
127 on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
128 {
129   PsppireOutputWindow *ow = PSPPIRE_OUTPUT_WINDOW (user_data);
130
131   gtk_widget_destroy (GTK_WIDGET (ow));
132
133   the_output_viewer = NULL;
134
135   unlink (OUTPUT_FILE_NAME);
136
137   return FALSE;
138 }
139
140
141
142 static void
143 cancel_urgency (GtkWindow *window,  gpointer data)
144 {
145   gtk_window_set_urgency_hint (window, FALSE);
146 }
147 /* Sets width and length according to the new size
148    of the output window */
149 static void
150 on_textview_resize (GtkWidget     *widget,
151                     GtkAllocation *allocation,
152                     gpointer       user_data)
153 {
154   PangoContext * context ;
155   PangoLayout *layout ;
156   PangoRectangle logical;
157   GtkStyle *style;
158   gint right_margin, left_margin;
159   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
160
161   context = gtk_widget_create_pango_context (widget);
162   layout = pango_layout_new (context);
163
164   style = gtk_widget_get_style (widget);
165
166   pango_layout_set_font_description (layout, style->font_desc);
167
168   /* Find the width of one character.  We can use any character, because
169      the textview has a monospaced font */
170   pango_layout_set_text (layout, "M", 1);
171
172   pango_layout_get_extents (layout,  NULL, &logical);
173
174   left_margin = gtk_text_view_get_left_margin (text_view);
175   right_margin = gtk_text_view_get_right_margin (text_view);
176
177   viewer_length = allocation->height / PANGO_PIXELS (logical.height);
178   viewer_width = (allocation->width - right_margin - left_margin)
179     / PANGO_PIXELS (logical.width);
180
181   g_object_unref (G_OBJECT (layout));
182   g_object_unref (G_OBJECT (context));
183 }
184
185
186 static void
187 psppire_output_window_init (PsppireOutputWindow *window)
188 {
189   GladeXML *xml = XML_NEW ("output-viewer.glade");
190
191   GtkWidget *box = gtk_vbox_new (FALSE, 0);
192
193   GtkWidget *sw = get_widget_assert (xml, "scrolledwindow1");
194
195   GtkWidget *menubar = get_widget_assert (xml, "menubar1");
196
197   window->textview = get_widget_assert (xml, "output-viewer-textview");
198
199
200   gtk_container_add (GTK_CONTAINER (window), box);
201
202
203   g_object_ref (menubar);
204   gtk_widget_unparent (menubar);
205
206   g_object_ref (sw);
207   gtk_widget_unparent (sw);
208
209
210   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
211   gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);
212
213
214   gtk_widget_show_all (box);
215
216   connect_help (xml);
217
218   window->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->textview));
219
220   g_signal_connect (window,
221                     "focus-in-event",
222                     G_CALLBACK (cancel_urgency),
223                     NULL);
224
225   {
226     /* Output uses ascii characters for tabular material.
227        So we need a monospaced font otherwise it'll look silly */
228     PangoFontDescription *font_desc =
229       pango_font_description_from_string ("monospace");
230
231     gtk_widget_modify_font (window->textview, font_desc);
232     pango_font_description_free (font_desc);
233   }
234
235   g_signal_connect (window->textview, "size-allocate",
236                     G_CALLBACK (on_textview_resize), NULL);
237
238   window->fp = NULL;
239
240   g_signal_connect (get_widget_assert (xml,"help_about"),
241                     "activate",
242                     G_CALLBACK (about_new),
243                     window);
244
245   g_signal_connect (get_widget_assert (xml,"help_reference"),
246                     "activate",
247                     G_CALLBACK (reference_manual),
248                     NULL);
249
250   g_signal_connect (get_widget_assert (xml,"windows_minimise-all"),
251                     "activate",
252                     G_CALLBACK (psppire_window_minimise_all),
253                     NULL);
254
255   PSPPIRE_WINDOW (window)->menu = GTK_MENU_SHELL (get_widget_assert (xml,"windows_menu"));
256
257
258   g_object_unref (xml);
259
260   g_signal_connect (window, "delete-event",
261                     G_CALLBACK (on_delete), window);
262 }
263
264
265 GtkWidget*
266 psppire_output_window_new (void)
267 {
268   return GTK_WIDGET (g_object_new (psppire_output_window_get_type (),
269                                    "usage", PSPPIRE_WINDOW_USAGE_OUTPUT,
270                                    NULL));
271 }
272
273 static void reload_viewer (PsppireOutputWindow *ow);
274
275 void
276 psppire_output_window_reload (void)
277 {
278   struct stat buf;
279
280   /* If there is no output, then don't do anything */
281   if (0 != stat (OUTPUT_FILE_NAME, &buf))
282     return ;
283
284   if ( NULL == the_output_viewer )
285     {
286       the_output_viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
287       gtk_widget_show (GTK_WIDGET (the_output_viewer));
288     }
289
290   reload_viewer (the_output_viewer);
291
292 }
293
294
295 static void
296 reload_viewer (PsppireOutputWindow *ow)
297 {
298   GtkTextIter end_iter;
299   GtkTextMark *mark ;
300
301   static char *line = NULL;
302
303   gboolean chars_inserted = FALSE;
304
305   gtk_text_buffer_get_end_iter (ow->buffer, &end_iter);
306
307   line = xrealloc (line, sizeof (char) * (viewer_width + 1));
308
309   mark = gtk_text_buffer_create_mark (ow->buffer, NULL, &end_iter, TRUE);
310
311 #ifdef __CYGWIN__
312   /*
313     Apparently Windoze is not capabale of writing to a file whilst
314     another (or the same) process is reading from it.   Therefore, we
315     must close the file after reading it, and clear the entire buffer
316     before writing to it.
317     This will be slower for large buffers, but should work
318     (in so far as anything ever works on windows).
319   */
320   {
321     GtkTextIter start_iter;
322     FILE *fp = fopen (OUTPUT_FILE_NAME, "r");
323     if ( !fp)
324       {
325         g_print ("Cannot open %s\n", OUTPUT_FILE_NAME);
326         return;
327       }
328
329     /* Delete all the entire buffer */
330     gtk_text_buffer_get_start_iter (ov->buffer, &start_iter);
331     gtk_text_buffer_delete (ov->buffer, &start_iter, &end_iter);
332
333
334     gtk_text_buffer_get_start_iter (ov->buffer, &start_iter);
335     /* Read in the next lot of text */
336     while (fgets (line, viewer_width + 1, fp) != NULL)
337       {
338         chars_inserted = TRUE;
339         gtk_text_buffer_insert (ov->buffer, &start_iter, line, -1);
340       }
341
342     fclose (fp);
343   }
344 #else
345   {
346     if ( ow->fp == NULL)
347       {
348         ow->fp = fopen (OUTPUT_FILE_NAME, "r");
349         if ( ow->fp == NULL)
350           {
351             g_print ("Cannot open %s\n", OUTPUT_FILE_NAME);
352             return;
353           }
354       }
355
356     /* Read in the next lot of text */
357     while (fgets (line, viewer_width + 1, ow->fp) != NULL)
358       {
359         chars_inserted = TRUE;
360         gtk_text_buffer_insert (ow->buffer, &end_iter, line, -1);
361       }
362   }
363 #endif
364
365   /* Scroll to where the start of this lot of text begins */
366   gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (ow->textview),
367                                 mark,
368                                 0.1, TRUE, 0.0, 0.0);
369
370
371   if ( chars_inserted )
372     gtk_window_set_urgency_hint ( GTK_WINDOW (ow), TRUE);
373 }
374
375
376