Ensure that windows opens the right file for output.
[pspp] / src / ui / gui / message-dialog.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2004, 2005 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
18 #include <stdio.h>
19 #include <stdarg.h>
20
21 #include <config.h>
22 #include <gettext.h>
23 #define _(msgid) gettext (msgid)
24 #define N_(msgid) msgid
25
26 #include <libpspp/message.h>
27 #include <libpspp/str.h>
28 #include <libpspp/msg-locator.h>
29 #include "message-dialog.h"
30 #include "progname.h"
31
32
33 #include <gtk/gtk.h>
34 #include <glade/glade.h>
35 #include <glib.h>
36
37 #include "helper.h"
38
39 static void enqueue_msg (const struct msg *m);
40 static gboolean popup_messages (gpointer);
41
42 #define MAX_EARLY_MESSAGES 100
43 static GQueue *early_queue;
44
45 static unsigned long dropped_messages;
46
47 #define MAX_LATE_MESSAGES 10
48 static GQueue *late_queue;
49
50 static int error_cnt, warning_cnt, note_cnt;
51
52 static GladeXML *message_xml;
53 static GtkDialog *message_dialog;
54
55 void
56 message_dialog_init (struct source_stream *ss)
57 {
58   early_queue = g_queue_new ();
59   dropped_messages = 0;
60   late_queue = g_queue_new ();
61   error_cnt = warning_cnt = note_cnt = 0;
62   msg_init (ss, enqueue_msg);
63   message_xml = XML_NEW ("message-dialog.glade");
64   message_dialog = GTK_DIALOG (get_widget_assert (message_xml,
65                                                   "message-dialog"));
66 }
67
68 void
69 message_dialog_done (void)
70 {
71   msg_done ();
72   g_queue_free (early_queue);
73   dropped_messages = 0;
74   g_queue_free (late_queue);
75   gtk_widget_destroy (GTK_WIDGET (message_dialog));
76   g_object_unref (message_xml);
77 }
78
79 static void
80 format_message (struct msg *m, struct string *msg)
81 {
82   const char *label;
83
84   if (m->where.file_name)
85     ds_put_format (msg, "%s:", m->where.file_name);
86   if (m->where.line_number != -1)
87     ds_put_format (msg, "%d:", m->where.line_number);
88   if (m->where.file_name || m->where.line_number != -1)
89     ds_put_char (msg, ' ');
90
91   switch (m->severity)
92     {
93     case MSG_ERROR:
94       switch (m->category)
95         {
96         case MSG_SYNTAX:
97           label = _("syntax error");
98           break;
99
100         case MSG_DATA:
101           label = _("data file error");
102           break;
103
104         case MSG_GENERAL:
105         default:
106           label = _("PSPP error");
107           break;
108         }
109       break;
110     case MSG_WARNING:
111       switch (m->category)
112         {
113         case MSG_SYNTAX:
114           label = _("syntax warning");
115           break;
116
117         case MSG_DATA:
118           label = _("data file warning");
119           break;
120
121         case MSG_GENERAL:
122         default:
123           label = _("PSPP warning");
124           break;
125         }
126       break;
127     case MSG_NOTE:
128     default:
129       switch (m->category)
130         {
131         case MSG_SYNTAX:
132           label = _("syntax information");
133           break;
134
135         case MSG_DATA:
136           label = _("data file information");
137           break;
138
139         case MSG_GENERAL:
140         default:
141           label = _("PSPP information");
142           break;
143         }
144       break;
145     }
146   ds_put_format (msg, "%s: %s\n", label, m->text);
147   msg_destroy (m);
148 }
149
150 static void
151 enqueue_msg (const struct msg *msg)
152 {
153   struct msg *m = msg_dup (msg);
154
155   switch (m->severity)
156     {
157     case MSG_ERROR:
158       error_cnt++;
159       break;
160     case MSG_WARNING:
161       warning_cnt++;
162       break;
163     case MSG_NOTE:
164       note_cnt++;
165       break;
166     }
167
168   if (g_queue_get_length (early_queue) < MAX_EARLY_MESSAGES)
169     {
170       if (g_queue_is_empty (early_queue))
171         g_idle_add (popup_messages, NULL);
172       g_queue_push_tail (early_queue, m);
173     }
174   else
175     {
176       if (g_queue_get_length (late_queue) >= MAX_LATE_MESSAGES)
177         {
178           struct msg *m = g_queue_pop_head (late_queue);
179           msg_destroy (m);
180           dropped_messages++;
181         }
182       g_queue_push_tail (late_queue, m);
183     }
184 }
185
186 gboolean
187 popup_messages (gpointer unused UNUSED)
188 {
189   GtkTextBuffer *text_buffer;
190   GtkTextIter end;
191   GtkTextView *text_view;
192   GtkLabel *label;
193   struct string lead = DS_EMPTY_INITIALIZER;
194   struct string msg = DS_EMPTY_INITIALIZER;
195   int message_cnt;
196
197   /* If a pointer grab is in effect, then the combination of that, and
198      a modal dialog box, will cause an impossible situation.
199      So don't pop it up just yet.
200   */
201   if ( gdk_pointer_is_grabbed ())
202     return TRUE;
203
204   /* Compose the lead-in. */
205   message_cnt = error_cnt + warning_cnt + note_cnt;
206   if (dropped_messages == 0)
207     ds_put_format (
208       &lead,
209       ngettext ("The PSPP processing engine reported the following message:",
210                 "The PSPP processing engine reported the following messages:",
211                 message_cnt));
212   else
213     {
214       ds_put_format (
215         &lead,
216         ngettext ("The PSPP processing engine reported %d message.",
217                   "The PSPP processing engine reported %d messages.",
218                   message_cnt),
219         message_cnt);
220       ds_put_cstr (&lead, "  ");
221       ds_put_format (
222         &lead,
223         ngettext ("%d of these messages are displayed below.",
224                   "%d of these messages are displayed below.",
225                   MAX_EARLY_MESSAGES + MAX_LATE_MESSAGES),
226         MAX_EARLY_MESSAGES + MAX_LATE_MESSAGES);
227     }
228
229
230   /* Compose the messages. */
231   while (!g_queue_is_empty (early_queue))
232     format_message (g_queue_pop_head (early_queue), &msg);
233   if (dropped_messages)
234     {
235       ds_put_format (&msg, "...\nOmitting %lu messages\n...\n",
236                      dropped_messages);
237       dropped_messages = 0;
238     }
239   while (!g_queue_is_empty (late_queue))
240     format_message (g_queue_pop_head (late_queue), &msg);
241
242   /* Set up the dialog. */
243   if (message_xml == NULL || message_dialog == NULL)
244     goto use_fallback;
245
246   text_buffer = gtk_text_buffer_new (NULL);
247   gtk_text_buffer_get_end_iter (text_buffer, &end);
248   gtk_text_buffer_insert (text_buffer, &end, ds_data (&msg), ds_length (&msg));
249
250   label = GTK_LABEL (get_widget_assert (message_xml, "lead-in"));
251   if (label == NULL)
252     goto use_fallback;
253   gtk_label_set_text (label, ds_cstr (&lead));
254
255   text_view = GTK_TEXT_VIEW (get_widget_assert (message_xml, "message"));
256   if (text_view == NULL)
257     goto use_fallback;
258   gtk_text_view_set_buffer (text_view, text_buffer);
259
260   gtk_dialog_run (message_dialog);
261   gtk_widget_hide (GTK_WIDGET (message_dialog));
262
263   ds_destroy (&lead);
264   ds_destroy (&msg);
265
266   return FALSE;
267
268 use_fallback:
269   g_warning ("Could not create message dialog.  "
270              "Is PSPPIRE properly installed?");
271   fputs (ds_cstr (&msg), stderr);
272   ds_destroy (&lead);
273   ds_destroy (&msg);
274   return FALSE;
275 }
276