Work around bug #24033 (appending to output in cygwin).
[pspp-builds.git] / src / ui / gui / output-viewer.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007 Free Software Foundation, Inc.
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 #include <gtk/gtk.h>
19 #include "window-manager.h"
20 #include "output-viewer.h"
21 #include "helper.h"
22 #include "about.h"
23
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27
28 #include <glade/glade.h>
29 #include <ctype.h>
30
31 #include "xalloc.h"
32
33 struct output_viewer
34 {
35   struct editor_window parent;
36   GtkTextBuffer *buffer;  /* The buffer which contains the text */
37   GtkWidget *textview ;
38   FILE *fp;               /* The file it's viewing */
39 };
40
41
42 static void
43 cancel_urgency (GtkWindow *window,  gpointer data)
44 {
45   gtk_window_set_urgency_hint (window, FALSE);
46 }
47
48
49 static struct output_viewer *the_output_viewer = NULL;
50
51 int viewer_length = 16;
52 int viewer_width = 59;
53
54 /* Callback for the "delete" action (clicking the x on the top right
55    hand corner of the window) */
56 static gboolean
57 on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
58 {
59   struct output_viewer *ov = user_data;
60
61   g_free (ov);
62
63   the_output_viewer = NULL;
64
65   unlink (OUTPUT_FILE_NAME);
66
67   return FALSE;
68 }
69
70
71 /* Sets width and length according to the new size
72    of the output window */
73 static void
74 on_textview_resize (GtkWidget     *widget,
75                     GtkAllocation *allocation,
76                     gpointer       user_data)
77 {
78   PangoContext * context ;
79   PangoLayout *layout ;
80   PangoRectangle logical;
81   GtkStyle *style;
82   gint right_margin, left_margin;
83   GtkTextView *text_view = GTK_TEXT_VIEW (widget);
84
85   context = gtk_widget_create_pango_context (widget);
86   layout = pango_layout_new (context);
87
88   style = gtk_widget_get_style (widget);
89
90   pango_layout_set_font_description (layout, style->font_desc);
91
92   /* Find the width of one character.  We can use any character, because
93      the textview has a monospaced font */
94   pango_layout_set_text (layout, "M", 1);
95
96   pango_layout_get_extents (layout,  NULL, &logical);
97
98   left_margin = gtk_text_view_get_left_margin (text_view);
99   right_margin = gtk_text_view_get_right_margin (text_view);
100
101   viewer_length = allocation->height / PANGO_PIXELS (logical.height);
102   viewer_width = (allocation->width - right_margin - left_margin)
103     / PANGO_PIXELS (logical.width);
104
105   g_object_unref (G_OBJECT (layout));
106   g_object_unref (G_OBJECT (context));
107 }
108
109
110
111 /*
112   Create a new output viewer
113 */
114 struct output_viewer *
115 new_output_viewer (void)
116 {
117   GladeXML *xml = XML_NEW ("output-viewer.glade");
118
119   struct output_viewer *ov ;
120   struct editor_window *e;
121
122   connect_help (xml);
123
124   ov = g_malloc (sizeof (*ov));
125
126   e = (struct editor_window *)ov;
127
128
129   e->window = GTK_WINDOW (get_widget_assert (xml, "output-viewer-window"));
130   ov->textview = get_widget_assert (xml, "output-viewer-textview");
131   ov->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (ov->textview));
132
133   g_signal_connect (e->window,
134                     "focus-in-event",
135                     G_CALLBACK (cancel_urgency),
136                     NULL);
137
138   {
139     /* Output uses ascii characters for tabular material.
140        So we need a monospaced font otherwise it'll look silly */
141     PangoFontDescription *font_desc =
142       pango_font_description_from_string ("monospace");
143
144     gtk_widget_modify_font (ov->textview, font_desc);
145     pango_font_description_free (font_desc);
146   }
147
148   g_signal_connect (ov->textview, "size-allocate",
149                     G_CALLBACK (on_textview_resize), NULL);
150
151   ov->fp = NULL;
152
153   g_signal_connect (get_widget_assert (xml,"help_about"),
154                     "activate",
155                     G_CALLBACK (about_new),
156                     e->window);
157
158   g_signal_connect (get_widget_assert (xml,"help_reference"),
159                     "activate",
160                     G_CALLBACK (reference_manual),
161                     NULL);
162
163   g_signal_connect (get_widget_assert (xml,"windows_minimise-all"),
164                     "activate",
165                     G_CALLBACK (minimise_all_windows),
166                     NULL);
167
168   g_object_unref (xml);
169
170
171   g_signal_connect (e->window, "delete-event",
172                     G_CALLBACK (on_delete), ov);
173
174   return ov;
175 }
176
177
178 void
179 reload_the_viewer (void)
180 {
181   struct stat buf;
182
183   /* If there is no output, then don't do anything */
184   if (0 != stat (OUTPUT_FILE_NAME, &buf))
185     return ;
186
187   if ( NULL == the_output_viewer )
188     {
189       the_output_viewer =
190         (struct output_viewer *) window_create (WINDOW_OUTPUT, NULL);
191     }
192
193   reload_viewer (the_output_viewer);
194 }
195
196
197 void
198 reload_viewer (struct output_viewer *ov)
199 {
200   GtkTextIter end_iter;
201   GtkTextMark *mark ;
202
203   static char *line = NULL;
204
205   gboolean chars_inserted = FALSE;
206
207   gtk_text_buffer_get_end_iter (ov->buffer, &end_iter);
208
209   line = xrealloc (line, sizeof (char) * (viewer_width + 1));
210
211
212   mark = gtk_text_buffer_create_mark (ov->buffer, NULL, &end_iter, TRUE);
213
214 #ifdef __CYGWIN__
215   /*
216     Apparently Windoze is not capabale of writing to a file whilst
217     another (or the same) process is reading from it.   Therefore, we
218     must close the file after reading it, and clear the entire buffer
219     before writing to it.
220     This will be slower for large buffers, but should work
221     (in so far as anything ever works on windows).
222   */
223   {
224     GtkTextIter start_iter;
225     FILE *fp = fopen (OUTPUT_FILE_NAME, "r");
226     if ( !fp)
227       {
228         g_print ("Cannot open %s\n", OUTPUT_FILE_NAME);
229         return;
230       }
231
232     /* Delete all the entire buffer */
233     gtk_text_buffer_get_start_iter (ov->buffer, &start_iter);
234     gtk_text_buffer_delete (ov->buffer, &start_iter, &end_iter);
235
236
237     gtk_text_buffer_get_start_iter (ov->buffer, &start_iter);
238     /* Read in the next lot of text */
239     while (fgets (line, viewer_width + 1, fp) != NULL)
240       {
241         chars_inserted = TRUE;
242         gtk_text_buffer_insert (ov->buffer, &start_iter, line, -1);
243       }
244
245     fclose (fp);
246   }
247 #else
248   {
249     if ( ov->fp == NULL)
250       {
251         ov->fp = fopen (OUTPUT_FILE_NAME, "r");
252         if ( ov->fp == NULL)
253           {
254             g_print ("Cannot open %s\n", OUTPUT_FILE_NAME);
255             return;
256           }
257       }
258
259     /* Read in the next lot of text */
260     while (fgets (line, viewer_width + 1, ov->fp) != NULL)
261       {
262         chars_inserted = TRUE;
263         gtk_text_buffer_insert (ov->buffer, &end_iter, line, -1);
264       }
265   }
266 #endif
267
268   /* Scroll to where the start of this lot of text begins */
269   gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (ov->textview),
270                                 mark,
271                                 0.1, TRUE, 0.0, 0.0);
272
273
274   if ( chars_inserted )
275     gtk_window_set_urgency_hint ( ((struct editor_window *)ov)->window, TRUE);
276 }
277
278
279