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