Consolidate multiple messages into single message dialog. Patch
authorBen Pfaff <blp@gnu.org>
Sun, 10 Feb 2008 07:08:45 +0000 (07:08 +0000)
committerBen Pfaff <blp@gnu.org>
Sun, 10 Feb 2008 07:08:45 +0000 (07:08 +0000)
#6405.  Thanks to John Darrington for review.

* automake.mk (dist_src_ui_gui_psppire_DATA): Add
message-dialog.glade.

* helper.c (give_help): Use GtkMessageDialog directly instead of
trying to reuse message-dialog code.

* message-dialog.c: Rewritten.

* message-dialog.glade: New file.

src/ui/gui/automake.mk
src/ui/gui/helper.c
src/ui/gui/message-dialog.c
src/ui/gui/message-dialog.glade [new file with mode: 0644]

index 3a1dd3a648d440e071a78bba6654ac84ffe8daa5..12be6283dfc180e9f05047dd9a97ba6d5b54b7fc 100644 (file)
@@ -53,6 +53,7 @@ dist_src_ui_gui_psppire_DATA = \
        $(top_srcdir)/src/ui/gui/descriptives-dialog.glade \
        $(top_srcdir)/src/ui/gui/crosstabs.glade \
        $(top_srcdir)/src/ui/gui/frequencies.glade \
+       $(top_srcdir)/src/ui/gui/message-dialog.glade \
        $(top_srcdir)/src/ui/gui/oneway.glade \
        $(top_srcdir)/src/ui/gui/output-viewer.glade \
        $(top_srcdir)/src/ui/gui/psppire.glade \
index da74882a8f0bbae0557c91b3d6de82256a6e490b..bc197e63adcd2a7c7416a7b1da41f19522ed22ae 100644 (file)
@@ -129,17 +129,16 @@ pspp_locale_to_utf8 (const gchar *text, gssize len, GError **err)
 static void
 give_help (void)
 {
-  static struct msg m = {
-    MSG_GENERAL,
-    MSG_NOTE,
-    {0, -1},
-    0,
-  };
-
-  if (! m.text)
-    m.text=g_strdup (_("Sorry. The help system hasn't yet been implemented."));
-
-  popup_message (&m);
+  GtkWidget *dialog;
+
+  dialog = gtk_message_dialog_new (NULL,
+                                   GTK_DIALOG_MODAL,
+                                   GTK_MESSAGE_INFO,
+                                   GTK_BUTTONS_CLOSE,
+                                   _("Sorry. The help system hasn't yet "
+                                     "been implemented."));
+  gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
 }
 
 void
index 708d397e066cd8332dd72cb00ef86c489fd1d5fb..525fca40e22894b6ff70823efa359cf56499d153 100644 (file)
 #include "helper.h"
 
 static void enqueue_msg (const struct msg *m);
+static gboolean popup_messages (gpointer);
 
+#define MAX_EARLY_MESSAGES 100
+static GQueue *early_queue;
 
-static GQueue *message_queue;
+static unsigned long dropped_messages;
 
+#define MAX_LATE_MESSAGES 10
+static GQueue *late_queue;
+
+static int error_cnt, warning_cnt, note_cnt;
+
+static GladeXML *message_xml;
+static GtkDialog *message_dialog;
 
 void
 message_dialog_init (struct source_stream *ss)
 {
-  message_queue = g_queue_new ();
+  early_queue = g_queue_new ();
+  dropped_messages = 0;
+  late_queue = g_queue_new ();
+  error_cnt = warning_cnt = note_cnt = 0;
   msg_init (ss, enqueue_msg);
+  message_xml = XML_NEW ("message-dialog.glade");
+  message_dialog = GTK_DIALOG (get_widget_assert (message_xml,
+                                                  "message-dialog"));
 }
 
 void
 message_dialog_done (void)
 {
   msg_done ();
-  g_queue_free (message_queue);
-}
-
-static gboolean
-dequeue_message (gpointer data)
-{
-  struct msg * m ;
-
-  /* If a pointer grab is in effect, then the combination of that, and
-     a modal dialog box, will cause an impossible situation.
-     So don't pop it up just yet.
-  */
-  if ( gdk_pointer_is_grabbed ())
-    return TRUE;
-
-  m = g_queue_pop_tail (message_queue);
-
-  if ( m )
-    {
-      popup_message (m);
-      msg_destroy (m);
-      return TRUE;
-    }
-
-  return FALSE;
+  g_queue_free (early_queue);
+  dropped_messages = 0;
+  g_queue_free (late_queue);
+  gtk_widget_destroy (GTK_WIDGET (message_dialog));
+  g_object_unref (message_xml);
 }
 
 static void
-enqueue_msg (const struct msg *msg)
+format_message (struct msg *m, struct string *msg)
 {
-  struct msg *m = msg_dup (msg);
-
-  g_queue_push_head (message_queue, m);
-
-  g_idle_add (dequeue_message, 0);
-}
-
+  const char *label;
 
-void
-popup_message (const struct msg *m)
-{
-  GtkWidget *dialog;
-  gchar *location = NULL;
-
-  gint message_type;
-  const char *msg;
+  if (m->where.file_name)
+    ds_put_format (msg, "%s:", m->where.file_name);
+  if (m->where.line_number != -1)
+    ds_put_format (msg, "%d:", m->where.line_number);
+  if (m->where.file_name || m->where.line_number != -1)
+    ds_put_char (msg, ' ');
 
   switch (m->severity)
     {
     case MSG_ERROR:
-      message_type = GTK_MESSAGE_ERROR;
       switch (m->category)
        {
        case MSG_SYNTAX:
-         msg = _("Syntax Error");
+         label = _("syntax error");
          break;
 
        case MSG_DATA:
-         msg = _("Data File Error");
+         label = _("data file error");
          break;
 
        case MSG_GENERAL:
        default:
-         msg = _("PSPP Error");
+         label = _("PSPP error");
          break;
-       };
+       }
       break;
     case MSG_WARNING:
-      message_type = GTK_MESSAGE_WARNING;
       switch (m->category)
        {
        case MSG_SYNTAX:
-         msg = _("Syntax Warning");
-      break;
+         label = _("syntax warning");
+          break;
 
        case MSG_DATA:
-         msg = _("Data File Warning");
+         label = _("data file warning");
          break;
 
        case MSG_GENERAL:
-    default:
-         msg = _("PSPP Warning");
-      break;
-    };
+        default:
+         label = _("PSPP warning");
+          break;
+        }
       break;
     case MSG_NOTE:
     default:
-      message_type = GTK_MESSAGE_INFO;
-  switch (m->category)
-    {
-    case MSG_SYNTAX:
-         msg = _("Syntax Information");
+      switch (m->category)
+        {
+        case MSG_SYNTAX:
+         label = _("syntax information");
+          break;
+
+        case MSG_DATA:
+         label = _("data file information");
+          break;
+
+        case MSG_GENERAL:
+        default:
+         label = _("PSPP information");
+         break;
+       }
       break;
+    }
+  ds_put_format (msg, "%s: %s\n", label, m->text);
+  msg_destroy (m);
+}
 
-    case MSG_DATA:
-         msg = _("Data File Information");
-      break;
+static void
+enqueue_msg (const struct msg *msg)
+{
+  struct msg *m = msg_dup (msg);
 
-    case MSG_GENERAL:
-    default:
-         msg = _("PSPP Information");
-         break;
-       };
+  switch (m->severity)
+    {
+    case MSG_ERROR:
+      error_cnt++;
       break;
-    };
-
-  dialog = gtk_message_dialog_new ( NULL,
-                                 GTK_DIALOG_MODAL,
-                                 message_type,
-                                 GTK_BUTTONS_CLOSE,
-                                 msg);
-  if ( m->where.line_number != -1)
+    case MSG_WARNING:
+      warning_cnt++;
+      break;
+    case MSG_NOTE:
+      note_cnt++;
+      break;
+    }
+
+  if (g_queue_get_length (early_queue) < MAX_EARLY_MESSAGES)
     {
-      location = g_strdup_printf (_("%s (line %d)"),
-                                 m->where.file_name ? m->where.file_name : "",
-                                 m->where.line_number);
+      if (g_queue_is_empty (early_queue))
+        g_idle_add (popup_messages, NULL);
+      g_queue_push_tail (early_queue, m);
     }
   else
     {
-      location = g_strdup_printf (_("%s"),
-                                 m->where.file_name ? m->where.file_name : "");    }
+      if (g_queue_get_length (late_queue) >= MAX_LATE_MESSAGES)
+        {
+          struct msg *m = g_queue_pop_head (late_queue);
+          msg_destroy (m);
+          dropped_messages++;
+        }
+      g_queue_push_tail (late_queue, m);
+    }
+}
 
-  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                                           _("%s %s"),
-                                           location,
-                                           m->text);
-  g_free (location);
+gboolean
+popup_messages (gpointer unused UNUSED)
+{
+  GtkTextBuffer *text_buffer;
+  GtkTextIter end;
+  GtkTextView *text_view;
+  GtkLabel *label;
+  struct string lead, msg;
+  int message_cnt;
 
-  gtk_window_set_keep_above (GTK_WINDOW (dialog), TRUE);
+  /* If a pointer grab is in effect, then the combination of that, and
+     a modal dialog box, will cause an impossible situation.
+     So don't pop it up just yet.
+  */
+  if ( gdk_pointer_is_grabbed ())
+    return TRUE;
+
+  /* Compose the lead-in. */
+  message_cnt = error_cnt + warning_cnt + note_cnt;
+  ds_init_empty (&lead);
+  if (dropped_messages == 0)
+    ds_put_format (
+      &lead,
+      ngettext ("The PSPP processing engine reported the following message:",
+                "The PSPP processing engine reported the following messages:",
+                message_cnt));
+  else
+    {
+      ds_put_format (
+        &lead,
+        ngettext ("The PSPP processing engine reported %d message.",
+                  "The PSPP processing engine reported %d messages.",
+                  message_cnt),
+        message_cnt);
+      ds_put_cstr (&lead, "  ");
+      ds_put_format (
+        &lead,
+        ngettext ("%d of these messages are displayed below.",
+                  "%d of these messages are displayed below.",
+                  MAX_EARLY_MESSAGES + MAX_LATE_MESSAGES),
+        MAX_EARLY_MESSAGES + MAX_LATE_MESSAGES);
+    }
+
+
+  /* Compose the messages. */
+  ds_init_empty (&msg);
+  while (!g_queue_is_empty (early_queue))
+    format_message (g_queue_pop_head (early_queue), &msg);
+  if (dropped_messages)
+    {
+      ds_put_format (&msg, "...\nOmitting %lu messages\n...\n",
+                     dropped_messages);
+      dropped_messages = 0;
+    }
+  while (!g_queue_is_empty (late_queue))
+    format_message (g_queue_pop_head (late_queue), &msg);
+
+  /* Set up the dialog. */
+  if (message_xml == NULL || message_dialog == NULL)
+    goto use_fallback;
+
+  text_buffer = gtk_text_buffer_new (NULL);
+  gtk_text_buffer_get_end_iter (text_buffer, &end);
+  gtk_text_buffer_insert (text_buffer, &end, ds_data (&msg), ds_length (&msg));
+  ds_destroy (&msg);
+
+  label = GTK_LABEL (get_widget_assert (message_xml, "lead-in"));
+  if (label == NULL)
+    goto use_fallback;
+  gtk_label_set_text (label, ds_cstr (&lead));
 
-  gtk_dialog_run (GTK_DIALOG (dialog));
+  text_view = GTK_TEXT_VIEW (get_widget_assert (message_xml, "message"));
+  if (text_view == NULL)
+    goto use_fallback;
+  gtk_text_view_set_buffer (text_view, text_buffer);
 
-  gtk_widget_destroy (dialog);
+  gtk_dialog_run (message_dialog);
+  gtk_widget_hide (GTK_WIDGET (message_dialog));
+
+  return FALSE;
+
+use_fallback:
+  g_warning ("Could not create message dialog.  "
+             "Is PSPPIRE properly installed?");
+  fputs (ds_cstr (&msg), stderr);
+  ds_destroy (&lead);
+  ds_destroy (&msg);
+  return FALSE;
 }
 
diff --git a/src/ui/gui/message-dialog.glade b/src/ui/gui/message-dialog.glade
new file mode 100644 (file)
index 0000000..719c1aa
--- /dev/null
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.0 on Sat Feb  9 22:47:30 2008 -->
+<glade-interface>
+  <widget class="GtkDialog" id="message-dialog">
+    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Messages Reported</property>
+    <property name="modal">True</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkHBox" id="hbox1">
+            <property name="visible">True</property>
+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+            <child>
+              <widget class="GtkImage" id="image1">
+                <property name="visible">True</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="stock">gtk-dialog-info</property>
+                <property name="icon_size">6</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkVBox" id="vbox1">
+                <property name="visible">True</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <child>
+                  <widget class="GtkLabel" id="lead-in">
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">The PSPP processor reported # errors.  The first # and last # are shown below:</property>
+                    <property name="wrap">True</property>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkScrolledWindow" id="scrolledwindow1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                    <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+                    <child>
+                      <widget class="GtkTextView" id="message">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="editable">False</property>
+                        <property name="wrap_mode">GTK_WRAP_WORD</property>
+                        <property name="cursor_visible">False</property>
+                      </widget>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="button1">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="label" translatable="yes">gtk-close</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>