Greatly simplify PSPP configuration.
[pspp-builds.git] / src / ui / gui / message-dialog.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2004, 2005, 2010 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/assertion.h>
27 #include <libpspp/message.h>
28 #include <libpspp/str.h>
29 #include <libpspp/msg-locator.h>
30 #include "message-dialog.h"
31 #include "progname.h"
32
33
34 #include <gtk/gtk.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 GtkBuilder *message_xml;
53 static GtkWidget *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 = builder_new ("message-dialog.ui");
64   message_dialog = get_widget_assert (message_xml, "message-dialog");
65
66   GTK_WIDGET_SET_FLAGS (get_widget_assert (message_xml, "close-button"),
67                         GTK_CAN_DEFAULT);
68
69 }
70
71 void
72 message_dialog_done (void)
73 {
74   msg_done ();
75   g_queue_free (early_queue);
76   dropped_messages = 0;
77   g_queue_free (late_queue);
78   gtk_widget_destroy (message_dialog);
79   g_object_unref (message_xml);
80 }
81
82 static void
83 format_message (struct msg *m, struct string *msg)
84 {
85   const char *label;
86
87   if (m->where.file_name)
88     ds_put_format (msg, "%s:", m->where.file_name);
89   if (m->where.line_number != -1)
90     ds_put_format (msg, "%d:", m->where.line_number);
91   if (m->where.file_name || m->where.line_number != -1)
92     ds_put_char (msg, ' ');
93
94   switch (m->severity)
95     {
96     case MSG_S_ERROR:
97       switch (m->category)
98         {
99         case MSG_C_SYNTAX:
100           label = _("syntax error");
101           break;
102
103         case MSG_C_DATA:
104           label = _("data file error");
105           break;
106
107         case MSG_C_GENERAL:
108         default:
109           label = _("PSPP error");
110           break;
111         }
112       break;
113     case MSG_S_WARNING:
114       switch (m->category)
115         {
116         case MSG_C_SYNTAX:
117           label = _("syntax warning");
118           break;
119
120         case MSG_C_DATA:
121           label = _("data file warning");
122           break;
123
124         case MSG_C_GENERAL:
125         default:
126           label = _("PSPP warning");
127           break;
128         }
129       break;
130     case MSG_S_NOTE:
131     default:
132       switch (m->category)
133         {
134         case MSG_C_SYNTAX:
135           label = _("syntax information");
136           break;
137
138         case MSG_C_DATA:
139           label = _("data file information");
140           break;
141
142         case MSG_C_GENERAL:
143         default:
144           label = _("PSPP information");
145           break;
146         }
147       break;
148     }
149   ds_put_format (msg, "%s: %s\n", label, m->text);
150   msg_destroy (m);
151 }
152
153 static void
154 enqueue_msg (const struct msg *msg)
155 {
156   struct msg *m = msg_dup (msg);
157
158   switch (m->severity)
159     {
160     case MSG_S_ERROR:
161       error_cnt++;
162       break;
163     case MSG_S_WARNING:
164       warning_cnt++;
165       break;
166     case MSG_S_NOTE:
167       note_cnt++;
168       break;
169     case MSG_N_SEVERITIES:
170       NOT_REACHED ();
171     }
172
173   if (g_queue_get_length (early_queue) < MAX_EARLY_MESSAGES)
174     {
175       if (g_queue_is_empty (early_queue))
176         g_idle_add (popup_messages, NULL);
177       g_queue_push_tail (early_queue, m);
178     }
179   else
180     {
181       if (g_queue_get_length (late_queue) >= MAX_LATE_MESSAGES)
182         {
183           struct msg *m = g_queue_pop_head (late_queue);
184           msg_destroy (m);
185           dropped_messages++;
186         }
187       g_queue_push_tail (late_queue, m);
188     }
189 }
190
191 static gboolean
192 popup_messages (gpointer unused UNUSED)
193 {
194   GtkTextBuffer *text_buffer;
195   GtkTextIter end;
196   GtkTextView *text_view;
197   GtkLabel *label;
198   struct string lead = DS_EMPTY_INITIALIZER;
199   struct string msg = DS_EMPTY_INITIALIZER;
200   int message_cnt;
201
202   gdk_threads_enter ();
203
204   /* Set up the dialog. */
205   if (message_xml == NULL || message_dialog == NULL)
206     goto use_fallback;
207
208   /* If a pointer grab is in effect, then the combination of that, and
209      a modal dialog box, will cause an impossible situation.
210      So don't pop it up just yet.
211   */
212   if ( gdk_display_pointer_is_grabbed (gtk_widget_get_display (message_dialog)))
213     {
214       ds_destroy (&lead);
215       ds_destroy (&msg);
216       gdk_threads_leave ();
217       return TRUE;
218     }
219
220   /* Compose the lead-in. */
221   message_cnt = error_cnt + warning_cnt + note_cnt;
222   if (dropped_messages == 0)
223     ds_put_format (
224       &lead,
225       ngettext ("The PSPP processing engine reported the following message:",
226                 "The PSPP processing engine reported the following messages:",
227                 message_cnt));
228   else
229     {
230       ds_put_format (
231         &lead,
232         ngettext ("The PSPP processing engine reported %d message.",
233                   "The PSPP processing engine reported %d messages.",
234                   message_cnt),
235         message_cnt);
236       ds_put_cstr (&lead, "  ");
237       ds_put_format (
238         &lead,
239         ngettext ("%d of these messages are displayed below.",
240                   "%d of these messages are displayed below.",
241                   MAX_EARLY_MESSAGES + MAX_LATE_MESSAGES),
242         MAX_EARLY_MESSAGES + MAX_LATE_MESSAGES);
243     }
244
245
246   /* Compose the messages. */
247   while (!g_queue_is_empty (early_queue))
248     format_message (g_queue_pop_head (early_queue), &msg);
249   if (dropped_messages)
250     {
251       ds_put_format (&msg, "...\nOmitting %lu messages\n...\n",
252                      dropped_messages);
253       dropped_messages = 0;
254     }
255   while (!g_queue_is_empty (late_queue))
256     format_message (g_queue_pop_head (late_queue), &msg);
257
258   text_buffer = gtk_text_buffer_new (NULL);
259   gtk_text_buffer_get_end_iter (text_buffer, &end);
260   gtk_text_buffer_insert (text_buffer, &end, ds_data (&msg), ds_length (&msg));
261
262   label = GTK_LABEL (get_widget_assert (message_xml, "lead-in"));
263   if (label == NULL)
264     goto use_fallback;
265   gtk_label_set_text (label, ds_cstr (&lead));
266
267   text_view = GTK_TEXT_VIEW (get_widget_assert (message_xml, "message"));
268   if (text_view == NULL)
269     goto use_fallback;
270   gtk_text_view_set_buffer (text_view, text_buffer);
271
272   gtk_widget_grab_default (get_widget_assert (message_xml, "close-button"));
273   gtk_widget_grab_focus (get_widget_assert (message_xml, "close-button"));
274   gtk_dialog_run ( GTK_DIALOG (message_dialog));
275   gtk_widget_hide (message_dialog);
276
277   ds_destroy (&lead);
278   ds_destroy (&msg);
279
280   gdk_threads_leave ();
281   return FALSE;
282
283 use_fallback:
284   g_warning ("Could not create message dialog.  "
285              "Is PSPPIRE properly installed?");
286   fputs (ds_cstr (&msg), stderr);
287   ds_destroy (&lead);
288   ds_destroy (&msg);
289   gdk_threads_leave ();
290   return FALSE;
291 }
292