Fix crash in find dialog and make code less horrible.
[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 "helper.h"
22
23 #include <libpspp/message.h>
24 #include <stdlib.h>
25
26 #include "about.h"
27
28 #include "psppire-output-window.h"
29
30
31 #include "xalloc.h"
32
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36
37 #include <gettext.h>
38 #define _(msgid) gettext (msgid)
39 #define N_(msgid) msgid
40
41
42
43 static void psppire_output_window_base_finalize (PsppireOutputWindowClass *, gpointer);
44 static void psppire_output_window_base_init     (PsppireOutputWindowClass *class);
45 static void psppire_output_window_class_init    (PsppireOutputWindowClass *class);
46 static void psppire_output_window_init          (PsppireOutputWindow      *window);
47
48
49 GType
50 psppire_output_window_get_type (void)
51 {
52   static GType psppire_output_window_type = 0;
53
54   if (!psppire_output_window_type)
55     {
56       static const GTypeInfo psppire_output_window_info =
57       {
58         sizeof (PsppireOutputWindowClass),
59         (GBaseInitFunc) psppire_output_window_base_init,
60         (GBaseFinalizeFunc) psppire_output_window_base_finalize,
61         (GClassInitFunc)psppire_output_window_class_init,
62         (GClassFinalizeFunc) NULL,
63         NULL,
64         sizeof (PsppireOutputWindow),
65         0,
66         (GInstanceInitFunc) psppire_output_window_init,
67       };
68
69       psppire_output_window_type =
70         g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireOutputWindow",
71                                 &psppire_output_window_info, 0);
72     }
73
74   return psppire_output_window_type;
75 }
76
77 static GObjectClass *parent_class;
78
79 static void
80 psppire_output_window_finalize (GObject *object)
81 {
82   if (G_OBJECT_CLASS (parent_class)->finalize)
83     (*G_OBJECT_CLASS (parent_class)->finalize) (object);
84 }
85
86
87 static void
88 psppire_output_window_class_init (PsppireOutputWindowClass *class)
89 {
90   parent_class = g_type_class_peek_parent (class);
91 }
92
93
94 static void
95 psppire_output_window_base_init (PsppireOutputWindowClass *class)
96 {
97   GObjectClass *object_class = G_OBJECT_CLASS (class);
98
99   object_class->finalize = psppire_output_window_finalize;
100 }
101
102
103
104 static void
105 psppire_output_window_base_finalize (PsppireOutputWindowClass *class,
106                                      gpointer class_data)
107 {
108 }
109
110
111 \f
112
113 static PsppireOutputWindow *the_output_viewer = NULL;
114
115
116 int viewer_length = 16;
117 int viewer_width = 59;
118
119 /* Callback for the "delete" action (clicking the x on the top right
120    hand corner of the window) */
121 static gboolean
122 on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
123 {
124   PsppireOutputWindow *ow = PSPPIRE_OUTPUT_WINDOW (user_data);
125
126   gtk_widget_destroy (GTK_WIDGET (ow));
127
128   the_output_viewer = NULL;
129
130   unlink (output_file_name());
131
132   return FALSE;
133 }
134
135
136
137 static void
138 cancel_urgency (GtkWindow *window,  gpointer data)
139 {
140   gtk_window_set_urgency_hint (window, FALSE);
141 }
142 /* Sets width and length according to the new size
143    of the output window */
144 static void
145 on_textview_resize (GtkWidget     *widget,
146                     GtkAllocation *allocation,
147                     gpointer       user_data)
148 {
149   PangoContext * context ;
150   PangoLayout *layout ;
151   PangoRectangle logical;
152   GtkStyle *style;
153   gint right_margin, left_margin;
154   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
155
156   context = gtk_widget_create_pango_context (widget);
157   layout = pango_layout_new (context);
158
159   style = gtk_widget_get_style (widget);
160
161   pango_layout_set_font_description (layout, style->font_desc);
162
163   /* Find the width of one character.  We can use any character, because
164      the textview has a monospaced font */
165   pango_layout_set_text (layout, "M", 1);
166
167   pango_layout_get_extents (layout,  NULL, &logical);
168
169   left_margin = gtk_text_view_get_left_margin (text_view);
170   right_margin = gtk_text_view_get_right_margin (text_view);
171
172   viewer_length = allocation->height / PANGO_PIXELS (logical.height);
173   viewer_width = (allocation->width - right_margin - left_margin)
174     / PANGO_PIXELS (logical.width);
175
176   g_object_unref (G_OBJECT (layout));
177   g_object_unref (G_OBJECT (context));
178 }
179
180
181 static void
182 psppire_output_window_init (PsppireOutputWindow *window)
183 {
184   GtkBuilder *xml = builder_new ("output-viewer.ui");
185
186   GtkWidget *box = gtk_vbox_new (FALSE, 0);
187
188   GtkWidget *sw = get_widget_assert (xml, "scrolledwindow1");
189
190   GtkWidget *menubar = get_widget_assert (xml, "menubar1");
191
192   window->textview = get_widget_assert (xml, "output-viewer-textview");
193
194
195   gtk_container_add (GTK_CONTAINER (window), box);
196
197
198   g_object_ref (menubar);
199   gtk_widget_unparent (menubar);
200
201   g_object_ref (sw);
202   gtk_widget_unparent (sw);
203
204
205   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
206   gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);
207
208
209   gtk_widget_show_all (box);
210
211   connect_help (xml);
212
213   window->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (window->textview));
214
215   g_signal_connect (window,
216                     "focus-in-event",
217                     G_CALLBACK (cancel_urgency),
218                     NULL);
219
220   {
221     /* Output uses ascii characters for tabular material.
222        So we need a monospaced font otherwise it'll look silly */
223     PangoFontDescription *font_desc =
224       pango_font_description_from_string ("monospace");
225
226     gtk_widget_modify_font (window->textview, font_desc);
227     pango_font_description_free (font_desc);
228   }
229
230   g_signal_connect (window->textview, "size-allocate",
231                     G_CALLBACK (on_textview_resize), NULL);
232
233   window->fp = NULL;
234
235   g_signal_connect (get_action_assert (xml,"help_about"),
236                     "activate",
237                     G_CALLBACK (about_new),
238                     window);
239
240   g_signal_connect (get_action_assert (xml,"help_reference"),
241                     "activate",
242                     G_CALLBACK (reference_manual),
243                     NULL);
244
245   g_signal_connect (get_action_assert (xml,"windows_minimise-all"),
246                     "activate",
247                     G_CALLBACK (psppire_window_minimise_all),
248                     NULL);
249
250   {
251     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (xml, "uimanager1", GTK_TYPE_UI_MANAGER));
252
253     PSPPIRE_WINDOW (window)->menu =
254       GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar1/windows_menuitem/windows_minimise-all")->parent);
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                                    "filename", "Output",
269                                    "description", _("Output Viewer"),
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   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_critical ("Cannot open %s\n", output_file_name());
326         return;
327       }
328
329     /* Delete all the entire buffer */
330     gtk_text_buffer_get_start_iter (ow->buffer, &start_iter);
331     gtk_text_buffer_delete (ow->buffer, &start_iter, &end_iter);
332
333
334     gtk_text_buffer_get_start_iter (ow->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 (ow->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_critical ("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