gnucash maint: Multiple changes pushed

Robert Fewell bobit at code.gnucash.org
Fri Mar 29 16:43:19 EDT 2019


Updated	 via  https://github.com/Gnucash/gnucash/commit/358468d0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2cb34d48 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/32e64979 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0c667822 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/63dfd181 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/be8216e2 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/bed7f99d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5eb6f76e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/89d2cde9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/22a7a05d (commit)
	 via  https://github.com/Gnucash/gnucash/commit/25277242 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/167d2096 (commit)
	from  https://github.com/Gnucash/gnucash/commit/4cb29101 (commit)



commit 358468d036b45f823aaeed1a7dcd2aa2e35bed4d
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Mar 29 17:39:36 2019 +0000

    Use a hash table to track transactions for associate dialog
    
    When generating the list of transactions to look at, a GList was used to
     keep a list of transactions already processed. Change this to use a
     hash table instead as it is quicker.

diff --git a/gnucash/gnome/dialog-trans-assoc.c b/gnucash/gnome/dialog-trans-assoc.c
index d29cdfa81..8be470317 100644
--- a/gnucash/gnome/dialog-trans-assoc.c
+++ b/gnucash/gnome/dialog-trans-assoc.c
@@ -43,7 +43,7 @@
 #include "Account.h"
 
 #define DIALOG_ASSOC_CM_CLASS    "dialog-trans-assoc"
-#define GNC_PREFS_GROUP         "dialogs.trans-assoc"
+#define GNC_PREFS_GROUP          "dialogs.trans-assoc"
 
 /** Enumeration for the tree-store */
 enum GncAssocColumn {DATE_TRANS, DESC_TRANS, URI_U, AVAILABLE, URI_SPLIT, URI, URI_RELATIVE};
@@ -373,7 +373,8 @@ get_trans_info (AssocDialog *assoc_dialog)
     GList        *accts, *ptr;
     GtkTreeModel *model;
     GtkTreeIter   iter;
-    GList        *splits, *trans_list = NULL;
+    GList        *splits;
+    GHashTable   *trans_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
 
     /* Get list of Accounts */
     accts = gnc_account_get_descendants_sorted (root);
@@ -399,8 +400,8 @@ get_trans_info (AssocDialog *assoc_dialog)
             Transaction *trans = xaccSplitGetParent (split);
             const gchar *uri;
 
-            // Look for trans already in trans_list
-            if (g_list_find (trans_list, trans) != NULL)
+            // Look for trans already in trans_hash
+            if (g_hash_table_lookup (trans_hash, trans))
                 continue;
 
             // fix an earlier error when storing relative paths in version 3.3
@@ -433,7 +434,7 @@ get_trans_info (AssocDialog *assoc_dialog)
                 g_free (uri_u);
                 g_free (scheme);
             }
-            trans_list = g_list_prepend (trans_list, trans); // add trans to trans_list
+            g_hash_table_insert (trans_hash, trans, trans); // add trans to trans_hash
         }
         qof_query_destroy (query);
         g_list_free (splits);
@@ -443,8 +444,8 @@ get_trans_info (AssocDialog *assoc_dialog)
     gtk_tree_view_set_model (GTK_TREE_VIEW(assoc_dialog->view), model);
     g_object_unref(G_OBJECT(model));
 
+    g_hash_table_destroy (trans_hash);
     g_list_free (accts);
-    g_list_free (trans_list);
 }
 
 static void

commit 2cb34d48741043128d3a8ccebf7fcaabb120db3a
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Mar 29 17:38:06 2019 +0000

    Minor changes to gnucash-sheet and source layout
    
    These changes were concerning the transaction associations functions

diff --git a/gnucash/register/register-gnome/gnucash-sheet.c b/gnucash/register/register-gnome/gnucash-sheet.c
index 9715fe640..abfaf6039 100644
--- a/gnucash/register/register-gnome/gnucash-sheet.c
+++ b/gnucash/register/register-gnome/gnucash-sheet.c
@@ -2496,7 +2496,7 @@ gnucash_get_style_classes (GnucashSheet *sheet, GtkStyleContext *stylectxt,
         if (sheet->use_gnc_color_theme) // only add this class if builtin colors used
             gtk_style_context_add_class (stylectxt, "register-foreground");
     }
-    
+
     switch (field_type)
     {
     default:
@@ -2694,15 +2694,18 @@ gnucash_sheet_tooltip (GtkWidget  *widget, gint x, gint y,
     tooltip_text = gnc_table_get_tooltip (table, virt_loc);
 
     // if tooltip_text empty, clear tooltip and return FALSE
-    if ((tooltip_text == NULL) || (g_strcmp0 (tooltip_text,"") == 0))
+    if (!tooltip_text || (g_strcmp0 (tooltip_text,"") == 0))
     {
         gtk_tooltip_set_text (tooltip, NULL);
         return FALSE;
     }
 
     block = gnucash_sheet_get_block (sheet, virt_loc.vcell_loc);
-    if (block == NULL)
+    if (!block)
+    {
+        g_free (tooltip_text);
         return FALSE;
+    }
 
     bx = block->origin_x;
     by = block->origin_y;
@@ -2712,15 +2715,15 @@ gnucash_sheet_tooltip (GtkWidget  *widget, gint x, gint y,
             virt_loc.phys_row_offset, virt_loc.phys_col_offset,
             &cx, &cy, &cw, &ch);
 
-     rect.x = cx + bx - hscroll_val;
-     rect.y = cy + by - vscroll_val;
-     rect.width = cw;
-     rect.height = ch;
+    rect.x = cx + bx - hscroll_val;
+    rect.y = cy + by - vscroll_val;
+    rect.width = cw;
+    rect.height = ch;
 
-     gtk_tooltip_set_tip_area (tooltip, &rect);
-     gtk_tooltip_set_text (tooltip, tooltip_text);
-     g_free (tooltip_text);
-     return TRUE;
+    gtk_tooltip_set_tip_area (tooltip, &rect);
+    gtk_tooltip_set_text (tooltip, tooltip_text);
+    g_free (tooltip_text);
+    return TRUE;
 }
 
 

commit 32e64979d0f9c785641fbb8e8d2f62de1ab806db
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Mar 29 17:37:27 2019 +0000

    Change the associate tooltip function
    
    Change the tooltip function to use the in house uri functions.

diff --git a/gnucash/register/ledger-core/split-register-model.c b/gnucash/register/ledger-core/split-register-model.c
index dd8c1032b..287891f7d 100644
--- a/gnucash/register/ledger-core/split-register-model.c
+++ b/gnucash/register/ledger-core/split-register-model.c
@@ -30,6 +30,8 @@
 #include "gnc-engine.h"
 #include "gnc-prefs.h"
 #include "gnc-ui.h"
+#include "gnc-uri-utils.h"
+#include "gnc-filepath-utils.h"
 #include "gnc-warnings.h"
 #include "pricecell.h"
 #include "recncell.h"
@@ -552,23 +554,37 @@ gnc_split_register_get_associate_tooltip (VirtualLocation virt_loc,
     uri = xaccTransGetAssociation (trans);
 
     // Check for uri is empty or NULL
-    if (g_strcmp0 (uri, "") != 0 && g_strcmp0 (uri, NULL) != 0)
+    if (uri && *uri != '\0')
     {
-        gboolean valid_path_head = FALSE;
-        gchar *path_head = gnc_prefs_get_string (GNC_PREFS_GROUP_GENERAL, "assoc-head");
+        gchar *scheme = gnc_uri_get_scheme (uri);
+        gchar *file_path = NULL;
 
-        if ((path_head != NULL) && (g_strcmp0 (path_head, "") != 0)) // not default entry
-            valid_path_head = TRUE;
-
-        if (valid_path_head && g_str_has_prefix (uri,"file:/") && !g_str_has_prefix (uri,"file://"))
+        if (!scheme) // relative path
         {
-            const gchar *part = uri + strlen ("file:");
-            gchar *new_uri = g_strconcat (path_head, part, NULL);
+            gchar *path_head = gnc_prefs_get_string (GNC_PREFS_GROUP_GENERAL, "assoc-head");
+
+            if (path_head && *path_head != '\0') // not default entry
+                file_path = gnc_file_path_absolute (gnc_uri_get_path (path_head), uri);
+            else
+                file_path = gnc_file_path_absolute (NULL, uri);
+
             g_free (path_head);
-            return g_strdup (new_uri);
         }
+
+        if (gnc_uri_is_file_scheme (scheme)) // absolute path
+            file_path = gnc_uri_get_path (uri);
+
+        g_free (scheme);
+
+        if (!file_path)
+            return g_uri_unescape_string (uri, NULL);
         else
-            return g_strdup (uri);
+        {
+            gchar *file_uri_u = g_uri_unescape_string (file_path, NULL);
+            const gchar *filename = gnc_uri_get_path (file_uri_u);
+            g_free (file_uri_u);
+            return g_strdup (filename);
+        }
     }
     else
         return NULL;
@@ -824,12 +840,16 @@ gnc_split_register_get_associate_entry (VirtualLocation virt_loc,
     uri = xaccTransGetAssociation (trans);
 
     // Check for uri is empty or NULL
-    if (g_strcmp0 (uri, "") != 0 && g_strcmp0 (uri, NULL) != 0)
+    if (uri && g_strcmp0 (uri, "") != 0)
     {
-        if (g_str_has_prefix (uri, "file:"))
+        gchar *scheme = gnc_uri_get_scheme (uri);
+
+        if (!scheme || g_strcmp0 (scheme, "file") == 0)
             associate = 'f';
         else
             associate = 'w';
+
+        g_free (scheme);
     }
     else
         associate = ' ';

commit 0c667822cc85c01ed50591ac85b05f49af36e959
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Mar 29 17:36:53 2019 +0000

    Change dialog-trans-assoc.glade
    
    Make the association column to be the one that expands and also set the
    ellipsize to the start for the renderer.

diff --git a/gnucash/gnome/dialog-trans-assoc.c b/gnucash/gnome/dialog-trans-assoc.c
index d0c9c7865..d29cdfa81 100644
--- a/gnucash/gnome/dialog-trans-assoc.c
+++ b/gnucash/gnome/dialog-trans-assoc.c
@@ -525,7 +525,6 @@ gnc_assoc_dialog_create (GtkWindow *parent, AssocDialog *assoc_dialog)
     gtk_tree_view_column_set_title (tree_column, _("Relative"));
     gtk_tree_view_append_column (GTK_TREE_VIEW(assoc_dialog->view), tree_column);
     gtk_tree_view_column_set_alignment (tree_column, 0.5);
-    gtk_tree_view_column_set_expand (tree_column, TRUE);
     cr = gtk_cell_renderer_pixbuf_new();
     gtk_tree_view_column_pack_start (tree_column, cr, TRUE);
     // connect 'active' and set 'xalign' property of the cell renderer
@@ -535,6 +534,10 @@ gnc_assoc_dialog_create (GtkWindow *parent, AssocDialog *assoc_dialog)
     g_signal_connect (assoc_dialog->view, "row-activated",
                       G_CALLBACK(row_selected_cb), (gpointer)assoc_dialog);
 
+    // set the Associate column to be the one that expands
+    tree_column = GTK_TREE_VIEW_COLUMN(gtk_builder_get_object (builder, "uri-entry"));
+    gtk_tree_view_column_set_expand (tree_column, TRUE);
+
     // Set grid lines option to preference
     gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(assoc_dialog->view), gnc_tree_view_get_grid_lines_pref ());
 
diff --git a/gnucash/gtkbuilder/dialog-trans-assoc.glade b/gnucash/gtkbuilder/dialog-trans-assoc.glade
index f10e9b9eb..c25f99238 100644
--- a/gnucash/gtkbuilder/dialog-trans-assoc.glade
+++ b/gnucash/gtkbuilder/dialog-trans-assoc.glade
@@ -152,7 +152,9 @@
                     <property name="title" translatable="yes">Association</property>
                     <property name="alignment">0.5</property>
                     <child>
-                      <object class="GtkCellRendererText" id="cellrenderertext3"/>
+                      <object class="GtkCellRendererText" id="cellrenderertext3">
+                        <property name="ellipsize">start</property>
+                      </object>
                       <attributes>
                         <attribute name="text">2</attribute>
                       </attributes>

commit 63dfd18111eb1dcd3d4c972cf65a75cfd939aa5c
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Mar 29 17:36:20 2019 +0000

    Update dialog-trans-assoc.c
    
    Update the transaction association dialog to use the in house uri
    functions and also fix a problem that mainly affects Windows when you
    associate a file that is in the root of the associated path head.

diff --git a/gnucash/gnome/dialog-trans-assoc.c b/gnucash/gnome/dialog-trans-assoc.c
index b8c840bb1..d0c9c7865 100644
--- a/gnucash/gnome/dialog-trans-assoc.c
+++ b/gnucash/gnome/dialog-trans-assoc.c
@@ -38,6 +38,8 @@
 #include "gnc-ui.h"
 #include "gnc-ui-util.h"
 #include "gnc-gnome-utils.h"
+#include "gnc-uri-utils.h"
+#include "gnc-filepath-utils.h"
 #include "Account.h"
 
 #define DIALOG_ASSOC_CM_CLASS    "dialog-trans-assoc"
@@ -51,7 +53,7 @@ typedef struct
     GtkWidget    *window;
     GtkWidget    *view;
     const gchar  *path_head;
-    gboolean      valid_path_head;
+    gboolean      path_head_set;
 }AssocDialog;
 
 /* This static indicates the debugging module that this .o belongs to.  */
@@ -76,7 +78,7 @@ gnc_assoc_dialog_window_destroy_cb (GtkWidget *object, gpointer user_data)
 }
 
 static gboolean
-gnc_assoc_dialog_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
+gnc_assoc_dialog_window_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
 {
     AssocDialog *assoc_dialog = user_data;
 
@@ -139,37 +141,40 @@ assoc_dialog_sort (AssocDialog *assoc_dialog)
     gtk_tree_sortable_set_sort_column_id (sortable, URI, order);
 }
 
-static const gchar *
-convert_uri_relative_to_uri (AssocDialog *assoc_dialog, const gchar *uri)
+static gchar *
+convert_uri_to_filename (AssocDialog *assoc_dialog, const gchar *uri, gchar *scheme)
 {
-    const gchar *new_uri;
+    gchar *file_path = NULL;
 
-    if (assoc_dialog->valid_path_head && g_str_has_prefix (uri,"file:/") &&
-        !g_str_has_prefix (uri,"file://")) // path is relative
+    if (!scheme) // relative path
     {
-        const gchar *part = uri + strlen ("file:");
-        new_uri = g_strconcat (assoc_dialog->path_head, part, NULL);
+        if (assoc_dialog->path_head_set) // not default entry
+            file_path = gnc_file_path_absolute (gnc_uri_get_path (assoc_dialog->path_head), uri);
+        else
+            file_path = gnc_file_path_absolute (NULL, uri);
     }
-    else
-        new_uri = g_strdup (uri);
 
-    return new_uri;
+    if (gnc_uri_is_file_scheme (scheme)) // absolute path
+        file_path = gnc_uri_get_path (uri);
+
+    return file_path;
 }
 
 static gchar *
-convert_uri_to_filename (AssocDialog *assoc_dialog, const gchar *uri)
+convert_uri_to_unescaped (AssocDialog *assoc_dialog, const gchar *uri, gchar *scheme)
 {
-    const gchar *new_uri = convert_uri_relative_to_uri (assoc_dialog, uri);
-    gchar *filename = g_filename_from_uri (new_uri, NULL, NULL);
+    gchar *uri_u = NULL;
+    gchar *file_path = NULL;
 
-    return filename;
-}
+    // if scheme is null or 'file' we should get a file path
+    file_path = convert_uri_to_filename (assoc_dialog, uri, scheme);
 
-static gchar *
-convert_uri_to_unescaped (AssocDialog *assoc_dialog, const gchar *uri)
-{
-    const gchar *new_uri = convert_uri_relative_to_uri (assoc_dialog, uri);
-    gchar *uri_u = g_uri_unescape_string (new_uri, NULL);
+    if (file_path)
+        uri_u = g_uri_unescape_string (file_path, NULL);
+    else
+        uri_u = g_uri_unescape_string (uri, NULL);
+
+    g_free (file_path);
 
     return uri_u;
 }
@@ -195,12 +200,15 @@ assoc_dialog_update (AssocDialog *assoc_dialog)
         GSocketConnectable *conn;
         gchar              *uri;
         gchar              *filename;
+        gchar              *scheme;
 
         gtk_tree_model_get (model, &iter, URI, &uri, -1);
 
-        filename = convert_uri_to_filename (assoc_dialog, uri);
+        scheme = gnc_uri_get_scheme (uri);
 
-        if (filename != NULL)
+        filename = convert_uri_to_unescaped (assoc_dialog, uri, scheme);
+
+        if (!scheme || gnc_uri_is_file_scheme (scheme))
         {
             if (g_file_test (filename, G_FILE_TEST_EXISTS))
                 gtk_list_store_set (GTK_LIST_STORE(model), &iter, AVAILABLE, _("File Found"), -1);
@@ -209,18 +217,21 @@ assoc_dialog_update (AssocDialog *assoc_dialog)
         }
         else
         {
+            gchar *escaped = g_uri_escape_string (uri, ":/.", TRUE);
             nm = g_network_monitor_get_default ();
-            conn = g_network_address_parse_uri (uri, 80, NULL);
+            conn = g_network_address_parse_uri (escaped, 80, NULL);
 
-            if (conn != NULL)
+            if (conn)
             {
                 if (g_network_monitor_can_reach (nm, conn, NULL, NULL))
                     gtk_list_store_set (GTK_LIST_STORE(model), &iter, AVAILABLE, _("Address Found"), -1);
                 else
                     gtk_list_store_set (GTK_LIST_STORE(model), &iter, AVAILABLE, _("Address Not Found"), -1);
             }
+            g_free (escaped);
         }
         g_free (uri);
+        g_free (scheme);
         g_free (filename);
         valid = gtk_tree_model_iter_next (model, &iter);
     }
@@ -268,13 +279,32 @@ row_selected_cb (GtkTreeView *view, GtkTreePath *path,
     // Open associated link
     if (gtk_tree_view_get_column (GTK_TREE_VIEW(assoc_dialog->view), URI_U) == col)
     {
-        const gchar *uri_out = convert_uri_relative_to_uri (assoc_dialog, uri);
-        gchar *uri_scheme = g_uri_parse_scheme (uri_out);
+        const gchar *uri_out = NULL;
+        gchar *uri_out_scheme;
+        gchar *uri_scheme = gnc_uri_get_scheme (uri);
+        gchar *file_path = NULL;
 
-        if (uri_scheme != NULL) // make sure we have a schme entry
+        if (!uri_scheme) // relative path
+        {
+            if (assoc_dialog->path_head_set) // not default entry
+                file_path = gnc_file_path_absolute (gnc_uri_get_path (assoc_dialog->path_head), uri);
+            else
+                file_path = gnc_file_path_absolute (NULL, uri);
+
+            uri_out = gnc_uri_create_uri ("file", NULL, 0, NULL, NULL, file_path);
+        }
+        g_free (file_path);
+        g_free (uri_scheme);
+
+        if (!uri_out)
+            uri_out = g_strdup (uri);
+
+        uri_out_scheme = gnc_uri_get_scheme (uri_out);
+
+        if (uri_out_scheme) // make sure we have a scheme entry
         {
             gnc_launch_assoc (uri_out);
-            g_free (uri_scheme);
+            g_free (uri_out_scheme);
         }
         else
             gnc_error_dialog (gnc_ui_get_gtk_window(GTK_WIDGET (view)),
@@ -289,7 +319,7 @@ row_selected_cb (GtkTreeView *view, GtkTreePath *path,
         Account       *account;
 
         /* This should never be true, but be paranoid */
-        if (split == NULL)
+        if (!split)
             return;
 
         account = xaccSplitGetAccount (split);
@@ -301,13 +331,40 @@ row_selected_cb (GtkTreeView *view, GtkTreePath *path,
         gsr = gnc_plugin_page_register_get_gsr (page);
         gnc_split_reg_raise (gsr);
 
-        if (gsr == NULL)
+        if (!gsr)
             return;
 
         gnc_split_reg_jump_to_split (gsr, split);
     }
 }
 
+static gchar*
+gsr_convert_associate_uri (Transaction *trans)
+{
+    const gchar *uri = xaccTransGetAssociation (trans); // get the existing uri
+    const gchar *part = NULL;
+
+    if (!uri)
+        return NULL;
+
+    if (g_str_has_prefix (uri, "file:") && !g_str_has_prefix (uri,"file://"))
+    {
+        // fix an earlier error when storing relative paths in version 3.3
+        // relative paths are stored without a leading "/" and in native form
+        if (g_str_has_prefix (uri,"file:/") && !g_str_has_prefix (uri,"file://"))
+            part = uri + strlen ("file:/");
+        else if (g_str_has_prefix (uri,"file:") && !g_str_has_prefix (uri,"file://"))
+            part = uri + strlen ("file:");
+
+        if (part)
+        {
+            xaccTransSetAssociation (trans, part);
+            return g_strdup (part);
+        }
+    }
+    return g_strdup (uri);
+}
+
 static void
 get_trans_info (AssocDialog *assoc_dialog)
 {
@@ -346,12 +403,14 @@ get_trans_info (AssocDialog *assoc_dialog)
             if (g_list_find (trans_list, trans) != NULL)
                 continue;
 
-            uri = xaccTransGetAssociation (trans);
+            // fix an earlier error when storing relative paths in version 3.3
+            uri = gsr_convert_associate_uri (trans);
 
-            if (g_strcmp0 (uri, "") != 0 && g_strcmp0 (uri, NULL) != 0)
+            if (uri && *uri != '\0')
             {
                 gchar *uri_u;
                 gboolean rel = FALSE;
+                gchar *scheme = gnc_uri_get_scheme (uri);
                 time64 t = xaccTransRetDatePosted (trans);
                 char datebuff[MAX_DATE_LENGTH + 1];
                 memset (datebuff, 0, sizeof(datebuff));
@@ -360,10 +419,10 @@ get_trans_info (AssocDialog *assoc_dialog)
                 qof_print_date_buff (datebuff, sizeof(datebuff), t);
                 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
 
-                if (g_str_has_prefix (uri,"file:/") && !g_str_has_prefix (uri,"file://")) // path is relative
+                if (!scheme) // path is relative
                     rel = TRUE;
 
-                uri_u = convert_uri_to_unescaped (assoc_dialog, uri);
+                uri_u = convert_uri_to_unescaped (assoc_dialog, uri, scheme);
 
                 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
                                     DATE_TRANS, datebuff,
@@ -372,6 +431,7 @@ get_trans_info (AssocDialog *assoc_dialog)
                                     URI_SPLIT, split, URI, uri,
                                     URI_RELATIVE, (rel == TRUE ? "emblem-default" : NULL), -1);
                 g_free (uri_u);
+                g_free (scheme);
             }
             trans_list = g_list_prepend (trans_list, trans); // add trans to trans_list
         }
@@ -423,27 +483,42 @@ gnc_assoc_dialog_create (GtkWindow *parent, AssocDialog *assoc_dialog)
 
     assoc_dialog->path_head = gnc_prefs_get_string (GNC_PREFS_GROUP_GENERAL, "assoc-head");
 
-    if ((assoc_dialog->path_head != NULL) && (g_strcmp0 (assoc_dialog->path_head, "") != 0)) // not default entry
+    if (assoc_dialog->path_head && g_strcmp0 (assoc_dialog->path_head, "") != 0) // not default entry
     {
-        gchar *uri_u = g_uri_unescape_string (assoc_dialog->path_head, NULL);
-        gchar *path_head_str = g_filename_from_uri (uri_u, NULL, NULL);
+        gchar *path_head_str = gnc_uri_get_path (assoc_dialog->path_head);
         gchar *path_head_label;
 
-        assoc_dialog->valid_path_head = TRUE;
-
         // test for current folder being present
         if (g_file_test (path_head_str, G_FILE_TEST_IS_DIR))
             path_head_label = g_strconcat (_("Path head for files is, "), path_head_str, NULL);
         else
             path_head_label = g_strconcat (_("Path head does not exist, "), path_head_str, NULL);
 
+        assoc_dialog->path_head_set = TRUE;
         gtk_label_set_text (GTK_LABEL(path_head), path_head_label);
         g_free (path_head_label);
-        g_free (uri_u);
         g_free (path_head_str);
     }
     else
-        assoc_dialog->valid_path_head = FALSE;
+    {
+        const gchar *doc = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
+        gchar *path_head_label;
+        gchar *path_ret;
+
+        if (doc)
+            path_ret = g_strdup (doc);
+        else
+            path_ret = g_strdup (gnc_userdata_dir ());
+
+        path_head_label = g_strdup_printf (_("Path head not set, using '%s' for relative paths"), path_ret);
+        assoc_dialog->path_head_set = FALSE;
+        gtk_label_set_text (GTK_LABEL(path_head), path_head_label);
+        g_free (path_head_label);
+        g_free (path_ret);
+    }
+
+    // Set the style context for this label so it can be easily manipulated with css
+    gnc_widget_set_style_context (GTK_WIDGET(path_head), "gnc-class-highlight");
 
     /* Need to add toggle renderers here to get the xalign to work. */
     tree_column = gtk_tree_view_column_new();
@@ -510,7 +585,7 @@ show_handler (const char *klass, gint component_id,
     ENTER(" ");
     if (!assoc_dialog)
     {
-        LEAVE("No data strucure");
+        LEAVE("No data structure");
         return(FALSE);
     }
     gtk_window_present (GTK_WINDOW(assoc_dialog->window));

commit be8216e235c336d657e732ea1328c44600cc48f7
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Mar 29 17:35:48 2019 +0000

    Update transaction association functions in gnc-split-reg.c
    
    Update the transaction association functions to use the in house uri
    functions and also fix a problem that mainly affects Windows when you
    associate a file that is in the root of the associated path head.

diff --git a/gnucash/gnome/gnc-split-reg.c b/gnucash/gnome/gnc-split-reg.c
index bf1ddec04..6a30ca880 100644
--- a/gnucash/gnome/gnc-split-reg.c
+++ b/gnucash/gnome/gnc-split-reg.c
@@ -51,6 +51,7 @@
 #include "gnc-ui-util.h"
 #include "gnc-ui.h"
 #include "gnc-uri-utils.h"
+#include "gnc-filepath-utils.h"
 #include "gnc-warnings.h"
 #include "gnucash-sheet.h"
 #include "gnucash-register.h"
@@ -1041,7 +1042,7 @@ gsr_default_associate_handler_file (GNCSplitReg *gsr, Transaction *trans, gboole
 {
     GtkWidget *dialog;
     gint       response;
-    gboolean   valid_path_head = FALSE;
+    gboolean   path_head_set = FALSE;
     gchar     *path_head = gnc_prefs_get_string (GNC_PREFS_GROUP_GENERAL, "assoc-head");
 
     dialog = gtk_file_chooser_dialog_new (_("Associate File with Transaction"),
@@ -1054,33 +1055,49 @@ gsr_default_associate_handler_file (GNCSplitReg *gsr, Transaction *trans, gboole
 
     gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER(dialog), FALSE);
 
-    if ((path_head != NULL) && (g_strcmp0 (path_head, "") != 0)) // not default entry
-        valid_path_head = TRUE;
+    path_head_set = (path_head && *path_head != '\0'); // not default entry
 
     if (have_uri)
     {
-        gchar *new_uri;
-        gchar *uri_label;
-        gchar *filename;
-
+        gchar *file_uri = NULL;
         const gchar *uri = xaccTransGetAssociation (trans);
+        gchar *scheme = gnc_uri_get_scheme (uri);
 
-        if (valid_path_head && g_str_has_prefix (uri,"file:/") && !g_str_has_prefix (uri,"file://"))
+        if (!scheme) // relative path
         {
-            const gchar *part = uri + strlen ("file:");
-            new_uri = g_strconcat (path_head, part, NULL);
+            gchar *file_path = NULL;
+            if (path_head_set) // not default entry
+                file_path = gnc_file_path_absolute (gnc_uri_get_path (path_head), uri);
+            else
+                file_path = gnc_file_path_absolute (NULL, uri);
+
+            file_uri = gnc_uri_create_uri ("file", NULL, 0, NULL, NULL, file_path);
+            g_free (file_path);
         }
-        else
-            new_uri = g_strdup (uri);
 
-        filename = g_uri_unescape_string (new_uri, NULL);
-        uri_label = g_strconcat (_("Existing Association is "), filename, NULL);
-        gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), gtk_label_new (uri_label));
-        gtk_file_chooser_set_uri (GTK_FILE_CHOOSER(dialog), new_uri);
+        if (g_strcmp0 (scheme, "file") == 0) // absolute path
+            file_uri = g_strdup (uri);
 
-        g_free (uri_label);
-        g_free (new_uri);
-        g_free (filename);
+        if (file_uri)
+        {
+            GtkWidget *label;
+            gchar *file_uri_u = g_uri_unescape_string (file_uri, NULL);
+            gchar *filename = gnc_uri_get_path (file_uri_u);
+            gchar *uri_label = g_strconcat (_("Existing Association is '"), filename, "'", NULL);
+            PINFO("Path head: '%s', URI: '%s', Filename: '%s'", path_head, uri, filename);
+            label = gtk_label_new (uri_label);
+            gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), label);
+            gtk_label_set_ellipsize (GTK_LABEL(label), PANGO_ELLIPSIZE_START);
+
+            // Set the style context for this label so it can be easily manipulated with css
+            gnc_widget_set_style_context (GTK_WIDGET(label), "gnc-class-highlight");
+            gtk_file_chooser_set_uri (GTK_FILE_CHOOSER(dialog), file_uri);
+
+            g_free (uri_label);
+            g_free (filename);
+            g_free (file_uri_u);
+            g_free (file_uri);
+        }
     }
     response = gtk_dialog_run (GTK_DIALOG (dialog));
 
@@ -1091,21 +1108,37 @@ gsr_default_associate_handler_file (GNCSplitReg *gsr, Transaction *trans, gboole
     {
         gchar *dialog_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
 
-        PINFO("Dialog File URI: %s\n", dialog_uri);
+        // prior to 3.5, assoc-head could be with or without a trailing '/'
+        if (path_head_set && !g_str_has_suffix (path_head, "/"))
+        {
+            gchar *folder_with_slash = g_strconcat (path_head, "/", NULL);
+            g_free (path_head);
+            path_head = g_strdup (folder_with_slash);
+            g_free (folder_with_slash);
+
+            if (!gnc_prefs_set_string (GNC_PREFS_GROUP_GENERAL, "assoc-head", path_head))
+                PINFO("Failed to save preference at %s, %s with %s",
+                       GNC_PREFS_GROUP_GENERAL, "assoc-head", path_head);
+        }
 
-        if (valid_path_head && g_str_has_prefix (dialog_uri, path_head))
+        PINFO("Dialog File URI: '%s', Path head: '%s'", dialog_uri, path_head);
+
+        // relative paths do not start with a '/'
+        if (path_head_set && g_str_has_prefix (dialog_uri, path_head))
         {
-            gchar *part = dialog_uri + strlen (path_head);
-            gchar *new_uri = g_strconcat ("file:", part, NULL);
-            xaccTransSetAssociation (trans, new_uri);
-            g_free (new_uri);
+            const gchar *part = dialog_uri + strlen (path_head);
+
+            PINFO("Dialog URI: '%s', Part: '%s'", dialog_uri, part);
+            xaccTransSetAssociation (trans, part);
         }
         else
+        {
+            PINFO("Dialog URI: '%s'", dialog_uri);
             xaccTransSetAssociation (trans, dialog_uri);
-
+        }
         g_free (dialog_uri);
     }
-    g_free (path_head);
+
     gtk_widget_destroy (dialog);
 }
 
@@ -1113,17 +1146,19 @@ static void
 gsr_default_associate_handler_location_ok_cb (GtkEditable *editable, gpointer user_data)
 {
     GtkWidget *ok_button = user_data;
-    gboolean have_scheme = TRUE;
+    gboolean have_scheme = FALSE;
     gchar *text = gtk_editable_get_chars (editable, 0, -1);
-    gchar *scheme = gnc_uri_get_scheme (text);
-
-    if (!scheme)
-        have_scheme = FALSE;
+    gchar *scheme;
 
+    if (text && *text != '\0')
+    {
+        scheme = gnc_uri_get_scheme (text);
+        if (scheme)
+            have_scheme = TRUE;
+        g_free (scheme);
+    }
     gtk_widget_set_sensitive (ok_button, have_scheme);
-
     g_free (text);
-    g_free (scheme);
 }
 
 static void
@@ -1191,6 +1226,33 @@ gsr_default_associate_handler_location (GNCSplitReg *gsr, Transaction *trans, gb
     gtk_widget_destroy (dialog);
 }
 
+static gchar*
+gsr_convert_associate_uri (Transaction *trans)
+{
+    const gchar *uri = xaccTransGetAssociation (trans); // get the existing uri
+    const gchar *part = NULL;
+
+    if (!uri)
+        return NULL;
+
+    if (g_str_has_prefix (uri, "file:") && !g_str_has_prefix (uri,"file://"))
+    {
+        // fix an error when storing relative paths in version earlier than 3.5
+        // relative paths are stored without a leading "/" and in native form
+        if (g_str_has_prefix (uri,"file:/") && !g_str_has_prefix (uri,"file://"))
+            part = uri + strlen ("file:/");
+        else if (g_str_has_prefix (uri,"file:") && !g_str_has_prefix (uri,"file://"))
+            part = uri + strlen ("file:");
+
+        if (part)
+        {
+            xaccTransSetAssociation (trans, part);
+            return g_strdup (part);
+        }
+    }
+    return g_strdup (uri);
+}
+
 /**
  * Associates a URI with the current transaction.
  **/
@@ -1205,7 +1267,7 @@ gsr_default_associate_handler (GNCSplitReg *gsr, gboolean uri_is_file)
     gboolean have_uri = FALSE;
 
     /* get the current split based on cursor position */
-    if (split == NULL)
+    if (!split)
     {
         gnc_split_register_cancel_cursor_split_changes (reg);
         return;
@@ -1217,21 +1279,24 @@ gsr_default_associate_handler (GNCSplitReg *gsr, gboolean uri_is_file)
     if (cursor_class == CURSOR_CLASS_NONE)
         return;
 
+    // fix an earlier error when storing relative paths in version 3.3
+    uri = gsr_convert_associate_uri (trans);
+
     if (is_trans_readonly_and_warn (GTK_WINDOW(gsr->window), trans))
         return;
 
-    // get the existing uri
-    uri = xaccTransGetAssociation (trans);
-
     // Check for uri is empty or NULL
-    if (g_strcmp0 (uri, "") != 0 && g_strcmp0 (uri, NULL) != 0)
+    if (uri && *uri != '\0')
     {
+        gchar *scheme = gnc_uri_get_scheme (uri);
         have_uri = TRUE;
 
-        if (g_str_has_prefix (uri, "file:")) // use the correct dialog
+        if (!scheme || g_strcmp0 (scheme, "file") == 0) // use the correct dialog
             uri_is_file = TRUE;
         else
             uri_is_file = FALSE;
+
+        g_free (scheme);
     }
 
     if (uri_is_file == TRUE)
@@ -1251,11 +1316,11 @@ gsr_default_execassociated_handler (GNCSplitReg *gsr, gpointer data)
     Transaction *trans;
     Split *split = gnc_split_register_get_current_split (reg);
     const char *uri;
-    const char *run_uri;
+    const char *run_uri = NULL;
     gchar *uri_scheme;
 
     /* get the current split based on cursor position */
-    if (split == NULL)
+    if (!split)
     {
         gnc_split_register_cancel_cursor_split_changes (reg);
         return;
@@ -1272,30 +1337,36 @@ gsr_default_execassociated_handler (GNCSplitReg *gsr, gpointer data)
         xaccTransDump (trans, "ExecAssociated");
 #endif
 
-    uri = xaccTransGetAssociation (trans);
+    // fix an earlier error when storing relative paths in version 3.3
+    uri = gsr_convert_associate_uri (trans);
 
-    if (g_strcmp0 (uri, "") == 0 && g_strcmp0 (uri, NULL) == 0)
+    if (!uri && g_strcmp0 (uri, "") == 0)
         gnc_error_dialog (GTK_WINDOW (gsr->window), "%s", _("This transaction is not associated with a URI."));
     else
     {
-        if (g_str_has_prefix (uri,"file:/") && !g_str_has_prefix (uri,"file://")) // Check for relative path
+        gchar *scheme = gnc_uri_get_scheme (uri);
+
+        if (!scheme) // relative path
         {
             gchar *path_head = gnc_prefs_get_string (GNC_PREFS_GROUP_GENERAL, "assoc-head");
+            gchar *file_path;
 
-            if ((path_head != NULL) && (g_strcmp0 (path_head, "") != 0)) // not default entry
-            {
-                const gchar *part = uri + strlen ("file:");
-                run_uri = g_strconcat (path_head, part, NULL);
-            }
+            if (path_head && g_strcmp0 (path_head, "") != 0) // not default entry
+                file_path = gnc_file_path_absolute (gnc_uri_get_path (path_head), uri);
             else
-                run_uri = g_strdup (uri);
+                file_path = gnc_file_path_absolute (NULL, uri);
+
+            run_uri = gnc_uri_create_uri ("file", NULL, 0, NULL, NULL, file_path);
+            g_free (path_head);
+            g_free (file_path);
         }
-        else
+
+        if (!run_uri)
             run_uri = g_strdup (uri);
 
-        uri_scheme = g_uri_parse_scheme (run_uri);
+        uri_scheme = gnc_uri_get_scheme (run_uri);
 
-        if (uri_scheme != NULL) // make sure we have a scheme entry
+        if (uri_scheme) // make sure we have a scheme entry
         {
             gnc_launch_assoc (run_uri);
             g_free (uri_scheme);

commit bed7f99dac48afa1d5bb257e4fc6e6b9dbe5316d
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Mar 29 17:35:20 2019 +0000

    Make sure the Association path head ends with a '/'

diff --git a/gnucash/gnome-utils/dialog-preferences.c b/gnucash/gnome-utils/dialog-preferences.c
index 49bd5e732..34ddfe456 100644
--- a/gnucash/gnome-utils/dialog-preferences.c
+++ b/gnucash/gnome-utils/dialog-preferences.c
@@ -752,6 +752,15 @@ file_chooser_selected_cb (GtkFileChooser *fc, gpointer user_data)
     const gchar *pref = g_object_get_data (G_OBJECT(fc), "pref");
     gchar       *folder = gtk_file_chooser_get_uri (fc);
 
+    // make sure path_head ends with a trailing '/', 3.5 onwards
+    if (!g_str_has_suffix (folder, "/"))
+    {
+        gchar *folder_with_slash = g_strconcat (folder, "/", NULL);
+        g_free (folder);
+        folder = g_strdup (folder_with_slash);
+        g_free (folder_with_slash);
+    }
+
     gtk_widget_hide (GTK_WIDGET(image));
 
     if (!gnc_prefs_set_string (group, pref, folder))
@@ -787,7 +796,7 @@ gnc_prefs_connect_file_chooser_button (GtkFileChooserButton *fcb, const gchar *b
 
     PINFO("Uri is %s", uri);
 
-    if ((uri != NULL) && (g_strcmp0 (uri, "") != 0)) // default entry
+    if (uri && *uri != '\0') // default entry
     {
         gchar *path_head = g_filename_from_uri (uri, NULL, NULL);
 

commit 5eb6f76e63a8cb18cad953561a287a32787c284c
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Mar 29 17:34:45 2019 +0000

    Change uri functions to work with valid Windows file uri's
    
    Windows file uri's can be of the form 'file:///N:/bob.txt' so change
    the gnc_uri_get_components to remove a left over '/' at the start so
    gnc_resolve_file_path gets the absolute path correctly. Also change
    gnc_uri_create_uri to add an extra '/' for Windows file uri's.

diff --git a/libgnucash/engine/gnc-uri-utils.c b/libgnucash/engine/gnc-uri-utils.c
index 7c0b2a006..56390041c 100644
--- a/libgnucash/engine/gnc-uri-utils.c
+++ b/libgnucash/engine/gnc-uri-utils.c
@@ -176,7 +176,15 @@ void gnc_uri_get_components (const gchar *uri,
 
     if ( gnc_uri_is_file_scheme ( *scheme ) )
     {
-        *path     = gnc_resolve_file_path ( splituri[1] );
+        /* a true file uri on windows can start file:///N:/
+           so we come here with /N:/                       */
+        if (g_str_has_prefix (splituri[1], "/") && g_strstr_len (splituri[1], -1,  ":") != NULL)
+        {
+            gchar *ptr = splituri[1];
+            *path = gnc_resolve_file_path ( ptr + 1 );
+        }
+        else
+            *path = gnc_resolve_file_path ( splituri[1] );
         g_strfreev ( splituri );
         return;
     }
@@ -304,15 +312,25 @@ gchar *gnc_uri_create_uri (const gchar *scheme,
          * path info as is.
          */
         gchar *abs_path;
+        gchar *uri_scheme;
         if (scheme && (!gnc_uri_is_known_scheme (scheme)) )
             abs_path = g_strdup ( path );
         else
             abs_path = gnc_resolve_file_path ( path );
-        if ( scheme == NULL )
-            uri = g_strdup_printf ( "file://%s", abs_path );
+
+        if (!scheme)
+            uri_scheme = g_strdup ("file");
         else
-            uri = g_strdup_printf ( "%s://%s", scheme, abs_path );
+            uri_scheme = g_strdup (scheme);
+
+        if (g_str_has_prefix (abs_path, "/"))
+            uri = g_strdup_printf ( "%s://%s", uri_scheme, abs_path );
+        else // for windows add an extra "/"
+            uri = g_strdup_printf ( "%s:///%s", uri_scheme, abs_path );
+
+        g_free (uri_scheme);
         g_free (abs_path);
+
         return uri;
     }
 

commit 89d2cde9792561eb2ccdb9f2c5c8aa651d25d2f0
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Mar 29 17:34:04 2019 +0000

    Remove some white space/tabs from gnc-filepath-utils.cpp

diff --git a/libgnucash/core-utils/gnc-filepath-utils.cpp b/libgnucash/core-utils/gnc-filepath-utils.cpp
index 6c5c8631d..29eedfcb9 100644
--- a/libgnucash/core-utils/gnc-filepath-utils.cpp
+++ b/libgnucash/core-utils/gnc-filepath-utils.cpp
@@ -374,8 +374,8 @@ gnc_validate_directory (const bfs::path &dirname)
          * we need to overrule it during build (when guile interferes)
          * and testing.
          */
-	bfs::path home_dir(g_get_home_dir(), cvt);
-	home_dir.imbue(bfs_locale);
+        bfs::path home_dir(g_get_home_dir(), cvt);
+        home_dir.imbue(bfs_locale);
         auto homedir_exists = bfs::exists(home_dir);
         auto is_descendant = dir_is_descendant (dirname, home_dir);
         if (!homedir_exists && is_descendant)
@@ -447,10 +447,10 @@ copy_recursive(const bfs::path& src, const bfs::path& dest)
             string cur_str = direntry->path().string();
 #endif
             auto cur_len = cur_str.size();
-	    string rel_str(cur_str, old_len, cur_len - old_len);
-	    bfs::path relpath(rel_str, cvt);
+            string rel_str(cur_str, old_len, cur_len - old_len);
+            bfs::path relpath(rel_str, cvt);
             auto newpath = bfs::absolute (relpath.relative_path(), dest);
-	    newpath.imbue(bfs_locale);
+            newpath.imbue(bfs_locale);
             bfs::copy(direntry->path(), newpath);
         }
     }
@@ -526,7 +526,6 @@ get_userdata_home(void)
     auto try_tmp_dir = true;
     auto userdata_home = get_user_data_dir();
 
-
     /* g_get_user_data_dir doesn't check whether the path exists nor attempts to
      * create it. So while it may return an actual path we may not be able to use it.
      * Let's check that now */
@@ -550,9 +549,9 @@ get_userdata_home(void)
        Hopefully we can always write there. */
     if (try_tmp_dir)
     {
-	bfs::path newpath(g_get_tmp_dir (), cvt);
+        bfs::path newpath(g_get_tmp_dir (), cvt);
         userdata_home = newpath / g_get_user_name ();
-	userdata_home.imbue(bfs_locale);
+        userdata_home.imbue(bfs_locale);
     }
     g_assert(!userdata_home.empty());
 

commit 22a7a05d50069ad36028110a2954d592b1889ace
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Mar 29 17:33:26 2019 +0000

     Add function to create an absolute file path
    
    Add function to create an absolute file path from a prefix path and a
    relative one. If the prefix is null, then the root directory of the
    current path is used.

diff --git a/libgnucash/core-utils/gnc-filepath-utils.cpp b/libgnucash/core-utils/gnc-filepath-utils.cpp
index 7371534b3..6c5c8631d 100644
--- a/libgnucash/core-utils/gnc-filepath-utils.cpp
+++ b/libgnucash/core-utils/gnc-filepath-utils.cpp
@@ -728,6 +728,7 @@ static std::string migrate_gnc_datahome()
 }
 
 
+
 #if defined G_OS_WIN32 ||defined MAC_INTEGRATION
 constexpr auto path_package = PACKAGE_NAME;
 #else
@@ -1017,6 +1018,35 @@ gnc_userdata_dir_as_path (void)
     return gnc_userdata_home;
 }
 
+gchar *gnc_file_path_absolute (const gchar *prefix, const gchar *relative)
+{
+    bfs::path path_relative (relative);
+    path_relative.imbue (bfs_locale);
+    bfs::path path_absolute;
+    bfs::path path_head;
+
+    if (prefix == nullptr)
+    {
+        const gchar *doc_dir = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
+        if (doc_dir == nullptr)
+            path_head = bfs::path (gnc_userdata_dir ()); // running as root maybe
+        else
+            path_head = bfs::path (doc_dir);
+
+        path_head.imbue (bfs_locale);
+        path_absolute = absolute (path_relative, path_head);
+    }
+    else
+    {
+        bfs::path path_head (prefix);
+        path_head.imbue (bfs_locale);
+        path_absolute = absolute (path_relative, path_head);
+    }
+    path_absolute.imbue (bfs_locale);
+
+    return g_strdup (path_absolute.string().c_str());
+}
+
 /** @fn gchar * gnc_build_userdata_path (const gchar *filename)
  *  @brief Make a path to filename in the user's configuration directory.
  *
diff --git a/libgnucash/core-utils/gnc-filepath-utils.h b/libgnucash/core-utils/gnc-filepath-utils.h
index 3e04efc95..f46143842 100644
--- a/libgnucash/core-utils/gnc-filepath-utils.h
+++ b/libgnucash/core-utils/gnc-filepath-utils.h
@@ -49,6 +49,15 @@ gchar *gnc_resolve_file_path (const gchar *filefrag);
  */
 gchar *gnc_file_path_relative_part (const gchar *prefix, const gchar *path);
 
+/** Given a prefix and a relative path, return the absolute path.
+ * @param prefix The prefix that is the head of the path
+ * @param relative The path to add to the prefix to form an absolute path
+ * @return a char* that must be g_freed containing the absolute path.
+ *
+ * If prefix is null, then the gnc_userdata_home is used as the prefix.
+ */
+gchar *gnc_file_path_absolute (const gchar *prefix, const gchar *relative);
+
 /** @brief Find an absolute path to a localized version of a given
  *  relative path to a html or html related file.
  *  If no localized version exists, an absolute path to the file

commit 25277242199a3a75c6c2f3ad20e33a2bbcb64c32
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Mar 29 17:32:43 2019 +0000

    Force the location association to have a scheme
    
    As the location and file associations use the same KVP, make sure a URL
    with a valid scheme is entered. This allows KVP associations that are
    not valid to be treated as relative paths.

diff --git a/gnucash/gnome/gnc-split-reg.c b/gnucash/gnome/gnc-split-reg.c
index 97ecec119..bf1ddec04 100644
--- a/gnucash/gnome/gnc-split-reg.c
+++ b/gnucash/gnome/gnc-split-reg.c
@@ -50,6 +50,7 @@
 #include "gnc-pricedb.h"
 #include "gnc-ui-util.h"
 #include "gnc-ui.h"
+#include "gnc-uri-utils.h"
 #include "gnc-warnings.h"
 #include "gnucash-sheet.h"
 #include "gnucash-register.h"
@@ -1108,10 +1109,27 @@ gsr_default_associate_handler_file (GNCSplitReg *gsr, Transaction *trans, gboole
     gtk_widget_destroy (dialog);
 }
 
+static void
+gsr_default_associate_handler_location_ok_cb (GtkEditable *editable, gpointer user_data)
+{
+    GtkWidget *ok_button = user_data;
+    gboolean have_scheme = TRUE;
+    gchar *text = gtk_editable_get_chars (editable, 0, -1);
+    gchar *scheme = gnc_uri_get_scheme (text);
+
+    if (!scheme)
+        have_scheme = FALSE;
+
+    gtk_widget_set_sensitive (ok_button, have_scheme);
+
+    g_free (text);
+    g_free (scheme);
+}
+
 static void
 gsr_default_associate_handler_location (GNCSplitReg *gsr, Transaction *trans, gboolean have_uri)
 {
-    GtkWidget *dialog, *entry, *label, *content_area;
+    GtkWidget *dialog, *entry, *label, *content_area, *ok_button;
     gint response;
 
     dialog = gtk_dialog_new_with_buttons (_("Associate Location with Transaction"),
@@ -1119,9 +1137,12 @@ gsr_default_associate_handler_location (GNCSplitReg *gsr, Transaction *trans, gb
                                      GTK_DIALOG_MODAL,
                                      _("_Remove"), GTK_RESPONSE_REJECT,
                                      _("_Cancel"), GTK_RESPONSE_CANCEL,
-                                     _("_OK"), GTK_RESPONSE_ACCEPT,
+                                     // OK Button added below
                                      NULL);
 
+    ok_button = gtk_dialog_add_button (GTK_DIALOG(dialog), _("_OK"), GTK_RESPONSE_ACCEPT);
+    gtk_widget_set_sensitive (ok_button, FALSE);
+
     content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
 
     // add the entry text
@@ -1129,6 +1150,9 @@ gsr_default_associate_handler_location (GNCSplitReg *gsr, Transaction *trans, gb
     gtk_entry_set_width_chars (GTK_ENTRY (entry), 80);
     gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
 
+    g_signal_connect (entry, "changed",
+        G_CALLBACK(gsr_default_associate_handler_location_ok_cb), ok_button);
+
     // add a label and set entry text if required
     if (have_uri)
     {
@@ -1136,7 +1160,7 @@ gsr_default_associate_handler_location (GNCSplitReg *gsr, Transaction *trans, gb
         gtk_entry_set_text (GTK_ENTRY (entry), xaccTransGetAssociation (trans));
     }
     else
-        label = gtk_label_new (_("Enter URL:"));
+        label = gtk_label_new (_("Enter URL like http://www.gnucash.org:"));
 
     // pack label and entry to content area
     gnc_label_set_alignment (label, 0.0, 0.5);
@@ -1160,6 +1184,7 @@ gsr_default_associate_handler_location (GNCSplitReg *gsr, Transaction *trans, gb
     if (response == GTK_RESPONSE_ACCEPT)
     {
         const gchar *dialog_uri = gtk_entry_get_text (GTK_ENTRY (entry));
+
         DEBUG("Location URI: %s\n", dialog_uri);
         xaccTransSetAssociation (trans, dialog_uri);
     }

commit 167d209688b82ae7f42d2f5e974677f10c48a96e
Author: Robert Fewell <14uBobIT at gmail.com>
Date:   Fri Mar 29 17:31:54 2019 +0000

    Change gnc_launch_assoc to use gnc_uri_get_scheme
    
    Change the gnc_launch_assoc function to use the gnc_uri_get_scheme
    instead of glib one and re-indent source file

diff --git a/gnucash/gnome-utils/gnc-gnome-utils.c b/gnucash/gnome-utils/gnc-gnome-utils.c
index 71ea21a09..008c9f62b 100644
--- a/gnucash/gnome-utils/gnc-gnome-utils.c
+++ b/gnucash/gnome-utils/gnc-gnome-utils.c
@@ -45,6 +45,7 @@
 #include "dialog-commodity.h"
 #include "dialog-totd.h"
 #include "gnc-ui-util.h"
+#include "gnc-uri-utils.h"
 #include "gnc-session.h"
 #include "qofbookslots.h"
 #ifdef G_OS_WIN32
@@ -482,32 +483,33 @@ void
 gnc_launch_assoc (const char *uri)
 {
     wchar_t *winuri = NULL;
-    char* scheme = g_uri_parse_scheme(uri);
     /* ShellExecuteW open doesn't decode http escapes if it's passed a
      * file URI so we have to do it. */
-    if (scheme && strcmp (scheme, "file") == 0)
+    if (gnc_uri_is_file_uri (uri))
     {
-	gchar *filename = g_filename_from_uri (uri, NULL, NULL);
-	winuri = (wchar_t *)g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
+        gchar *uri_ue = g_uri_unescape_string (uri, NULL);
+        gchar *filename = gnc_uri_get_path (uri_ue);
+        winuri = (wchar_t *)g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
+        g_free (filename);
+        g_free (uri_ue);
     }
     else
-	winuri = (wchar_t *)g_utf8_to_utf16(uri, -1, NULL, NULL, NULL);
+        winuri = (wchar_t *)g_utf8_to_utf16(uri, -1, NULL, NULL, NULL);
 
     if (winuri)
     {
-	wchar_t *wincmd = (wchar_t *)g_utf8_to_utf16("open", -1,
-						     NULL, NULL, NULL);
-	if ((INT_PTR)ShellExecuteW(NULL, wincmd, winuri,
-				   NULL, NULL, SW_SHOWNORMAL) <= 32)
-	{
-	    const gchar *message =
-		_("GnuCash could not find the associated file");
-	    gnc_error_dialog(NULL, "%s: %s", message, uri);
-	}
-	g_free (wincmd);
-	g_free (winuri);
+        wchar_t *wincmd = (wchar_t *)g_utf8_to_utf16("open", -1,
+                                 NULL, NULL, NULL);
+        if ((INT_PTR)ShellExecuteW(NULL, wincmd, winuri,
+                       NULL, NULL, SW_SHOWNORMAL) <= 32)
+        {
+            const gchar *message =
+            _("GnuCash could not find the associated file");
+            gnc_error_dialog(NULL, "%s: %s", message, uri);
+        }
+        g_free (wincmd);
+        g_free (winuri);
     }
-    g_free (scheme);
 }
 
 #else



Summary of changes:
 gnucash/gnome-utils/dialog-preferences.c           |  11 +-
 gnucash/gnome-utils/gnc-gnome-utils.c              |  36 ++--
 gnucash/gnome/dialog-trans-assoc.c                 | 181 +++++++++++++------
 gnucash/gnome/gnc-split-reg.c                      | 196 +++++++++++++++------
 gnucash/gtkbuilder/dialog-trans-assoc.glade        |   4 +-
 .../register/ledger-core/split-register-model.c    |  46 +++--
 gnucash/register/register-gnome/gnucash-sheet.c    |  25 +--
 libgnucash/core-utils/gnc-filepath-utils.cpp       |  45 ++++-
 libgnucash/core-utils/gnc-filepath-utils.h         |   9 +
 libgnucash/engine/gnc-uri-utils.c                  |  26 ++-
 10 files changed, 423 insertions(+), 156 deletions(-)



More information about the gnucash-changes mailing list