r17373 - gnucash/branches/2.2/src - [r17245-r17247] Add many new features to the progress dialog API

Andreas Köhler andi5 at cvs.gnucash.org
Tue Jul 22 18:31:30 EDT 2008


Author: andi5
Date: 2008-07-22 18:31:29 -0400 (Tue, 22 Jul 2008)
New Revision: 17373
Trac: http://svn.gnucash.org/trac/changeset/17373

Modified:
   gnucash/branches/2.2/src/gnome/dialog-progress.c
   gnucash/branches/2.2/src/gnome/dialog-progress.h
   gnucash/branches/2.2/src/gnome/glade/progress.glade
   gnucash/branches/2.2/src/import-export/qif-import/druid-qif-import.c
   gnucash/branches/2.2/src/import-export/qif-import/qif-file.scm
   gnucash/branches/2.2/src/import-export/qif-import/qif-import.scm
   gnucash/branches/2.2/src/import-export/qif-import/qif-parse.scm
   gnucash/branches/2.2/src/import-export/qif-import/qif-to-gnc.scm
   gnucash/branches/2.2/src/import-export/qif-import/qif-utils.scm
   gnucash/branches/2.2/src/import-export/qif-import/qif.glade
   gnucash/branches/2.2/src/scm/string.scm
Log:
[r17245-r17247] Add many new features to the progress dialog API

The following changes are included:

-Add many new features to the progress dialog API, including support for n
 levels of suboperations. Fully compatible with existing code.
-Add doxygen documentation for the progress dialog API (none previously
 existed).
-Add a progress bar page to the QIF importer with pause and cancel functions for
 the loading and parsing phases.
-Log loading and parsing error messages on the page instead of using popups,
 which resolves bug #309359.
-Add a pair of new procedures to the library of Scheme string routines.
-Finally, some miscellaneous QIF importer clean up.

Add or improve many QIF importer error and warning messages.  Should help
users in debugging their own QIF files.

Some minor corrections to the doxygen documentation for progress-dialog.h.


Modified: gnucash/branches/2.2/src/gnome/dialog-progress.c
===================================================================
--- gnucash/branches/2.2/src/gnome/dialog-progress.c	2008-07-22 22:31:19 UTC (rev 17372)
+++ gnucash/branches/2.2/src/gnome/dialog-progress.c	2008-07-22 22:31:29 UTC (rev 17373)
@@ -36,12 +36,24 @@
 {
   GtkWidget *dialog;
 
-  GtkWidget *heading_label;
+  GtkWidget *primary_label;
+  GtkWidget *secondary_label;
   GtkWidget *progress_bar;
+  GtkWidget *sub_label;
+  GtkWidget *log;
 
   GtkWidget *ok_button;
   GtkWidget *cancel_button;
 
+  /* The stack of virtual progress bars. */
+  GList     *bars;
+  /* The fraction of the current bar that is filled. */
+  gdouble    bar_value;
+  /* The value of the real (top-level) bar before the last push. */
+  gdouble    total_offset;
+  /* The product of all weights in the stack. */
+  gdouble    total_weight;
+
   GNCProgressCancelFunc cancel_func;
   gpointer user_data;
 
@@ -54,32 +66,47 @@
   gboolean title_set;
 };
 
+typedef struct
+{
+  gdouble offset;
+  gdouble weight;
+} VirtualBar;
 
 static void
-gnc_progress_maybe_destroy (GNCProgressDialog *progress)
+gnc_progress_maybe_destroy(GNCProgressDialog *progress)
 {
+  g_return_if_fail(progress);
+
   if (!(progress->closed && progress->destroyed))
     return;
 
-  gtk_widget_destroy(progress->dialog);
+  if (progress->dialog != NULL)
+    gtk_widget_destroy(progress->dialog);
 }
 
+
 static void
 ok_cb(GtkWidget * widget, gpointer data)
 {
-  GNCProgressDialog *progress = data; 
+  GNCProgressDialog *progress = data;
 
-  gtk_widget_hide(progress->dialog);
+  g_return_if_fail(progress);
+
+  if (progress->dialog != NULL)
+    gtk_widget_hide(progress->dialog);
   progress->closed = TRUE;
-  gnc_progress_maybe_destroy (progress);
+  gnc_progress_maybe_destroy(progress);
 }
 
+
 static void
 cancel_cb(GtkWidget * widget, gpointer data)
 {
-  GNCProgressDialog *progress = data; 
+  GNCProgressDialog *progress = data;
 
-  if (progress->cancel_func && !progress->cancel_func (progress->user_data))
+  g_return_if_fail(progress);
+
+  if (progress->cancel_func && !progress->cancel_func(progress->user_data))
     return;
 
   if (progress->cancel_scm_func != SCM_UNDEFINED)
@@ -88,35 +115,41 @@
 
     result = scm_call_0(progress->cancel_scm_func);
 
-    if (!SCM_NFALSEP (result))
+    if (!SCM_NFALSEP(result))
       return;
   }
 
-  gtk_widget_hide(progress->dialog);
+  if (progress->dialog != NULL)
+    gtk_widget_hide(progress->dialog);
   progress->closed = TRUE;
-  gnc_progress_maybe_destroy (progress);
+  gnc_progress_maybe_destroy(progress);
 }
 
+
 static gboolean
 delete_cb(GtkWidget *widget, GdkEvent  *event, gpointer data)
 {
-  GNCProgressDialog *progress = data; 
+  GNCProgressDialog *progress = data;
 
+  g_return_val_if_fail(progress, TRUE);
+
   if (progress->finished)
   {
-    gtk_widget_hide(progress->dialog);
+    if (progress->dialog != NULL)
+      gtk_widget_hide(progress->dialog);
     progress->closed = TRUE;
-    gnc_progress_maybe_destroy (progress);
+    gnc_progress_maybe_destroy(progress);
     return TRUE;
   }
 
   if (progress->cancel_func)
   {
-    if (progress->cancel_func (progress->user_data))
+    if (progress->cancel_func(progress->user_data))
     {
-      gtk_widget_hide(progress->dialog);
+      if (progress->dialog != NULL)
+        gtk_widget_hide(progress->dialog);
       progress->closed = TRUE;
-      gnc_progress_maybe_destroy (progress);
+      gnc_progress_maybe_destroy(progress);
       return TRUE;
     }
   }
@@ -127,11 +160,12 @@
 
     result = scm_call_0(progress->cancel_scm_func);
 
-    if (SCM_NFALSEP (result))
+    if (SCM_NFALSEP(result))
     {
-      gtk_widget_hide(progress->dialog);
+      if (progress->dialog != NULL)
+        gtk_widget_hide(progress->dialog);
       progress->closed = TRUE;
-      gnc_progress_maybe_destroy (progress);
+      gnc_progress_maybe_destroy(progress);
       return TRUE;
     }
   }
@@ -140,20 +174,24 @@
   return TRUE;
 }
 
+
 static void
 destroy_cb(GtkObject *object, gpointer data)
 {
   GNCProgressDialog *progress = data;
 
+  g_return_if_fail(progress);
+
   /* Make sure the callbacks aren't invoked */
   progress->cancel_func = NULL;
   if (progress->cancel_scm_func != SCM_UNDEFINED)
-    scm_gc_unprotect_object (progress->cancel_scm_func);
+    scm_gc_unprotect_object(progress->cancel_scm_func);
   progress->cancel_scm_func = SCM_UNDEFINED;
 
   g_free(progress);
 }
 
+
 static void
 gnc_progress_dialog_create(GtkWidget * parent, GNCProgressDialog *progress)
 {
@@ -161,38 +199,51 @@
   GtkObject *tdo;
   GladeXML  *xml;
 
-  xml = gnc_glade_xml_new ("progress.glade", "Progress Dialog");
+  g_return_if_fail(progress);
 
-  dialog = glade_xml_get_widget (xml, "Progress Dialog");
+  xml = gnc_glade_xml_new("progress.glade", "Progress Dialog");
+
+  dialog = glade_xml_get_widget(xml, "Progress Dialog");
   progress->dialog = dialog;
-  tdo = GTK_OBJECT (dialog);
+  tdo = GTK_OBJECT(dialog);
 
   /* parent */
   if (parent != NULL)
     gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent));
 
-  g_signal_connect (tdo, "delete_event",
-		    G_CALLBACK (delete_cb), progress);
+  g_signal_connect(tdo, "delete_event", G_CALLBACK(delete_cb), progress);
 
-  g_signal_connect (tdo, "destroy", G_CALLBACK (destroy_cb), progress);
+  g_signal_connect(tdo, "destroy", G_CALLBACK(destroy_cb), progress);
 
-  progress->heading_label = glade_xml_get_widget (xml, "heading_label");
-  gtk_widget_hide(progress->heading_label);
+  progress->primary_label = glade_xml_get_widget(xml, "primary_label");
+  gtk_widget_hide(progress->primary_label);
 
-  progress->progress_bar = glade_xml_get_widget (xml, "progress_bar");
+  progress->secondary_label = glade_xml_get_widget(xml, "secondary_label");
+  gtk_widget_hide(progress->secondary_label);
 
-  progress->ok_button = glade_xml_get_widget (xml, "ok_button");
+  progress->progress_bar = glade_xml_get_widget(xml, "progress_bar");
+  progress->total_offset = 0;
+  progress->total_weight = 1;
+  progress->bar_value = 0;
 
+  progress->sub_label = glade_xml_get_widget(xml, "sub_label");
+  gtk_widget_hide(progress->sub_label);
+
+  progress->log = glade_xml_get_widget(xml, "progress_log");
+  gtk_widget_hide(glade_xml_get_widget(xml, "progress_log_window"));
+
+  progress->ok_button = glade_xml_get_widget(xml, "ok_button");
+
   g_signal_connect(progress->ok_button, "clicked",
-		   G_CALLBACK(ok_cb), progress);
+                   G_CALLBACK(ok_cb), progress);
 
   if (!progress->use_ok_button)
-    gtk_widget_hide (progress->ok_button);
+    gtk_widget_hide(progress->ok_button);
 
-  progress->cancel_button = glade_xml_get_widget (xml, "cancel_button");
+  progress->cancel_button = glade_xml_get_widget(xml, "cancel_button");
 
   g_signal_connect(progress->cancel_button, "clicked",
-		   G_CALLBACK(cancel_cb), progress);
+                   G_CALLBACK(cancel_cb), progress);
 
   progress->cancel_func = NULL;
   progress->user_data = NULL;
@@ -205,8 +256,9 @@
   progress->title_set = FALSE;
 }
 
+
 GNCProgressDialog *
-gnc_progress_dialog_new (GtkWidget * parent, gboolean use_ok_button)
+gnc_progress_dialog_new(GtkWidget * parent, gboolean use_ok_button)
 {
   GNCProgressDialog *progress;
 
@@ -218,147 +270,527 @@
 
   gtk_widget_show(progress->dialog);
 
-  gnc_progress_dialog_update (progress);
+  gnc_progress_dialog_update(progress);
 
   return progress;
 }
 
+
+GNCProgressDialog *
+gnc_progress_dialog_custom(GtkLabel       *primary,
+                           GtkLabel       *secondary,
+                           GtkProgressBar *bar,
+                           GtkLabel       *suboperation,
+                           GtkTextView    *log)
+{
+  GNCProgressDialog *progress;
+
+  progress = g_new0(GNCProgressDialog, 1);
+
+  /* Set up widgets. */
+  progress->dialog = NULL;
+  progress->primary_label = GTK_WIDGET(primary);
+  progress->secondary_label = GTK_WIDGET(secondary);
+  progress->progress_bar = GTK_WIDGET(bar);
+  progress->sub_label = GTK_WIDGET(suboperation);
+  progress->log = GTK_WIDGET(log);
+  progress->ok_button = NULL;
+  progress->cancel_button = NULL;
+
+  /* Initialize all other items. */
+  progress->total_offset = 0;
+  progress->total_weight = 1;
+  progress->bar_value = 0;
+  progress->cancel_func = NULL;
+  progress->user_data = NULL;
+  progress->cancel_scm_func = SCM_UNDEFINED;
+  progress->use_ok_button = FALSE;
+  progress->closed = FALSE;
+  progress->finished = FALSE;
+  progress->destroyed = FALSE;
+  progress->title_set = FALSE;
+
+  return progress;
+}
+
+
 void
-gnc_progress_dialog_set_title (GNCProgressDialog *progress, const char *title)
+gnc_progress_dialog_set_title(GNCProgressDialog *progress, const char *title)
 {
-  if (progress == NULL)
+  g_return_if_fail(progress);
+
+  if (!progress->dialog)
     return;
 
   if (title == NULL)
     title = "";
 
-  gtk_window_set_title (GTK_WINDOW (progress->dialog), title);
+  gtk_window_set_title(GTK_WINDOW(progress->dialog), title);
 
   progress->title_set = TRUE;
 
-  gnc_progress_dialog_update (progress);
+  gnc_progress_dialog_update(progress);
 }
 
+
 void
-gnc_progress_dialog_set_heading (GNCProgressDialog *progress,
+gnc_progress_dialog_set_primary(GNCProgressDialog *progress,
+                                const gchar *str)
+{
+  g_return_if_fail(progress);
+
+  if (progress->primary_label == NULL)
+    return;
+
+  if (str == NULL || *str == '\0')
+    gtk_widget_hide(progress->primary_label);
+  else
+  {
+    /* Display the primary text with the HIG-recommended style. */
+    char *markup = g_markup_printf_escaped("<span weight=\"bold\" size=\"larger\">%s</span>", str);
+
+    gtk_label_set_markup(GTK_LABEL(progress->primary_label), markup);
+    g_free(markup);
+    gtk_widget_show(progress->primary_label);
+  }
+
+  gnc_progress_dialog_update(progress);
+}
+
+
+void
+gnc_progress_dialog_set_heading(GNCProgressDialog *progress,
                                  const char *heading)
 {
-  if (progress == NULL)
+  g_return_if_fail(progress);
+
+  if (progress->primary_label == NULL)
     return;
 
   if (heading == NULL || *heading == '\0')
-    gtk_widget_hide (progress->heading_label);
+    gtk_widget_hide(progress->primary_label);
   else
   {
-    gtk_label_set_text (GTK_LABEL (progress->heading_label), heading);
-    gtk_widget_show (progress->heading_label);
+    gtk_label_set_text(GTK_LABEL(progress->primary_label), heading);
+    gtk_widget_show(progress->primary_label);
   }
 
-  gnc_progress_dialog_update (progress);
+  gnc_progress_dialog_update(progress);
 }
 
+
 void
-gnc_progress_dialog_set_cancel_func (GNCProgressDialog *progress,
-                                     GNCProgressCancelFunc cancel_func,
-                                     gpointer user_data)
+gnc_progress_dialog_set_secondary(GNCProgressDialog *progress,
+                                  const gchar *str)
 {
-  if (progress == NULL)
+  g_return_if_fail(progress);
+
+  if (progress->secondary_label == NULL)
     return;
 
+  if (str == NULL || *str == '\0')
+    gtk_widget_hide(progress->secondary_label);
+  else
+  {
+    gtk_label_set_text(GTK_LABEL(progress->secondary_label), str);
+    gtk_widget_show(progress->secondary_label);
+  }
+
+  gnc_progress_dialog_update(progress);
+}
+
+
+void
+gnc_progress_dialog_set_sub(GNCProgressDialog *progress,
+                            const gchar *str)
+{
+  g_return_if_fail(progress);
+
+  if (progress->sub_label == NULL)
+    return;
+
+  if (str == NULL || *str == '\0')
+    gtk_widget_hide(progress->sub_label);
+  else
+  {
+    /* Display the suboperation text with the HIG-recommended style. */
+    char *markup = g_markup_printf_escaped("<span style=\"italic\">%s</span>", str);
+
+    gtk_label_set_markup(GTK_LABEL(progress->sub_label), markup);
+    g_free(markup);
+    gtk_widget_show(progress->sub_label);
+  }
+
+  gnc_progress_dialog_update(progress);
+}
+
+
+void
+gnc_progress_dialog_reset_log(GNCProgressDialog *progress)
+{
+  GtkTextBuffer *buf;
+
+  g_return_if_fail(progress);
+
+  if (progress->log == NULL)
+    return;
+
+  /* Reset the text buffer. */
+  buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(progress->log));
+  gtk_text_buffer_set_text(buf, "", -1);
+  gtk_text_buffer_set_modified(buf, FALSE);
+
+  /* Show the log and its parent (in case it is in a scrolled window). */
+  gtk_widget_show(progress->log);
+  gtk_widget_show(gtk_widget_get_parent(progress->log));
+
+  gnc_progress_dialog_update(progress);
+}
+
+
+void
+gnc_progress_dialog_append_log(GNCProgressDialog *progress, const gchar *str)
+{
+  GtkTextBuffer *buf;
+  GtkTextIter    iter;
+
+  g_return_if_fail(progress);
+
+  if (progress->log == NULL || !str || !*str)
+    return;
+
+  /* Append to the text buffer. */
+  buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(progress->log));
+  gtk_text_buffer_get_end_iter(buf, &iter);
+  gtk_text_buffer_insert(buf, &iter, str, -1);
+
+  gnc_progress_dialog_update(progress);
+}
+
+
+void
+gnc_progress_dialog_pause(GNCProgressDialog *progress)
+{
+  gchar *suffix;
+
+  g_return_if_fail(progress);
+
+  suffix = g_strconcat(" ", _("(paused)"), NULL);
+
+  if (progress->sub_label && GTK_WIDGET_VISIBLE(progress->sub_label))
+  {
+    const gchar *txt = gtk_label_get_text(GTK_LABEL(progress->sub_label));
+
+    if (txt && !g_str_has_suffix(txt, suffix))
+    {
+      gchar *newtxt = g_strconcat(txt, suffix, NULL);
+      gnc_progress_dialog_set_sub(progress, newtxt);
+      g_free(newtxt);
+    }
+  }
+  else if (progress->dialog)
+  {
+    const gchar *txt = gtk_window_get_title(GTK_WINDOW(progress->dialog));
+
+    if (txt && !g_str_has_suffix(txt, suffix))
+    {
+      gchar *newtxt = g_strconcat(txt, suffix, NULL);
+      gtk_window_set_title(GTK_WINDOW(progress->dialog), newtxt);
+      g_free(newtxt);
+    }
+  }
+  else if (progress->primary_label &&
+           GTK_WIDGET_VISIBLE(progress->primary_label))
+  {
+    const gchar *txt = gtk_label_get_text(GTK_LABEL(progress->primary_label));
+
+    if (txt && !g_str_has_suffix(txt, suffix))
+    {
+      gchar *newtxt = g_strconcat(txt, suffix, NULL);
+      gnc_progress_dialog_set_primary(progress, newtxt);
+      g_free(newtxt);
+    }
+  }
+
+  g_free(suffix);
+
+  gnc_progress_dialog_update(progress);
+}
+
+void
+gnc_progress_dialog_resume(GNCProgressDialog *progress)
+{
+  gchar *suffix;
+
+  g_return_if_fail(progress);
+
+  suffix = g_strconcat(" ", _("(paused)"), NULL);
+
+  /* Remove any pause indication from the suboperation label. */
+  if (progress->sub_label)
+  {
+    const gchar *txt = gtk_label_get_text(GTK_LABEL(progress->sub_label));
+
+    if (txt && g_str_has_suffix(txt, suffix))
+    {
+      gchar *newtxt = g_strndup(txt, strlen(txt) - strlen(suffix));
+      gnc_progress_dialog_set_sub(progress, newtxt);
+      g_free(newtxt);
+    }
+  }
+
+  /* Remove any pause indication from the window title. */
+  if (progress->dialog)
+  {
+    const gchar *txt = gtk_window_get_title(GTK_WINDOW(progress->dialog));
+
+    if (txt && g_str_has_suffix(txt, suffix))
+    {
+      gchar *newtxt = g_strndup(txt, strlen(txt) - strlen(suffix));
+      gtk_window_set_title(GTK_WINDOW(progress->dialog), newtxt);
+      g_free(newtxt);
+    }
+  }
+
+  /* Remove any pause indication from the primary text. */
+  if (progress->primary_label)
+  {
+    const gchar *txt = gtk_label_get_text(GTK_LABEL(progress->primary_label));
+
+    if (txt && g_str_has_suffix(txt, suffix))
+    {
+      gchar *newtxt = g_strndup(txt, strlen(txt) - strlen(suffix));
+      gnc_progress_dialog_set_primary(progress, newtxt);
+      g_free(newtxt);
+    }
+  }
+
+  g_free(suffix);
+
+  gnc_progress_dialog_update(progress);
+}
+
+
+void
+gnc_progress_dialog_set_cancel_func(GNCProgressDialog *progress,
+                                    GNCProgressCancelFunc cancel_func,
+                                    gpointer user_data)
+{
+  g_return_if_fail(progress);
+
+  if (progress->cancel_button == NULL)
+    return;
+
   progress->cancel_func = cancel_func;
   progress->user_data = user_data;
 
   if (cancel_func)
-    gtk_widget_show (progress->cancel_button);
+    gtk_widget_show(progress->cancel_button);
 }
 
+
 void
-gnc_progress_dialog_set_cancel_scm_func (GNCProgressDialog *progress,
-                                         SCM cancel_scm_func)
+gnc_progress_dialog_set_cancel_scm_func(GNCProgressDialog *progress,
+                                        SCM cancel_scm_func)
 {
-  if (progress == NULL)
+  g_return_if_fail(progress);
+
+  if (progress->cancel_button == NULL)
     return;
 
   if (progress->cancel_scm_func != SCM_UNDEFINED)
-    scm_gc_unprotect_object (progress->cancel_scm_func);
+    scm_gc_unprotect_object(progress->cancel_scm_func);
 
   if (SCM_PROCEDUREP(cancel_scm_func))
   {
     progress->cancel_scm_func = cancel_scm_func;
-    scm_gc_protect_object (cancel_scm_func);
-    gtk_widget_show (progress->cancel_button);
+    scm_gc_protect_object(cancel_scm_func);
+    gtk_widget_show(progress->cancel_button);
   }
   else
     progress->cancel_scm_func = SCM_UNDEFINED;
 }
 
+
 void
-gnc_progress_dialog_set_value (GNCProgressDialog *progress, gdouble value)
+gnc_progress_dialog_set_value(GNCProgressDialog *progress, gdouble value)
 {
-  if (progress == NULL)
+  GtkProgressBar *bar;
+
+  g_return_if_fail(progress);
+
+  /* Get the progress bar widget. */
+  bar = GTK_PROGRESS_BAR(progress->progress_bar);
+  if (bar == NULL)
     return;
 
-  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress->progress_bar), value);
+  /* Update the progress bar. If value is over 1,
+   * the bar will pulse instead of fill. */
+  if (value > 1)
+      gtk_progress_bar_pulse(bar);
+  else
+  {
+    progress->bar_value = value > 0 ? value : 0;
+    gtk_progress_bar_set_fraction(bar,
+      progress->total_offset + progress->bar_value * progress->total_weight);
+  }
 
-  gnc_progress_dialog_update (progress);
+  gnc_progress_dialog_update(progress);
 }
 
+
+guint
+gnc_progress_dialog_push(GNCProgressDialog *progress, gdouble weight)
+{
+  GtkProgressBar *bar;
+  VirtualBar     *newbar;
+
+  g_return_val_if_fail(progress, 0);
+  g_return_val_if_fail(weight > 0, 0);
+
+  /* Get the progress bar widget. */
+  bar = GTK_PROGRESS_BAR(progress->progress_bar);
+  if (bar == NULL)
+    return 0;
+
+  /* Create the new virtual progress bar. */
+  newbar = g_new0(VirtualBar, 1);
+  newbar->offset = progress->bar_value;
+  if (newbar->offset + weight > 1)
+    /* The requested weight is more than the unfilled portion of the bar. */
+    newbar->weight = 1 - newbar->offset;
+  else
+    newbar->weight = weight;
+  progress->bars = g_list_prepend(progress->bars, newbar);
+
+  /* Set the total effective offset and weight */
+  progress->total_offset = gtk_progress_bar_get_fraction(bar);
+  progress->total_weight *= newbar->weight;
+
+  /* Set the new bar as unfilled. */
+  progress->bar_value = 0;
+
+  return g_list_length(progress->bars);
+}
+
+
+guint
+gnc_progress_dialog_pop(GNCProgressDialog *progress)
+{
+  VirtualBar     *bar;
+
+  g_return_val_if_fail(progress, 0);
+
+  /* Get the progress bar widget. */
+  if (progress->progress_bar == NULL || progress->bars == NULL)
+    return 0;
+
+  /* Pop the bar off the bar stack. */
+  bar = progress->bars->data;
+  progress->bars = g_list_delete_link(progress->bars, progress->bars);
+
+  /* Determine the value of the current bar. */
+  progress->bar_value = bar->offset + bar->weight * progress->bar_value;
+
+  /* Set the total effective offset and weight. */
+  if (progress->bars == NULL)
+  {
+    progress->total_offset = 0;
+    progress->total_weight = 1;
+  }
+  else
+  {
+    progress->total_offset -= bar->offset *
+                              ((VirtualBar *) progress->bars->data)->weight;
+    progress->total_weight /= bar->weight;
+  }
+  g_free(bar);
+
+  if (progress->bars == NULL)
+    return 0;
+  return g_list_length(progress->bars);
+}
+
+
+guint
+gnc_progress_dialog_pop_full(GNCProgressDialog *progress)
+{
+  gnc_progress_dialog_set_value(progress, 1);
+  return gnc_progress_dialog_pop(progress);
+}
+
+
 void
-gnc_progress_dialog_update (GNCProgressDialog *progress)
+gnc_progress_dialog_reset_value(GNCProgressDialog *progress)
 {
+  g_return_if_fail(progress);
+
+  /* Return to the top level. */
+  while (gnc_progress_dialog_pop(progress));
+
+  /* Reset the bar to empty. */
+  gnc_progress_dialog_set_value(progress, 0);
+}
+
+
+void
+gnc_progress_dialog_update(GNCProgressDialog *progress)
+{
   while (gtk_events_pending())
     gtk_main_iteration();
 }
 
+
 void
-gnc_progress_dialog_finish (GNCProgressDialog *progress)
+gnc_progress_dialog_finish(GNCProgressDialog *progress)
 {
-  if (progress == NULL)
-    return;
+  g_return_if_fail(progress);
 
   if (!progress->use_ok_button)
   {
-    gtk_widget_hide (progress->dialog);
+    if (progress->dialog != NULL)
+      gtk_widget_hide(progress->dialog);
     progress->closed = TRUE;
   }
 
-  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(progress->progress_bar), 1.0);
+  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress->progress_bar), 1.0);
 
-  gtk_widget_set_sensitive (progress->ok_button, TRUE);
-  gtk_widget_set_sensitive (progress->cancel_button, FALSE);
+  gtk_widget_set_sensitive(progress->ok_button, TRUE);
+  gtk_widget_set_sensitive(progress->cancel_button, FALSE);
 
-  if (GTK_WIDGET_VISIBLE(progress->heading_label))
-    gnc_progress_dialog_set_heading (progress, _("Complete"));
+  if (GTK_WIDGET_VISIBLE(progress->primary_label))
+    gnc_progress_dialog_set_heading(progress, _("Complete"));
 
   if (!progress->title_set)
-    gtk_window_set_title (GTK_WINDOW (progress->dialog), _("Complete"));
+    gtk_window_set_title(GTK_WINDOW(progress->dialog), _("Complete"));
 
-  gtk_window_set_modal (GTK_WINDOW (progress->dialog), FALSE);
+  gtk_window_set_modal(GTK_WINDOW(progress->dialog), FALSE);
 
   progress->finished = TRUE;
 
-  gnc_progress_dialog_update (progress);
+  gnc_progress_dialog_update(progress);
 }
 
+
 void
-gnc_progress_dialog_destroy (GNCProgressDialog *progress)
+gnc_progress_dialog_destroy(GNCProgressDialog *progress)
 {
-  if (progress == NULL)
-    return;
+  g_return_if_fail(progress);
 
   /* Make sure the callbacks aren't invoked */
   progress->cancel_func = NULL;
   if (progress->cancel_scm_func != SCM_UNDEFINED)
-    scm_gc_unprotect_object (progress->cancel_scm_func);
+    scm_gc_unprotect_object(progress->cancel_scm_func);
   progress->cancel_scm_func = SCM_UNDEFINED;
 
   if (!progress->finished)
   {
-    gtk_widget_hide (progress->dialog);
+    if (progress->dialog != NULL)
+      gtk_widget_hide(progress->dialog);
     progress->closed = TRUE;
   }
 
   progress->destroyed = TRUE;
 
-  gnc_progress_maybe_destroy (progress);
+  gnc_progress_maybe_destroy(progress);
 }

Modified: gnucash/branches/2.2/src/gnome/dialog-progress.h
===================================================================
--- gnucash/branches/2.2/src/gnome/dialog-progress.h	2008-07-22 22:31:19 UTC (rev 17372)
+++ gnucash/branches/2.2/src/gnome/dialog-progress.h	2008-07-22 22:31:29 UTC (rev 17373)
@@ -21,6 +21,30 @@
  *                                                                  *
 \********************************************************************/
 
+/** @addtogroup Dialogs
+    @{ */
+/** @addtogroup ProgDialog Progress Dialog
+    @{ */
+/** @file dialog-progress.h
+    @brief Dialog for displaying progress of long-running operations.
+    @author Copyright (C) 2000 Dave Peticolas
+    @author Copyright (C) 2008 Charles Day
+
+    These functions constitute an API for streamlining the creation
+    and management of progress dialogs. Once registered with the API,
+    the dialog's display and behavior can be controlled via simple API
+    calls that prevent the caller from needing to know anything about
+    the underlying GUI.
+
+    A pop-up progress dialog can be created, displayed, and registered
+    with the API by calling gnc_progress_dialog_new().  Alternatively,
+    existing widgets can be registered with the API by calling
+    gnc_progress_dialog_custom(). This method allows custom-made dialogs
+    to hand off the management of typical progress-related widgets, and
+    allows long-running operations report progress in a standard way.
+*/
+
+
 #ifndef DIALOG_PROGRESS_H
 #define DIALOG_PROGRESS_H
 
@@ -32,51 +56,230 @@
 typedef gboolean (*GNCProgressCancelFunc) (gpointer user_data);
 
 
-/* Create and return a dialog for displaying the progress of
- * an activity. Useful for long-running operations. */
-GNCProgressDialog * gnc_progress_dialog_new (GtkWidget *parent,
-                                             gboolean use_ok_button);
+/** Displays a pop-up dialog for showing the progress of a
+ *  long-running activity.
+ *
+ *  By default only a title and progress bar are shown, but
+ *  additional visual elements such as a Cancel button, text
+ *  log, and additional labels can be activated by following
+ *  with calls to some of the other API functions.
+ *  
+ *  @param parent The parent window for which the progress dialog
+ *  becomes modal.
+ *  
+ *  @param use_ok_button If @c TRUE, an OK button is shown and must be
+ *  clicked when progress is completed.
+ *
+ *  @return A ::GNCProgressDialog that identifies the dialog and
+ *  is needed when making subsequent API calls. */
+GNCProgressDialog * gnc_progress_dialog_new(GtkWidget *parent,
+                                            gboolean use_ok_button);
 
-/* Set the title of the progress dialog. */
-void gnc_progress_dialog_set_title (GNCProgressDialog *progress,
-                                    const char *title);
+/** Creates a dialog for displaying the progress of an activity using
+ *  existing widgets. This allows long-running operations to update the
+ *  progress in a custom dialog instead of a new pop-up.
+ *
+ *  @param primary a @c GtkLabel widget to use for primary text
+ *
+ *  @param secondary a @c GtkLabel widget to use for secondary text
+ *
+ *  @param bar a @c GtkProgressBar widget for filling or pulsing
+ *
+ *  @param suboperation a @c GtkLabel widget to use for suboperation text
+ *
+ *  @param log a @c GtkTextView widget for logging progress textually
+ *
+ *  Any of the parameters may be passed as @c NULL if management of
+ *  that visual element is not desired.
+ *
+ *  @return A ::GNCProgressDialog that identifies the dialog and
+ *  is needed when making subsequent API calls. */
+GNCProgressDialog * gnc_progress_dialog_custom(GtkLabel       *primary,
+                                               GtkLabel       *secondary,
+                                               GtkProgressBar *bar,
+                                               GtkLabel       *suboperation,
+                                               GtkTextView    *log);
 
-/* Set the heading (the text above the progress meter) of
- * the progress dialog. If it is NULL or blank, the heading
- * is hidden (this is the default state). */
-void gnc_progress_dialog_set_heading (GNCProgressDialog *progress,
-                                      const char *heading);
+/** Set the title of a pop-up progress dialog. This function has no effect
+ *  on dialogs registered using gnc_progress_dialog_custom().
+ *
+ *  @param progress a ::GNCProgressDialog
+ *
+ *  @param title the window title to display */
+void gnc_progress_dialog_set_title(GNCProgressDialog *progress,
+                                   const char *title);
 
-/* Set the C function which will be called if the user hits the
- * 'cancel' button. The cancel function returns a boolean value.
- * If the value is TRUE, the window is hidden. */
-void gnc_progress_dialog_set_cancel_func (GNCProgressDialog *progress,
-                                          GNCProgressCancelFunc cancel_func,
-                                          gpointer user_data);
+/** Set the primary text of the progress dialog. The text will
+ *  be displayed using the HIG-recommended style. If @a str is @c NULL
+ *  or blank, the label is hidden (this is the default state).
+ *
+ *  @param progress a ::GNCProgressDialog
+ *
+ *  @param str the text to be displayed */
+void gnc_progress_dialog_set_primary(GNCProgressDialog *progress,
+                                     const gchar *str);
 
-/* Set a guile function which will be called if the user hits cancel.
- * Will be called after the C function, if any. The function should
- * return #t if the dialog should be hidden. If there is no C or guile
- * cancel callback (the default state), the cancel button is inactive. */
-void gnc_progress_dialog_set_cancel_scm_func (GNCProgressDialog *progress,
-                                              SCM cancel_scm_func);
+/** Set the primary text of the progress dialog. If @a str is @c NULL
+ *  or blank, the label is hidden (this is the default state).
+ *
+ *  @param progress a ::GNCProgressDialog
+ *
+ *  @param str the text to be displayed
+ *
+ * NOTE: For HIG-compliant dialogs, use gnc_progress_dialog_set_primary()
+ * instead. */
+void gnc_progress_dialog_set_heading(GNCProgressDialog *progress,
+                                     const char *heading);
 
-/* Set the value of the progress dialog. */
-void gnc_progress_dialog_set_value (GNCProgressDialog *progress, gdouble value);
+/** Set the secondary text of the progress dialog. The text will
+ *  be displayed using the HIG-recommended style. If @a str is @c NULL
+ *  or blank, the label is hidden (this is the default state).
+ *
+ *  @param progress a ::GNCProgressDialog
+ *
+ *  @param str the text to be displayed */
+void gnc_progress_dialog_set_secondary(GNCProgressDialog *progress,
+                                       const gchar *str);
 
-/* Update the GUI of the progress dialog, and call any pending cancel
- * callbacks. This function will be called automatically by the other
- * functions, including gnc_progress_dialog_set_value. */
-void gnc_progress_dialog_update (GNCProgressDialog *progress);
+/** Set the suboperation text of the progress dialog. The text will
+ *  be displayed using the HIG-recommended style. If @a str is @c NULL
+ *  or blank, the label is hidden (this is the default state).
+ *
+ *  @param progress a ::GNCProgressDialog
+ *
+ *  @param str the text to be displayed */
+void gnc_progress_dialog_set_sub(GNCProgressDialog *progress,
+                                 const gchar *str);
 
-/* Set the progress meter to fully complete, change the heading, if
- * any, to "Complete", enable the 'OK' button, and make the dialog
- * non-modal. */
-void gnc_progress_dialog_finish (GNCProgressDialog *progress);
+/** Show the progress log and delete any existing text. If the dialog was
+ *  created via gnc_progress_dialog_new(), the log is not shown by default.
+ *  Calling this function will make it appear.
+ *
+ *  @param progress a ::GNCProgressDialog */
+void gnc_progress_dialog_reset_log(GNCProgressDialog *progress);
 
-/* Destroy the dialog. If gnc_progress_dialog_finish has been called,
- * the dialog will not be destroyed until the user dismisses the window.
- * This function must be called in order to reclaim the dialog's memory. */
-void gnc_progress_dialog_destroy (GNCProgressDialog *progress);
+/** Append @a str to the progress log.
+ *
+ *  @param progress a ::GNCProgressDialog
+ *
+ *  @param str the text to be appended */
+void gnc_progress_dialog_append_log(GNCProgressDialog *progress,
+                                    const gchar *str);
 
+/** Show that progress has been paused by appending "(paused)" to the
+ *  suboperation text, the window title, or the primary text. The first
+ *  that is both known and currently shown will be the one used.
+ *
+ *  @param progress a ::GNCProgressDialog */
+void gnc_progress_dialog_pause(GNCProgressDialog *progress);
+
+/** Remove any indication that progress has paused by removing any existing
+ *  "(paused)" suffix from the suboperation text, the window title, and the
+ *  primary text.
+ *
+ *  @param progress a ::GNCProgressDialog */
+void gnc_progress_dialog_resume(GNCProgressDialog *progress);
+
+/** Show a Cancel button and set the C function which will be called when it
+ *  is pressed by the user. The cancel function must return a boolean value.
+ *  If the value is @c TRUE, the window is hidden.
+ *
+ *  @param progress a ::GNCProgressDialog
+ *
+ *  @param cancel_func the callback function
+ *
+ *  @param user_data user data to be passed to @a cancel_func */
+void gnc_progress_dialog_set_cancel_func(GNCProgressDialog *progress,
+                                         GNCProgressCancelFunc cancel_func,
+                                         gpointer user_data);
+
+/** Show a Cancel button and set the Guile procedure that will be called
+ *  when it is pressed by the user. It will be called after any C function
+ *  registered with gnc_progress_dialog_set_cancel_func(). The procedure
+ *  must return @c #t if the dialog should be hidden. If there is no C or Guile
+ *  cancel callback (the default state), the Cancel button is hidden.
+ *
+ *  @param progress a ::GNCProgressDialog
+ *
+ *  @param cancel_scm_func the Guile callback procedure */
+void gnc_progress_dialog_set_cancel_scm_func(GNCProgressDialog *progress,
+                                             SCM cancel_scm_func);
+
+/** Set the fraction of the progress bar to fill, where 0 is empty and
+ *  1 is full. If @a value is over 1, the bar will pulse instead of fill.
+ *
+ *  @param progress a ::GNCProgressDialog
+ *
+ *  @param value the fraction of the bar to fill */
+void gnc_progress_dialog_set_value(GNCProgressDialog *progress, gdouble value);
+
+/** Create a new "virtual" progress bar that, as it becomes full, will fill
+ *  the current bar by the fraction specified by @a weight. All calls to 
+ *  gnc_progress_dialog_set_value() will operate on the new bar until
+ *  gnc_progress_dialog_pop() is called.
+ *
+ *  This can be used to split an operation into weighted sub-operations. For
+ *  example, if a particular suboperation should fill 30% of the bar, call
+ *  gnc_progress_dialog_push() with a @a weight of 0.3. Calls to
+ *  gnc_progress_dialog_set_value() will fill the virtual bar, which
+ *  in turn trickles up at the 0.3 rate.
+ *
+ *  Multiple calls to gnc_progress_dialog_push() can be used to create a
+ *  stack of virtual bars, each subordinate to the last. This allows a task
+ *  to be split into any number of levels of sub-tasks.
+ *
+ *  @param progress a ::GNCProgressDialog
+ *
+ *  @param weight the requested fraction of the current bar that the new bar
+ *  will represent (The fraction actually assigned will be the lesser of the
+ *  requested amount and the amount of the bar that is unfilled.) 
+ *
+ *  @return the number of times that gnc_progress_dialog_pop() would have to
+ *  be called to return to the top level. */
+guint gnc_progress_dialog_push(GNCProgressDialog *progress, gdouble weight);
+
+/** Moves up one level in the stack of virtual bars. See
+ *  gnc_progress_dialog_push() for an explanation of virtual bars.
+ *
+ *  @param progress a ::GNCProgressDialog
+ *
+ *  @return the number of times that gnc_progress_dialog_pop() would have to
+ *  be called again to return to the top level. */
+guint gnc_progress_dialog_pop(GNCProgressDialog *progress);
+
+/** Fills the current progress bar, then calls gnc_progress_dialog_pop().
+ *
+ *  @param progress a ::GNCProgressDialog
+ *
+ *  @return the value returned by gnc_progress_dialog_pop() */
+guint gnc_progress_dialog_pop_full(GNCProgressDialog *progress);
+
+/** Pop up to the top level and clear the progress bar.
+ *
+ *  @param progress a ::GNCProgressDialog */
+void gnc_progress_dialog_reset_value(GNCProgressDialog *progress);
+
+/** Update the GUI of the progress dialog, and call any pending cancel
+ *  callbacks. This function will be called automatically by the other
+ *  functions, including gnc_progress_dialog_set_value.
+ *
+ *  @param progress a ::GNCProgressDialog */
+void gnc_progress_dialog_update(GNCProgressDialog *progress);
+
+/** Set the progress meter to fully complete, change the heading, if
+ *  any, to "Complete", enable the 'OK' button, and make the dialog
+ *  non-modal.
+ *
+ *  @param progress a ::GNCProgressDialog */
+void gnc_progress_dialog_finish(GNCProgressDialog *progress);
+
+/** Destroy the dialog. If gnc_progress_dialog_finish has been called,
+ *  the dialog will not be destroyed until the user dismisses the window.
+ *  This function must be called in order to reclaim the dialog's memory.
+ *
+ *  @param progress a ::GNCProgressDialog */
+void gnc_progress_dialog_destroy(GNCProgressDialog *progress);
+
 #endif
+/** @} */
+/** @} */

Modified: gnucash/branches/2.2/src/gnome/glade/progress.glade
===================================================================
--- gnucash/branches/2.2/src/gnome/glade/progress.glade	2008-07-22 22:31:19 UTC (rev 17372)
+++ gnucash/branches/2.2/src/gnome/glade/progress.glade	2008-07-22 22:31:29 UTC (rev 17373)
@@ -25,18 +25,18 @@
       <property name="border_width">5</property>
       <property name="visible">True</property>
       <property name="homogeneous">False</property>
-      <property name="spacing">6</property>
+      <property name="spacing">12</property>
 
       <child>
-	<widget class="GtkLabel" id="heading_label">
+	<widget class="GtkLabel" id="primary_label">
 	  <property name="visible">True</property>
-	  <property name="label" translatable="yes">Heading</property>
+	  <property name="label" translatable="no">&lt;span weight=&quot;bold&quot; size=&quot;larger&quot;&gt;Primary text&lt;/span&gt;</property>
 	  <property name="use_underline">False</property>
-	  <property name="use_markup">False</property>
-	  <property name="justify">GTK_JUSTIFY_CENTER</property>
-	  <property name="wrap">False</property>
+	  <property name="use_markup">True</property>
+	  <property name="justify">GTK_JUSTIFY_LEFT</property>
+	  <property name="wrap">True</property>
 	  <property name="selectable">False</property>
-	  <property name="xalign">0.5</property>
+	  <property name="xalign">0</property>
 	  <property name="yalign">0.5</property>
 	  <property name="xpad">0</property>
 	  <property name="ypad">0</property>
@@ -53,12 +53,22 @@
       </child>
 
       <child>
-	<widget class="GtkProgressBar" id="progress_bar">
+	<widget class="GtkLabel" id="secondary_label">
 	  <property name="visible">True</property>
-	  <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
-	  <property name="fraction">0</property>
-	  <property name="pulse_step">0.10000000149</property>
+	  <property name="label" translatable="no">Secondary text.</property>
+	  <property name="use_underline">False</property>
+	  <property name="use_markup">False</property>
+	  <property name="justify">GTK_JUSTIFY_LEFT</property>
+	  <property name="wrap">True</property>
+	  <property name="selectable">False</property>
+	  <property name="xalign">0</property>
+	  <property name="yalign">0.5</property>
+	  <property name="xpad">0</property>
+	  <property name="ypad">0</property>
 	  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	  <property name="width_chars">-1</property>
+	  <property name="single_line_mode">False</property>
+	  <property name="angle">0</property>
 	</widget>
 	<packing>
 	  <property name="padding">0</property>
@@ -68,6 +78,90 @@
       </child>
 
       <child>
+	<widget class="GtkVBox" id="progress_vbox1">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkProgressBar" id="progress_bar">
+	      <property name="visible">True</property>
+	      <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
+	      <property name="fraction">0</property>
+	      <property name="pulse_step">0.10000000149</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkLabel" id="sub_label">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="no">Sub-operation text</property>
+	      <property name="label" translatable="no">&lt;span style=&quot;italic&quot;&gt;Sub-operation text&lt;/span&gt;</property>
+	      <property name="use_underline">False</property>
+	      <property name="use_markup">True</property>
+	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap">True</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">0</property>
+	      <property name="ypad">0</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	      <property name="width_chars">-1</property>
+	      <property name="single_line_mode">False</property>
+	      <property name="angle">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">6</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+        <widget class="GtkScrolledWindow" id="progress_log_window">
+          <property name="visible">True</property>
+          <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+          <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+          <property name="shadow_type">GTK_SHADOW_IN</property>
+          <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+          <child>
+	    <widget class="GtkTextView" id="progress_log">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="editable">False</property>
+	      <property name="overwrite">False</property>
+	      <property name="accepts_tab">False</property>
+	      <property name="justification">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap_mode">GTK_WRAP_WORD</property>
+	      <property name="cursor_visible">False</property>
+	      <property name="pixels_above_lines">0</property>
+	      <property name="pixels_below_lines">0</property>
+	      <property name="pixels_inside_wrap">0</property>
+	      <property name="left_margin">0</property>
+	      <property name="right_margin">0</property>
+	      <property name="indent">0</property>
+	      <property name="text" translatable="no">1234567890123456789012345678901234567890</property>
+	    </widget>
+          </child>
+        </widget>
+      </child>
+
+      <child>
 	<widget class="GtkHButtonBox" id="hbuttonbox3">
 	  <property name="visible">True</property>
 	  <property name="layout_style">GTK_BUTTONBOX_END</property>

Modified: gnucash/branches/2.2/src/import-export/qif-import/druid-qif-import.c
===================================================================
--- gnucash/branches/2.2/src/import-export/qif-import/druid-qif-import.c	2008-07-22 22:31:19 UTC (rev 17372)
+++ gnucash/branches/2.2/src/import-export/qif-import/druid-qif-import.c	2008-07-22 22:31:29 UTC (rev 17373)
@@ -38,6 +38,7 @@
 #include "Transaction.h"
 #include "dialog-account-picker.h"
 #include "dialog-commodity.h"
+#include "dialog-progress.h"
 #include "dialog-utils.h"
 #include "druid-qif-import.h"
 #include "druid-utils.h"
@@ -87,9 +88,22 @@
 struct _qifimportwindow {
   GtkWidget * window;
   GtkWidget * druid;
+
+  /* Widgets on the file selection page. */
   GtkWidget * filename_entry;
+
+  /* File loading progress page. */
+  GtkWidget * load_pause;
+  GtkWidget * load_log;
+  GNCProgressDialog *load_progress;
+
+  /* Widgets on the default account page. */
   GtkWidget * acct_entry;
+
+  /* Widgets on the date format page. */
   GtkWidget * date_format_combo;
+
+  /* Widgets on the files loaded page. */
   GtkWidget * selected_file_view;
 
   /* Widgets on the account matching page. */
@@ -107,7 +121,10 @@
   GtkWidget * memo_view_count;
   GtkWidget * memo_view_btn;
 
+  /* Widgets on the currency page. */
   GtkWidget * currency_picker;
+
+  /* Widgets on the duplicates page. */
   GtkWidget * new_transaction_view;
   GtkWidget * old_transaction_view;
 
@@ -117,6 +134,8 @@
   GList     * doc_pages;
 
   gboolean  show_doc_pages;
+  gboolean  ask_date_format;
+  gboolean  busy;
 
   SCM       imported_files;
   SCM       selected_file;
@@ -157,7 +176,7 @@
 static GdkColor std_logo_bg_color = { 0, 65535, 65535, 65535 };
 static GdkColor std_title_color =  { 0, 65535, 65535, 65535 };
 
-#define NUM_PRE_PAGES 13
+#define NUM_PRE_PAGES 14
 #define NUM_POST_PAGES 3
 #define NUM_DOC_PAGES  6
 
@@ -309,12 +328,6 @@
 
     /* Free the memory allocated for the page's struct. */
     g_free(page);
-
-    /*
-     * FIXME -- Do we need to call gtk_widget_destroy() for anything?
-     *          Or will they all get destroyed when the druid gets destroyed?
-     *          Any other cleanup?
-     */
   }
 
   /* Free the list of pages. */
@@ -329,35 +342,38 @@
 \********************************************************************/
 
 void
-gnc_ui_qif_import_druid_destroy(QIFImportWindow * window)
+gnc_ui_qif_import_druid_destroy(QIFImportWindow * wind)
 {
-  if (!window)
+  if (!wind)
     return;
 
+  /* Destroy the progress dialog helpers. */
+  gnc_progress_dialog_destroy(wind->load_progress);
+
   /* Destroy any commodity pages. */
-  gnc_ui_qif_import_commodity_destroy(window);
+  gnc_ui_qif_import_commodity_destroy(wind);
 
-  gnc_unregister_gui_component_by_data(DRUID_QIF_IMPORT_CM_CLASS, window);
+  gnc_unregister_gui_component_by_data(DRUID_QIF_IMPORT_CM_CLASS, wind);
 
-  gtk_widget_destroy(window->window);
+  gtk_widget_destroy(wind->window);
 
-  scm_gc_unprotect_object(window->imported_files);
-  scm_gc_unprotect_object(window->selected_file);
-  scm_gc_unprotect_object(window->gnc_acct_info);
-  scm_gc_unprotect_object(window->cat_display_info);
-  scm_gc_unprotect_object(window->cat_map_info);
-  scm_gc_unprotect_object(window->memo_display_info);
-  scm_gc_unprotect_object(window->memo_map_info);
-  scm_gc_unprotect_object(window->acct_display_info);
-  scm_gc_unprotect_object(window->acct_map_info);
-  scm_gc_unprotect_object(window->security_hash);
-  scm_gc_unprotect_object(window->security_prefs);
-  scm_gc_unprotect_object(window->new_securities);
-  scm_gc_unprotect_object(window->ticker_map);
-  scm_gc_unprotect_object(window->imported_account_tree);
-  scm_gc_unprotect_object(window->match_transactions);
+  scm_gc_unprotect_object(wind->imported_files);
+  scm_gc_unprotect_object(wind->selected_file);
+  scm_gc_unprotect_object(wind->gnc_acct_info);
+  scm_gc_unprotect_object(wind->cat_display_info);
+  scm_gc_unprotect_object(wind->cat_map_info);
+  scm_gc_unprotect_object(wind->memo_display_info);
+  scm_gc_unprotect_object(wind->memo_map_info);
+  scm_gc_unprotect_object(wind->acct_display_info);
+  scm_gc_unprotect_object(wind->acct_map_info);
+  scm_gc_unprotect_object(wind->security_hash);
+  scm_gc_unprotect_object(wind->security_prefs);
+  scm_gc_unprotect_object(wind->new_securities);
+  scm_gc_unprotect_object(wind->ticker_map);
+  scm_gc_unprotect_object(wind->imported_account_tree);
+  scm_gc_unprotect_object(wind->match_transactions);
 
-  g_free(window);
+  g_free(wind);
 }
 
 
@@ -653,209 +669,432 @@
                                     gpointer user_data)
 {
   QIFImportWindow * wind = user_data;
+  const gchar * path_to_load;
 
+  /* Get the file name. */
+  path_to_load = gtk_entry_get_text(GTK_ENTRY(wind->filename_entry));
+
+  /* Validate the chosen filename. */
+  if (strlen(path_to_load) == 0)
+    gnc_error_dialog(wind->window, _("Please select a file to load."));
+  else if (g_access(path_to_load, R_OK) < 0)
+    gnc_error_dialog(wind->window,
+                     _("File not found or read permission denied. "
+                       "Please select another file."));
+  else
+  {
+    SCM qif_file_loaded = scm_c_eval_string("qif-dialog:qif-file-loaded?");
+
+    /* See if the file is already loaded. */
+    if (scm_call_2(qif_file_loaded,
+                   scm_makfrom0str(path_to_load),
+                   wind->imported_files) == SCM_BOOL_T)
+      gnc_error_dialog(wind->window,
+                       _("That QIF file is already loaded. "
+                         "Please select another file."));
+    else
+    {
+      /* Passed all checks; proceed to the next page. */
+      return gnc_ui_qif_import_generic_next_cb(page, arg1, wind);
+    }
+  }
+
+  /* Stay on this page. */
+  return TRUE;
+}
+
+
+/********************************************************************
+ * gnc_ui_qif_import_load_progress_prepare_cb
+ *
+ * Prepare the progress page for display.
+ ********************************************************************/
+
+static void
+gnc_ui_qif_import_load_progress_prepare_cb(GnomeDruidPage * page,
+                                           gpointer arg1,
+                                           gpointer user_data)
+{
+  QIFImportWindow   *wind = user_data;
+
+  /* Reset the progress display. */
+  gnc_progress_dialog_set_primary(wind->load_progress, "");
+  gnc_progress_dialog_set_secondary(wind->load_progress,
+    _("GnuCash will now load your QIF file. If there are no errors or warnings, you will automatically proceed to the next step. Otherwise, the details will be shown below for your review."));
+  gnc_progress_dialog_set_sub(wind->load_progress, " ");
+  gnc_progress_dialog_reset_value(wind->load_progress);
+  gnc_progress_dialog_reset_log(wind->load_progress);
+
+  /* Disable the "Forward" button for now.
+   *
+   * NOTE: Due to bug 91001 in GnomeDruid, gnome_druid_set_buttons_sensitive()
+   *       will not work in prepare callbacks unless they are run AFTER the
+   *       standard one. Make sure the Glade line has the callback set up with
+   *       after=yes. For example:
+   *         <signal name="prepare" handler="my_prepare_cb" after="yes"/>   */
+  gnome_druid_set_buttons_sensitive(GNOME_DRUID(wind->druid),
+                                    TRUE, FALSE, TRUE, TRUE);
+
+  /* Generate a show signal once the page is displayed. This
+   * will kick off our (potentially) long-running operations. */
+  gtk_widget_hide(GTK_WIDGET(page));
+  gtk_widget_show(GTK_WIDGET(page));
+}
+
+
+/********************************************************************
+ * gnc_ui_qif_import_load_progress_show_cb
+ *
+ * Start the long-running operation.
+ ********************************************************************/
+
+static void
+gnc_ui_qif_import_load_progress_show_cb(GtkWidget *widget,
+                                        gpointer user_data)
+{
+  QIFImportWindow   *wind = user_data;
   const gchar * path_to_load;
-  const gchar * default_acctname = NULL;
 
   SCM make_qif_file   = scm_c_eval_string("make-qif-file");
   SCM qif_file_load   = scm_c_eval_string("qif-file:read-file");
   SCM qif_file_parse  = scm_c_eval_string("qif-file:parse-fields");
-  SCM qif_file_loaded = scm_c_eval_string("qif-dialog:qif-file-loaded?");
   SCM unload_qif_file = scm_c_eval_string("qif-dialog:unload-qif-file");
-  SCM check_from_acct = scm_c_eval_string("qif-file:check-from-acct");
-  SCM default_acct    = scm_c_eval_string("qif-file:path-to-accountname");
   SCM parse_results   = scm_c_eval_string("qif-file:parse-fields-results");
-  SCM date_formats;
-  SCM scm_filename;
   SCM scm_qiffile;
   SCM imported_files = SCM_EOL;
   SCM load_return, parse_return;
-  SCM window;
-  int ask_date_format = FALSE;
+  SCM progress;
 
-  /* get the file name */
+  /* Raise the busy flag so the druid can't be canceled unexpectedly. */
+  wind->busy = TRUE;
+  gtk_widget_set_sensitive(wind->load_pause, TRUE);
+
+  /* Get the file name. */
   path_to_load = gtk_entry_get_text(GTK_ENTRY(wind->filename_entry));
-  window = SWIG_NewPointerObj(wind->window, SWIG_TypeQuery("_p_GtkWidget"), 0);
 
-  /* check a few error conditions before we get started */
-  if(strlen(path_to_load) == 0) {
-    /* stay here if no file specified */
-    gnc_error_dialog(wind->window, _("Please select a file to load."));
-    return TRUE;
-  }
-  else if (g_access(path_to_load, R_OK) < 0) {
-    /* stay here if bad file */
-    gnc_error_dialog(wind->window,
-                     _("File not found or read permission denied. "
-                       "Please select another file."));
-    return TRUE;
-  }
-  else {
-    /* convert filename to scm */
-    scm_filename   = scm_makfrom0str(path_to_load);
-    imported_files = wind->imported_files;
+  /* Create the <qif-file> object. */
+  scm_qiffile          = scm_call_0(make_qif_file);
+  scm_gc_unprotect_object(wind->selected_file);
+  wind->selected_file  = scm_qiffile;
+  scm_gc_protect_object(wind->selected_file);
+  imported_files       = scm_cons(scm_qiffile, wind->imported_files);
 
-    if(scm_call_2(qif_file_loaded, scm_filename, wind->imported_files)
-       == SCM_BOOL_T) {
-      gnc_error_dialog(wind->window,
-                                _("That QIF file is already loaded. "
-                                  "Please select another file."));
-      return TRUE;
-    }
+  /* Create SCM for the progress helper. */
+  progress = SWIG_NewPointerObj(wind->load_progress,
+                                SWIG_TypeQuery("_p__GNCProgressDialog"),
+                                0);
 
-    /* turn on the busy cursor */
-    gnc_set_busy_cursor(NULL, TRUE);
+  /* Clear any previous pause or cancel state. */
+  scm_c_eval_string("(qif-import:reset-cancel-pause)");
 
-    /* create the <qif-file> object */
-    scm_qiffile          = scm_call_0(make_qif_file);
-    imported_files       = scm_cons(scm_qiffile, imported_files);
 
-    scm_gc_unprotect_object(wind->selected_file);
-    wind->selected_file  = scm_qiffile;
-    scm_gc_protect_object(wind->selected_file);
+  /*
+   * Load the file.
+   *
+   * The loader returns:
+   *  success:   ()
+   *  failure:   (#f error-message)
+   *  warning:   (#t error-message)
+   *  cancel:    #t
+   *  exception: #f
+   */
 
-    /* load the file */
-    load_return = scm_call_4(qif_file_load, SCM_CAR(imported_files),
-                             scm_filename, wind->ticker_map, window);
+  /* This step will fill 70% of the bar. */
+  gnc_progress_dialog_push(wind->load_progress, 0.7);
+  load_return = scm_call_4(qif_file_load,
+                           SCM_CAR(imported_files),
+                           scm_makfrom0str(path_to_load),
+                           wind->ticker_map,
+                           progress);
+  gnc_progress_dialog_pop(wind->load_progress);
+  if (load_return == SCM_BOOL_T)
+  {
+    /* Canceled by the user. */
+    wind->busy = FALSE;
+    gtk_widget_set_sensitive(wind->load_pause, FALSE);
+    return;
+  }
+  else if (load_return == SCM_BOOL_F || !SCM_LISTP(load_return))
+  {
+    gnc_progress_dialog_append_log(wind->load_progress,
+                     _( "A bug was detected while reading the QIF file."));
+    gnc_progress_dialog_set_sub(wind->load_progress, _("Failed"));
+    gnc_progress_dialog_reset_value(wind->load_progress);
+    gnc_error_dialog(wind->window,
+                     _( "A bug was detected while reading the QIF file."));
+    /* FIXME: How should we request that the user report this problem? */
 
-    /* turn back the cursor */
-    gnc_unset_busy_cursor(NULL);
+    wind->busy = FALSE;
+    gtk_widget_set_sensitive(wind->load_pause, FALSE);
+    return;
+  }
+  else if (!SCM_NULLP(load_return))
+  {
+    const gchar *str = SCM_STRING_CHARS(SCM_CADR(load_return));
 
-    /* a list returned is (#f error-message) for an error,
-     * (#t error-message) for a warning, or just #f for an
-     * exception. */
-    if(SCM_LISTP(load_return) &&
-       (SCM_CAR(load_return) == SCM_BOOL_T)) {
-      const gchar *warn_str = SCM_STRING_CHARS(SCM_CADR(load_return));
-      gnc_warning_dialog(GTK_WIDGET(wind->window),
-                         _("QIF file load warning: %s"),
-                         warn_str ? warn_str : "(null)");
-    }
-
-    /* check success of the file load */
-    if(load_return == SCM_BOOL_F) {
-      gnc_error_dialog(wind->window,
-                       _( "An error occurred while loading the QIF file."));
-      return TRUE;
-    }
-    else if ((load_return != SCM_BOOL_T) &&
-             (!SCM_LISTP(load_return) ||
-              (SCM_CAR(load_return) != SCM_BOOL_T))) {
-      const gchar *warn_str = SCM_STRING_CHARS(SCM_CADR(load_return));
-      gnc_error_dialog(wind->window,
-                       _("QIF file load failed: %s"),
-                       warn_str ? warn_str : "(null)");
-
-      imported_files =
-        scm_call_2(unload_qif_file, scm_qiffile, imported_files);
-
+    if (SCM_CAR(load_return) == SCM_BOOL_F)
+    {
+      imported_files = scm_call_2(unload_qif_file, scm_qiffile, imported_files);
       scm_gc_unprotect_object(wind->imported_files);
       wind->imported_files = imported_files;
       scm_gc_protect_object(wind->imported_files);
 
-      return TRUE;
+      gnc_progress_dialog_set_sub(wind->load_progress, _("Failed"));
+      gnc_progress_dialog_reset_value(wind->load_progress);
+
+      wind->busy = FALSE;
+      gtk_widget_set_sensitive(wind->load_pause, FALSE);
+      return;
     }
-    else {
-      /* turn on the busy cursor */
-      gnc_set_busy_cursor(NULL, TRUE);
+  }
 
-      /* call the field parser */
-      parse_return = scm_call_1(qif_file_parse, SCM_CAR(imported_files));
 
-      /* parser returns:
-       *   success: #t
-       *   failure: (#f . ((type . errror) ...))
-       *   warning: (#t . ((type . error) ...))
-       *
-       * warning means that (potentially) the date format is
+  /*
+   * Parse the fields.
+   *
+   * The parser returns:
+   *   success:   ()
+   *   failure:   (#f . ((type . error) ...))
+   *   warning:   (#t . ((type . error) ...))
+   *   cancel:    #t
+   *   exception: #f
+   */
+
+  /* This step will fill the remainder of the bar. */
+  gnc_progress_dialog_push(wind->load_progress, 1);
+  parse_return = scm_call_2(qif_file_parse, SCM_CAR(imported_files), progress);
+  gnc_progress_dialog_pop(wind->load_progress);
+  wind->ask_date_format = FALSE;
+  if (parse_return == SCM_BOOL_T)
+  {
+    /* Canceled by the user. */
+    imported_files = scm_call_2(unload_qif_file, scm_qiffile, imported_files);
+    wind->busy = FALSE;
+    gtk_widget_set_sensitive(wind->load_pause, FALSE);
+    return;
+  }
+  else if (parse_return == SCM_BOOL_F || !SCM_LISTP(parse_return))
+  {
+    imported_files = scm_call_2(unload_qif_file, scm_qiffile, imported_files);
+    gnc_progress_dialog_append_log(wind->load_progress,
+                     _( "A bug was detected while parsing the QIF file."));
+    gnc_progress_dialog_set_sub(wind->load_progress, _("Failed"));
+    gnc_progress_dialog_reset_value(wind->load_progress);
+    gnc_error_dialog(wind->window,
+                     _( "A bug was detected while parsing the QIF file."));
+    /* FIXME: How should we request that the user report this problem? */
+
+    wind->busy = FALSE;
+    gtk_widget_set_sensitive(wind->load_pause, FALSE);
+    return;
+  }
+  else if (!SCM_NULLP(parse_return))
+  {
+    /* Are there only warnings? */
+    if (SCM_CAR(parse_return) == SCM_BOOL_T)
+    {
+      SCM date_formats;
+
+      /* A warning means that (potentially) the date format is
        * ambiguous.  So search the results for the "date" type and if
-       * it's found, set up the format selector page.
-       */
-      if(SCM_LISTP(parse_return) &&
-         (SCM_CAR(parse_return) == SCM_BOOL_T)) {
+       * it's found, set up the format selector page. */
+      if ((date_formats = scm_call_2(parse_results,
+                                     SCM_CDR(parse_return),
+                                     scm_str2symbol("date"))) != SCM_BOOL_F)
+      {
         gint n_items;
 
-        /* clear the combo box */
+        /* Clear the date format combo box. */
         gtk_combo_box_set_active(GTK_COMBO_BOX(wind->date_format_combo), -1);
         n_items = gtk_tree_model_iter_n_children(
-          gtk_combo_box_get_model(GTK_COMBO_BOX(wind->date_format_combo)), NULL);
+          gtk_combo_box_get_model(GTK_COMBO_BOX(wind->date_format_combo)),
+                                  NULL);
         while (n_items-- > 0)
           gtk_combo_box_remove_text(GTK_COMBO_BOX(wind->date_format_combo), 0);
 
-        if ((date_formats = scm_call_2(parse_results,
-                                       SCM_CDR(parse_return),
-                                       scm_str2symbol("date"))) != SCM_BOOL_F) {
-          while(SCM_LISTP(date_formats) && !SCM_NULLP(date_formats)) {
-            gtk_combo_box_append_text(GTK_COMBO_BOX(wind->date_format_combo),
-                                      SCM_SYMBOL_CHARS(SCM_CAR(date_formats)));
-            date_formats = SCM_CDR(date_formats);
-          }
+        /* Add the formats for the user to select from. */
+        while(SCM_LISTP(date_formats) && !SCM_NULLP(date_formats))
+        {
+          gtk_combo_box_append_text(GTK_COMBO_BOX(wind->date_format_combo),
+                                    SCM_SYMBOL_CHARS(SCM_CAR(date_formats)));
+          date_formats = SCM_CDR(date_formats);
+        }
         gtk_combo_box_set_active(GTK_COMBO_BOX(wind->date_format_combo), 0);
 
-          ask_date_format = TRUE;
-
-        } else {
-          /* FIXME: we've got a "warning" but it's not the date! */
-          ;
-        }
+        wind->ask_date_format = TRUE;
       }
+    }
+    else
+    {
+      /* Parsing failed. */
+      imported_files = scm_call_2(unload_qif_file, scm_qiffile, imported_files);
+      gnc_progress_dialog_set_sub(wind->load_progress, _("Failed"));
+      gnc_progress_dialog_reset_value(wind->load_progress);
 
-      /* turn back the cursor */
-      gnc_unset_busy_cursor(NULL);
+      wind->busy = FALSE;
+      gtk_widget_set_sensitive(wind->load_pause, FALSE);
+      return;
+    }
+  }
 
-      /* Can this ever happen??? */
-      if(parse_return == SCM_BOOL_F) {
-        gnc_error_dialog(wind->window,
-                         _("An error occurred while parsing the QIF file."));
-        imported_files =
-          scm_call_2(unload_qif_file, scm_qiffile, imported_files);
-        return TRUE;
-      }
-      else if((parse_return != SCM_BOOL_T) &&
-         (!SCM_LISTP(parse_return) ||
-          (SCM_CAR(parse_return) != SCM_BOOL_T))) {
-        const gchar *warn_str = SCM_STRING_CHARS(SCM_CDADR(parse_return));
-        gnc_error_dialog(wind->window,
-                         _("QIF file parse failed: %s"),
-                         warn_str ? warn_str : "(null)");
+  /* The file was loaded successfully. */
+  gnc_progress_dialog_set_sub(wind->load_progress, _("Loading completed"));
+  gnc_progress_dialog_set_value(wind->load_progress, 1);
 
-        imported_files =
-          scm_call_2(unload_qif_file, scm_qiffile, imported_files);
+  scm_gc_unprotect_object(wind->imported_files);
+  wind->imported_files = imported_files;
+  scm_gc_protect_object(wind->imported_files);
 
-        return TRUE;
-      }
-    }
+  /* Enable all buttons. */
+  gnome_druid_set_buttons_sensitive(GNOME_DRUID(wind->druid),
+                                    TRUE, TRUE, TRUE, TRUE);
 
-    scm_gc_unprotect_object(wind->imported_files);
-    wind->imported_files = imported_files;
-    scm_gc_protect_object(wind->imported_files);
+  /* If the log is empty, move on to the next page automatically. */
+  if (gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(GTK_TEXT_VIEW(wind->load_log))) == 0)
+    gnome_druid_page_next(GNOME_DRUID_PAGE(widget));
 
-    if(ask_date_format) {
-      /* we need to get a date format, so go to the next page */
-      return gnc_ui_qif_import_generic_next_cb(page, arg1, wind);
-    }
-    else if(scm_call_1(check_from_acct, SCM_CAR(imported_files)) != SCM_BOOL_T) {
-      /* skip to the "ask account name" page */
-      default_acctname =
-        SCM_STRING_CHARS(scm_call_1(default_acct, SCM_CAR(imported_files)));
-      gtk_entry_set_text(GTK_ENTRY(wind->acct_entry), default_acctname);
+  wind->busy = FALSE;
+  gtk_widget_set_sensitive(wind->load_pause, FALSE);
+  return;
+}
 
-      gnome_druid_set_page(GNOME_DRUID(wind->druid),
-                           get_named_page(wind, "account_name_page"));
-      return TRUE;
-    }
-    else {
-      /* skip ahead to the "loaded files" page */
-      gnome_druid_set_page(GNOME_DRUID(wind->druid),
-                           get_named_page(wind, "loaded_files_page"));
-      return TRUE;
-    }
+
+/********************************************************************
+ * gnc_ui_qif_import_load_progress_pause_cb
+ *
+ * Invoked when the "Pause" button is clicked.
+ ********************************************************************/
+
+static void
+gnc_ui_qif_import_load_progress_pause_cb(GtkButton * button,
+                                         gpointer user_data)
+{
+  QIFImportWindow *wind = user_data;
+  SCM toggle_pause      = scm_c_eval_string("qif-import:toggle-pause");
+  SCM progress;
+
+  if (!wind->busy)
+    return;
+
+  /* Create SCM for the progress helper. */
+  progress = SWIG_NewPointerObj(wind->load_progress,
+                                SWIG_TypeQuery("_p__GNCProgressDialog"),
+                                0);
+
+  /* Pause (or resume) the currently running operation. */
+  scm_call_1(toggle_pause, progress);
+
+  /* Swap the button label between pause and resume. */
+  if (strcmp(gtk_button_get_label(button), _("_Resume")))
+  {
+    gtk_button_set_use_stock(button, FALSE);
+    gtk_button_set_use_underline(button, TRUE);
+    gtk_button_set_label(button, _("_Resume"));
   }
+  else
+  {
+    gtk_button_set_use_stock(button, TRUE);
+    gtk_button_set_use_underline(button, FALSE);
+    gtk_button_set_label(button, "gtk-media-pause");
+  }
+}
 
+
+/********************************************************************
+ * gnc_ui_qif_import_load_progress_next_cb
+ *
+ * Determine the next page after loading a file successfully.
+ ********************************************************************/
+
+static gboolean
+gnc_ui_qif_import_load_progress_next_cb(GnomeDruidPage * page,
+                                        gpointer arg1,
+                                        gpointer user_data)
+{
+  QIFImportWindow *wind = user_data;
+  SCM check_from_acct   = scm_c_eval_string("qif-file:check-from-acct");
+
+  if (wind->ask_date_format)
+  {
+    /* We need to get a date format, so go to the next page. */
+    return gnc_ui_qif_import_generic_next_cb(page, arg1, user_data);
+  }
+  else if (scm_call_1(check_from_acct, wind->selected_file) != SCM_BOOL_T)
+  {
+    SCM default_acct = scm_c_eval_string("qif-file:path-to-accountname");
+    const gchar * default_acctname;
+
+    /* Go to the "ask account name" page. */
+    default_acctname = SCM_STRING_CHARS(scm_call_1(default_acct,
+                                                   wind->selected_file));
+    gtk_entry_set_text(GTK_ENTRY(wind->acct_entry), default_acctname);
+
+    gnome_druid_set_page(GNOME_DRUID(wind->druid),
+                         get_named_page(wind, "account_name_page"));
+    return TRUE;
+  }
+
+  /* Skip ahead to the "loaded files" page. */
+  gnome_druid_set_page(GNOME_DRUID(wind->druid),
+                       get_named_page(wind, "loaded_files_page"));
+
+  return TRUE;
+}
+
+
+/****************************************************************
+ * load_progress_back_timeout_cb
+ *
+ * This timer callback function waits until the busy flag
+ * has been cleared before going back to the previous page.
+ ****************************************************************/
+
+static gboolean
+load_progress_back_timeout_cb(gpointer data)
+{
+  QIFImportWindow *wind = data;
+
+  if (wind->busy)
+    /* Wait for timer to go off again. */
+    return TRUE;
+
+  /* The busy flag was lowered. Go back to the previous page. */
+  gnome_druid_page_back(get_named_page(wind, "load_progress_page"));
+
+  /* Cancel the timer. */
   return FALSE;
 }
 
+
+/********************************************************************
+ * gnc_ui_qif_import_load_progress_back_cb
+ *
+ * Return to the previous page, waiting if necessary.
+ ********************************************************************/
+
 static gboolean
+gnc_ui_qif_import_load_progress_back_cb(GnomeDruidPage * page,
+                                        gpointer arg1,
+                                        gpointer user_data)
+{
+  QIFImportWindow *wind = user_data;
+
+  if (wind->busy)
+  {
+    /* Cancel any long-running Scheme operation. */
+    scm_c_eval_string("(qif-import:cancel)");
+
+    /* Wait for the busy flag to be lowered. */
+    g_timeout_add(200, load_progress_back_timeout_cb, user_data);
+
+    return TRUE;
+  }
+
+  return gnc_ui_qif_import_generic_back_cb(page, arg1, user_data);
+}
+
+
+static gboolean
 gnc_ui_qif_import_date_format_next_cb(GnomeDruidPage * page,
                                       gpointer arg1,
                                       gpointer user_data)
@@ -867,23 +1106,35 @@
   SCM  format_sym;
   gchar *text;
 
+  /* Get the selected date format. */
   text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(wind->date_format_combo));
+  if (!text)
+  {
+    g_critical("QIF import: BUG DETECTED in gnc_ui_qif_import_date_format_next_cb. Format is NULL.");
+    return TRUE;
+  }
   format_sym = scm_str2symbol(text);
   g_free(text);
+
+  /* Reparse the dates using the selected format. */
   scm_call_2(reparse_dates, wind->selected_file, format_sym);
 
-  if(scm_call_1(check_from_acct, wind->selected_file) != SCM_BOOL_T) {
-    SCM default_acct    = scm_c_eval_string("qif-file:path-to-accountname");
+  /* Determine the next page to display. */
+  if (scm_call_1(check_from_acct, wind->selected_file) != SCM_BOOL_T)
+  {
+    /* There is an account name missing. Ask the user to provide one. */
+    SCM default_acct = scm_c_eval_string("qif-file:path-to-accountname");
     const gchar * default_acctname;
 
     default_acctname = SCM_STRING_CHARS(scm_call_1(default_acct,
-                                                    wind->selected_file));
+                                                   wind->selected_file));
     gtk_entry_set_text(GTK_ENTRY(wind->acct_entry), default_acctname);
 
     return FALSE;
   }
-  else {
-    /* skip ahead to the "loaded files" page */
+  else
+  {
+    /* Skip ahead to the "loaded files" page. */
     gnome_druid_set_page(GNOME_DRUID(wind->druid),
                          get_named_page(wind, "loaded_files_page"));
 
@@ -2533,15 +2784,14 @@
 
 
 /****************************************************************
- * gnc_ui_qif_import_cancel_cb
+ * do_cancel
  *
- * Invoked when the "Cancel" button is clicked.
+ * Clears out any imported data and shuts down the importer.
  ****************************************************************/
 
 static void
-gnc_ui_qif_import_cancel_cb(GnomeDruid * druid, gpointer user_data)
+do_cancel(QIFImportWindow * wind)
 {
-  QIFImportWindow     *wind = user_data;
   GList               *pageptr;
   GnomeDruidPage      *gtkpage;
   QIFDruidPage        *page;
@@ -2575,6 +2825,54 @@
 }
 
 
+/****************************************************************
+ * cancel_timeout_cb
+ *
+ * This timer callback function waits until the busy flag
+ * has been cleared before acting to cancel the import.
+ ****************************************************************/
+
+static gboolean
+cancel_timeout_cb(gpointer data)
+{
+  QIFImportWindow *wind = data;
+
+  if (wind->busy)
+    /* Wait for timer to go off again. */
+    return TRUE;
+
+  /* The busy flag was lowered. Perform the cancel. */
+  do_cancel(wind);
+
+  /* Cancel the timer. */
+  return FALSE;
+}
+
+
+/****************************************************************
+ * gnc_ui_qif_import_cancel_cb
+ *
+ * Invoked when the "Cancel" button is clicked.
+ ****************************************************************/
+
+static void
+gnc_ui_qif_import_cancel_cb(GnomeDruid * druid, gpointer user_data)
+{
+  QIFImportWindow     *wind = user_data;
+
+  if (wind->busy)
+  {
+    /* Cancel any long-running Scheme operation. */
+    scm_c_eval_string("(qif-import:cancel)");
+
+    /* Wait for the busy flag to be lowered. */
+    g_timeout_add(200, cancel_timeout_cb, user_data);
+  }
+  else
+    do_cancel(wind);
+}
+
+
 SCM
 gnc_ui_qif_import_druid_get_mappings(QIFImportWindow * w)
 {
@@ -2634,7 +2932,8 @@
 
 
   char * pre_page_names[NUM_PRE_PAGES] = {
-    "start_page", "load_file_page", "date_format_page", "account_name_page",
+    "start_page", "load_file_page", "load_progress_page",
+    "date_format_page", "account_name_page",
     "loaded_files_page", "account_doc_page", "account_match_page",
     "category_doc_page", "category_match_page", "memo_doc_page",
     "memo_match_page", "currency_page", "commodity_doc_page"
@@ -2678,6 +2977,26 @@
      G_CALLBACK(gnc_ui_qif_import_load_file_next_cb), retval);
 
   glade_xml_signal_connect_data
+    (xml, "gnc_ui_qif_import_load_progress_prepare_cb",
+     G_CALLBACK(gnc_ui_qif_import_load_progress_prepare_cb), retval);
+
+  glade_xml_signal_connect_data
+    (xml, "gnc_ui_qif_import_load_progress_show_cb",
+     G_CALLBACK(gnc_ui_qif_import_load_progress_show_cb), retval);
+
+  glade_xml_signal_connect_data
+    (xml, "gnc_ui_qif_import_load_progress_pause_cb",
+     G_CALLBACK(gnc_ui_qif_import_load_progress_pause_cb), retval);
+
+  glade_xml_signal_connect_data
+    (xml, "gnc_ui_qif_import_load_progress_next_cb",
+     G_CALLBACK(gnc_ui_qif_import_load_progress_next_cb), retval);
+
+  glade_xml_signal_connect_data
+    (xml, "gnc_ui_qif_import_load_progress_back_cb",
+     G_CALLBACK(gnc_ui_qif_import_load_progress_back_cb), retval);
+
+  glade_xml_signal_connect_data
     (xml, "gnc_ui_qif_import_date_format_next_cb",
      G_CALLBACK(gnc_ui_qif_import_date_format_next_cb), retval);
 
@@ -2764,12 +3083,21 @@
   retval->new_securities    =  SCM_BOOL_F;
   retval->new_namespaces    =  NULL;
   retval->ticker_map        =  SCM_BOOL_F;
-  retval->imported_account_tree   = SCM_BOOL_F;
-  retval->match_transactions = SCM_BOOL_F;
-  retval->selected_transaction = 0;
+  retval->imported_account_tree = SCM_BOOL_F;
+  retval->match_transactions    = SCM_BOOL_F;
+  retval->selected_transaction  = 0;
+  retval->busy              = FALSE;
 
   retval->druid           = glade_xml_get_widget(xml, "qif_import_druid");
   retval->filename_entry  = glade_xml_get_widget(xml, "qif_filename_entry");
+  retval->load_pause      = glade_xml_get_widget(xml, "load_progress_pause");
+  retval->load_log        = glade_xml_get_widget(xml, "load_progress_log");
+  retval->load_progress = gnc_progress_dialog_custom(
+    GTK_LABEL(glade_xml_get_widget(xml, "load_progress_primary")),
+    GTK_LABEL(glade_xml_get_widget(xml, "load_progress_secondary")),
+    GTK_PROGRESS_BAR(glade_xml_get_widget(xml, "load_progress_bar")),
+    GTK_LABEL(glade_xml_get_widget(xml, "load_progress_sub")),
+    GTK_TEXT_VIEW(retval->load_log));
   retval->acct_entry      = glade_xml_get_widget(xml, "qif_account_entry");
   retval->date_format_combo = glade_xml_get_widget(xml, "date_format_combobox");
   retval->selected_file_view = glade_xml_get_widget(xml, "selected_file_view");

Modified: gnucash/branches/2.2/src/import-export/qif-import/qif-file.scm
===================================================================
--- gnucash/branches/2.2/src/import-export/qif-import/qif-file.scm	2008-07-22 22:31:19 UTC (rev 17372)
+++ gnucash/branches/2.2/src/import-export/qif-import/qif-file.scm	2008-07-22 22:31:29 UTC (rev 17373)
@@ -29,13 +29,18 @@
 ;;  Suck in all the lines. Don't do any string interpretation,
 ;;  just store the fields "raw".
 ;;
+;;  The return value will be:
+;;    success:   ()
+;;    failure:   (#f error-message)
+;;    warning:   (#t error-message)
+;;    cancel:    #t
+;;    exception: #f
+;;
 ;; FIXME: This function really should be able to return multiple
 ;;        errors and warnings rather than a single one.
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(define (qif-file:read-file self path ticker-map window)
-  (let ((progress-dialog '())
-        (retval #f))
+(define (qif-file:read-file self path ticker-map progress-dialog)
 
   ;; This procedure does all the work. We'll define it, then call it safely.
   (define (private-read)
@@ -47,18 +52,35 @@
           (default-split #f)
           (first-xtn #f)
           (ignore-accounts #f)
-          (return-val #t)
+          (private-retval '())
           (line-num 0)
           (line #f)
           (tag #f)
           (value #f)
-          (heinous-error #f)
-          (missing-date-warned #f)
+          (abort-read #f)
           (delimiters (string #\cr #\nl))
           (file-stats #f)
           (file-size 0)
           (bytes-read 0))
 
+      ;; This procedure simplifies handling of warnings.
+      (define (mywarn . args)
+        (let ((str (gnc:list-display-to-string
+                     (append (list (_ "Line") " " line-num ": ") args))))
+          (set! private-retval (list #t str))
+          (qif-import:log progress-dialog "qif-file:read-file" str)))
+
+
+      ;; This procedure simplifies handling of failures
+      (define (myfail . args)
+        (let ((str (gnc:list-display-to-string
+                         (append (list (_ "Line") " " line-num ": ") args))))
+          (set! private-retval (list #f str))
+          (qif-import:log progress-dialog "qif-file:read-file"
+                          (string-append str "\n" (_ "Read aborted.")))
+          (set! abort-read #t)))
+
+
       (qif-file:set-path! self path)
       (if (not (access? path R_OK))
           ;; A UTF-8 encoded path won't succeed on some systems, such as
@@ -68,12 +90,9 @@
       (set! file-size (stat:size file-stats))
 
 
-      (if (> file-size 10000)
-          (begin
-            (set! progress-dialog (gnc-progress-dialog-new window #f))
-            (gnc-progress-dialog-set-title progress-dialog (_ "Progress"))
-            (gnc-progress-dialog-set-heading progress-dialog
-                                             (_ "Loading QIF file..."))))
+      (if progress-dialog
+          (gnc-progress-dialog-set-sub progress-dialog
+                                       (string-append (_ "Reading") " " path)))
 
       (with-input-from-file path
         (lambda ()
@@ -101,26 +120,13 @@
                                 (not (gnc-utf8? converted-value)))
                             (begin
                               (set! value (gnc-utf8-strip-invalid-strdup value))
-                              (set! return-val
-                                    (list #t (string-append
-                               (_ "This file is not encoded in UTF-8 or ASCII.")
-                               " "
-                               (_ "Some characters have been discarded."))))
-                              (gnc:warn "qif-file:read-file:"
-                                        " stripping invalid characters"
-                                        " at line " line-num
-                                        "\nAfter: [" value "]"))
+                              (mywarn
+                               (_ "Some characters have been discarded.")
+                               " " (_"Converted to: ") value))
                             (begin
-                              (set! return-val
-                                    (list #t (string-append
-                               (_ "This file is not encoded in UTF-8 or ASCII.")
-                               " "
-                               (_ "Some characters have been converted according to your locale."))))
-                              (gnc:warn "qif-file:read-file:"
-                                        " converting characters by locale"
-                                        " at line " line-num
-                                        "\nBefore: [" value "]"
-                                        "\nAfter:  [" converted-value "]")
+                              (mywarn
+                               (_ "Some characters have been converted according to your locale.")
+                               " " (_"Converted to: ") converted-value)
                               (set! value converted-value)))))
 
                   (if (eq? tag #\!)
@@ -178,9 +184,8 @@
                            (if (string-match "^option:"
                                              (symbol->string qstate-type))
                                (begin
-                                 (gnc:warn "qif-file:read-file:"
-                                           " ignoring '" qstate-type "' option"
-                                           " at line " line-num)
+                                 (mywarn (_ "Ignoring unknown option") " '"
+                                         qstate-type "'")
                                  (set! qstate-type old-qstate))))))
 
 
@@ -312,15 +317,9 @@
 
                             (if (qif-xtn:date current-xtn)
                                 (qif-file:add-xtn! self current-xtn)
-                                ;; The date is missing! Warn the user if they
-                                ;; haven't been warned already.
-                                (if (not missing-date-warned)
-                                    (begin
-                                      (set! missing-date-warned #t)
-                                      (gnc-warning-dialog '() (string-append
-                               (_ "One or more transactions is missing a date.")
-                               "\n"
-                               (_ "Some transactions may be discarded."))))))
+                                ;; The date is missing! Warn the user.
+                                (mywarn (_ "Date required.") " "
+                                        (_ "Discarding this transaction.")))
 
                             ;;(write current-xtn) (newline)
                             (set! current-xtn (make-qif-xtn))
@@ -352,10 +351,7 @@
                             (set! current-xtn (make-qif-class)))
 
                            (else
-                            (gnc:warn "qif-file:read-file:"
-                                      " ignoring class '" tag "'"
-                                      " at line " line-num
-                                      "\nLine content: [" line "]"))))
+                            (mywarn (_ "Ignoring class line") ": " line))))
 
 
                         ;;;;;;;;;;;;;;;;;;
@@ -423,9 +419,7 @@
                             (set! current-xtn (make-qif-cat)))
 
                            (else
-                            (gnc:warn "qif-file:read-file:"
-                                      " ignoring category '" tag "' line."
-                                      "\nLine content: [" line "]"))))
+                            (mywarn (_ "Ignoring category line") ": " line))))
 
 
                         ;;;;;;;;;;;;;;;;;;;
@@ -456,37 +450,35 @@
                             (set! current-xtn (make-qif-stock-symbol)))
 
                            (else
-                            (gnc:warn "qif-file:read-file:"
-                                      " ignoring security '" tag "'"
-                                      " at line " line-num
-                                      "\nLine content: [" line "]"))))
+                            (mywarn (_ "Ignoring security line") ": " line))))
 
 
                         ;; trying to sneak one by, eh?
                         (else
                           (if (and (not qstate-type)
                                    (not (string=? (string-trim line) "")))
-                              (begin
-                                (gnc:warn "qif-file:read-file:"
-                                          " file does not appear to be a QIF"
-                                          " at line " line-num
-                                          "\nLine content: [" line "]")
-                                (set! return-val
-                                      (list #f "File does not appear to be a QIF file."))
-                                (set! heinous-error #t))))))
+                              (myfail
+                                (_ "File does not appear to be in QIF format")
+                                ": " line)))))
 
-                  ;; Update the progress bar for each line read.
-                  (if (and (not (null? progress-dialog))
+                  ;; Report the progress.
+                  (if (and progress-dialog
                            (zero? (remainder line-num 32)))
-                      (gnc-progress-dialog-set-value progress-dialog
-                                                     (/ bytes-read file-size)))
+                      (begin
+                        (gnc-progress-dialog-set-value progress-dialog
+                                                       (/ bytes-read file-size))
+                        (qif-import:check-pause progress-dialog)
+                        (if qif-import:canceled
+                            (begin
+                              (set! private-retval #t)
+                              (set! abort-read #t)))))
 
                   ;; This is if we read a normal (non-null, non-eof) line...
-                  (if (not heinous-error)
+                  (if (not abort-read)
                       (line-loop)))
 
                 ;; ...and this is if we read a null or eof line.
-                (if (and (not heinous-error)
+                (if (and (not abort-read)
                          (not (eof-object? line)))
                     (line-loop))))))
 
@@ -494,19 +486,21 @@
       ;; they appeared in the file.  This is important in a few cases.
       (qif-file:set-xtns! self (reverse (qif-file:xtns self)))
 
-      return-val))
+      private-retval))
 
 
-    ;; Safely read the file.
-    (set! retval (gnc:backtrace-if-exception private-read))
+  (gnc:backtrace-if-exception
+    (lambda ()
+      (let ((retval #f))
+        ;; Safely read the file.
+        (set! retval (gnc:backtrace-if-exception private-read))
 
-    ;; Get rid of the progress dialog (if any).
-    (if (not (null? progress-dialog))
-        (begin
-          (gnc-progress-dialog-set-value progress-dialog 1)
-          (gnc-progress-dialog-destroy progress-dialog)))
+        ;; Fill the progress dialog.
+        (if (and progress-dialog
+                 (list? retval))
+          (gnc-progress-dialog-set-value progress-dialog 1))
 
-    retval))
+        retval))))
 
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -574,135 +568,238 @@
    qif-parse:parse-date/format
    (qif-file:xtns self)
    qif-parse:print-date
-   'error-on-ambiguity
-   (lambda (t e) e) 'date))
+   'error-on-ambiguity (lambda (t e) e) 'date
+   (lambda (fraction) #t)))
 
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;  qif-file:parse-fields-results
+;;  qif-file:parse-fields
 ;;
-;;  Take the results from qif-file:parse fields and find the
-;;  results for a particular type of parse.
+;;  Take a previously-read qif file and convert fields from
+;;  strings to the appropriate type.
+;;
+;;  The return value will be:
+;;    success:   ()
+;;    failure:   (#f . ((type . error) ...))
+;;    warning:   (#t . ((type . error) ...))
+;;    cancel:    #t
+;;    exception: #f
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(define (qif-file:parse-fields-results results type)
-  (define (test-results results)
-    (if (null? results) #f
-        (let* ((this-res (car results))
-               (this-type (car this-res)))
-          (if (eq? this-type type)
-              (cdr this-res)
-              (test-results (cdr results))))))
+(define (qif-file:parse-fields self progress-dialog)
 
-  (if results (test-results results) #f))
+  ;; This procedure does all the work. We'll define it, then call it safely.
+  (define (private-parse)
+   (let ((error #f)
+         (update-count 0)
+         (all-ok #f))
 
+     ;; This procedure sets a suboperation name.
+     (define (set-sub str)
+       (if progress-dialog
+           (gnc-progress-dialog-set-sub progress-dialog str))
+       #t)
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;  qif-file:parse-fields
-;;
-;;  Take a previously-read qif file and convert fields from
-;;  strings to the appropriate type.
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(define (qif-file:parse-fields self)
-;  (false-if-exception
-   (let* ((error #f)
-          (all-ok #f)
-          (set-error
-           (lambda (t e)
-             (if (not error)
-                 (set! error (list (cons t e)))
-                 (set! error (cons (cons t e) error)))))
-          (errlist-to-string
-           (lambda (lst)
-             (with-output-to-string
-               (lambda ()
-                 (for-each
-                  (lambda (elt)
-                    (display elt))
-                  lst))))))
+     ;; This procedure sets a suboperation weight.
+     (define (start-sub weight)
+       (if progress-dialog
+           (gnc-progress-dialog-push progress-dialog weight))
+       #t)
+
+
+     ;; This procedure finishes a suboperation.
+     (define (finish-sub)
+       (if progress-dialog
+           (gnc-progress-dialog-pop-full progress-dialog))
+       #t)
+
+
+     ;; This procedure handles progress reporting, pause, and cancel.
+     (define (update-progress fraction)
+       (set! update-count (+ 1 update-count))
+       (if (and progress-dialog
+                (zero? (remainder update-count 32)))
+           (begin
+             (gnc-progress-dialog-set-value progress-dialog fraction)
+             (qif-import:check-pause progress-dialog)
+             (if qif-import:canceled
+                 (throw 'cancel)))))
+
+
+     ;; This procedure is the generic error handler for parsing.
+     (define (add-error t e)
+       ;; Log the error message.
+       (if (string? e)
+           (qif-import:log progress-dialog
+                           "qif-file:parse-fields"
+                           (string-append (case t
+                                            ((date) (_ "Transaction date"))
+                                            ((split-amounts) (_ "Transaction amount"))
+                                            ((share-price) (_ "Share price"))
+                                            ((num-shares) (_ "Share quantity"))
+                                            ((action) (_ "Investment action"))
+                                            ((cleared) (_ "Reconciliation status"))
+                                            ((commission) (_ "Commission"))
+                                            ((acct-type) (_ "Account type"))
+                                            ((tax-class) (_ "Tax class"))
+                                            ((budget-amt) (_ "Category budget amount"))
+                                            ((budget) (_ "Account budget amount"))
+                                            ((limit) (_ "Credit limit"))
+                                            (else (symbol->string t)))
+                                          ": " e)))
+       ;; Save the error condition.
+       (if (not error)
+           (set! error (list (cons t e)))
+           (set! error (cons (cons t e) error))))
+
+
      (and
-      ;; fields of categories.
+      ;;
+      ;; Fields of categories.
+      ;;
+      (set-sub (_ "Parsing categories"))
+      ;; The category tasks will be 5% of the overall parsing effort.
+      (start-sub 0.05)
+
+      ;; Tax classes; assume this is 50% of the category parsing effort.
+      (start-sub 0.5)
       (check-and-parse-field
        qif-cat:tax-class qif-cat:set-tax-class! gnc-numeric-equal
        qif-parse:check-number-format '(decimal comma)
        qif-parse:parse-number/format (qif-file:cats self)
        qif-parse:print-number
-       'guess-on-ambiguity
-       set-error 'tax-class)
+       'guess-on-ambiguity add-error 'tax-class
+       update-progress)
+      (finish-sub)
 
+      ;; Budget amounts; this is the last task for category parsing.
+      (start-sub 1)
       (check-and-parse-field
        qif-cat:budget-amt qif-cat:set-budget-amt! gnc-numeric-equal
        qif-parse:check-number-format '(decimal comma)
        qif-parse:parse-number/format (qif-file:cats self)
        qif-parse:print-number
-       'guess-on-ambiguity
-       set-error 'budget-amt)
+       'guess-on-ambiguity add-error 'budget-amt
+       update-progress)
+      (finish-sub)
 
-      ;; fields of accounts
+      (finish-sub)
+
+
+      ;;
+      ;; Fields of accounts
+      ;;
+      (set-sub (_ "Parsing accounts"))
+      ;; The account tasks will be 5% of the overall parsing effort.
+      (start-sub 0.05)
+
+      ;; Account limits; assume this is 20% of the account parsing effort.
+      (start-sub 0.2)
       (check-and-parse-field
        qif-acct:limit qif-acct:set-limit! gnc-numeric-equal
        qif-parse:check-number-format '(decimal comma)
        qif-parse:parse-number/format (qif-file:accounts self)
        qif-parse:print-number
-       'guess-on-ambiguity
-       set-error 'limit)
+       'guess-on-ambiguity add-error 'limit
+       update-progress)
+      (finish-sub)
 
+      ;; Budget amounts; assume this is 20% of the account parsing effort.
+      (start-sub 0.2)
       (check-and-parse-field
        qif-acct:budget qif-acct:set-budget! gnc-numeric-equal
        qif-parse:check-number-format '(decimal comma)
        qif-parse:parse-number/format (qif-file:accounts self)
        qif-parse:print-number
-       'guess-on-ambiguity
-       set-error 'budget)
+       'guess-on-ambiguity add-error 'budget
+       update-progress)
+      (finish-sub)
 
+      ;; Account types; this is the last task for account parsing.
+      (start-sub 1)
       (parse-field
        qif-acct:type qif-acct:set-type!
        qif-parse:parse-acct-type (qif-file:accounts self)
-       set-error)
+       add-error 'acct-type
+       update-progress)
+      (finish-sub)
 
+      (finish-sub)
+
+
+      ;;
       ;; fields of transactions
+      ;;
+      (set-sub (_ "Parsing transactions"))
+      ;; Transaction parsing takes up the rest of the overall parsing effort.
+      (start-sub 1)
+
+      ;; Dates; assume this is 15% of the transaction effort.
+      (start-sub 0.15)
       (check-and-parse-field
        qif-xtn:date qif-xtn:set-date! equal?
        qif-parse:check-date-format '(m-d-y d-m-y y-m-d y-d-m)
        qif-parse:parse-date/format
        (qif-file:xtns self)
        qif-parse:print-date
-       'error-on-ambiguity
-       set-error 'date)
+       'error-on-ambiguity add-error 'date
+       update-progress)
+      (finish-sub)
 
+      ;; Clear flags; assume this is 5% of the transaction effort.
+      (start-sub 0.05)
       (parse-field
        qif-xtn:cleared qif-xtn:set-cleared!
-       qif-parse:parse-cleared-field (qif-file:xtns self) set-error)
+       qif-parse:parse-cleared-field (qif-file:xtns self)
+       add-error 'cleared
+       update-progress)
+      (finish-sub)
 
+      ;; Investment actions; assume this is 10% of the transaction effort.
+      (start-sub 0.1)
       (parse-field
        qif-xtn:action qif-xtn:set-action!
-       qif-parse:parse-action-field (qif-file:xtns self) set-error)
+       qif-parse:parse-action-field (qif-file:xtns self)
+       add-error 'action
+       update-progress)
+      (finish-sub)
 
+      ;; Share prices; assume this is 10% of the transaction effort.
+      (start-sub 0.1)
       (check-and-parse-field
        qif-xtn:share-price qif-xtn:set-share-price! gnc-numeric-equal
        qif-parse:check-number-format '(decimal comma)
        qif-parse:parse-number/format (qif-file:xtns self)
        qif-parse:print-number
-       'guess-on-ambiguity
-       set-error 'share-price)
+       'guess-on-ambiguity add-error 'share-price
+       update-progress)
+      (finish-sub)
 
+      ;; Share quantities; assume this is 10% of the transaction effort.
+      (start-sub 0.1)
       (check-and-parse-field
        qif-xtn:num-shares qif-xtn:set-num-shares! gnc-numeric-equal
        qif-parse:check-number-format '(decimal comma)
        qif-parse:parse-number/format (qif-file:xtns self)
        qif-parse:print-number
-       'guess-on-ambiguity
-       set-error 'num-shares)
+       'guess-on-ambiguity add-error 'num-shares
+       update-progress)
+      (finish-sub)
 
+      ;; Commissions; assume this is 10% of the transaction effort.
+      (start-sub 0.1)
       (check-and-parse-field
        qif-xtn:commission qif-xtn:set-commission! gnc-numeric-equal
        qif-parse:check-number-format '(decimal comma)
        qif-parse:parse-number/format (qif-file:xtns self)
        qif-parse:print-number
-       'guess-on-ambiguity
-       set-error 'commission)
+       'guess-on-ambiguity add-error 'commission
+       update-progress)
+      (finish-sub)
 
+      ;; Splits; this is the rest of the transaction effort.
+      (start-sub 1)
       ;; this one's a little tricky... it checks and sets all the
       ;; split amounts for the transaction together.
       (check-and-parse-field
@@ -710,19 +807,30 @@
        qif-parse:check-number-formats '(decimal comma)
        qif-parse:parse-numbers/format (qif-file:xtns self)
        qif-parse:print-numbers
-       'guess-on-ambiguity
-       set-error 'split-amounts)
+       'guess-on-ambiguity add-error 'split-amounts
+       update-progress)
+      (finish-sub)
 
+      (finish-sub)
+
+
       (begin
         (set! all-ok #t)
         #t))
 
-     (cond (error
+     ;; Determine what to return.
+     (cond (qif-import:canceled
+            #t)
+           (error
             (cons all-ok error))
-           (#t #t))))
-;)
+           (else '()))))
 
 
+  ;; Safely read the file and return the result.
+  (gnc:backtrace-if-exception
+    (lambda () (catch 'cancel private-parse (lambda (key . args) #t)))))
+
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;  parse-field
 ;;
@@ -731,13 +839,18 @@
 ;;  of objects.
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(define (parse-field getter setter parser objects errormsg)
-  (for-each
-   (lambda (obj)
-     (let ((unparsed (getter obj)))
+(define (parse-field getter setter parser objects errorproc errortype reporter)
+  (let ((work-to-do (length objects))
+        (work-done 0)
+        (unparsed #f))
+    (for-each
+     (lambda (obj)
+       (set! unparsed (getter obj))
        (if (and unparsed (string? unparsed))
-           (setter obj (parser unparsed)))))
-   objects)
+           (setter obj (parser unparsed errorproc errortype)))
+       (set! work-done (+ 1 work-done))
+       (reporter (/ work-done work-to-do)))
+     objects))
   #t)
 
 
@@ -753,11 +866,16 @@
 
 (define (check-and-parse-field getter setter equiv-thunk checker
                                formats parser objects printer
-                               on-error errormsg errortype)
-  ;; first find the right format for the field
-  (let ((do-parsing #f)
-        (retval #t)
-        (format #f))
+                               on-error errorproc errortype
+                               reporter)
+  (let* ((do-parsing #f)
+         (retval #t)
+         (format #f)
+         (len (length objects))
+         (work-to-do (* len 2))
+         (work-done 0))
+
+    ;; first find the right format for the field
     ;; loop over objects.  If the formats list ever gets down
     ;; to 1 element, we can stop right there.
     (if (not (null? objects))
@@ -767,7 +885,9 @@
             (if val
                 (begin
                   (set! do-parsing #t)
-                  (set! formats (checker val formats)))))
+                  (set! formats (checker val formats))))
+            (set! work-done (+ 1 work-done))
+            (reporter (/ work-done work-to-do)))
           (if (and (not (null? formats))
                    ;; (not (null? (cdr formats)))
                    (not (null? rest)))
@@ -786,24 +906,30 @@
     (cond
      ((or (not formats)
           (null? formats))
-      (errormsg errortype "Data for number or date does not match a known format.")
+      ;; Data was not in any of the supplied formats.
+      (errorproc errortype (_ "Unrecognized or inconsistent format."))
       (set! retval #f)
       (set! do-parsing #f))
+
      ((and (not (null? (cdr formats))) do-parsing)
-      ;; there are multiple formats that fit.  If they all produce the
+      ;; There are multiple formats that fit.  If they all produce the
       ;; same interpretation for every data point in the set, then
       ;; just ignore the format ambiguity.  Otherwise, it's really an
       ;; error.  ATM since there's no way to correct the error let's
       ;; just leave it be.
       (if (or (eq? on-error 'guess-on-ambiguity)
               (all-formats-equivalent? getter parser equiv-thunk formats
-                                       objects printer errormsg errortype))
+                                       objects printer errorproc errortype))
           (set! format (car formats))
           (begin
-            (errormsg errortype formats)
+            (errorproc errortype formats)
             (set! do-parsing #f)
+            ;; NOTE: It seems like this ought to be (set! retval #f) instead,
+            ;;       but that would stop all parsing dead in its tracks. Not
+            ;;       sure that this can happen to anything other than dates,
+            ;;       and those will get reparsed anyway.
             (set! retval #t))))
-     (#t
+     (else
       (set! format (car formats))))
 
     ;; do-parsing is false if there were no objects with non-#f values
@@ -812,19 +938,25 @@
     ;; all of them once, but at least not twice.
     (if do-parsing
         (for-each
-         (lambda (current)
-           (let ((val (getter current))
-                 (parsed #f))
-             (if val
-                 (begin
-                   (set! parsed (parser val format))
-                   (if parsed
-                       (setter current parsed)
-                       (begin
-                         (set! retval #f)
-                         (errormsg errortype
-                          "Data format inconsistent in QIF file.")))))))
+          (lambda (current)
+            (let ((val (getter current))
+                  (parsed #f))
+              (if val
+                  (begin
+                    (set! parsed (parser val format))
+                    (if parsed
+                        (setter current parsed)
+                        (begin
+                          (set! retval #f)
+                          (errorproc errortype
+                           (_ "Parsing failed.")))))))
+            (set! work-done (+ 1 work-done))
+            (reporter (/ work-done work-to-do)))
          objects))
+
+    (if retval
+        (reporter 1))
+
     retval))
 
 
@@ -839,35 +971,51 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (define (all-formats-equivalent? getter parser equiv-thunk formats objects
-                                 printer errormsg errortype)
+                                 printer errorproc errortype)
   (let ((all-ok #t))
     (let obj-loop ((objlist objects))
       (let* ((unparsed (getter (car objlist)))
              (parsed #f))
         (if (string? unparsed)
             (begin
+              ;; Parse using the first format in the list.
               (set! parsed (parser unparsed (car formats)))
+              ;; For each remaining format, see if the result is the same.
               (for-each
                (lambda (fmt)
                  (let ((this-parsed (parser unparsed fmt)))
                    (if (not (equiv-thunk parsed this-parsed))
                        (begin
                          (set! all-ok #f)
-                         (errormsg errortype
-                          (with-output-to-string
-                            (lambda ()
-                              (for-each
-                               (lambda (elt)
-                                 (display elt))
-                               (list
-                                "Parse ambiguity : between formats "
-                                formats "\nValue " unparsed " could be "
-                                (printer parsed) " or "
-                                (printer this-parsed)
-                                "\nand no evidence exists to distinguish."
-                                "\nUsing " (printer parsed) ". "
-                                "\nSee help for more info.")))))))))
+                         (if (not (eq? errortype 'date))
+                             (errorproc errortype
+                                        (gnc:list-display-to-string (list
+                              (_ "Parse ambiguity between formats") " "
+                              formats "\n"
+                              (sprintf #f (_ "Value '%s' could be %s or %s.")
+                                       parsed
+                                       (printer parsed)
+                                       (printer this-parsed))))))))))
                (cdr formats))))
         (if (and all-ok (not (null? (cdr objlist))))
             (obj-loop (cdr objlist)))))
     all-ok))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;  qif-file:parse-fields-results
+;;
+;;  Take the results from qif-file:parse fields and find the
+;;  first result for a particular type of parse.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define (qif-file:parse-fields-results results type)
+  (define (test-results results)
+    (if (null? results) #f
+        (let* ((this-res (car results))
+               (this-type (car this-res)))
+          (if (eq? this-type type)
+              (cdr this-res)
+              (test-results (cdr results))))))
+
+  (if results (test-results results) #f))

Modified: gnucash/branches/2.2/src/import-export/qif-import/qif-import.scm
===================================================================
--- gnucash/branches/2.2/src/import-export/qif-import/qif-import.scm	2008-07-22 22:31:19 UTC (rev 17372)
+++ gnucash/branches/2.2/src/import-export/qif-import/qif-import.scm	2008-07-22 22:31:29 UTC (rev 17373)
@@ -44,6 +44,9 @@
 (export qif-import:load-map-prefs)
 (export qif-import:qif-to-gnc)
 (export qif-import:qif-to-gnc-undo)
+(export qif-import:reset-cancel-pause)
+(export qif-import:cancel)
+(export qif-import:toggle-pause)
 
 (export qif-map-entry:gnc-name)
 (export qif-map-entry:set-gnc-name!)

Modified: gnucash/branches/2.2/src/import-export/qif-import/qif-parse.scm
===================================================================
--- gnucash/branches/2.2/src/import-export/qif-import/qif-parse.scm	2008-07-22 22:31:19 UTC (rev 17372)
+++ gnucash/branches/2.2/src/import-export/qif-import/qif-parse.scm	2008-07-22 22:31:29 UTC (rev 17373)
@@ -138,7 +138,7 @@
 ;;  conventions.
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(define (qif-parse:parse-acct-type read-value)
+(define (qif-parse:parse-acct-type read-value errorproc errortype)
   (let ((mangled-string
          (string-downcase! (string-remove-trailing-space
                             (string-remove-leading-space read-value)))))
@@ -162,9 +162,9 @@
      ((string=? mangled-string "mutual")
       (list GNC-BANK-TYPE))
      (#t
-      (gnc:warn "qif-parse:parse-acct-type: unrecognized account type ["
-                read-value
-                "]... substituting Bank.")
+      (errorproc errortype
+                 (sprintf #f (_ "Unrecognized account type '%s'. Defaulting to Bank.")
+                          read-value))
       (list GNC-BANK-TYPE)))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -185,7 +185,7 @@
     (string->symbol bang-field)))
 
 
-(define (qif-parse:parse-action-field read-value)
+(define (qif-parse:parse-action-field read-value errorproc errortype)
   (if read-value
       (let ((action-symbol (string-to-canonical-symbol read-value)))
         (case action-symbol
@@ -269,12 +269,8 @@
 ;          ((vest)
 ;           'vest)
           (else
-           (gnc-warning-dialog '()
-            (string-append
-             (sprintf #f (_ "The file contains an unknown Action '%s'.")
-                      read-value)
-             "\n"
-             (_ "Some transactions may be discarded.")))
+           (errorproc errortype
+                      (sprintf #f (_ "Unrecognized action '%s'.") read-value))
            #f)))
       #f))
 
@@ -284,7 +280,7 @@
 ;;  budget related stuff I don't understand.
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(define (qif-parse:parse-cleared-field read-value)
+(define (qif-parse:parse-cleared-field read-value errorproc errortype)
   (if (and (string? read-value)
            (> (string-length read-value) 0))
       (let ((secondchar (string-ref read-value 0)))
@@ -296,7 +292,7 @@
               ((or (eq? secondchar #\?)
                    (eq? secondchar #\!))
                'budgeted)
-              (#t
+              (else
                #f)))
       #f))
 

Modified: gnucash/branches/2.2/src/import-export/qif-import/qif-to-gnc.scm
===================================================================
--- gnucash/branches/2.2/src/import-export/qif-import/qif-to-gnc.scm	2008-07-22 22:31:19 UTC (rev 17372)
+++ gnucash/branches/2.2/src/import-export/qif-import/qif-to-gnc.scm	2008-07-22 22:31:29 UTC (rev 17373)
@@ -327,9 +327,14 @@
            (if (> work-to-do 100)
                (begin
                  (set! progress-dialog (gnc-progress-dialog-new window #f))
-                 (gnc-progress-dialog-set-title progress-dialog (_ "Progress"))
-                 (gnc-progress-dialog-set-heading progress-dialog
-                                             (_ "Importing transactions..."))))
+                 (gnc-progress-dialog-set-title progress-dialog
+                                                (_ "Importing QIF data"))
+                 (gnc-progress-dialog-set-primary progress-dialog
+                                                  (_ "Importing QIF data"))
+                 (gnc-progress-dialog-set-secondary progress-dialog
+                   (_ "GnuCash is converting your QIF data."))
+                 (gnc-progress-dialog-set-sub progress-dialog
+                                              (_ "Importing transactions"))))
 
 
            ;; now run through the markable transactions marking any
@@ -337,12 +342,13 @@
            (if (> (length markable-xtns) 1)
                (let xloop ((xtn (car markable-xtns))
                            (rest (cdr markable-xtns)))
+                 ;; Update the progress.
                  (set! work-done (+ 1 work-done))
-                 (if (not (null? progress-dialog))
-                     (begin
-                       (gnc-progress-dialog-set-value
-                        progress-dialog (/ work-done work-to-do))
-                       (gnc-progress-dialog-update progress-dialog)))
+                 (if (and (not (null? progress-dialog))
+                          (zero? (remainder work-done 32)))
+                     (gnc-progress-dialog-set-value progress-dialog
+                                                    (/ work-done work-to-do)))
+
                  (if (not (qif-xtn:mark xtn))
                      (qif-import:mark-matching-xtns xtn rest))
                  (if (not (null? (cdr rest)))
@@ -354,32 +360,36 @@
             (lambda (qif-file)
               (for-each
                (lambda (xtn)
+                 ;; Update the progress.
                  (set! work-done (+ 1 work-done))
-                 (if (not (null? progress-dialog))
-                     (begin
-                       (gnc-progress-dialog-set-value
-                        progress-dialog (/ work-done work-to-do))
-                       (gnc-progress-dialog-update progress-dialog)))
+                 (if (and (not (null? progress-dialog))
+                          (zero? (remainder work-done 32)))
+                     (gnc-progress-dialog-set-value progress-dialog
+                                                    (/ work-done work-to-do)))
+
                  (if (not (qif-xtn:mark xtn))
-                     (begin
-                       ;; create and fill in the GNC transaction
-                       (let ((gnc-xtn (xaccMallocTransaction
-                                       (gnc-get-current-book))))
-                         (xaccTransBeginEdit gnc-xtn)
+                     ;; create and fill in the GNC transaction
+                     (let ((gnc-xtn (xaccMallocTransaction
+                                     (gnc-get-current-book))))
+                       (xaccTransBeginEdit gnc-xtn)
 
-                         ;; FIXME. This is probably wrong
-                         (xaccTransSetCurrency gnc-xtn (gnc-default-currency))
+                       ;; FIXME. This is probably wrong
+                       (xaccTransSetCurrency gnc-xtn (gnc-default-currency))
 
-                         ;; build the transaction
-                         (qif-import:qif-xtn-to-gnc-xtn
-                          xtn qif-file gnc-xtn gnc-acct-hash
-                          qif-acct-map qif-cat-map qif-memo-map)
+                       ;; build the transaction
+                       (qif-import:qif-xtn-to-gnc-xtn
+                        xtn qif-file gnc-xtn gnc-acct-hash
+                        qif-acct-map qif-cat-map qif-memo-map)
 
-                         ;; rebalance and commit everything
-                         (xaccTransCommitEdit gnc-xtn)))))
+                       ;; rebalance and commit everything
+                       (xaccTransCommitEdit gnc-xtn))))
                (qif-file:xtns qif-file)))
             sorted-qif-files-list)
 
+           ;; Finished.
+           (if (not (null? progress-dialog))
+               (gnc-progress-dialog-set-value progress-dialog 1))
+
            new-root))))
 
     ;; Get rid of the progress dialog (if any).
@@ -482,7 +492,6 @@
                                        (gnc-get-current-book)))
                        (far-acct-info #f)
                        (far-acct-name #f)
-                       (far-acct-type #f)
                        (far-acct #f)
                        (split-amt (qif-split:amount qif-split))
                        ;; For split transactions, get this split's memo.
@@ -572,7 +581,6 @@
                (commission-acct #f)
                (commission-amt (qif-xtn:commission qif-xtn))
                (commission-split #f)
-               (defer-share-price #f)
                (gnc-far-split (xaccMallocSplit (gnc-get-current-book))))
 
           (if (not num-shares) (set! num-shares (gnc-numeric-zero)))
@@ -796,11 +804,9 @@
          (date (qif-xtn:date xtn))
          (amount (n- (qif-split:amount split)))
          (group-amount #f)
-         (memo (qif-split:memo split))
          (security-name (qif-xtn:security-name xtn))
          (action (qif-xtn:action xtn))
          (bank-xtn? (not security-name))
-         (cleared? #f)
          (different-acct-splits '())
          (same-acct-splits '())
          (how #f)
@@ -895,8 +901,7 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (define (qif-import:xtn-has-matches? xtn acct-name date amount group-amt)
-  (let ((matching-splits '())
-        (same-acct-splits '())
+  (let ((same-acct-splits '())
         (this-group-amt (gnc-numeric-zero))
         (how #f)
         (date-matches
@@ -959,9 +964,7 @@
           ;; we can still have a many-to-one match.
           (if (and (not how)
                    (gnc-numeric-equal this-group-amt amount))
-              (begin
-                (set! how
-                      (cons 'many-to-one same-acct-splits))))))
+              (set! how (cons 'many-to-one same-acct-splits)))))
 
     ;; we're all done.  'how' either is #f or a
     ;; cons of the way-it-matched and a list of the matching

Modified: gnucash/branches/2.2/src/import-export/qif-import/qif-utils.scm
===================================================================
--- gnucash/branches/2.2/src/import-export/qif-import/qif-utils.scm	2008-07-22 22:31:19 UTC (rev 17372)
+++ gnucash/branches/2.2/src/import-export/qif-import/qif-utils.scm	2008-07-22 22:31:29 UTC (rev 17373)
@@ -7,6 +7,8 @@
 
 (use-modules (ice-9 regex))
 
+(define qif-import:paused #f)
+(define qif-import:canceled #f)
 
 (define (simple-filter pred list)
   (let ((retval '()))
@@ -67,3 +69,32 @@
    (string-downcase
     (string-remove-leading-space
      (string-remove-trailing-space str)))))
+
+
+(define (qif-import:log progress-dialog proc str)
+  (if progress-dialog
+      (gnc-progress-dialog-append-log progress-dialog (string-append str "\n"))
+      (gnc:warn proc ": " str)))
+
+(define (qif-import:reset-cancel-pause)
+  (set! qif-import:paused #f)
+  (set! qif-import:canceled #f))
+
+(define (qif-import:cancel)
+  (set! qif-import:canceled #t))
+
+(define (qif-import:toggle-pause progress-dialog)
+  (if qif-import:paused
+      (begin
+        (set! qif-import:paused #f)
+        (if progress-dialog
+            (gnc-progress-dialog-resume progress-dialog)))
+      (begin
+        (set! qif-import:paused #t)
+        (if progress-dialog
+            (gnc-progress-dialog-pause progress-dialog)))))
+
+(define (qif-import:check-pause progress-dialog)
+  (while (and qif-import:paused (not qif-import:canceled))
+    (gnc-progress-dialog-update progress-dialog)))
+

Modified: gnucash/branches/2.2/src/import-export/qif-import/qif.glade
===================================================================
--- gnucash/branches/2.2/src/import-export/qif-import/qif.glade	2008-07-22 22:31:19 UTC (rev 17372)
+++ gnucash/branches/2.2/src/import-export/qif-import/qif.glade	2008-07-22 22:31:29 UTC (rev 17373)
@@ -87,55 +87,55 @@
 	  <property name="homogeneous">False</property>
 	  <property name="spacing">0</property>
 
-	      <child>
-		<widget class="GtkLabel" id="label1">
-		  <property name="visible">True</property>
-		  <property name="label" translatable="yes">_Select or add a GnuCash account:</property>
-		  <property name="use_underline">Yes</property>
-		  <property name="use_markup">False</property>
-		  <property name="justify">GTK_JUSTIFY_LEFT</property>
-		  <property name="wrap">False</property>
-		  <property name="selectable">False</property>
-		  <property name="xalign">0</property>
-		  <property name="yalign">0.5</property>
-		  <property name="xpad">5</property>
-		  <property name="ypad">0</property>
-                  <property name="mnemonic_widget">account_tree</property>
-		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-		  <property name="width_chars">-1</property>
-		  <property name="single_line_mode">False</property>
-		  <property name="angle">0</property>
-		</widget>
-		<packing>
-		  <property name="padding">5</property>
-		  <property name="expand">False</property>
-		  <property name="fill">False</property>
-		</packing>
-	      </child>
+	  <child>
+	    <widget class="GtkLabel" id="label1">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">_Select or add a GnuCash account:</property>
+	      <property name="use_underline">Yes</property>
+	      <property name="use_markup">False</property>
+	      <property name="justify">GTK_JUSTIFY_LEFT</property>
+	      <property name="wrap">False</property>
+	      <property name="selectable">False</property>
+	      <property name="xalign">0</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">5</property>
+	      <property name="ypad">0</property>
+              <property name="mnemonic_widget">account_tree</property>
+	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	      <property name="width_chars">-1</property>
+	      <property name="single_line_mode">False</property>
+	      <property name="angle">0</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">5</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
 
+	  <child>
+	    <widget class="GtkScrolledWindow" id="scrolledwindow24">
+	      <property name="visible">True</property>
+	      <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+	      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+	      <property name="shadow_type">GTK_SHADOW_IN</property>
+	      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
 	      <child>
-		<widget class="GtkScrolledWindow" id="scrolledwindow24">
-		  <property name="visible">True</property>
-		  <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-		  <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-		  <property name="shadow_type">GTK_SHADOW_IN</property>
-		  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
-
-		  <child>
-		    <widget class="GtkTreeView" id="account_tree">
-		      <property name="visible">True</property>
-		      <property name="can_focus">True</property>
-		      <property name="headers_visible">True</property>
-		      <property name="rules_hint">True</property>
-		      <property name="reorderable">False</property>
-		      <property name="enable_search">True</property>
-		      <property name="fixed_height_mode">False</property>
-		      <property name="hover_selection">False</property>
-		      <property name="hover_expand">False</property>
-		    </widget>
-		  </child>
-		</widget>
+	        <widget class="GtkTreeView" id="account_tree">
+	          <property name="visible">True</property>
+	          <property name="can_focus">True</property>
+	          <property name="headers_visible">True</property>
+	          <property name="rules_hint">True</property>
+	          <property name="reorderable">False</property>
+	          <property name="enable_search">True</property>
+	          <property name="fixed_height_mode">False</property>
+	          <property name="hover_selection">False</property>
+	          <property name="hover_expand">False</property>
+	        </widget>
 	      </child>
+	    </widget>
+	  </child>
 	</widget>
 	<packing>
 	  <property name="padding">0</property>
@@ -193,7 +193,7 @@
 
 	  <child internal-child="vbox">
 	    <widget class="GtkVBox" id="druid-vbox8">
-	      <property name="border_width">25</property>
+	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
 	      <property name="spacing">0</property>
@@ -282,6 +282,186 @@
       </child>
 
       <child>
+	<widget class="GnomeDruidPageStandard" id="load_progress_page">
+	  <property name="visible">True</property>
+	  <property name="title" translatable="yes">Load QIF files</property>
+	  <signal name="prepare" handler="gnc_ui_qif_import_load_progress_prepare_cb" after="yes"/>
+	  <signal name="show" handler="gnc_ui_qif_import_load_progress_show_cb" after="yes"/>
+	  <signal name="next" handler="gnc_ui_qif_import_load_progress_next_cb"/>
+	  <signal name="back" handler="gnc_ui_qif_import_load_progress_back_cb"/>
+
+	  <child internal-child="vbox">
+	    <widget class="GtkVBox" id="druid-vbox7609">
+	      <property name="border_width">12</property>
+	      <property name="visible">True</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">12</property>
+
+	      <child>
+		<widget class="GtkLabel" id="load_progress_primary">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="no">&lt;span weight=&quot;bold&quot; size=&quot;larger&quot;&gt;Primary text&lt;/span&gt;</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">True</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">True</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel" id="load_progress_secondary">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="no">Secondary text.</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">False</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">True</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkVBox" id="load_progress_vbox1">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">0</property>
+
+		  <child>
+		    <widget class="GtkProgressBar" id="load_progress_bar">
+	              <property name="visible">True</property>
+	              <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
+	              <property name="fraction">0</property>
+	              <property name="pulse_step">0.10000000149</property>
+	              <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		    </widget>
+		    <packing>
+	              <property name="padding">0</property>
+	              <property name="expand">False</property>
+	              <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkLabel" id="load_progress_sub">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="no">Sub-operation text</property>
+		      <property name="label" translatable="no">&lt;span style=&quot;italic&quot;&gt;Sub-operation text&lt;/span&gt;</property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">True</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">True</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkHButtonBox" id="load_progress_hbuttonbox1">
+		      <property name="visible">True</property>
+		      <property name="layout_style">GTK_BUTTONBOX_END</property>
+  
+		      <child>
+	                <widget class="GtkButton" id="load_progress_pause">
+	                  <property name="visible">True</property>
+	                  <property name="sensitive">True</property>
+	                  <property name="can_default">True</property>
+	                  <property name="can_focus">True</property>
+	                  <property name="label">gtk-media-pause</property>
+	                  <property name="use_stock">True</property>
+	                  <property name="relief">GTK_RELIEF_NORMAL</property>
+	                  <property name="focus_on_click">True</property>
+	                  <signal name="clicked" handler="gnc_ui_qif_import_load_progress_pause_cb"/>
+	                </widget>
+	              </child>
+		    </widget>
+		    <packing>
+	              <property name="padding">0</property>
+	              <property name="expand">False</property>
+	              <property name="fill">False</property>
+		    </packing>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">6</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+	        <widget class="GtkScrolledWindow" id="scrolledwindow25">
+	          <property name="visible">True</property>
+	          <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+	          <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+	          <property name="shadow_type">GTK_SHADOW_IN</property>
+	          <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+	          <child>
+		    <widget class="GtkTextView" id="load_progress_log">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="editable">False</property>
+		      <property name="overwrite">False</property>
+		      <property name="accepts_tab">False</property>
+		      <property name="justification">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap_mode">GTK_WRAP_WORD</property>
+		      <property name="cursor_visible">False</property>
+		      <property name="pixels_above_lines">0</property>
+		      <property name="pixels_below_lines">0</property>
+		      <property name="pixels_inside_wrap">0</property>
+		      <property name="left_margin">0</property>
+		      <property name="right_margin">0</property>
+		      <property name="indent">0</property>
+		      <property name="text" translatable="no">Log for errors and warnings</property>
+		    </widget>
+	          </child>
+	        </widget>
+	      </child>
+	    </widget>
+	  </child>
+	</widget>
+      </child>
+
+      <child>
 	<widget class="GnomeDruidPageStandard" id="date_format_page">
 	  <property name="visible">True</property>
 	  <property name="title" translatable="yes">Set a date format for this QIF file</property>
@@ -291,7 +471,7 @@
 
 	  <child internal-child="vbox">
 	    <widget class="GtkVBox" id="druid-vbox22">
-	      <property name="border_width">25</property>
+	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
 	      <property name="spacing">0</property>
@@ -378,7 +558,7 @@
 
 	  <child internal-child="vbox">
 	    <widget class="GtkVBox" id="druid-vbox9">
-	      <property name="border_width">25</property>
+	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
 	      <property name="spacing">0</property>
@@ -509,7 +689,7 @@
 
 	  <child internal-child="vbox">
 	    <widget class="GtkVBox" id="druid-vbox1">
-	      <property name="border_width">25</property>
+	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
 	      <property name="spacing">4</property>
@@ -622,7 +802,7 @@
 
 	  <child internal-child="vbox">
 	    <widget class="GtkVBox" id="druid-vbox13">
-	      <property name="border_width">25</property>
+	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
 	      <property name="spacing">0</property>
@@ -669,7 +849,7 @@
 
 	  <child internal-child="vbox">
 	    <widget class="GtkVBox" id="druid-vbox3">
-	      <property name="border_width">25</property>
+	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
 	      <property name="spacing">0</property>
@@ -825,7 +1005,7 @@
 
 	  <child internal-child="vbox">
 	    <widget class="GtkVBox" id="druid-vbox18">
-	      <property name="border_width">25</property>
+	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
 	      <property name="spacing">0</property>
@@ -873,7 +1053,7 @@
 
 	  <child internal-child="vbox">
 	    <widget class="GtkVBox" id="druid-vbox4">
-	      <property name="border_width">25</property>
+	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
 	      <property name="spacing">0</property>
@@ -1028,7 +1208,7 @@
 
 	  <child internal-child="vbox">
 	    <widget class="GtkVBox" id="druid-vbox39">
-	      <property name="border_width">25</property>
+	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
 	      <property name="spacing">0</property>
@@ -1074,7 +1254,7 @@
 
 	  <child internal-child="vbox">
 	    <widget class="GtkVBox" id="druid-vbox40">
-	      <property name="border_width">25</property>
+	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
 	      <property name="spacing">0</property>
@@ -1229,7 +1409,7 @@
 
 	  <child internal-child="vbox">
 	    <widget class="GtkVBox" id="druid-vbox16">
-	      <property name="border_width">25</property>
+	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
 	      <property name="spacing">0</property>
@@ -1316,7 +1496,7 @@
 
 	  <child internal-child="vbox">
 	    <widget class="GtkVBox" id="druid-vbox17">
-	      <property name="border_width">25</property>
+	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
 	      <property name="spacing">0</property>
@@ -1376,8 +1556,6 @@
 
 On the next page, you will be asked to confirm that an existing transaction matches an imported transaction.  Imported transactions are shown on the left side of the page, and possible matches for the selected left-hand transaction are shown to the right.  There may be several old transactions that could match an imported transaction; you will be able to select the correct one by double-clicking in the &quot;Dup?&quot; column of the correct transaction.
 
-You can control the rules used by GnuCash to find duplicate transactions in the &quot;QIF Import&quot; section of the GnuCash Preferences dialog.
-
 Click &quot;Forward&quot; to find duplicate transactions. </property>
 		  <property name="use_underline">False</property>
 		  <property name="use_markup">False</property>
@@ -1490,7 +1668,7 @@
 
 	  <child internal-child="vbox">
 	    <widget class="GtkVBox" id="druid-vbox35">
-	      <property name="border_width">5</property>
+	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
 	      <property name="spacing">0</property>
@@ -1506,7 +1684,7 @@
 		      <property name="visible">True</property>
 		      <property name="label_xalign">0</property>
 		      <property name="label_yalign">0.5</property>
-		      <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+		      <property name="shadow_type">GTK_SHADOW_NONE</property>
 
 		      <child>
 			<widget class="GtkScrolledWindow" id="scrolledwindow22">
@@ -1536,7 +1714,7 @@
 			<widget class="GtkLabel" id="label847716">
 			  <property name="visible">True</property>
 			  <property name="label" translatable="yes">Imported transactions with duplicates</property>
-			  <property name="use_underline">False</property>
+			  <property name="use_underline">True</property>
 			  <property name="use_markup">False</property>
 			  <property name="justify">GTK_JUSTIFY_LEFT</property>
 			  <property name="wrap">False</property>
@@ -1567,7 +1745,7 @@
 		      <property name="visible">True</property>
 		      <property name="label_xalign">0</property>
 		      <property name="label_yalign">0.5</property>
-		      <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+		      <property name="shadow_type">GTK_SHADOW_NONE</property>
 
 		      <child>
 			<widget class="GtkScrolledWindow" id="scrolledwindow23">
@@ -1597,7 +1775,7 @@
 			<widget class="GtkLabel" id="label847717">
 			  <property name="visible">True</property>
 			  <property name="label" translatable="yes">Possible duplicates for selected new transaction</property>
-			  <property name="use_underline">False</property>
+			  <property name="use_underline">True</property>
 			  <property name="use_markup">False</property>
 			  <property name="justify">GTK_JUSTIFY_LEFT</property>
 			  <property name="wrap">False</property>

Modified: gnucash/branches/2.2/src/scm/string.scm
===================================================================
--- gnucash/branches/2.2/src/scm/string.scm	2008-07-22 22:31:19 UTC (rev 17372)
+++ gnucash/branches/2.2/src/scm/string.scm	2008-07-22 22:31:29 UTC (rev 17373)
@@ -119,3 +119,24 @@
 
 (define-public (gnc:string-delete-chars s chars)
   (string-delete s (lambda (c) (string-index chars c))))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;  gnc:list-display
+;;
+;;  Run the display procedure on each element in a list.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define-public (gnc:list-display lst)
+  (for-each (lambda (elt) (display elt)) lst))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;  gnc:list-display-to-string
+;;
+;;  Return a string containing the output that would be generated
+;;  by running the display procedure on each element in a list.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(define-public (gnc:list-display-to-string lst)
+  (with-output-to-string (lambda () (gnc:list-display lst))))
+



More information about the gnucash-changes mailing list