Converted output-viewer.glade to GtkBuilder
[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_WINDOW_TYPE, "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_object_assert (xml,"help_about"),
236                     "activate",
237                     G_CALLBACK (about_new),
238                     window);
239
240   g_signal_connect (get_object_assert (xml,"help_reference"),
241                     "activate",
242                     G_CALLBACK (reference_manual),
243                     NULL);
244
245   g_signal_connect (get_object_assert (xml,"windows_minimise-all"),
246                     "activate",
247                     G_CALLBACK (psppire_window_minimise_all),
248                     NULL);
249
250   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (xml, "uimanager1"));
251
252   PSPPIRE_WINDOW (window)->menu =
253     GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar1/windows_menuitem/windows_minimise-all")->parent);
254
255   g_object_unref (xml);
256
257   g_signal_connect (window, "delete-event",
258                     G_CALLBACK (on_delete), window);
259 }
260
261
262 GtkWidget*
263 psppire_output_window_new (void)
264 {
265   return GTK_WIDGET (g_object_new (psppire_output_window_get_type (),
266                                    "usage", PSPPIRE_WINDOW_USAGE_OUTPUT,
267                                    NULL));
268 }
269
270 static void reload_viewer (PsppireOutputWindow *ow);
271
272 void
273 psppire_output_window_reload (void)
274 {
275   struct stat buf;
276
277   /* If there is no output, then don't do anything */
278   if (0 != stat (OUTPUT_FILE_NAME, &buf))
279     return ;
280
281   if ( NULL == the_output_viewer )
282     {
283       the_output_viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
284       gtk_widget_show (GTK_WIDGET (the_output_viewer));
285     }
286
287   reload_viewer (the_output_viewer);
288
289 }
290
291
292 static void
293 reload_viewer (PsppireOutputWindow *ow)
294 {
295   GtkTextIter end_iter;
296   GtkTextMark *mark ;
297
298   static char *line = NULL;
299
300   gboolean chars_inserted = FALSE;
301
302   gtk_text_buffer_get_end_iter (ow->buffer, &end_iter);
303
304   line = xrealloc (line, sizeof (char) * (viewer_width + 1));
305
306   mark = gtk_text_buffer_create_mark (ow->buffer, NULL, &end_iter, TRUE);
307
308 #ifdef __CYGWIN__
309   /*
310     Apparently Windoze is not capabale of writing to a file whilst
311     another (or the same) process is reading from it.   Therefore, we
312     must close the file after reading it, and clear the entire buffer
313     before writing to it.
314     This will be slower for large buffers, but should work
315     (in so far as anything ever works on windows).
316   */
317   {
318     GtkTextIter start_iter;
319     FILE *fp = fopen (OUTPUT_FILE_NAME, "r");
320     if ( !fp)
321       {
322         g_print ("Cannot open %s\n", OUTPUT_FILE_NAME);
323         return;
324       }
325
326     /* Delete all the entire buffer */
327     gtk_text_buffer_get_start_iter (ov->buffer, &start_iter);
328     gtk_text_buffer_delete (ov->buffer, &start_iter, &end_iter);
329
330
331     gtk_text_buffer_get_start_iter (ov->buffer, &start_iter);
332     /* Read in the next lot of text */
333     while (fgets (line, viewer_width + 1, fp) != NULL)
334       {
335         chars_inserted = TRUE;
336         gtk_text_buffer_insert (ov->buffer, &start_iter, line, -1);
337       }
338
339     fclose (fp);
340   }
341 #else
342   {
343     if ( ow->fp == NULL)
344       {
345         ow->fp = fopen (OUTPUT_FILE_NAME, "r");
346         if ( ow->fp == NULL)
347           {
348             g_print ("Cannot open %s\n", OUTPUT_FILE_NAME);
349             return;
350           }
351       }
352
353     /* Read in the next lot of text */
354     while (fgets (line, viewer_width + 1, ow->fp) != NULL)
355       {
356         chars_inserted = TRUE;
357         gtk_text_buffer_insert (ow->buffer, &end_iter, line, -1);
358       }
359   }
360 #endif
361
362   /* Scroll to where the start of this lot of text begins */
363   gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (ow->textview),
364                                 mark,
365                                 0.1, TRUE, 0.0, 0.0);
366
367
368   if ( chars_inserted )
369     gtk_window_set_urgency_hint ( GTK_WINDOW (ow), TRUE);
370 }
371
372
373