gnucash master: Multiple changes pushed

Robert Fewell bobit at code.gnucash.org
Thu May 28 06:29:26 EDT 2020


Updated	 via  https://github.com/Gnucash/gnucash/commit/3457dd15 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/8bf426e2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/e69c8a23 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2dcc0b7a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/55d73851 (commit)
	from  https://github.com/Gnucash/gnucash/commit/4fd5a2cd (commit)



commit 3457dd150b489088d2469133ff24954f748134d4
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu May 28 11:19:32 2020 +0100

    Add file name to dialog used in ofx import

diff --git a/gnucash/import-export/ofx/gnc-ofx-import.c b/gnucash/import-export/ofx/gnc-ofx-import.c
index a1b7e2663..88456acf9 100644
--- a/gnucash/import-export/ofx/gnc-ofx-import.c
+++ b/gnucash/import-export/ofx/gnc-ofx-import.c
@@ -1173,7 +1173,8 @@ gnc_file_ofx_import_process_file (ofx_info* info)
     {
         gnc_gen_trans_list_delete (info->gnc_ofx_importer_gui);
         if(info->num_trans_processed)
-            gnc_info_dialog (parent, _("OFX file(s) imported, %d transactions processed, no transactions to match"), info->num_trans_processed);
+            gnc_info_dialog (parent, _("OFX file '%s' imported, %d transactions processed, no transactions to match"),
+                                     selected_filename, info->num_trans_processed);
         // Process the next OFX file if any.
         gnc_ofx_process_next_file (NULL, info);
     }

commit 8bf426e274fad91420b91d502c348fda0071fe5b
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu May 28 11:11:32 2020 +0100

    Rename a couple of functions used in multiple OFX file import

diff --git a/gnucash/gnome/window-reconcile.c b/gnucash/gnome/window-reconcile.c
index 078e47bb2..b61df7561 100644
--- a/gnucash/gnome/window-reconcile.c
+++ b/gnucash/gnome/window-reconcile.c
@@ -2117,12 +2117,12 @@ gnc_ui_reconcile_window_raise(RecnWindow * recnData)
     gtk_window_present(GTK_WINDOW(recnData->window));
 }
 
-GtkWidget*
-gnc_ui_reconcile_window_get_widget(RecnWindow * recnData)
+GtkWindow *
+gnc_ui_reconcile_window_get_window (RecnWindow * recnData)
 {
     if (recnData == NULL || recnData->window == NULL)
         return NULL;
-    return recnData->window;
+    return GTK_WINDOW(recnData->window);
 }
 
 
diff --git a/gnucash/gnome/window-reconcile.h b/gnucash/gnome/window-reconcile.h
index 5ca431b33..9f4695bd9 100644
--- a/gnucash/gnome/window-reconcile.h
+++ b/gnucash/gnome/window-reconcile.h
@@ -62,6 +62,8 @@ RecnWindow *recnWindowWithBalance (GtkWidget *parent, Account *account,
                                    gnc_numeric new_ending,
                                    time64 statement_date);
 
-void gnc_ui_reconcile_window_raise(RecnWindow * recnData);
-GtkWidget* gnc_ui_reconcile_window_get_widget(RecnWindow * recnData);
+void gnc_ui_reconcile_window_raise (RecnWindow * recnData);
+
+GtkWindow *gnc_ui_reconcile_window_get_window (RecnWindow * recnData);
+
 #endif /* WINDOW_RECONCILE_H */
diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index 6dafc2160..305c71a65 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -1475,14 +1475,17 @@ void gnc_gen_trans_list_add_trans (GNCImportMainMatcher *gui, Transaction *trans
     return;
 }/* end gnc_import_add_trans() */
 
-void gnc_gen_trans_list_show_reconcile_after_close(GNCImportMainMatcher *info, gboolean reconcile_after_close, gboolean active)
+void
+gnc_gen_trans_list_show_reconcile_after_close_button (GNCImportMainMatcher *info,
+                                                      gboolean reconcile_after_close,
+                                                      gboolean active)
 {
-    gtk_widget_set_visible (info->reconcile_after_close,reconcile_after_close);
+    gtk_widget_set_visible (info->reconcile_after_close, reconcile_after_close);
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (info->reconcile_after_close), active);
 }
 
 GtkWidget*
-gnc_gen_trans_list_get_reconcile_widget(GNCImportMainMatcher *info)
+gnc_gen_trans_list_get_reconcile_after_close_button (GNCImportMainMatcher *info)
 {
     return info->reconcile_after_close;
 }
diff --git a/gnucash/import-export/import-main-matcher.h b/gnucash/import-export/import-main-matcher.h
index fdb84a69d..06b98cf41 100644
--- a/gnucash/import-export/import-main-matcher.h
+++ b/gnucash/import-export/import-main-matcher.h
@@ -196,8 +196,19 @@ gboolean gnc_gen_trans_list_empty(GNCImportMainMatcher *info);
  */
 void gnc_gen_trans_list_show_all(GNCImportMainMatcher *info);
 
-void gnc_gen_trans_list_show_reconcile_after_close(GNCImportMainMatcher *info, gboolean reconcile_after_close, gboolean active);
-GtkWidget* gnc_gen_trans_list_get_reconcile_widget(GNCImportMainMatcher *info);
+/** Show and set the reconcile after close check button.
+ * @param info A pointer to a the GNCImportMainMatcher structure.
+ * @param reconcile_after_close A gboolean that shows or hides the button.
+ * @param active A gboolean to set or clear the check button.
+ */
+void gnc_gen_trans_list_show_reconcile_after_close_button (GNCImportMainMatcher *info,
+                                                           gboolean reconcile_after_close,
+                                                           gboolean active);
+/** Returns the reconcile after close check button.
+ * @param info A pointer to a the GNCImportMainMatcher structure.
+ * @return The check button.
+ */
+GtkWidget* gnc_gen_trans_list_get_reconcile_after_close_button (GNCImportMainMatcher *info);
 
 #endif
 /**@}*/
diff --git a/gnucash/import-export/ofx/gnc-ofx-import.c b/gnucash/import-export/ofx/gnc-ofx-import.c
index ff5292a1e..a1b7e2663 100644
--- a/gnucash/import-export/ofx/gnc-ofx-import.c
+++ b/gnucash/import-export/ofx/gnc-ofx-import.c
@@ -1107,9 +1107,13 @@ gnc_ofx_match_done (GtkDialog *dialog, gint response_id, gpointer user_data)
             gnc_numeric value = double_to_gnc_numeric (info->statement->ledger_balance,
                                                        xaccAccountGetCommoditySCU (account),
                                                        GNC_HOW_RND_ROUND_HALF_UP);
-            RecnWindow* rec_window = recnWindowWithBalance (GTK_WIDGET (info->parent), account, value, info->statement->ledger_balance_date);
+
+            RecnWindow* rec_window = recnWindowWithBalance (GTK_WIDGET (info->parent), account, value,
+                                                            info->statement->ledger_balance_date);
+
             // Connect to destroy, at which point we'll process the next OFX file..
-            g_signal_connect (G_OBJECT (gnc_ui_reconcile_window_get_widget (rec_window)), "destroy", G_CALLBACK (gnc_ofx_process_next_file), info);
+            g_signal_connect (G_OBJECT(gnc_ui_reconcile_window_get_window (rec_window)), "destroy",
+                              G_CALLBACK(gnc_ofx_process_next_file), info);
         }
     }
     else
@@ -1175,15 +1179,19 @@ gnc_file_ofx_import_process_file (ofx_info* info)
     }
     else
     {
-        // Show the match dialog and connect to the "response" signal so we can trigger a reconcile if the user clicks OK when done matching transactions.
-        g_signal_connect (G_OBJECT (gnc_gen_trans_list_widget (info->gnc_ofx_importer_gui)), "response", G_CALLBACK (gnc_ofx_match_done), info);
+        /* Show the match dialog and connect to the "response" signal so we can trigger a reconcile when
+           the user clicks OK when done matching transactions if required. */
+        g_signal_connect (G_OBJECT(gnc_gen_trans_list_widget (info->gnc_ofx_importer_gui)), "response",
+                          G_CALLBACK (gnc_ofx_match_done), info);
+
         gnc_gen_trans_list_show_all (info->gnc_ofx_importer_gui);
         
         // Show or hide the check box for reconciling after match, depending on whether a statement was received.
-        gnc_gen_trans_list_show_reconcile_after_close (info->gnc_ofx_importer_gui, info->statement != NULL, info->run_reconcile);
+        gnc_gen_trans_list_show_reconcile_after_close_button (info->gnc_ofx_importer_gui, info->statement != NULL, info->run_reconcile);
+
         // Finally connect to the reconcile after match check box so we can be notified if the user wants/does not want to reconcile.
-        g_signal_connect (G_OBJECT (gnc_gen_trans_list_get_reconcile_widget (info->gnc_ofx_importer_gui)), "toggled",
-                          G_CALLBACK (reconcile_when_close_toggled_cb), info);
+        g_signal_connect (G_OBJECT(gnc_gen_trans_list_get_reconcile_after_close_button (info->gnc_ofx_importer_gui)), "toggled",
+                          G_CALLBACK(reconcile_when_close_toggled_cb), info);
     }
     g_free(selected_filename);
 }

commit e69c8a237cca534ccfb957a709ecd8339d3858cd
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu May 28 10:49:56 2020 +0100

    Set the reconcile after close button in import-main-matcher
    
    Set the default visibility of the reconcile after close check button
    to be hidden and set it so it does not respond to gtk_show_all.

diff --git a/gnucash/gtkbuilder/dialog-import.glade b/gnucash/gtkbuilder/dialog-import.glade
index 9fd5d95e7..d4762680d 100644
--- a/gnucash/gtkbuilder/dialog-import.glade
+++ b/gnucash/gtkbuilder/dialog-import.glade
@@ -946,19 +946,19 @@
           </packing>
         </child>
         <child>
-            <object class="GtkCheckButton" id="reconcile_after_close_button">
-                <property name="label" translatable="yes">Reconcile after match</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">False</property>
-                <property name="use_underline">True</property>
-                <property name="draw_indicator">True</property>
-            </object>
-            <packing>
-                <property name="expand">False</property>
-                <property name="fill">True</property>
-                <property name="position">2</property>
-            </packing>
+          <object class="GtkCheckButton" id="reconcile_after_close_button">
+            <property name="label" translatable="yes">Reconcile after match</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">False</property>
+            <property name="no_show_all">True</property>
+            <property name="use_underline">True</property>
+            <property name="draw_indicator">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
         </child>
       </object>
       <packing>
diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index 6641a0f19..6dafc2160 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -197,8 +197,6 @@ gboolean gnc_gen_trans_list_empty(GNCImportMainMatcher *info)
 void gnc_gen_trans_list_show_all(GNCImportMainMatcher *info)
 {
     gtk_widget_show_all (GTK_WIDGET (info->main_widget));
-    // By default, do not show this check box.
-    gnc_gen_trans_list_show_reconcile_after_close (info, FALSE, FALSE);
 }
 
 void
@@ -1095,7 +1093,8 @@ GNCImportMainMatcher * gnc_gen_trans_assist_new (GtkWidget *parent,
     g_signal_connect (G_OBJECT(info->show_matched_info), "toggled",
                       G_CALLBACK(show_matched_info_toggled_cb), info);
 
-    info->reconcile_after_close = GTK_WIDGET(gtk_builder_get_object (builder, "reconcile_when_close_button"));
+    // Create the checkbox, but do not show it by default.
+    info->reconcile_after_close = GTK_WIDGET(gtk_builder_get_object (builder, "reconcile_after_close_button"));
 
     show_update = gnc_import_Settings_get_action_update_enabled (info->user_settings);
     gnc_gen_trans_init_view (info, all_from_same_account, show_update);

commit 2dcc0b7a192614e421ca6e44e4a3f5a99bece2eb
Merge: 4fd5a2cd5 55d73851e
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Thu May 28 10:32:32 2020 +0100

    Merge Jean Laroche's branch 'add_multi_ofx_import_master' PR 697 to master


commit 55d73851e239168a3d4deebca7b08e855650c0ee
Author: jean <you at example.com>
Date:   Thu May 14 17:48:41 2020 -0700

    Implement multiple-ofx import with reconcile
    
    I had to rebase against master which included the reconcile after import and there were a few conflicts.
    So this is the new version. It includes all the recommendations made in the original PR but the code now includes the reconcile part.

diff --git a/gnucash/gnome-utils/gnc-file.c b/gnucash/gnome-utils/gnc-file.c
index 5b1d67c20..33ea97f77 100644
--- a/gnucash/gnome-utils/gnc-file.c
+++ b/gnucash/gnome-utils/gnc-file.c
@@ -60,29 +60,15 @@ static QofLogModule log_module = GNC_MOD_GUI;
 static GNCShutdownCB shutdown_cb = NULL;
 static gint save_in_progress = 0;
 
-
-/********************************************************************\
- * gnc_file_dialog                                                  *
- *   Pops up a file selection dialog (either a "Save As" or an      *
- *   "Open"), and returns the name of the file the user selected.   *
- *   (This function does not return until the user selects a file   *
- *   or presses "Cancel" or the window manager destroy button)      *
- *                                                                  *
- * Args:   title        - the title of the window                   *
- *         filters      - list of GtkFileFilters to use, will be    *
-                          freed automatically                       *
- *         default_dir  - start the chooser in this directory       *
- *         type         - what type of dialog (open, save, etc.)    *
- * Return: containing the name of the file the user selected        *
-\********************************************************************/
-
-char *
-gnc_file_dialog (GtkWindow *parent,
-                 const char * title,
-                 GList * filters,
-                 const char * starting_dir,
-                 GNCFileDialogType type
-                )
+// gnc_file_dialog_int is used both by gnc_file_dialog and gnc_file_dialog_multi
+static GSList *
+gnc_file_dialog_int (GtkWindow *parent,
+                     const char * title,
+                     GList * filters,
+                     const char * starting_dir,
+                     GNCFileDialogType type,
+                     gboolean multi
+                     )
 {
     GtkWidget *file_box;
     const char *internal_name;
@@ -91,6 +77,7 @@ gnc_file_dialog (GtkWindow *parent,
     const gchar *ok_icon = NULL;
     GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
     gint response;
+    GSList* file_name_list = NULL;
 
     ENTER(" ");
 
@@ -130,6 +117,9 @@ gnc_file_dialog (GtkWindow *parent,
                    action,
                    _("_Cancel"), GTK_RESPONSE_CANCEL,
                    NULL);
+    if (multi)
+        gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (file_box), TRUE);
+    
     if (ok_icon)
         gnc_gtk_dialog_add_button(file_box, okbutton, ok_icon, GTK_RESPONSE_ACCEPT);
     else
@@ -174,23 +164,85 @@ gnc_file_dialog (GtkWindow *parent,
 
     if (response == GTK_RESPONSE_ACCEPT)
     {
-        /* look for constructs like postgres://foo */
-        internal_name = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER (file_box));
-        if (internal_name != NULL)
+        if (multi)
         {
-            if (strstr (internal_name, "file://") == internal_name)
+            file_name_list = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (file_box));
+        }
+        else
+        {
+            /* look for constructs like postgres://foo */
+            internal_name = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER (file_box));
+            if (internal_name != NULL)
             {
-                /* nope, a local file name */
-                internal_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (file_box));
+                if (strstr (internal_name, "file://") == internal_name)
+                {
+                    /* nope, a local file name */
+                    internal_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (file_box));
+                }
+                file_name = g_strdup(internal_name);
             }
-            file_name = g_strdup(internal_name);
+            file_name_list = g_slist_append (file_name_list, file_name);
         }
     }
     gtk_widget_destroy(GTK_WIDGET(file_box));
     LEAVE("%s", file_name ? file_name : "(null)");
+    return file_name_list;
+}
+
+/********************************************************************\
+ * gnc_file_dialog                                                  *
+ *   Pops up a file selection dialog (either a "Save As" or an      *
+ *   "Open"), and returns the name of the file the user selected.   *
+ *   (This function does not return until the user selects a file   *
+ *   or presses "Cancel" or the window manager destroy button)      *
+ *                                                                  *
+ * Args:   title        - the title of the window                   *
+ *         filters      - list of GtkFileFilters to use, will be    *
+ *                        freed automatically                       *
+ *         default_dir  - start the chooser in this directory       *
+ *         type         - what type of dialog (open, save, etc.)    *
+ * Return: containing the name of the file the user selected        *
+ \********************************************************************/
+char *
+gnc_file_dialog (GtkWindow *parent,
+                 const char * title,
+                 GList * filters,
+                 const char * starting_dir,
+                 GNCFileDialogType type
+                 )
+{
+    gchar* file_name = NULL;
+    GSList* ret = gnc_file_dialog_int (parent, title, filters, starting_dir, type, FALSE);
+    if (ret)
+        file_name = g_strdup (ret->data);
+    g_slist_free_full (ret, g_free);
     return file_name;
 }
 
+/********************************************************************\
+ * gnc_file_dialog_multi                                            *
+ *   Pops up a file selection dialog (either a "Save As" or an      *
+ *   "Open"), and returns the name of the files the user selected.  *
+ *   Similar to gnc_file_dialog with allowing multi-file selection  *
+ *                                                                  *
+ * Args:   title        - the title of the window                   *
+ *         filters      - list of GtkFileFilters to use, will be    *
+ *                        freed automatically                       *
+ *         default_dir  - start the chooser in this directory       *
+ *         type         - what type of dialog (open, save, etc.)    *
+ * Return: GList containing the names of the selected files         *
+ \********************************************************************/
+
+GSList *
+gnc_file_dialog_multi (GtkWindow *parent,
+                       const char * title,
+                       GList * filters,
+                       const char * starting_dir,
+                       GNCFileDialogType type
+                       )
+{
+    return gnc_file_dialog_int (parent, title, filters, starting_dir, type, TRUE);
+}
 
 gboolean
 show_session_error (GtkWindow *parent,
diff --git a/gnucash/gnome-utils/gnc-file.h b/gnucash/gnome-utils/gnc-file.h
index eb38e705b..b038e0d00 100644
--- a/gnucash/gnome-utils/gnc-file.h
+++ b/gnucash/gnome-utils/gnc-file.h
@@ -149,6 +149,12 @@ char * gnc_file_dialog (GtkWindow *parent,
                         const char * starting_dir,
                         GNCFileDialogType type);
 
+GSList * gnc_file_dialog_multi (GtkWindow *parent,
+                                const char * title,
+                                GList * filters,
+                                const char * starting_dir,
+                                GNCFileDialogType type);
+
 gboolean gnc_file_open_file (GtkWindow *parent,
                              const char *filename,
                              gboolean open_readonly);
diff --git a/gnucash/gnome/window-reconcile.c b/gnucash/gnome/window-reconcile.c
index 1d553cec3..078e47bb2 100644
--- a/gnucash/gnome/window-reconcile.c
+++ b/gnucash/gnome/window-reconcile.c
@@ -1800,7 +1800,6 @@ recnWindowWithBalance (GtkWidget *parent, Account *account, gnc_numeric new_endi
         gnc_register_gui_component (WINDOW_RECONCILE_CM_CLASS,
                                     refresh_handler, close_handler,
                                     recnData);
-    // This window should close if we close the session.
     gnc_gui_component_set_session (recnData->component_id, gnc_get_current_session());
 
     recn_set_watches (recnData);
@@ -2118,6 +2117,15 @@ gnc_ui_reconcile_window_raise(RecnWindow * recnData)
     gtk_window_present(GTK_WINDOW(recnData->window));
 }
 
+GtkWidget*
+gnc_ui_reconcile_window_get_widget(RecnWindow * recnData)
+{
+    if (recnData == NULL || recnData->window == NULL)
+        return NULL;
+    return recnData->window;
+}
+
+
 
 /********************************************************************\
  * recn_destroy_cb                                                  *
diff --git a/gnucash/gnome/window-reconcile.h b/gnucash/gnome/window-reconcile.h
index dac318dd4..5ca431b33 100644
--- a/gnucash/gnome/window-reconcile.h
+++ b/gnucash/gnome/window-reconcile.h
@@ -63,5 +63,5 @@ RecnWindow *recnWindowWithBalance (GtkWidget *parent, Account *account,
                                    time64 statement_date);
 
 void gnc_ui_reconcile_window_raise(RecnWindow * recnData);
-
+GtkWidget* gnc_ui_reconcile_window_get_widget(RecnWindow * recnData);
 #endif /* WINDOW_RECONCILE_H */
diff --git a/gnucash/gtkbuilder/dialog-import.glade b/gnucash/gtkbuilder/dialog-import.glade
index 1490defd8..9fd5d95e7 100644
--- a/gnucash/gtkbuilder/dialog-import.glade
+++ b/gnucash/gtkbuilder/dialog-import.glade
@@ -945,6 +945,21 @@
             <property name="position">1</property>
           </packing>
         </child>
+        <child>
+            <object class="GtkCheckButton" id="reconcile_after_close_button">
+                <property name="label" translatable="yes">Reconcile after match</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+                <property name="draw_indicator">True</property>
+            </object>
+            <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+            </packing>
+        </child>
       </object>
       <packing>
         <property name="expand">False</property>
diff --git a/gnucash/import-export/import-main-matcher.c b/gnucash/import-export/import-main-matcher.c
index 786c160b8..551531276 100644
--- a/gnucash/import-export/import-main-matcher.c
+++ b/gnucash/import-export/import-main-matcher.c
@@ -69,6 +69,7 @@ struct _main_matcher_info
     GtkTreeViewColumn *account_column;
     GtkWidget         *show_account_column;
     GtkWidget         *show_matched_info;
+    GtkWidget         *reconcile_after_close;
     gboolean add_toggled;   // flag to indicate that add has been toggled to stop selection
     gint id;
 };
@@ -196,6 +197,8 @@ gboolean gnc_gen_trans_list_empty(GNCImportMainMatcher *info)
 void gnc_gen_trans_list_show_all(GNCImportMainMatcher *info)
 {
     gtk_widget_show_all (GTK_WIDGET (info->main_widget));
+    // By default, do not show this check box.
+    gnc_gen_trans_list_show_reconcile_after_close (info, FALSE, FALSE);
 }
 
 void
@@ -985,6 +988,9 @@ GNCImportMainMatcher *gnc_gen_trans_list_new (GtkWidget *parent,
     g_signal_connect (G_OBJECT(info->show_matched_info), "toggled",
                       G_CALLBACK(show_matched_info_toggled_cb), info);
 
+    // Create the checkbox, but do not show it by default.
+    info->reconcile_after_close = GTK_WIDGET(gtk_builder_get_object (builder, "reconcile_after_close_button"));
+
     show_update = gnc_import_Settings_get_action_update_enabled (info->user_settings);
     gnc_gen_trans_init_view (info, all_from_same_account, show_update);
     heading_label = GTK_WIDGET(gtk_builder_get_object (builder, "heading_label"));
@@ -1006,7 +1012,7 @@ GNCImportMainMatcher *gnc_gen_trans_list_new (GtkWidget *parent,
     gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, info);
 
     g_object_unref (G_OBJECT(builder));
-    
+
     // Register this UI, it needs to be closed when the session is closed.
     info->id = gnc_register_gui_component (IMPORT_MAIN_MATCHER_CM_CLASS,
                                            NULL, /* no refresh handler */
@@ -1069,6 +1075,8 @@ GNCImportMainMatcher * gnc_gen_trans_assist_new (GtkWidget *parent,
     g_signal_connect (G_OBJECT(info->show_matched_info), "toggled",
                       G_CALLBACK(show_matched_info_toggled_cb), info);
 
+    info->reconcile_after_close = GTK_WIDGET(gtk_builder_get_object (builder, "reconcile_when_close_button"));
+
     show_update = gnc_import_Settings_get_action_update_enabled (info->user_settings);
     gnc_gen_trans_init_view (info, all_from_same_account, show_update);
     heading_label = GTK_WIDGET(gtk_builder_get_object (builder, "heading_label"));
@@ -1448,6 +1456,18 @@ void gnc_gen_trans_list_add_trans (GNCImportMainMatcher *gui, Transaction *trans
     return;
 }/* end gnc_import_add_trans() */
 
+void gnc_gen_trans_list_show_reconcile_after_close(GNCImportMainMatcher *info, gboolean reconcile_after_close, gboolean active)
+{
+    gtk_widget_set_visible (info->reconcile_after_close,reconcile_after_close);
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (info->reconcile_after_close), active);
+}
+
+GtkWidget*
+gnc_gen_trans_list_get_reconcile_widget(GNCImportMainMatcher *info)
+{
+    return info->reconcile_after_close;
+}
+
 void gnc_gen_trans_list_add_trans_with_ref_id (GNCImportMainMatcher *gui, Transaction *trans, guint32 ref_id)
 {
     GNCImportTransInfo * transaction_info = NULL;
diff --git a/gnucash/import-export/import-main-matcher.h b/gnucash/import-export/import-main-matcher.h
index 7e1e33dd5..fdb84a69d 100644
--- a/gnucash/import-export/import-main-matcher.h
+++ b/gnucash/import-export/import-main-matcher.h
@@ -196,5 +196,8 @@ gboolean gnc_gen_trans_list_empty(GNCImportMainMatcher *info);
  */
 void gnc_gen_trans_list_show_all(GNCImportMainMatcher *info);
 
+void gnc_gen_trans_list_show_reconcile_after_close(GNCImportMainMatcher *info, gboolean reconcile_after_close, gboolean active);
+GtkWidget* gnc_gen_trans_list_get_reconcile_widget(GNCImportMainMatcher *info);
+
 #endif
 /**@}*/
diff --git a/gnucash/import-export/ofx/gnc-ofx-import.c b/gnucash/import-export/ofx/gnc-ofx-import.c
index 578f20d49..ff5292a1e 100644
--- a/gnucash/import-export/ofx/gnc-ofx-import.c
+++ b/gnucash/import-export/ofx/gnc-ofx-import.c
@@ -67,16 +67,20 @@ static Account *ofx_parent_account = NULL;
 // Structure we use to gather information about statement balance/account etc.
 typedef struct _ofx_info
 {
-    gint num_trans_processed;
-    GSList* statement;
     GtkWindow* parent;
     GNCImportMainMatcher *gnc_ofx_importer_gui;
     Account *last_import_account;
     Account *last_investment_account;
     Account *last_income_account;
-    GList *created_commodites   ;
+    gint num_trans_processed;               // Number of transactions processed
+    struct OfxStatementData* statement;     // Statement, if any
+    gboolean run_reconcile;                 // If TRUE the reconcile window is opened after matching.
+    GList *created_commodites;
+    GSList* file_list;                      // List of OFX files to import
 } ofx_info ;
 
+GList *ofx_created_commodites = NULL;
+
 /*
 int ofx_proc_status_cb(struct OfxStatusData data)
 {
@@ -118,13 +122,11 @@ set_associated_income_account(Account* investment_account,
     xaccAccountCommitEdit(investment_account);
 }
 
-int ofx_proc_statement_cb(struct OfxStatementData data,
-                          void *statement_user_data);
-int ofx_proc_security_cb(const struct OfxSecurityData data,
-                         void *security_user_data);
+int ofx_proc_statement_cb (struct OfxStatementData data, void * statement_user_data);
+int ofx_proc_security_cb (const struct OfxSecurityData data, void * security_user_data);
 int ofx_proc_transaction_cb (struct OfxTransactionData data, void *user_data);
-int ofx_proc_account_cb(struct OfxAccountData data, void * account_user_data);
-static double ofx_get_investment_amount(const struct OfxTransactionData* data);
+int ofx_proc_account_cb (struct OfxAccountData data, void * account_user_data);
+static double ofx_get_investment_amount (const struct OfxTransactionData* data);
 
 static const gchar *gnc_ofx_ttype_to_string(TransactionType t)
 {
@@ -917,11 +919,10 @@ int ofx_proc_transaction_cb(struct OfxTransactionData data, void *user_data)
 int ofx_proc_statement_cb (struct OfxStatementData data, void * statement_user_data)
 {
     ofx_info* info = (ofx_info*) statement_user_data;
-    struct OfxStatementData* statement = g_new (struct OfxStatementData, 1);
-    *statement = data;
-    info->statement = g_slist_append (info->statement, statement);
+    info->statement = g_new (struct OfxStatementData, 1);
+    *info->statement = data;
     return 0;
-}//end ofx_proc_statement()
+}
 
 
 int ofx_proc_account_cb(struct OfxAccountData data, void * account_user_data)
@@ -1003,22 +1004,23 @@ int ofx_proc_account_cb(struct OfxAccountData data, void * account_user_data)
 
         gnc_utf8_strip_invalid(data.account_name);
         gnc_utf8_strip_invalid(data.account_id);
-        account_description = g_strdup_printf( /* This string is a default account
+        account_description = g_strdup_printf (/* This string is a default account
                                                   name. It MUST NOT contain the
                                                   character ':' anywhere in it or
                                                   in any translation.  */
-                                  "%s \"%s\"",
-                                  account_type_name,
-                                  data.account_name);
-        /* use the info->parent as import-matcher is not displayed yet */
-        account = gnc_import_select_account (GTK_WIDGET(info->parent),
+                                               "%s \"%s\"",
+                                               account_type_name,
+                                               data.account_name);
+        account = gnc_import_select_account (gnc_gen_trans_list_widget(info->gnc_ofx_importer_gui),
                                              data.account_id, 1,
                                              account_description, default_commodity,
-                                             default_type, info->last_import_account, NULL);
+                                             default_type, NULL, NULL);
+
         if (account)
         {
             info->last_import_account = account;
         }
+
         g_free(account_description);
     }
     else
@@ -1051,6 +1053,142 @@ double ofx_get_investment_amount(const struct OfxTransactionData* data)
     }
 }
 
+// Forward declaration, required because several static functions depend on one-another.
+static void
+gnc_file_ofx_import_process_file (ofx_info* info);
+
+// gnc_ofx_process_next_file processes the next file in the info->file_list.
+static void
+gnc_ofx_process_next_file (GtkDialog *dialog, gpointer user_data)
+{
+    ofx_info* info = (ofx_info*) user_data;
+    // Free the statement (if it was allocated)
+    g_free (info->statement);
+    info->statement = NULL;
+
+    // Done with the previous OFX file, process the next one if any.
+    info->file_list = g_slist_delete_link(info->file_list, info->file_list);
+    if (info->file_list)
+        gnc_file_ofx_import_process_file (info);
+    else
+    {
+        // Final cleanup.
+        g_free (info);
+        if (ofx_created_commodites)
+        {
+            /* FIXME: Present some result window about the newly created
+             * commodities */
+            g_warning ("Created %d new commodities during import", g_list_length(ofx_created_commodites));
+            g_list_free (ofx_created_commodites);
+            ofx_created_commodites = NULL;
+        }
+        else
+        {
+            //g_warning("No new commodities created");
+        }
+    }
+}
+
+
+// This callback is called when the user is done matching transactions.
+static void
+gnc_ofx_match_done (GtkDialog *dialog, gint response_id, gpointer user_data)
+{
+    ofx_info* info = (ofx_info*) user_data;
+
+    if (response_id == GTK_RESPONSE_OK && info->run_reconcile && info->statement)
+    {
+        // Open a reconcile window.
+        Account* account = gnc_import_select_account (gnc_gen_trans_list_widget(info->gnc_ofx_importer_gui),
+                                                      info->statement->account_id,
+                                                      0, NULL, NULL, ACCT_TYPE_NONE, NULL, NULL);
+        if (account && info->statement->ledger_balance_valid)
+        {
+            gnc_numeric value = double_to_gnc_numeric (info->statement->ledger_balance,
+                                                       xaccAccountGetCommoditySCU (account),
+                                                       GNC_HOW_RND_ROUND_HALF_UP);
+            RecnWindow* rec_window = recnWindowWithBalance (GTK_WIDGET (info->parent), account, value, info->statement->ledger_balance_date);
+            // Connect to destroy, at which point we'll process the next OFX file..
+            g_signal_connect (G_OBJECT (gnc_ui_reconcile_window_get_widget (rec_window)), "destroy", G_CALLBACK (gnc_ofx_process_next_file), info);
+        }
+    }
+    else
+    {
+        gtk_widget_hide (GTK_WIDGET(dialog));
+        gnc_ofx_process_next_file (dialog, info);
+    }
+}
+
+// This callback is triggered when the user checks or unchecks the reconcile after match
+// check box in the matching dialog.
+static void
+reconcile_when_close_toggled_cb (GtkToggleButton *togglebutton, ofx_info* info)
+{
+    info->run_reconcile = gtk_toggle_button_get_active (togglebutton);
+}
+
+// Aux function to process the OFX file in info->file_list
+static void
+gnc_file_ofx_import_process_file (ofx_info* info)
+{
+    LibofxContextPtr libofx_context = libofx_get_new_context();
+    char* filename = NULL;
+    char * selected_filename = NULL;
+    GtkWindow *parent = info->parent;
+
+    if (info->file_list == NULL)
+        return;
+
+    filename = info->file_list->data;
+
+#ifdef G_OS_WIN32
+    selected_filename = g_win32_locale_filename_from_utf8 (filename);
+    g_free (filename);
+#else
+    selected_filename = filename;
+#endif
+    DEBUG("Filename found: %s", selected_filename);
+
+    // Reset the reconciliation information.
+    info->num_trans_processed = 0;
+    info->statement = NULL;
+
+    /* Initialize libofx and set the callbacks*/
+    ofx_set_statement_cb (libofx_context, ofx_proc_statement_cb, info);
+    ofx_set_account_cb (libofx_context, ofx_proc_account_cb, info);
+    ofx_set_transaction_cb (libofx_context, ofx_proc_transaction_cb, info);
+    ofx_set_security_cb (libofx_context, ofx_proc_security_cb, info);
+    /*ofx_set_status_cb(libofx_context, ofx_proc_status_cb, 0);*/
+
+    // Create the match dialog, and run the ofx file through the importer.
+    info->gnc_ofx_importer_gui = gnc_gen_trans_list_new (GTK_WIDGET(parent), NULL, TRUE, 42, FALSE);
+    libofx_proc_file (libofx_context, selected_filename, AUTODETECT);
+
+    // See whether the view has anything in it and warn the user if not.
+    if(gnc_gen_trans_list_empty (info->gnc_ofx_importer_gui))
+    {
+        gnc_gen_trans_list_delete (info->gnc_ofx_importer_gui);
+        if(info->num_trans_processed)
+            gnc_info_dialog (parent, _("OFX file(s) imported, %d transactions processed, no transactions to match"), info->num_trans_processed);
+        // Process the next OFX file if any.
+        gnc_ofx_process_next_file (NULL, info);
+    }
+    else
+    {
+        // Show the match dialog and connect to the "response" signal so we can trigger a reconcile if the user clicks OK when done matching transactions.
+        g_signal_connect (G_OBJECT (gnc_gen_trans_list_widget (info->gnc_ofx_importer_gui)), "response", G_CALLBACK (gnc_ofx_match_done), info);
+        gnc_gen_trans_list_show_all (info->gnc_ofx_importer_gui);
+        
+        // Show or hide the check box for reconciling after match, depending on whether a statement was received.
+        gnc_gen_trans_list_show_reconcile_after_close (info->gnc_ofx_importer_gui, info->statement != NULL, info->run_reconcile);
+        // Finally connect to the reconcile after match check box so we can be notified if the user wants/does not want to reconcile.
+        g_signal_connect (G_OBJECT (gnc_gen_trans_list_get_reconcile_widget (info->gnc_ofx_importer_gui)), "toggled",
+                          G_CALLBACK (reconcile_when_close_toggled_cb), info);
+    }
+    g_free(selected_filename);
+}
+
+// The main import function. Starts the chain of file imports (if there are several)
 void gnc_file_ofx_import (GtkWindow *parent)
 {
     extern int ofx_PARSER_msg;
@@ -1059,14 +1197,14 @@ void gnc_file_ofx_import (GtkWindow *parent)
     extern int ofx_ERROR_msg;
     extern int ofx_INFO_msg;
     extern int ofx_STATUS_msg;
-    char *selected_filename;
+    GSList* selected_filenames = NULL;
     char *default_dir;
     LibofxContextPtr libofx_context = libofx_get_new_context();
     GList *filters = NULL;
+    GSList* iter = NULL;
+    ofx_info* info = NULL;
     GtkFileFilter* filter = gtk_file_filter_new ();
-    GSList *iter = NULL;
-    // Create the structure we're using to gather reconciliation information.
-    ofx_info info = {0, NULL, parent, NULL, NULL, NULL, NULL, NULL};
+    
 
     ofx_PARSER_msg = false;
     ofx_DEBUG_msg = false;
@@ -1082,97 +1220,34 @@ void gnc_file_ofx_import (GtkWindow *parent)
     gtk_file_filter_add_pattern (filter, "*.[oqOQ][fF][xX]");
     filters = g_list_prepend( filters, filter );
 
-    selected_filename = gnc_file_dialog(parent,
-                                        _("Select an OFX/QFX file to process"),
-                                        filters,
-                                        default_dir,
-                                        GNC_FILE_DIALOG_IMPORT);
+    selected_filenames = gnc_file_dialog_multi (parent,
+                                                _("Select one or multiple OFX/QFX file(s) to process"),
+                                                filters,
+                                                default_dir,
+                                                GNC_FILE_DIALOG_IMPORT);
     g_free(default_dir);
 
-    if (selected_filename != NULL)
+    if (selected_filenames)
     {
-#ifdef G_OS_WIN32
-        gchar *conv_name;
-#endif
-
         /* Remember the directory as the default. */
-        default_dir = g_path_get_dirname(selected_filename);
+        default_dir = g_path_get_dirname(selected_filenames->data);
         gnc_set_default_directory(GNC_PREFS_GROUP, default_dir);
         g_free(default_dir);
 
-        /*strncpy(file,selected_filename, 255);*/
-        DEBUG("Filename found: %s", selected_filename);
-
-        /* Create the Generic transaction importer GUI. */
-        info.gnc_ofx_importer_gui = gnc_gen_trans_list_new (GTK_WIDGET(parent), NULL, FALSE, 42, FALSE);
-
         /* Look up the needed preferences */
         auto_create_commodity =
             gnc_prefs_get_bool (GNC_PREFS_GROUP_IMPORT, GNC_PREF_AUTO_COMMODITY);
 
-        /* Initialize libofx and set the callbacks*/
-        ofx_set_statement_cb (libofx_context, ofx_proc_statement_cb, &info);
-        ofx_set_account_cb (libofx_context, ofx_proc_account_cb, &info);
-        ofx_set_transaction_cb (libofx_context, ofx_proc_transaction_cb, &info);
-        ofx_set_security_cb (libofx_context, ofx_proc_security_cb, &info);
-        /*ofx_set_status_cb(libofx_context, ofx_proc_status_cb, 0);*/
-
-#ifdef G_OS_WIN32
-        conv_name = g_win32_locale_filename_from_utf8(selected_filename);
-        g_free(selected_filename);
-        selected_filename = conv_name;
-#endif
-
-        DEBUG("Opening selected file");
-        libofx_proc_file(libofx_context, selected_filename, AUTODETECT);
-        // See whether the view has anything in it and warn the user if not.
-        if(gnc_gen_trans_list_empty(info.gnc_ofx_importer_gui))
-        {
-            gnc_gen_trans_list_delete (info.gnc_ofx_importer_gui);
-            if(info.num_trans_processed)
-                gnc_info_dialog (parent, _("OFX file imported, %d transactions processed, no transactions to match"), info.num_trans_processed);
-        }
-        else
-        {
-            gnc_gen_trans_list_show_all(info.gnc_ofx_importer_gui);
-        }
-        // Open a reconcile window for each balance statement found.
-        for (iter=info.statement; iter; iter=iter->next)
-        {
-            struct OfxStatementData* statement = (struct OfxStatementData*) iter->data;
-            Account* account = gnc_import_select_account (gnc_gen_trans_list_widget(info.gnc_ofx_importer_gui),
-                                                          statement->account_id,
-                                                          0, NULL, NULL, ACCT_TYPE_NONE,
-                                                          NULL, NULL);
-            if (account)
-            {
-                if (statement->ledger_balance_valid)
-                {
-                    gnc_numeric value =
-                         double_to_gnc_numeric (statement->ledger_balance,
-                                                xaccAccountGetCommoditySCU (account),
-                                                GNC_HOW_RND_ROUND_HALF_UP);
-                    recnWindowWithBalance (GTK_WIDGET (parent),
-                                           account,
-                                           value,
-                                           statement->ledger_balance_date);
-                }
-            }
-        }
-        g_free (selected_filename);
-        g_slist_free_full (info.statement,g_free);
-    }
-
-    if (info.created_commodites)
-    {
-        /* FIXME: Present some result window about the newly created
-         * commodities */
-        g_warning("Created %d new commodities during import", g_list_length(info.created_commodites));
-        g_list_free(info.created_commodites);
-    }
-    else
-    {
-        //g_warning("No new commodities created");
+        DEBUG("Opening selected file(s)");
+        // Create the structure that holds the list of files to process and the statement info.
+        info = g_new(ofx_info,1);
+        info->num_trans_processed = 0;
+        info->statement = NULL;
+        info->parent = parent;
+        info->run_reconcile = FALSE;
+        info->file_list = selected_filenames;
+        // Call the aux import function.
+        gnc_file_ofx_import_process_file (info);
     }
 }
 



Summary of changes:
 gnucash/gnome-utils/gnc-file.c              | 112 ++++++++---
 gnucash/gnome-utils/gnc-file.h              |   6 +
 gnucash/gnome/window-reconcile.c            |  10 +-
 gnucash/gnome/window-reconcile.h            |   4 +-
 gnucash/gtkbuilder/dialog-import.glade      |  15 ++
 gnucash/import-export/import-main-matcher.c |  22 +++
 gnucash/import-export/import-main-matcher.h |  14 ++
 gnucash/import-export/ofx/gnc-ofx-import.c  | 292 ++++++++++++++++++----------
 8 files changed, 339 insertions(+), 136 deletions(-)



More information about the gnucash-changes mailing list