AUDIT: r17252 - gnucash/trunk/src/import-export/qif-import - Bug #313660: The QIF importer now allows users to pause, cancel, or go back a page during data conversion. Any errors and warnings are logged as they occur, directly in the druid, for the user to review.

Charles Day cedayiv at cvs.gnucash.org
Wed Jul 2 16:56:34 EDT 2008


Author: cedayiv
Date: 2008-07-02 16:56:33 -0400 (Wed, 02 Jul 2008)
New Revision: 17252
Trac: http://svn.gnucash.org/trac/changeset/17252

Modified:
   gnucash/trunk/src/import-export/qif-import/druid-qif-import.c
   gnucash/trunk/src/import-export/qif-import/qif-merge-groups.scm
   gnucash/trunk/src/import-export/qif-import/qif-to-gnc.scm
   gnucash/trunk/src/import-export/qif-import/qif.glade
Log:
Bug #313660: The QIF importer now allows users to pause, cancel, or go back a page during data conversion. Any errors and warnings are logged as they occur, directly in the druid, for the user to review.
BP


Modified: gnucash/trunk/src/import-export/qif-import/druid-qif-import.c
===================================================================
--- gnucash/trunk/src/import-export/qif-import/druid-qif-import.c	2008-06-25 18:29:57 UTC (rev 17251)
+++ gnucash/trunk/src/import-export/qif-import/druid-qif-import.c	2008-07-02 20:56:33 UTC (rev 17252)
@@ -124,6 +124,11 @@
   /* Widgets on the currency page. */
   GtkWidget * currency_picker;
 
+  /* Conversion progress page. */
+  GtkWidget * convert_pause;
+  GtkWidget * convert_log;
+  GNCProgressDialog *convert_progress;
+
   /* Widgets on the duplicates page. */
   GtkWidget * new_transaction_view;
   GtkWidget * old_transaction_view;
@@ -177,7 +182,7 @@
 static GdkColor std_title_color =  { 0, 65535, 65535, 65535 };
 
 #define NUM_PRE_PAGES 14
-#define NUM_POST_PAGES 3
+#define NUM_POST_PAGES 4
 #define NUM_DOC_PAGES  6
 
 static GnomeDruidPage *
@@ -707,7 +712,7 @@
 /********************************************************************
  * gnc_ui_qif_import_load_progress_prepare_cb
  *
- * Prepare the progress page for display.
+ * Prepare the file loading progress page for display.
  ********************************************************************/
 
 static void
@@ -738,6 +743,7 @@
   /* 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_set_sensitive(GTK_WIDGET(page), TRUE);
   gtk_widget_show(GTK_WIDGET(page));
 }
 
@@ -810,12 +816,24 @@
   if (load_return == SCM_BOOL_T)
   {
     /* Canceled by the user. */
+
+    /* Disable the pause button. */
+    gtk_widget_set_sensitive(wind->load_pause, FALSE);
+
+    /* Inform the user. */
+    gnc_progress_dialog_set_sub(wind->load_progress, _("Canceled"));
+
     wind->busy = FALSE;
-    gtk_widget_set_sensitive(wind->load_pause, FALSE);
     return;
   }
   else if (load_return == SCM_BOOL_F || !SCM_LISTP(load_return))
   {
+    /* A bug was detected. */
+
+    /* Disable the pause button. */
+    gtk_widget_set_sensitive(wind->load_pause, FALSE);
+
+    /* Inform the user. */
     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"));
@@ -825,7 +843,6 @@
     /* 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(load_return))
@@ -842,8 +859,8 @@
       gnc_progress_dialog_set_sub(wind->load_progress, _("Failed"));
       gnc_progress_dialog_reset_value(wind->load_progress);
 
+      gtk_widget_set_sensitive(wind->load_pause, FALSE);
       wind->busy = FALSE;
-      gtk_widget_set_sensitive(wind->load_pause, FALSE);
       return;
     }
   }
@@ -868,14 +885,32 @@
   if (parse_return == SCM_BOOL_T)
   {
     /* Canceled by the user. */
+
+    /* Disable the pause button. */
+    gtk_widget_set_sensitive(wind->load_pause, FALSE);
+
+    /* Unload the file. */
+    gnc_progress_dialog_set_sub(wind->load_progress, _("Cleaning up"));
     imported_files = scm_call_2(unload_qif_file, scm_qiffile, imported_files);
+
+    /* Inform the user. */
+    gnc_progress_dialog_set_sub(wind->load_progress, _("Canceled"));
+
     wind->busy = FALSE;
-    gtk_widget_set_sensitive(wind->load_pause, FALSE);
     return;
   }
   else if (parse_return == SCM_BOOL_F || !SCM_LISTP(parse_return))
   {
+    /* A bug was detected. */
+
+    /* Disable the pause button. */
+    gtk_widget_set_sensitive(wind->load_pause, FALSE);
+
+    /* Unload the file. */
+    gnc_progress_dialog_set_sub(wind->load_progress, _("Cleaning up"));
     imported_files = scm_call_2(unload_qif_file, scm_qiffile, imported_files);
+
+    /* Inform the user. */
     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"));
@@ -885,7 +920,6 @@
     /* 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))
@@ -931,8 +965,8 @@
       gnc_progress_dialog_set_sub(wind->load_progress, _("Failed"));
       gnc_progress_dialog_reset_value(wind->load_progress);
 
+      gtk_widget_set_sensitive(wind->load_pause, FALSE);
       wind->busy = FALSE;
-      gtk_widget_set_sensitive(wind->load_pause, FALSE);
       return;
     }
   }
@@ -953,8 +987,8 @@
   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));
 
+  gtk_widget_set_sensitive(wind->load_pause, FALSE);
   wind->busy = FALSE;
-  gtk_widget_set_sensitive(wind->load_pause, FALSE);
   return;
 }
 
@@ -1903,268 +1937,6 @@
 
 
 /****************************************************************
- * gnc_ui_qif_import_commodity_update
- *
- * This function updates the commodities based on the values for
- * mnemonic, namespace, and name approved by the user.
- ****************************************************************/
-
-static void
-gnc_ui_qif_import_commodity_update(QIFImportWindow * wind)
-{
-  GList          *pageptr;
-  GnomeDruidPage *gtkpage;
-  QIFDruidPage   *page;
-  const gchar    *mnemonic = NULL;
-  gchar          *namespace = NULL;
-  const gchar    *fullname = NULL;
-  gnc_commodity  *tab_commodity;
-
-  for (pageptr = wind->commodity_pages; pageptr; pageptr=pageptr->next)
-  {
-    gtkpage   = GNOME_DRUID_PAGE(pageptr->data);
-    page      = g_object_get_data(G_OBJECT(gtkpage), "page_struct");
-
-    /* Get any changes from the commodity page. */
-    mnemonic  = gtk_entry_get_text(GTK_ENTRY(page->new_mnemonic_entry));
-    namespace = gnc_ui_namespace_picker_ns(page->new_type_combo);
-    fullname  = gtk_entry_get_text(GTK_ENTRY(page->new_name_entry));
-
-    /* Update the commodity with the new values. */
-    gnc_commodity_set_namespace(page->commodity, namespace);
-    gnc_commodity_set_fullname(page->commodity, fullname);
-    gnc_commodity_set_mnemonic(page->commodity, mnemonic);
-
-    /* Add the commodity to the commodity table (if it isn't a duplicate). */
-    tab_commodity = gnc_commodity_table_lookup(gnc_get_current_commodities(),
-                                               namespace, mnemonic);
-    if (!tab_commodity || tab_commodity == page->commodity)
-      tab_commodity = gnc_commodity_table_insert(gnc_get_current_commodities(),
-                                                 page->commodity);
-
-    /* Update the security hash table. */
-    scm_hash_set_x(wind->security_hash,
-                   page->hash_key,
-                   SWIG_NewPointerObj(tab_commodity,
-                                      SWIG_TypeQuery("_p_gnc_commodity"), 0));
-
-    g_free(namespace);
-  }
-}
-
-
-/****************************************************************
- * gnc_ui_qif_import_prepare_duplicates
- *
- * This function prepares the duplicates checking page.
- ****************************************************************/
-
-static void
-gnc_ui_qif_import_prepare_duplicates(QIFImportWindow * wind)
-{
-  GtkTreeView      *view;
-  GtkListStore     *store;
-  SCM               duplicates;
-  SCM               current_xtn;
-  Transaction      *gnc_xtn;
-  Split            *gnc_split;
-  GtkTreeIter       iter;
-  GtkTreeSelection *selection;
-  GtkTreePath      *path;
-  const gchar      *amount_str;
-  int               rownum = 0;
-
-  view = GTK_TREE_VIEW(wind->new_transaction_view);
-  store = GTK_LIST_STORE(gtk_tree_view_get_model(view));
-  gtk_list_store_clear(store);
-
-  /* Loop through the list of new, potentially duplicate transactions. */
-  duplicates = wind->match_transactions;
-  while (!SCM_NULLP(duplicates))
-  {
-    current_xtn = SCM_CAAR(duplicates);
-    #define FUNC_NAME "xaccTransCountSplits"
-    gnc_xtn     = SWIG_MustGetPtr(current_xtn,
-                                  SWIG_TypeQuery("_p_Transaction"), 1, 0);
-    #undef FUNC_NAME
-    if (xaccTransCountSplits(gnc_xtn) > 2)
-      amount_str = _("(split)");
-    else
-    {
-      gnc_split = xaccTransGetSplit(gnc_xtn, 0);
-      amount_str =
-        xaccPrintAmount(gnc_numeric_abs(xaccSplitGetValue(gnc_split)),
-                        gnc_account_print_info
-                        (xaccSplitGetAccount(gnc_split), TRUE));
-    }
-
-    gtk_list_store_append(store, &iter);
-    gtk_list_store_set
-      (store, &iter,
-       QIF_TRANS_COL_INDEX, rownum++,
-       QIF_TRANS_COL_DATE,
-       gnc_print_date(xaccTransRetDatePostedTS(gnc_xtn)),
-       QIF_TRANS_COL_DESCRIPTION, xaccTransGetDescription(gnc_xtn),
-       QIF_TRANS_COL_AMOUNT, amount_str,
-       -1);
-
-    duplicates = SCM_CDR(duplicates);
-  }
-
-  selection = gtk_tree_view_get_selection(view);
-  path = gtk_tree_path_new_from_indices(0, -1);
-  gtk_tree_selection_select_path(selection, path);
-  gtk_tree_path_free(path);
-}
-
-
-/****************************************************************
- * gnc_ui_qif_import_convert_undo
- *
- * This function launches the Scheme procedure that un-imports
- * any imported accounts and transactions.
- ****************************************************************/
-
-static void
-gnc_ui_qif_import_convert_undo(QIFImportWindow * wind)
-{
-  SCM undo = scm_c_eval_string("qif-import:qif-to-gnc-undo");
-
-  /* Undo the conversion. */
-  scm_call_1(undo, wind->imported_account_tree);
-
-  /* There's no imported account tree any more. */
-  scm_gc_unprotect_object(wind->imported_account_tree);
-  wind->imported_account_tree = SCM_BOOL_F;
-  scm_gc_protect_object(wind->imported_account_tree);
-
-
-  /* Get rid of the list of matched transactions. */
-  scm_gc_unprotect_object(wind->match_transactions);
-  wind->match_transactions = SCM_BOOL_F;
-  scm_gc_protect_object(wind->match_transactions);
-}
-
-
-/****************************************************************
- * gnc_ui_qif_import_convert
- *
- * This function launches the Scheme procedures that actually do
- * the work of (a) converting the QIF data into GnuCash accounts
- * transactions, and (b) checking for possible duplication. Then
- * the next druid page is prepared and displayed.
- ****************************************************************/
-
-static gboolean
-gnc_ui_qif_import_convert(QIFImportWindow * wind)
-{
-  SCM   qif_to_gnc      = scm_c_eval_string("qif-import:qif-to-gnc");
-  SCM   find_duplicates = scm_c_eval_string("gnc:account-tree-find-duplicates");
-  SCM   retval;
-  SCM   window;
-
-  /* Get the default currency. */
-  gchar *currname =
-    gtk_combo_box_get_active_text(GTK_COMBO_BOX(wind->currency_picker));
-
-  /* Let the user know we're busy. */
-  gnc_suspend_gui_refresh();
-  gnc_set_busy_cursor(NULL, TRUE);
-
-  /* Update the commodities. */
-  gnc_ui_qif_import_commodity_update(wind);
-
-  /* Call a Scheme function to do the work.  The return value is the
-   * root account of an account tree containing all the new accounts
-   * and transactions */
-  window = SWIG_NewPointerObj(wind->window, SWIG_TypeQuery("_p_GtkWidget"), 0);
-  retval = scm_apply(qif_to_gnc,
-                     SCM_LIST7(wind->imported_files,
-                               wind->acct_map_info,
-                               wind->cat_map_info,
-                               wind->memo_map_info,
-                               wind->security_hash,
-                               scm_makfrom0str(currname),
-                               window),
-                     SCM_EOL);
-  g_free(currname);
-  gnc_unset_busy_cursor(NULL);
-
-  if (retval == SCM_BOOL_F)
-  {
-    /* An error occurred during conversion. */
-
-    /* Remove any converted data. */
-    gnc_ui_qif_import_convert_undo(wind);
-
-    /* We don't know what data structures may have become corrupted,
-     * so we shouldn't allow further action. Display the failure
-     * page next, and only allow the user to cancel. */
-    gnome_druid_set_page(GNOME_DRUID(wind->druid),
-                         get_named_page(wind, "failed_page"));
-    gnome_druid_set_buttons_sensitive(GNOME_DRUID(wind->druid),
-                                      FALSE, FALSE, TRUE, TRUE);
-  }
-  else
-  {
-    /* Save the imported account tree. */
-    scm_gc_unprotect_object(wind->imported_account_tree);
-    wind->imported_account_tree = retval;
-    scm_gc_protect_object(wind->imported_account_tree);
-
-    /* Detect duplicate transactions. */
-    gnc_set_busy_cursor(NULL, TRUE);
-    retval = scm_call_3(find_duplicates,
-                        scm_c_eval_string("(gnc-get-current-root-account)"),
-                        wind->imported_account_tree, window);
-    gnc_unset_busy_cursor(NULL);
-
-    /* Save the results. */
-    scm_gc_unprotect_object(wind->match_transactions);
-    wind->match_transactions = retval;
-    scm_gc_protect_object(wind->match_transactions);
-
-    /* Were any potential duplicates found? */
-    if (retval == SCM_BOOL_F)
-    {
-      /* An error occurred during duplicate checking. */
-
-      /* Remove any converted data. */
-      gnc_ui_qif_import_convert_undo(wind);
-
-      /* Display the failure page. */
-      gnome_druid_set_page(GNOME_DRUID(wind->druid),
-                           get_named_page(wind, "failed_page"));
-      gnome_druid_set_buttons_sensitive(GNOME_DRUID(wind->druid),
-                                        FALSE, FALSE, TRUE, TRUE);
-    }
-    else if (SCM_NULLP(retval))
-    {
-      /* No potential duplicates, so skip to the last page. */
-      gnome_druid_set_page(GNOME_DRUID(wind->druid),
-                           get_named_page(wind, "end_page"));
-    }
-    else
-    {
-      /* Prepare the duplicates page. */
-      gnc_ui_qif_import_prepare_duplicates(wind);
-
-      /* Display the next page. */
-      if (wind->show_doc_pages)
-        gnome_druid_set_page(GNOME_DRUID(wind->druid),
-                             get_named_page(wind, "match_doc_page"));
-      else
-        gnome_druid_set_page(GNOME_DRUID(wind->druid),
-                             get_named_page(wind, "match_duplicates_page"));
-    }
-  }
-
-  gnc_resume_gui_refresh();
-  return TRUE;
-}
-
-
-/****************************************************************
  * gnc_ui_qif_import_new_securities
  *
  * This function creates or updates the list of QIF securities
@@ -2340,14 +2112,7 @@
   else
     g_free(namespace);
 
-  if(page == (g_list_last(wind->commodity_pages))->data)
-  {
-    /* it's time to import the accounts. */
-    gnc_ui_qif_import_convert(wind);
-    return TRUE;
-  }
-  else
-    return FALSE;
+  return FALSE;
 }
 
 
@@ -2553,54 +2318,517 @@
 {
   QIFImportWindow * wind = user_data;
 
-  gnc_set_busy_cursor(NULL, TRUE);
+  /* If there are new securities, prepare the security pages. */
+  if (gnc_ui_qif_import_new_securities(wind))
+    prepare_security_pages(wind);
 
-  if (gnc_ui_qif_import_new_securities(wind))
+  return gnc_ui_qif_import_generic_next_cb(page, arg1, wind);
+}
+
+
+/****************************************************************
+ * gnc_ui_qif_import_commodity_update
+ *
+ * This function updates the commodities based on the values for
+ * mnemonic, namespace, and name approved by the user.
+ ****************************************************************/
+
+static void
+gnc_ui_qif_import_commodity_update(QIFImportWindow * wind)
+{
+  GList          *pageptr;
+  GnomeDruidPage *gtkpage;
+  QIFDruidPage   *page;
+  const gchar    *mnemonic = NULL;
+  gchar          *namespace = NULL;
+  const gchar    *fullname = NULL;
+  gnc_commodity  *tab_commodity;
+
+  for (pageptr = wind->commodity_pages; pageptr; pageptr=pageptr->next)
   {
-    /* There are new commodities, so show a commodity page next. */
-    if (wind->show_doc_pages)
-      gnome_druid_set_page(GNOME_DRUID(wind->druid),
-                           get_named_page(wind, "commodity_doc_page"));
+    gtkpage   = GNOME_DRUID_PAGE(pageptr->data);
+    page      = g_object_get_data(G_OBJECT(gtkpage), "page_struct");
+
+    /* Get any changes from the commodity page. */
+    mnemonic  = gtk_entry_get_text(GTK_ENTRY(page->new_mnemonic_entry));
+    namespace = gnc_ui_namespace_picker_ns(page->new_type_combo);
+    fullname  = gtk_entry_get_text(GTK_ENTRY(page->new_name_entry));
+
+    /* Update the commodity with the new values. */
+    gnc_commodity_set_namespace(page->commodity, namespace);
+    gnc_commodity_set_fullname(page->commodity, fullname);
+    gnc_commodity_set_mnemonic(page->commodity, mnemonic);
+
+    /* Add the commodity to the commodity table (if it isn't a duplicate). */
+    tab_commodity = gnc_commodity_table_lookup(gnc_get_current_commodities(),
+                                               namespace, mnemonic);
+    if (!tab_commodity || tab_commodity == page->commodity)
+      tab_commodity = gnc_commodity_table_insert(gnc_get_current_commodities(),
+                                                 page->commodity);
+
+    /* Update the security hash table. */
+    scm_hash_set_x(wind->security_hash,
+                   page->hash_key,
+                   SWIG_NewPointerObj(tab_commodity,
+                                      SWIG_TypeQuery("_p_gnc_commodity"), 0));
+
+    g_free(namespace);
+  }
+}
+
+
+/****************************************************************
+ * gnc_ui_qif_import_prepare_duplicates
+ *
+ * This function prepares the duplicates checking page.
+ ****************************************************************/
+
+static void
+gnc_ui_qif_import_prepare_duplicates(QIFImportWindow * wind)
+{
+  GtkTreeView      *view;
+  GtkListStore     *store;
+  SCM               duplicates;
+  SCM               current_xtn;
+  Transaction      *gnc_xtn;
+  Split            *gnc_split;
+  GtkTreeIter       iter;
+  GtkTreeSelection *selection;
+  GtkTreePath      *path;
+  const gchar      *amount_str;
+  int               rownum = 0;
+
+  view = GTK_TREE_VIEW(wind->new_transaction_view);
+  store = GTK_LIST_STORE(gtk_tree_view_get_model(view));
+  gtk_list_store_clear(store);
+
+  if (!SCM_LISTP(wind->match_transactions))
+    return;
+
+  /* Loop through the list of new, potentially duplicate transactions. */
+  duplicates = wind->match_transactions;
+  while (!SCM_NULLP(duplicates))
+  {
+    current_xtn = SCM_CAAR(duplicates);
+    #define FUNC_NAME "xaccTransCountSplits"
+    gnc_xtn     = SWIG_MustGetPtr(current_xtn,
+                                  SWIG_TypeQuery("_p_Transaction"), 1, 0);
+    #undef FUNC_NAME
+    if (xaccTransCountSplits(gnc_xtn) > 2)
+      amount_str = _("(split)");
     else
     {
-      prepare_security_pages(wind);
-      gnome_druid_set_page(GNOME_DRUID(wind->druid),
-                           GNOME_DRUID_PAGE(wind->commodity_pages->data));
+      gnc_split = xaccTransGetSplit(gnc_xtn, 0);
+      amount_str =
+        xaccPrintAmount(gnc_numeric_abs(xaccSplitGetValue(gnc_split)),
+                        gnc_account_print_info
+                        (xaccSplitGetAccount(gnc_split), TRUE));
     }
+
+    gtk_list_store_append(store, &iter);
+    gtk_list_store_set
+      (store, &iter,
+       QIF_TRANS_COL_INDEX, rownum++,
+       QIF_TRANS_COL_DATE,
+       gnc_print_date(xaccTransRetDatePostedTS(gnc_xtn)),
+       QIF_TRANS_COL_DESCRIPTION, xaccTransGetDescription(gnc_xtn),
+       QIF_TRANS_COL_AMOUNT, amount_str,
+       -1);
+
+    duplicates = SCM_CDR(duplicates);
   }
-  else
-    /* It's time to import the accounts. */
-    gnc_ui_qif_import_convert(wind);
 
+  selection = gtk_tree_view_get_selection(view);
+  path = gtk_tree_path_new_from_indices(0, -1);
+  gtk_tree_selection_select_path(selection, path);
+  gtk_tree_path_free(path);
+}
+
+
+/****************************************************************
+ * gnc_ui_qif_import_convert_undo
+ *
+ * This function launches the Scheme procedure that un-imports
+ * any imported accounts and transactions.
+ ****************************************************************/
+
+static void
+gnc_ui_qif_import_convert_undo(QIFImportWindow * wind)
+{
+  SCM undo = scm_c_eval_string("qif-import:qif-to-gnc-undo");
+
+  gnc_set_busy_cursor(NULL, TRUE);
+
+  /* Undo the conversion. */
+  scm_call_1(undo, wind->imported_account_tree);
+
+  /* There's no imported account tree any more. */
+  scm_gc_unprotect_object(wind->imported_account_tree);
+  wind->imported_account_tree = SCM_BOOL_F;
+  scm_gc_protect_object(wind->imported_account_tree);
+
+
+  /* Get rid of the list of matched transactions. */
+  scm_gc_unprotect_object(wind->match_transactions);
+  wind->match_transactions = SCM_BOOL_F;
+  scm_gc_protect_object(wind->match_transactions);
+
   gnc_unset_busy_cursor(NULL);
-  return TRUE;
 }
 
 
 /********************************************************************
- * gnc_ui_qif_import_commodity_prepare_cb
+ * gnc_ui_qif_import_convert_progress_prepare_cb
  *
- * build a mapping of QIF security name to gnc_commodity
+ * Prepare the data conversion progress page for display.
  ********************************************************************/
 
 static void
-gnc_ui_qif_import_commodity_prepare_cb(GnomeDruidPage * page,
-                                       gpointer arg1,
-                                       gpointer user_data)
+gnc_ui_qif_import_convert_progress_prepare_cb(GnomeDruidPage * page,
+                                              gpointer arg1,
+                                              gpointer user_data)
 {
-  QIFImportWindow * wind = user_data;
+  QIFImportWindow   *wind = user_data;
 
-  /* This shouldn't happen, but do the right thing if it does. */
-  if (wind->new_securities == SCM_BOOL_F || SCM_NULLP(wind->new_securities))
+  /* Reset the progress display. */
+  gnc_progress_dialog_set_primary(wind->convert_progress, "");
+  gnc_progress_dialog_set_secondary(wind->convert_progress,
+    _("GnuCash is now importing your QIF data. 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->convert_progress, " ");
+  gnc_progress_dialog_reset_value(wind->convert_progress);
+  gnc_progress_dialog_reset_log(wind->convert_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_set_sensitive(GTK_WIDGET(page), TRUE);
+  gtk_widget_show(GTK_WIDGET(page));
+}
+
+
+/********************************************************************
+ * gnc_ui_qif_import_convert_progress_show_cb
+ *
+ * Perform the conversion.
+ ********************************************************************/
+
+static void
+gnc_ui_qif_import_convert_progress_show_cb(GtkWidget *widget,
+                                           gpointer user_data)
+{
+  QIFImportWindow   *wind = user_data;
+  SCM qif_to_gnc      = scm_c_eval_string("qif-import:qif-to-gnc");
+  SCM find_duplicates = scm_c_eval_string("gnc:account-tree-find-duplicates");
+  SCM retval;
+
+  /* SCM for the progress dialog. */
+  SCM progress = SWIG_NewPointerObj(wind->convert_progress,
+                                    SWIG_TypeQuery("_p__GNCProgressDialog"),
+                                    0);
+
+  /* The default currency. */
+  gchar *currname =
+    gtk_combo_box_get_active_text(GTK_COMBO_BOX(wind->currency_picker));
+
+  /* Raise the busy flag so the druid can't be canceled unexpectedly. */
+  wind->busy = TRUE;
+  gtk_widget_set_sensitive(wind->convert_pause, TRUE);
+
+  /* Clear any previous pause or cancel state. */
+  scm_c_eval_string("(qif-import:reset-cancel-pause)");
+
+  /* Update the commodities. */
+  gnc_ui_qif_import_commodity_update(wind);
+
+
+  /*
+   * Convert the QIF data into GnuCash data.
+   *
+   * A Scheme function does all the work.  The return value is the
+   * root account of an account tree containing all the new accounts
+   * and transactions. Upon failure, #f is returned. If the user
+   * cancels, #t is returned.
+   */
+
+  /* This step will fill 70% of the bar. */
+  gnc_progress_dialog_push(wind->convert_progress, 0.7);
+  retval = scm_apply(qif_to_gnc,
+                     SCM_LIST7(wind->imported_files,
+                               wind->acct_map_info,
+                               wind->cat_map_info,
+                               wind->memo_map_info,
+                               wind->security_hash,
+                               scm_makfrom0str(currname),
+                               progress),
+                     SCM_EOL);
+  gnc_progress_dialog_pop(wind->convert_progress);
+  g_free(currname);
+
+  if (retval == SCM_BOOL_T)
   {
-    g_warning("QIF import: BUG DETECTED! Reached commodity doc page with nothing to do!");
-    gnc_ui_qif_import_convert(wind);
+    /* Canceled by the user. */
+
+    /* Disable the pause button. */
+    gtk_widget_set_sensitive(wind->convert_pause, FALSE);
+
+    /* Remove any converted data. */
+    gnc_progress_dialog_set_sub(wind->convert_progress, _("Cleaning up"));
+    gnc_ui_qif_import_convert_undo(wind);
+
+    /* Inform the user. */
+    gnc_progress_dialog_set_sub(wind->convert_progress, _("Canceled"));
+    gnc_progress_dialog_reset_value(wind->convert_progress);
+
+    wind->busy = FALSE;
+    return;
   }
+  else if (retval == SCM_BOOL_F)
+  {
+    /* An bug was encountered during conversion. */
+
+    /* Disable the pause button. */
+    gtk_widget_set_sensitive(wind->convert_pause, FALSE);
+
+    /* Remove any converted data. */
+    gnc_progress_dialog_set_sub(wind->convert_progress, _("Cleaning up"));
+    gnc_ui_qif_import_convert_undo(wind);
+
+    /* Inform the user. */
+    gnc_progress_dialog_append_log(wind->convert_progress,
+                     _( "A bug was detected while converting the QIF data."));
+    gnc_progress_dialog_set_sub(wind->convert_progress, _("Failed"));
+    gnc_progress_dialog_reset_value(wind->convert_progress);
+    gnc_error_dialog(wind->window,
+                     _( "A bug was detected while converting the QIF data."));
+    /* FIXME: How should we request that the user report this problem? */
+
+    wind->busy = FALSE;
+    return;
+  }
+  else if (SCM_SYMBOLP(retval))
+  {
+    /* An error was encountered during conversion. */
+
+    /* Disable the pause button. */
+    gtk_widget_set_sensitive(wind->convert_pause, FALSE);
+
+    /* Remove any converted data. */
+    gnc_progress_dialog_set_sub(wind->convert_progress, _("Cleaning up"));
+    gnc_ui_qif_import_convert_undo(wind);
+
+    /* Inform the user. */
+    gnc_progress_dialog_set_sub(wind->convert_progress, _("Failed"));
+    gnc_progress_dialog_reset_value(wind->convert_progress);
+
+    wind->busy = FALSE;
+    return;
+  }
+
+
+  /* Save the imported account tree. */
+  scm_gc_unprotect_object(wind->imported_account_tree);
+  wind->imported_account_tree = retval;
+  scm_gc_protect_object(wind->imported_account_tree);
+
+
+  /*
+   * Detect potentially duplicated transactions.
+   */
+
+  /* This step will fill the remainder of the bar. */
+  gnc_progress_dialog_push(wind->convert_progress, 1);
+  retval = scm_call_3(find_duplicates,
+                      scm_c_eval_string("(gnc-get-current-root-account)"),
+                      wind->imported_account_tree, progress);
+  gnc_progress_dialog_pop(wind->convert_progress);
+
+  /* Save the results. */
+  scm_gc_unprotect_object(wind->match_transactions);
+  wind->match_transactions = retval;
+  scm_gc_protect_object(wind->match_transactions);
+
+  if (retval == SCM_BOOL_T)
+  {
+    /* Canceled by the user. */
+    gtk_widget_set_sensitive(wind->convert_pause, FALSE);
+    gnc_progress_dialog_set_sub(wind->convert_progress, _("Canceling"));
+    wind->busy = FALSE;
+    return;
+  }
+  else if (retval == SCM_BOOL_F)
+  {
+    /* An error occurred during duplicate checking. */
+
+    /* Remove any converted data. */
+    gnc_progress_dialog_set_sub(wind->convert_progress, _("Cleaning up"));
+    gnc_ui_qif_import_convert_undo(wind);
+
+    /* Inform the user. */
+    gnc_progress_dialog_append_log(wind->convert_progress,
+                     _( "A bug was detected while detecting duplicates."));
+    gnc_progress_dialog_set_sub(wind->convert_progress, _("Failed"));
+    gnc_progress_dialog_reset_value(wind->convert_progress);
+    gnc_error_dialog(wind->window,
+                     _( "A bug was detected while detecting duplicates."));
+    /* FIXME: How should we request that the user report this problem? */
+
+    gtk_widget_set_sensitive(wind->convert_pause, FALSE);
+    wind->busy = FALSE;
+    return;
+  }
+
+
+  /* The conversion completed successfully. */
+  gnc_progress_dialog_set_sub(wind->convert_progress,
+                              _("Conversion completed"));
+  gnc_progress_dialog_set_value(wind->convert_progress, 1);
+
+  /* Enable all buttons. */
+  gnome_druid_set_buttons_sensitive(GNOME_DRUID(wind->druid),
+                                    TRUE, TRUE, TRUE, TRUE);
+
+  /* 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->convert_log))) == 0)
+    gnome_druid_page_next(GNOME_DRUID_PAGE(widget));
+
+  gtk_widget_set_sensitive(wind->convert_pause, FALSE);
+  wind->busy = FALSE;
+  return;
+}
+
+
+/********************************************************************
+ * gnc_ui_qif_import_convert_progress_pause_cb
+ *
+ * Invoked when the "Pause" button is clicked.
+ ********************************************************************/
+
+static void
+gnc_ui_qif_import_convert_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->convert_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
-    prepare_security_pages(wind);
+  {
+    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_convert_progress_next_cb
+ *
+ * Determine the next page after converting successfully.
+ ********************************************************************/
+
+static gboolean
+gnc_ui_qif_import_convert_progress_next_cb(GnomeDruidPage * page,
+                                           gpointer arg1,
+                                           gpointer user_data)
+{
+  QIFImportWindow *wind = user_data;
+
+  if (SCM_NULLP(wind->match_transactions))
+  {
+    /* No potential duplicates, so skip to the last page. */
+    gnome_druid_set_page(GNOME_DRUID(wind->druid),
+                         get_named_page(wind, "end_page"));
+    return TRUE;
+  }
+
+  /* Prepare the duplicates page. */
+  gnc_ui_qif_import_prepare_duplicates(wind);
+
+  /* Display the next page. */
+  return gnc_ui_qif_import_generic_next_cb(page, arg1, wind);
+}
+
+
+/****************************************************************
+ * convert_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
+convert_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, "convert_progress_page"));
+
+  /* Cancel the timer. */
+  return FALSE;
+}
+
+
+/********************************************************************
+ * gnc_ui_qif_import_convert_progress_back_cb
+ *
+ * Return to the previous page, waiting if necessary.
+ ********************************************************************/
+
+static gboolean
+gnc_ui_qif_import_convert_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, convert_progress_back_timeout_cb, user_data);
+
+    return TRUE;
+  }
+
+  return gnc_ui_qif_import_generic_back_cb(page, arg1, user_data);
+}
+
+
 static void
 refresh_old_transactions(QIFImportWindow * wind, int selection)
 {
@@ -2797,6 +3025,8 @@
   QIFDruidPage        *page;
   gnc_commodity_table *table;
 
+  gnc_set_busy_cursor(NULL, TRUE);
+
   /* Remove any converted data. */
   gnc_ui_qif_import_convert_undo(wind);
 
@@ -2820,6 +3050,8 @@
                                               wind->new_namespaces);
   }
 
+  gnc_unset_busy_cursor(NULL);
+
   /* Destroy the druid. */
   gnc_ui_qif_import_druid_destroy(wind);
 }
@@ -2940,7 +3172,8 @@
   };
 
   char * post_page_names[NUM_POST_PAGES] = {
-    "match_doc_page", "match_duplicates_page", "end_page"
+    "convert_progress_page", "match_doc_page", "match_duplicates_page",
+    "end_page"
   };
 
   char * doc_page_names[NUM_DOC_PAGES] = {
@@ -3061,10 +3294,26 @@
      G_CALLBACK(gnc_ui_qif_import_currency_next_cb), retval);
 
   glade_xml_signal_connect_data
-    (xml, "gnc_ui_qif_import_commodity_prepare_cb",
-     G_CALLBACK(gnc_ui_qif_import_commodity_prepare_cb), retval);
+    (xml, "gnc_ui_qif_import_convert_progress_prepare_cb",
+     G_CALLBACK(gnc_ui_qif_import_convert_progress_prepare_cb), retval);
 
   glade_xml_signal_connect_data
+    (xml, "gnc_ui_qif_import_convert_progress_show_cb",
+     G_CALLBACK(gnc_ui_qif_import_convert_progress_show_cb), retval);
+
+  glade_xml_signal_connect_data
+    (xml, "gnc_ui_qif_import_convert_progress_pause_cb",
+     G_CALLBACK(gnc_ui_qif_import_convert_progress_pause_cb), retval);
+
+  glade_xml_signal_connect_data
+    (xml, "gnc_ui_qif_import_convert_progress_next_cb",
+     G_CALLBACK(gnc_ui_qif_import_convert_progress_next_cb), retval);
+
+  glade_xml_signal_connect_data
+    (xml, "gnc_ui_qif_import_convert_progress_back_cb",
+     G_CALLBACK(gnc_ui_qif_import_convert_progress_back_cb), retval);
+
+  glade_xml_signal_connect_data
     (xml, "gnc_ui_qif_import_finish_cb",
      G_CALLBACK(gnc_ui_qif_import_finish_cb), retval);
 
@@ -3111,6 +3360,14 @@
   retval->memo_view       = glade_xml_get_widget(xml, "memo_page_view");
   retval->memo_view_count = glade_xml_get_widget(xml, "memo_page_count");
   retval->memo_view_btn   = glade_xml_get_widget(xml, "memo_page_change");
+  retval->convert_pause   = glade_xml_get_widget(xml, "convert_progress_pause");
+  retval->convert_log     = glade_xml_get_widget(xml, "convert_progress_log");
+  retval->convert_progress = gnc_progress_dialog_custom(
+    GTK_LABEL(glade_xml_get_widget(xml, "convert_progress_primary")),
+    GTK_LABEL(glade_xml_get_widget(xml, "convert_progress_secondary")),
+    GTK_PROGRESS_BAR(glade_xml_get_widget(xml, "convert_progress_bar")),
+    GTK_LABEL(glade_xml_get_widget(xml, "convert_progress_sub")),
+    GTK_TEXT_VIEW(retval->convert_log));
   retval->new_transaction_view =
     glade_xml_get_widget(xml, "new_transaction_view");
   retval->old_transaction_view =

Modified: gnucash/trunk/src/import-export/qif-import/qif-merge-groups.scm
===================================================================
--- gnucash/trunk/src/import-export/qif-import/qif-merge-groups.scm	2008-06-25 18:29:57 UTC (rev 17251)
+++ gnucash/trunk/src/import-export/qif-import/qif-merge-groups.scm	2008-07-02 20:56:33 UTC (rev 17252)
@@ -50,161 +50,177 @@
 ;;  and change #f to #t where duplication is found.
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(define (gnc:account-tree-find-duplicates old-root new-root window)
+(define (gnc:account-tree-find-duplicates old-root new-root progress-dialog)
 
-  ;; Given a list of accounts, this predicate returns true if any
-  ;; of those accounts are involved in a transaction.
-  (define (has-any-xtns? acctlist)
-    (if (null? acctlist)
-        #f
-        (let ((splits (xaccAccountGetSplitList (car acctlist))))
-          (if (null? splits)
-              (has-any-xtns? (cdr acctlist))
-              #t))))
+  ;; This procedure does all the work. We'll define it, then call it safely.
+  (define (private-find)
 
-  (let ((old-accounts (gnc-account-get-descendants-sorted old-root)))
-    (if (has-any-xtns? old-accounts)
-        ;; Get all the transactions in the new tree, then iterate over them
-        ;; trying to find matches in the old tree.  If there are matches,
-        ;; push the matches onto a list.
-        (let* ((new-xtns (gnc:account-tree-get-transactions new-root))
-               (progress-dialog '())
-               (work-to-do (length new-xtns))
-               (work-done 0)
-               (matches '()))
+    ;; Given a list of accounts, this predicate returns true if any
+    ;; of those accounts are involved in a transaction.
+    (define (has-any-xtns? acctlist)
+      (if (null? acctlist)
+          #f
+          (let ((splits (xaccAccountGetSplitList (car acctlist))))
+            (if (null? splits)
+                (has-any-xtns? (cdr acctlist))
+                #t))))
 
-          ;; Use a progress dialog if this might take a while.
-          (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
-                (_ "Finding duplicate transactions..."))))
 
-          ;; For each transaction in the new account tree, build a query
-          ;; that matches possibly duplicate transactions in the old tree.
-          (for-each
-            (lambda (xtn)
-              (let ((query (qof-query-create-for-splits))
-                    (num-splits 0))
-                (set! work-done (+ 1 work-done))
-                (if (not (null? progress-dialog))
+    (let ((old-accounts (gnc-account-get-descendants-sorted old-root)))
+      (if (has-any-xtns? old-accounts)
+          ;; Get all the transactions in the new tree, then iterate over them
+          ;; trying to find matches in the old tree.  If there are matches,
+          ;; push the matches onto a list.
+          (let* ((new-xtns (gnc:account-tree-get-transactions new-root))
+                 (work-to-do (length new-xtns))
+                 (work-done 0)
+                 (matches '()))
+
+            ;; This procedure handles progress reporting, pause, and cancel.
+            (define (update-progress)
+              (set! work-done (+ 1 work-done))
+              (if (and progress-dialog
+                       (zero? (remainder work-done 8)))
                   (begin
                     (gnc-progress-dialog-set-value progress-dialog
                                                    (/ work-done work-to-do))
-                    (gnc-progress-dialog-update progress-dialog)))
+                    (qif-import:check-pause progress-dialog)
+                    (if qif-import:canceled
+                        (throw 'cancel)))))
 
-                (qof-query-set-book query (gnc-account-get-book old-root))
 
-                ;; First, we only want to find only transactions
-                ;; from accounts in the old tree.
-                (xaccQueryAddAccountMatch query
-                                          old-accounts
-                                          QOF-GUID-MATCH-ANY QOF-QUERY-AND)
+            (if progress-dialog
+                (gnc-progress-dialog-set-sub progress-dialog
+                                         (_ "Finding duplicate transactions")))
 
-                ;; The date should be close to the same.. +/- a week.
-                (let ((date (gnc-transaction-get-date-posted xtn)))
-                  (xaccQueryAddDateMatchTS query #t
-                                           (decdate date WeekDelta) #t
-                                           (incdate date WeekDelta)
-                                           QOF-QUERY-AND))
+            ;; For each transaction in the new account tree, build a query
+            ;; that matches possibly duplicate transactions in the old tree.
+            (for-each
+              (lambda (xtn)
+                (let ((query (qof-query-create-for-splits))
+                      (num-splits 0))
+                  (qof-query-set-book query (gnc-account-get-book old-root))
 
-                ;; For each split in the new transaction, add a
-                ;; term that can match on its properties.
-                (let ((q-splits (qof-query-create-for-splits)))
-                  (for-each
-                    (lambda (split)
-                      (set! num-splits (+ num-splits 1))
-                      (let ((sq (qof-query-create-for-splits)))
-                        (qof-query-set-book sq (gnc-account-get-book old-root))
+                  ;; First, we only want to find only transactions
+                  ;; from accounts in the old tree.
+                  (xaccQueryAddAccountMatch query
+                                            old-accounts
+                                            QOF-GUID-MATCH-ANY QOF-QUERY-AND)
 
-                        ;; Require a match on the account name. If the name
-                        ;; doesn't exist in the old tree (indicating a new
-                        ;; account), the match will be NULL and the query
-                        ;; won't find anything.  Optimize this later.
-                        (xaccQueryAddSingleAccountMatch
-                          sq
-                          (gnc-account-lookup-by-full-name old-root
-                            (gnc-account-get-full-name
-                              (xaccSplitGetAccount split)))
-                          QOF-QUERY-AND)
+                  ;; The date should be close to the same.. +/- a week.
+                  (let ((date (gnc-transaction-get-date-posted xtn)))
+                    (xaccQueryAddDateMatchTS query #t
+                                             (decdate date WeekDelta) #t
+                                             (incdate date WeekDelta)
+                                             QOF-QUERY-AND))
 
-                        ;; Require the value of the split in the new tree
-                        ;; to match the the value of the split in the old
-                        ;; tree.  We should really check for fuzziness.
-                        (xaccQueryAddValueMatch sq
-                                                (xaccSplitGetValue split)
-                                                QOF-NUMERIC-MATCH-ANY
-                                                QOF-COMPARE-EQUAL
-                                                QOF-QUERY-AND)
+                  ;; For each split in the new transaction, add a
+                  ;; term that can match on its properties.
+                  (let ((q-splits (qof-query-create-for-splits)))
+                    (for-each
+                      (lambda (split)
+                        (set! num-splits (+ num-splits 1))
+                        (let ((sq (qof-query-create-for-splits)))
+                          (qof-query-set-book sq (gnc-account-get-book old-root))
 
-                        ;; Now merge into the split query.  Reminder: q-splits
-                        ;; must be merged with an OR. Otherwise, nothing will
-                        ;; match. (For example, something can be equal to 4 or
-                        ;; to -4, but not both.)
-                        (let ((q-new (qof-query-merge q-splits
-                                                      sq
-                                                      QOF-QUERY-OR)))
-                          (qof-query-destroy q-splits)
-                          (qof-query-destroy sq)
-                          (set! q-splits q-new))))
-                    (xaccTransGetSplitList xtn))
+                          ;; Require a match on the account name. If the name
+                          ;; doesn't exist in the old tree (indicating a new
+                          ;; account), the match will be NULL and the query
+                          ;; won't find anything.  Optimize this later.
+                          (xaccQueryAddSingleAccountMatch
+                            sq
+                            (gnc-account-lookup-by-full-name old-root
+                              (gnc-account-get-full-name
+                                (xaccSplitGetAccount split)))
+                            QOF-QUERY-AND)
 
-                  ;; Now q-splits will find every split that is the same as
-                  ;; any one split of the new-root transaction.  Merge it in.
-                  (let ((q-new (qof-query-merge query
-                                                q-splits
-                                                QOF-QUERY-AND)))
-                    (qof-query-destroy query)
-                    (qof-query-destroy q-splits)
-                    (set! query q-new)))
+                          ;; Require the value of the split in the new tree
+                          ;; to match the the value of the split in the old
+                          ;; tree.  We should really check for fuzziness.
+                          (xaccQueryAddValueMatch sq
+                                                  (xaccSplitGetValue split)
+                                                  QOF-NUMERIC-MATCH-ANY
+                                                  QOF-COMPARE-EQUAL
+                                                  QOF-QUERY-AND)
 
-                ;; Now that we have built a query that finds matching splits
-                ;; in the old tree, run it and build a list of transactions
-                ;; from the results.
-                ;;
-                ;; If the transaction from the new tree has more than two
-                ;; splits, then we'll assume that it fully reflects what
-                ;; occurred, and only consider transactions in the old tree
-                ;; that match with every single split.
-                ;;
-                ;; All other new transactions could be incomplete, so we'll
-                ;; consider transactions from the old tree to be possible
-                ;; duplicates even if only one split matches.
-                ;;
-                ;; For more information, see bug 481528.
-                (let ((old-xtns (xaccQueryGetTransactions
-                                  query
-                                  (if (> num-splits 2)
-                                      QUERY-TXN-MATCH-ALL
-                                      QUERY-TXN-MATCH-ANY))))
+                          ;; Now merge into the split query.  Reminder: q-splits
+                          ;; must be merged with an OR. Otherwise, nothing will
+                          ;; match. (For example, something can be equal to 4 or
+                          ;; to -4, but not both.)
+                          (let ((q-new (qof-query-merge q-splits
+                                                        sq
+                                                        QOF-QUERY-OR)))
+                            (qof-query-destroy q-splits)
+                            (qof-query-destroy sq)
+                            (set! q-splits q-new))))
+                      (xaccTransGetSplitList xtn))
 
-                  ;; Turn the resulting list of possibly duplicated
-                  ;; transactions into an association list.
-                  (set! old-xtns (map
-                                   (lambda (elt)
-                                     (cons elt #f)) old-xtns))
+                    ;; Now q-splits will find every split that is the same as
+                    ;; any one split of the new-root transaction.  Merge it in.
+                    (let ((q-new (qof-query-merge query
+                                                  q-splits
+                                                  QOF-QUERY-AND)))
+                      (qof-query-destroy query)
+                      (qof-query-destroy q-splits)
+                      (set! query q-new)))
 
-                  ;; If anything matched the query, add it to our "matches"
-                  ;; association list, keyed by the new-root transaction.
-                  (if (not (null? old-xtns))
-                      (set! matches (cons (cons xtn old-xtns) matches))))
+                  ;; Now that we have built a query that finds matching splits
+                  ;; in the old tree, run it and build a list of transactions
+                  ;; from the results.
+                  ;;
+                  ;; If the transaction from the new tree has more than two
+                  ;; splits, then we'll assume that it fully reflects what
+                  ;; occurred, and only consider transactions in the old tree
+                  ;; that match with every single split.
+                  ;;
+                  ;; All other new transactions could be incomplete, so we'll
+                  ;; consider transactions from the old tree to be possible
+                  ;; duplicates even if only one split matches.
+                  ;;
+                  ;; For more information, see bug 481528.
+                  (let ((old-xtns (xaccQueryGetTransactions
+                                    query
+                                    (if (> num-splits 2)
+                                        QUERY-TXN-MATCH-ALL
+                                        QUERY-TXN-MATCH-ANY))))
 
-                (qof-query-destroy query)))
-            new-xtns)
+                    ;; Turn the resulting list of possibly duplicated
+                    ;; transactions into an association list.
+                    (set! old-xtns (map
+                                     (lambda (elt)
+                                       (cons elt #f)) old-xtns))
 
-          ;; Get rid of the progress dialog.
-          (if (not (null? progress-dialog))
-              (gnc-progress-dialog-destroy progress-dialog))
+                    ;; If anything matched the query, add it to our "matches"
+                    ;; association list, keyed by the new-root transaction.
+                    (if (not (null? old-xtns))
+                        (set! matches (cons (cons xtn old-xtns) matches))))
 
-          ;; Return the matches.
-          matches)
+                  (qof-query-destroy query))
+                (update-progress))
+              new-xtns)
 
-        ;; Since there are either no accounts or no transactions in the old
-        ;; tree, duplicate checking is unnecessary. Return an empty list.
-        '())))
+            ;; Finished.
+            (if progress-dialog
+                (gnc-progress-dialog-set-value progress-dialog 1))
 
+            ;; Return the matches.
+            matches)
 
+          ;; Since there are either no accounts or no transactions in the old
+          ;; tree, duplicate checking is unnecessary.
+          (begin
+            ;; Finished.
+            (if progress-dialog
+                (gnc-progress-dialog-set-value progress-dialog 1))
+
+            ;; Return an empty list.
+            '()))))
+
+  ;; Safely do the work and return the result.
+  (gnc:backtrace-if-exception
+    (lambda () (catch 'cancel private-find (lambda (key . args) #t)))))
+
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;  gnc:prune-matching-transactions
 ;;

Modified: gnucash/trunk/src/import-export/qif-import/qif-to-gnc.scm
===================================================================
--- gnucash/trunk/src/import-export/qif-import/qif-to-gnc.scm	2008-06-25 18:29:57 UTC (rev 17251)
+++ gnucash/trunk/src/import-export/qif-import/qif-to-gnc.scm	2008-07-02 20:56:33 UTC (rev 17252)
@@ -201,204 +201,215 @@
 ;; This is the top-level of the back end conversion from QIF
 ;; to GnuCash. All the account mappings and so on should be
 ;; done before this is called.
+;;
+;; This procedure returns:
+;;   success: the root of the imported tree
+;;   failure: a symbol indicating the reason
+;;   cancel:  #t
+;;   bug:     #f
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (define (qif-import:qif-to-gnc qif-files-list
                                qif-acct-map qif-cat-map
                                qif-memo-map stock-map
-                               default-currency-name window)
-  (let ((progress-dialog '())
-        (retval #f))
-    (set! retval
-      (gnc:backtrace-if-exception
-       (lambda ()
-         (let* ((old-root (gnc-get-current-root-account))
-                (new-root (xaccMallocAccount (gnc-get-current-book)))
-                (gnc-acct-hash (make-hash-table 20))
-                (sep (gnc-get-account-separator-string))
-                (default-currency
-                  (gnc-commodity-table-find-full
-                   (gnc-commodity-table-get-table (gnc-get-current-book))
-                   GNC_COMMODITY_NS_CURRENCY default-currency-name))
-                (sorted-accounts-list '())
-                (markable-xtns '())
-                (sorted-qif-files-list
-                 (sort qif-files-list
-                       (lambda (a b)
-                         (> (length (qif-file:xtns a))
-                            (length (qif-file:xtns b))))))
-                (work-to-do 0)
-                (work-done 0))
+                               default-currency-name progress-dialog)
 
-           ;; first, build a local account tree that mirrors the gnucash
-           ;; accounts in the mapping data.  we need to iterate over the
-           ;; cat-map and the acct-map to build the list
-           (hash-fold
-            (lambda (k v p)
-              (if (qif-map-entry:display? v)
-                  (set! sorted-accounts-list
-                        (cons v sorted-accounts-list)))
-              #t)
-            #t qif-acct-map)
+  ;; This procedure does all the work. We'll define it, then call it safely.
+  (define (private-convert)
+    (let* ((old-root (gnc-get-current-root-account))
+           (new-root (xaccMallocAccount (gnc-get-current-book)))
+           (gnc-acct-hash (make-hash-table 20))
+           (sep (gnc-get-account-separator-string))
+           (default-currency
+             (gnc-commodity-table-find-full
+              (gnc-commodity-table-get-table (gnc-get-current-book))
+              GNC_COMMODITY_NS_CURRENCY default-currency-name))
+           (sorted-accounts-list '())
+           (markable-xtns '())
+           (sorted-qif-files-list (sort qif-files-list
+                                        (lambda (a b)
+                                          (> (length (qif-file:xtns a))
+                                             (length (qif-file:xtns b))))))
+           (work-to-do 0)
+           (work-done 0))
 
-           (hash-fold
-            (lambda (k v p)
-              (if (qif-map-entry:display? v)
-                  (set! sorted-accounts-list
-                        (cons v sorted-accounts-list)))
-              #t)
-            #t qif-cat-map)
+      ;; This procedure handles progress reporting, pause, and cancel.
+      (define (update-progress)
+        (set! work-done (+ 1 work-done))
+        (if (and progress-dialog
+                 (zero? (remainder work-done 8)))
+            (begin
+              (gnc-progress-dialog-set-value progress-dialog
+                                             (/ work-done work-to-do))
+              (qif-import:check-pause progress-dialog)
+              (if qif-import:canceled
+                  (throw 'cancel)))))
 
-           (hash-fold
-            (lambda (k v p)
-              (if (qif-map-entry:display? v)
-                  (set! sorted-accounts-list
-                        (cons v sorted-accounts-list)))
-              #t)
-            #t qif-memo-map)
 
-           ;; sort the account info on the depth of the account path.  if a
-           ;; short part is explicitly mentioned, make sure it gets created
-           ;; before the deeper path, which will create the parent accounts
-           ;; without the information about their type.
-           (set! sorted-accounts-list
-                 (sort sorted-accounts-list
-                       (lambda (a b)
-                         (< (gnc:substring-count (qif-map-entry:gnc-name a)
-                                                 sep)
-                            (gnc:substring-count (qif-map-entry:gnc-name b)
-                                                 sep)))))
+      (if progress-dialog
+          (gnc-progress-dialog-set-sub progress-dialog
+                                      (_ "Preparing to convert your QIF data")))
 
-           ;; make all the accounts
-           (for-each
-            (lambda (acctinfo)
-              (let* ((security
-                      (and stock-map
-                           (hash-ref stock-map
-                                     (qif-import:get-account-name
-                                      (qif-map-entry:qif-name acctinfo)))))
-                     (ok-types (qif-map-entry:allowed-types acctinfo))
-                     (equity? (memv GNC-EQUITY-TYPE ok-types))
-                     (stock? (or (memv GNC-STOCK-TYPE ok-types)
-                                 (memv GNC-MUTUAL-TYPE ok-types))))
+      ;; Build a list of all accounts to create for the import tree.
+      ;; We need to iterate over the account, category, and payee/memo
+      ;; mappings to build the list.
+      (hash-fold
+       (lambda (k v p)
+         (if (qif-map-entry:display? v)
+             (set! sorted-accounts-list
+                   (cons v sorted-accounts-list)))
+         #t)
+       #t qif-acct-map)
 
-                ;; Debug
-                ;; (for-each
-                ;;  (lambda (expr)
-                ;;    (display expr))
-                ;;  (list "Account: " acctinfo "\nsecurity = " security
-                ;;     "\nequity? = " equity?
-                ;;     "\n"))
+      (hash-fold
+       (lambda (k v p)
+         (if (qif-map-entry:display? v)
+             (set! sorted-accounts-list
+                   (cons v sorted-accounts-list)))
+         #t)
+       #t qif-cat-map)
 
-                (cond ((and equity? security)  ;; a "retained holdings" acct
-                       (qif-import:find-or-make-acct acctinfo #f
-                                                     security #t
-                                                     default-currency
-                                                     gnc-acct-hash
-                                                     old-root new-root))
-                      ((and security (or stock?
-                                         (gnc-commodity-is-currency security)))
-                       (qif-import:find-or-make-acct
-                        acctinfo #f security #t default-currency
-                        gnc-acct-hash old-root new-root))
-                      (#t
-                       (qif-import:find-or-make-acct
-                        acctinfo #f default-currency #t default-currency
-                        gnc-acct-hash old-root new-root)))))
-            sorted-accounts-list)
+      (hash-fold
+       (lambda (k v p)
+         (if (qif-map-entry:display? v)
+             (set! sorted-accounts-list
+                   (cons v sorted-accounts-list)))
+         #t)
+       #t qif-memo-map)
+      (set! work-to-do (length sorted-accounts-list))
 
-           ;; before trying to mark transactions, prune down the list of
-           ;; ones to match.
-           (for-each
-            (lambda (qif-file)
-              (for-each
-               (lambda (xtn)
-                 (set! work-to-do (+ 1 work-to-do))
-                 (let splitloop ((splits (qif-xtn:splits xtn)))
-                   (if (qif-split:category-is-account? (car splits))
-                       (begin
-                         (set! markable-xtns (cons xtn markable-xtns))
-                         (set! work-to-do (+ 1 work-to-do)))
-                       (if (not (null? (cdr splits)))
-                           (splitloop (cdr splits))))))
-               (qif-file:xtns qif-file)))
-            qif-files-list)
+      ;; Before trying to mark transactions, prune down the list to
+      ;; those that are transfers between QIF accounts.
+      (for-each
+       (lambda (qif-file)
+         (for-each
+          (lambda (xtn)
+            (set! work-to-do (+ 1 work-to-do))
+            (let splitloop ((splits (qif-xtn:splits xtn)))
+              (if (qif-split:category-is-account? (car splits))
+                  (begin
+                    (set! markable-xtns (cons xtn markable-xtns))
+                    (set! work-to-do (+ 1 work-to-do)))
+                  (if (not (null? (cdr splits)))
+                      (splitloop (cdr splits))))))
+          (qif-file:xtns qif-file)))
+       qif-files-list)
 
-           (if (> work-to-do 100)
-               (begin
-                 (set! progress-dialog (gnc-progress-dialog-new window #f))
-                 (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"))))
 
+      ;; Build a local account tree to hold converted transactions.
+      (if progress-dialog
+          (gnc-progress-dialog-set-sub progress-dialog
+                                       (_ "Creating accounts")))
 
-           ;; now run through the markable transactions marking any
-           ;; duplicates.  marked transactions/splits won't get imported.
-           (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 (and (not (null? progress-dialog))
-                          (zero? (remainder work-done 32)))
-                     (gnc-progress-dialog-set-value progress-dialog
-                                                    (/ work-done work-to-do)))
+      ;; Sort the account list on the depth of the account path.  If a
+      ;; short part is explicitly mentioned, make sure it gets created
+      ;; before the deeper path that would create the parent accounts
+      ;; without enough information about their type.
+      (set! sorted-accounts-list
+            (sort sorted-accounts-list
+                  (lambda (a b)
+                    (< (gnc:substring-count (qif-map-entry:gnc-name a)
+                                            sep)
+                       (gnc:substring-count (qif-map-entry:gnc-name b)
+                                            sep)))))
 
-                 (if (not (qif-xtn:mark xtn))
-                     (qif-import:mark-matching-xtns xtn rest))
-                 (if (not (null? (cdr rest)))
-                     (xloop (car rest) (cdr rest)))))
+      ;; Make all the accounts.
+      (for-each
+       (lambda (acctinfo)
+         (let* ((security
+                 (and stock-map
+                      (hash-ref stock-map
+                                (qif-import:get-account-name
+                                 (qif-map-entry:qif-name acctinfo)))))
+                (ok-types (qif-map-entry:allowed-types acctinfo))
+                (equity? (memv GNC-EQUITY-TYPE ok-types))
+                (stock? (or (memv GNC-STOCK-TYPE ok-types)
+                            (memv GNC-MUTUAL-TYPE ok-types))))
 
-           ;; iterate over files. Going in the sort order by number of
-           ;; transactions should give us a small speed advantage.
-           (for-each
-            (lambda (qif-file)
-              (for-each
-               (lambda (xtn)
-                 ;; Update the progress.
-                 (set! work-done (+ 1 work-done))
-                 (if (and (not (null? progress-dialog))
-                          (zero? (remainder work-done 32)))
-                     (gnc-progress-dialog-set-value progress-dialog
-                                                    (/ work-done work-to-do)))
+           (update-progress)
+           (cond ((and equity? security)  ;; a "retained holdings" acct
+                  (qif-import:find-or-make-acct acctinfo #f
+                                                security #t
+                                                default-currency
+                                                gnc-acct-hash
+                                                old-root new-root))
+                 ((and security (or stock?
+                                    (gnc-commodity-is-currency security)))
+                  (qif-import:find-or-make-acct
+                   acctinfo #f security #t default-currency
+                   gnc-acct-hash old-root new-root))
+                 (#t
+                  (qif-import:find-or-make-acct
+                   acctinfo #f default-currency #t default-currency
+                   gnc-acct-hash old-root new-root)))))
+       sorted-accounts-list)
 
-                 (if (not (qif-xtn:mark xtn))
-                     ;; create and fill in the GNC transaction
-                     (let ((gnc-xtn (xaccMallocTransaction
-                                     (gnc-get-current-book))))
-                       (xaccTransBeginEdit gnc-xtn)
+      ;; Run through the markable transactions marking any
+      ;; duplicates.  marked transactions/splits won't get imported.
+      (if progress-dialog
+          (gnc-progress-dialog-set-sub progress-dialog
+                                    (_ "Matching transfers between accounts")))
+      (if (> (length markable-xtns) 1)
+          (let xloop ((xtn (car markable-xtns))
+                      (rest (cdr markable-xtns)))
+            ;; Update the progress.
+            (update-progress)
 
-                       ;; FIXME. This is probably wrong
-                       (xaccTransSetCurrency gnc-xtn (gnc-default-currency))
+            (if (not (qif-xtn:mark xtn))
+                (qif-import:mark-matching-xtns xtn rest))
+            (if (not (null? (cdr rest)))
+                (xloop (car rest) (cdr rest)))))
 
-                       ;; 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)
+      ;; Iterate over files. Going in the sort order by number of
+      ;; transactions should give us a small speed advantage.
+      (for-each
+       (lambda (qif-file)
+         (if progress-dialog
+             (gnc-progress-dialog-set-sub progress-dialog
+                                          (string-append (_ "Converting") " "
+                                                     (qif-file:path qif-file))))
+         (for-each
+          (lambda (xtn)
+            ;; Update the progress.
+            (update-progress)
 
-                       ;; rebalance and commit everything
-                       (xaccTransCommitEdit gnc-xtn))))
-               (qif-file:xtns qif-file)))
-            sorted-qif-files-list)
+            (if (not (qif-xtn:mark xtn))
+                ;; Convert into a GnuCash transaction.
+                (let ((gnc-xtn (xaccMallocTransaction
+                                (gnc-get-current-book))))
+                  (xaccTransBeginEdit gnc-xtn)
 
-           ;; Finished.
-           (if (not (null? progress-dialog))
-               (gnc-progress-dialog-set-value progress-dialog 1))
+                  ;; FIXME. This is probably wrong
+                  (xaccTransSetCurrency gnc-xtn (gnc-default-currency))
 
-           new-root))))
+                  ;; 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
+                                                 progress-dialog)
 
-    ;; Get rid of the progress dialog (if any).
-    (if (not (null? progress-dialog))
-        (gnc-progress-dialog-destroy progress-dialog))
+                  ;; rebalance and commit everything
+                  (xaccTransCommitEdit gnc-xtn))))
+          (qif-file:xtns qif-file)))
+       sorted-qif-files-list)
 
-    retval))
+      ;; Finished.
+      (if progress-dialog
+          (gnc-progress-dialog-set-value progress-dialog 1))
 
+      new-root))
 
+  ;; Safely convert the files and return the result.
+  (gnc:backtrace-if-exception
+    (lambda ()
+      (catch 'cancel
+             (lambda ()
+               (catch 'bad-date private-convert (lambda (key . args) key)))
+             (lambda (key . args) #t)))))
+
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; qif-import:qif-xtn-to-gnc-xtn
 ;; translate a single transaction to a set of gnucash splits and
@@ -407,7 +418,8 @@
 
 (define (qif-import:qif-xtn-to-gnc-xtn qif-xtn qif-file gnc-xtn
                                        gnc-acct-hash
-                                       qif-acct-map qif-cat-map qif-memo-map)
+                                       qif-acct-map qif-cat-map qif-memo-map
+                                       progress-dialog)
   (let ((splits (qif-xtn:splits qif-xtn))
         (gnc-near-split (xaccMallocSplit (gnc-get-current-book)))
         (near-split-total (gnc-numeric-zero))
@@ -434,12 +446,18 @@
     ;; Set the transaction date.
     (cond
       ((not qif-date)
+        (qif-import:log progress-dialog
+                        "qif-import:qif-xtn-to-gnc-xtn"
+                        (_ "Missing transaction date."))
         (throw 'bad-date
                "qif-import:qif-xtn-to-gnc-xtn"
                "Missing transaction date."
                #f
                #f))
       ((< (list-ref qif-date 2) 1970)
+        (qif-import:log progress-dialog
+                        "qif-import:qif-xtn-to-gnc-xtn"
+                        (_ "Dates earlier than 1970 are not supported."))
         (throw 'bad-date
                "qif-import:qif-xtn-to-gnc-xtn"
                "Invalid transaction year (~A)."

Modified: gnucash/trunk/src/import-export/qif-import/qif.glade
===================================================================
--- gnucash/trunk/src/import-export/qif-import/qif.glade	2008-06-25 18:29:57 UTC (rev 17251)
+++ gnucash/trunk/src/import-export/qif-import/qif.glade	2008-07-02 20:56:33 UTC (rev 17252)
@@ -1490,7 +1490,6 @@
 	  <property name="visible">True</property>
 	  <property name="title" translatable="yes">Tradable commodities</property>
 	  <property name="title_foreground">#f5f5f5f5f5f5</property>
-	  <signal name="prepare" handler="gnc_ui_qif_import_commodity_prepare_cb"/>
 	  <signal name="next" handler="gnc_ui_qif_import_generic_next_cb"/>
 	  <signal name="back" handler="gnc_ui_qif_import_generic_back_cb"/>
 
@@ -1535,34 +1534,56 @@
       </child>
 
       <child>
-	<widget class="GnomeDruidPageStandard" id="match_doc_page">
+	<widget class="GnomeDruidPageStandard" id="convert_progress_page">
 	  <property name="visible">True</property>
-	  <property name="title" translatable="yes">Match duplicate transactions</property>
-	  <property name="title_foreground">#f5f5f5f5f5f5</property>
-	  <signal name="next" handler="gnc_ui_qif_import_generic_next_cb"/>
-	  <signal name="back" handler="gnc_ui_qif_import_generic_back_cb"/>
+	  <property name="title" translatable="yes">QIF Import</property>
+	  <signal name="prepare" handler="gnc_ui_qif_import_convert_progress_prepare_cb" after="yes"/>
+	  <signal name="show" handler="gnc_ui_qif_import_convert_progress_show_cb" after="yes"/>
+	  <signal name="next" handler="gnc_ui_qif_import_convert_progress_next_cb"/>
+	  <signal name="back" handler="gnc_ui_qif_import_convert_progress_back_cb"/>
 
 	  <child internal-child="vbox">
-	    <widget class="GtkVBox" id="druid-vbox34">
-	      <property name="border_width">5</property>
+	    <widget class="GtkVBox" id="convert_progress_vbox7609">
+	      <property name="border_width">12</property>
 	      <property name="visible">True</property>
 	      <property name="homogeneous">False</property>
-	      <property name="spacing">0</property>
+	      <property name="spacing">12</property>
 
 	      <child>
-		<widget class="GtkLabel" id="label847694">
+		<widget class="GtkLabel" id="convert_progress_primary">
 		  <property name="visible">True</property>
-		  <property name="label" translatable="yes">If you are importing a QIF file downloaded from a bank or other financial institution, some of the information in the QIF file may duplicate information already in your GnuCash accounts.  GnuCash will try to detect duplicates of existing transactions.  
+		  <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>
 
-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.
-
-Click &quot;Forward&quot; to find duplicate transactions. </property>
+	      <child>
+		<widget class="GtkLabel" id="convert_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.5</property>
+		  <property name="xalign">0</property>
 		  <property name="yalign">0.5</property>
 		  <property name="xpad">0</property>
 		  <property name="ypad">0</property>
@@ -1577,62 +1598,40 @@
 		  <property name="fill">False</property>
 		</packing>
 	      </child>
-	    </widget>
-	  </child>
-	</widget>
-      </child>
 
-      <child>
-	<widget class="GnomeDruidPageStandard" id="failed_page">
-	  <property name="visible">True</property>
-	  <property name="title" translatable="yes">Cancel import</property>
-	  <property name="title_foreground">#f5f5f5f5f5f5</property>
-	  <signal name="next" handler="gnc_ui_qif_import_generic_next_cb"/>
-	  <signal name="back" handler="gnc_ui_qif_import_generic_back_cb"/>
+	      <child>
+		<widget class="GtkVBox" id="convert_progress_vbox1">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">0</property>
 
-	  <child internal-child="vbox">
-	    <widget class="GtkVBox" id="failed_vbox">
-	      <property name="visible">True</property>
-	      <property name="homogeneous">False</property>
-	      <property name="spacing">12</property>
-
-      	      <child>
-	        <widget class="GtkHBox" id="failed_hbox">
-	          <property name="border_width">6</property>
-	          <property name="visible">True</property>
-	          <property name="homogeneous">False</property>
-	          <property name="spacing">12</property>
-
-	          <child>
-	            <widget class="GtkImage" id="failed_image">
+		  <child>
+		    <widget class="GtkProgressBar" id="convert_progress_bar">
 	              <property name="visible">True</property>
-	              <property name="stock">gtk-dialog-warning</property>
-	              <property name="icon_size">6</property>
-	              <property name="xalign">0.5</property>
-	              <property name="yalign">0</property>
-	              <property name="xpad">0</property>
-	              <property name="ypad">0</property>
-	            </widget>
-	            <packing>
+	              <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>
+		    </packing>
+		  </child>
 
-	          <child>
-		    <widget class="GtkLabel" id="label_failed">
+		  <child>
+		    <widget class="GtkLabel" id="convert_progress_sub">
 		      <property name="visible">True</property>
-		      <property name="label" translatable="yes">&lt;span weight=&quot;bold&quot; size=&quot;larger&quot;&gt;The QIF import has failed&lt;/span&gt;
-
-An error occurred while importing transactions into GnuCash. Your accounts are unchanged.</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">True</property>
-		      <property name="xalign">0.5</property>
-		      <property name="yalign">0</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>
@@ -1645,13 +1644,69 @@
 		      <property name="expand">False</property>
 		      <property name="fill">False</property>
 		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkHButtonBox" id="convert_progress_hbuttonbox1">
+		      <property name="visible">True</property>
+		      <property name="layout_style">GTK_BUTTONBOX_END</property>
+  
+		      <child>
+	                <widget class="GtkButton" id="convert_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_convert_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="convert_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>
-	        <packing>
-	          <property name="padding">0</property>
-	          <property name="expand">False</property>
-	          <property name="fill">False</property>
-	        </packing>
 	      </child>
 	    </widget>
 	  </child>
@@ -1659,6 +1714,54 @@
       </child>
 
       <child>
+	<widget class="GnomeDruidPageStandard" id="match_doc_page">
+	  <property name="visible">True</property>
+	  <property name="title" translatable="yes">Match duplicate transactions</property>
+	  <property name="title_foreground">#f5f5f5f5f5f5</property>
+	  <signal name="next" handler="gnc_ui_qif_import_generic_next_cb"/>
+	  <signal name="back" handler="gnc_ui_qif_import_generic_back_cb"/>
+
+	  <child internal-child="vbox">
+	    <widget class="GtkVBox" id="druid-vbox34">
+	      <property name="border_width">5</property>
+	      <property name="visible">True</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">0</property>
+
+	      <child>
+		<widget class="GtkLabel" id="label847694">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">If you are importing a QIF file downloaded from a bank or other financial institution, some of the information in the QIF file may duplicate information already in your GnuCash accounts.  GnuCash will try to detect duplicates of existing transactions.  
+
+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.
+
+Click &quot;Forward&quot; to find duplicate transactions. </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.5</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>
+	  </child>
+	</widget>
+      </child>
+
+      <child>
 	<widget class="GnomeDruidPageStandard" id="match_duplicates_page">
 	  <property name="visible">True</property>
 	  <property name="title" translatable="yes">Select possible duplicates</property>



More information about the gnucash-changes mailing list