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