r23350 - gnucash/trunk/src - Move state handling code to separate file and improve on it

Geert Janssens gjanssens at code.gnucash.org
Tue Oct 29 15:26:03 EDT 2013


Author: gjanssens
Date: 2013-10-29 15:26:02 -0400 (Tue, 29 Oct 2013)
New Revision: 23350
Trac: http://svn.gnucash.org/trac/changeset/23350

Added:
   gnucash/trunk/src/app-utils/gnc-state.c
   gnucash/trunk/src/app-utils/gnc-state.h
Modified:
   gnucash/trunk/src/app-utils/Makefile.am
   gnucash/trunk/src/app-utils/file-utils.c
   gnucash/trunk/src/app-utils/file-utils.h
   gnucash/trunk/src/gnome-utils/gnc-main-window.c
   gnucash/trunk/src/gnome/top-level.c
Log:
Move state handling code to separate file and improve on it

- keep the state information in memory while gnucash is running
- upon saving the state, only delete those sections that will be
  recreated by the save_all_state function

Modified: gnucash/trunk/src/app-utils/Makefile.am
===================================================================
--- gnucash/trunk/src/app-utils/Makefile.am	2013-10-29 19:25:52 UTC (rev 23349)
+++ gnucash/trunk/src/app-utils/Makefile.am	2013-10-29 19:26:02 UTC (rev 23350)
@@ -58,6 +58,7 @@
   gnc-helpers.c \
   gnc-prefs-utils.c \
   gnc-sx-instance-model.c \
+  gnc-state.c \
   gncmod-app-utils.c \
   gnc-ui-balances.c \
   gnc-ui-util.c \
@@ -85,6 +86,7 @@
   gnc-helpers.h \
   gnc-prefs-utils.h \
   gnc-sx-instance-model.h \
+  gnc-state.h \
   gnc-ui-balances.h \
   gnc-ui-util.h \
   guile-util.h \

Modified: gnucash/trunk/src/app-utils/file-utils.c
===================================================================
--- gnucash/trunk/src/app-utils/file-utils.c	2013-10-29 19:25:52 UTC (rev 23349)
+++ gnucash/trunk/src/app-utils/file-utils.c	2013-10-29 19:26:02 UTC (rev 23350)
@@ -44,7 +44,7 @@
 #include "gnc-uri-utils.h"
 
 /* This static indicates the debugging module that this .o belongs to.  */
-static QofLogModule log_module = GNC_MOD_GUILE;
+static QofLogModule log_module = G_LOG_DOMAIN;
 
 /********************************************************************\
 \********************************************************************/

Modified: gnucash/trunk/src/app-utils/file-utils.h
===================================================================
--- gnucash/trunk/src/app-utils/file-utils.h	2013-10-29 19:25:52 UTC (rev 23349)
+++ gnucash/trunk/src/app-utils/file-utils.h	2013-10-29 19:26:02 UTC (rev 23350)
@@ -76,29 +76,6 @@
  */
 gint64 gnc_getline (gchar **line, FILE *file);
 
-
-/* Definitions shared by file-utils.c and gnc-main-window.c */
-#define STATE_FILE_TOP           "Top"
-#define STATE_FILE_BOOK_GUID     "BookGuid"
-#define STATE_FILE_EXT           ".gcm"
-
-/** Find the state file that corresponds to this URL and guid.
- *
- * The URL is used to compute the base name of the file (which will be in
- *  ~/.gnucash/books) and the guid is used to differentiate when the
- *  user has multiple data files with the same name.
- *
- *  @param url The url of the data file being used.
- *
- *  @param guid The guid of the book associated with this data file.
- *
- *  @param filename Return the next available file name if the
- *  data file cannot be found.
- *
- *  @return The name of the data file that was located.
- */
-GKeyFile *gnc_find_state_file (const gchar *url, const gchar *guid, gchar **filename);
-
 #endif /* GNC_FILE_UTILS_H */
 /** @} */
 /** @} */

Added: gnucash/trunk/src/app-utils/gnc-state.c
===================================================================
--- gnucash/trunk/src/app-utils/gnc-state.c	                        (rev 0)
+++ gnucash/trunk/src/app-utils/gnc-state.c	2013-10-29 19:26:02 UTC (rev 23350)
@@ -0,0 +1,256 @@
+/********************************************************************\
+ * file-utils.c -- simple file utilities                            *
+ * Copyright (C) 1997 Robin D. Clark <rclark at cs.hmc.edu>            *
+ * Copyright (C) 1998 Rob Browning                                  *
+ * Copyright (C) 1998-2000 Linas Vepstas <linas at linas.org>          *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, write to the Free Software      *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.        *
+\********************************************************************/
+
+#include "config.h"
+
+#include <glib.h>
+//#include <glib/gstdio.h>
+//#include <errno.h>
+
+#include "gnc-state.h"
+//#include "gnc-engine.h"
+#include "gnc-filepath-utils.h"
+#include "gnc-gkeyfile-utils.h"
+#include "gnc-uri-utils.h"
+#include "qof.h"
+
+/* This static indicates the debugging module that this .o belongs to.  */
+static QofLogModule log_module = G_LOG_DOMAIN;
+
+/* Absolute path to the state file for the current book
+ * Before 2.4.1, this file didn't have an extension.
+ * The code will look for such pre 2.4.0 file if no post 2.4.1
+ * version is found. If there is an old style file, save the
+ * name here as well. The old style state file will then be
+ * converted into a new style one the next time state is saved.
+ */
+static gchar* state_file_name = NULL;
+static gchar* state_file_name_pre_241 = NULL;
+/* State file data for current book */
+static GKeyFile *state_file = NULL;
+
+/* Determine which file name to use for the state file. This name is based
+ * the current book's uri and guid.
+ *
+ * The state files will be searched for in the books directory in GnuCash'
+ * private configuration directory. This configuration directory is
+ * platform dependent and can be overridden with environment variable
+ * DOT_GNUCASH_DIR. On linux for example this is ~/.gnucash by default.
+ *
+ * The URL is used to compute the base name of the state file and the
+ * guid is used to differentiate when the user has multiple data files
+ * with the same name.
+ *
+ * As of GnuCash 2.4.1 state files will have their own extension to
+ * differentiate them from data files saved by the user. New state
+ * files will always be created with such an extension. But GnuCash
+ * will continue to search for state files without an extension if
+ * no proper state file with extension is found. */
+
+
+static void
+gnc_state_set_base (const QofSession *session)
+{
+    gchar *basename, *original = NULL, *filename, *file_guid;
+    gchar *sf_extension = NULL, *newstyle_filename = NULL;
+    const gchar *uri, *guid_string;
+    QofBook *book;
+    const GncGUID *guid;
+    GKeyFile *key_file = NULL;
+    gint i;
+
+    /* Reset filenames possibly found in a previous run */
+    g_free (state_file_name);
+    g_free (state_file_name_pre_241);
+    state_file_name = NULL;
+    state_file_name_pre_241 = NULL;
+
+    uri = qof_session_get_url(session);
+    ENTER("session %p (%s)", session, uri ? uri : "(null)");
+    if (!uri)
+    {
+        LEAVE("no uri, nothing to do");
+        return;
+    }
+
+    /* Get the book GncGUID */
+    book = qof_session_get_book(session);
+    guid = qof_entity_get_guid(QOF_INSTANCE(book));
+    guid_string = guid_to_string(guid);
+
+    if (gnc_uri_is_file_uri (uri))
+    {
+        /* The book_uri is a true file, use its basename. */
+        gchar *path = gnc_uri_get_path (uri);
+        basename = g_path_get_basename (path);
+        g_free (path);
+    }
+    else
+    {
+        /* The book_uri is composed of database connection parameters. */
+        gchar* protocol = NULL;
+        gchar* host = NULL;
+        gchar* dbname = NULL;
+        gchar* username = NULL;
+        gchar* password = NULL;
+        gint portnum = 0;
+        gnc_uri_get_components (uri, &protocol, &host, &portnum,
+                                &username, &password, &dbname);
+
+        basename = g_strjoin ("_", protocol, host, username, dbname, NULL);
+        g_free (protocol);
+        g_free (host);
+        g_free (username);
+        g_free (password);
+        g_free (dbname);
+    }
+
+    DEBUG ("Basename %s", basename);
+    original = gnc_build_book_path (basename);
+    g_free (basename);
+    DEBUG ("Original %s", original);
+
+    sf_extension = g_strdup (STATE_FILE_EXT);
+    i = 1;
+    while (1)
+    {
+        if (i == 1)
+            filename = g_strconcat (original, sf_extension, NULL);
+        else
+            filename = g_strdup_printf ("%s_%d%s", original, i, sf_extension);
+        DEBUG ("Trying %s", filename);
+        key_file = gnc_key_file_load_from_file (filename, TRUE, FALSE, NULL);
+        DEBUG ("Result %p", key_file);
+
+        if (!key_file)
+        {
+            DEBUG ("No key file by that name");
+            if (g_strcmp0 (sf_extension, STATE_FILE_EXT) == 0)
+            {
+                DEBUG ("Trying old state file names for compatibility");
+                i = 1;
+                g_free ( sf_extension);
+                sf_extension = g_strdup ("");
+
+                /* Regardless of whether or not an old state file is found,
+                 * the currently tested name should be used for the future
+                 * state file.
+                 */
+                state_file_name = filename;
+                continue;
+            }
+
+            /* No old style file found. We'll return with the new file name
+             * we set earlier, and no existing key file. */
+            g_free (filename);
+            break;
+        }
+
+        file_guid = g_key_file_get_string (key_file,
+                                           STATE_FILE_TOP, STATE_FILE_BOOK_GUID,
+                                           NULL);
+        DEBUG ("File GncGUID is %s", file_guid ? file_guid : "<not found>");
+        if (g_strcmp0 (guid_string, file_guid) == 0)
+        {
+            DEBUG ("Matched !!!");
+            /* Save the found file for later use. Which name to save to
+             * depends on whether it was an old or new style file name
+             */
+            if (g_strcmp0 (sf_extension, STATE_FILE_EXT) == 0)
+                state_file_name = filename;
+            else
+                state_file_name_pre_241 = filename;
+
+            g_free (file_guid);
+            break;
+        }
+        DEBUG ("Clean up this pass");
+        g_free (file_guid);
+        g_key_file_free (key_file);
+        g_free (filename);
+        i++;
+    }
+
+    DEBUG("Clean up");
+    g_free(original);
+    g_key_file_free (key_file);
+
+    LEAVE ();
+}
+
+GKeyFile *gnc_state_load (const QofSession *session)
+{
+
+    GKeyFile *keyfile = NULL;
+    GError *error = NULL;
+
+    /* Drop possible previous state_file first */
+    if (state_file)
+    {
+        g_key_file_free (state_file);
+        state_file = NULL;
+    }
+
+    gnc_state_set_base (session);
+
+    if (state_file_name_pre_241)
+        state_file = gnc_key_file_load_from_file (state_file_name_pre_241,
+                                                  TRUE, TRUE, NULL);
+    else if (state_file_name)
+        state_file = gnc_key_file_load_from_file (state_file_name,
+                                                  TRUE, TRUE, NULL);
+
+    return gnc_state_get_current ();
+
+}
+
+void gnc_state_save (const QofSession *session)
+{
+    GError *error = NULL;
+
+    gnc_state_set_base (session);
+
+    /* Write it all out to disk */
+    if (state_file_name)
+        gnc_key_file_save_to_file(state_file_name, state_file, &error);
+    else
+        PWARN ("No state file name set, can't save state");
+
+    if (error)
+    {
+        PERR ("Error: Failure saving state file.\n  %s",
+                   error->message);
+        g_error_free (error);
+    }
+}
+
+GKeyFile *gnc_state_get_current (void)
+{
+    if (!state_file)
+    {
+        PWARN ("No pre-existing state found, creating new one");
+        state_file = g_key_file_new ();
+    }
+
+    return state_file;
+
+}
+

Copied: gnucash/trunk/src/app-utils/gnc-state.h (from rev 23349, gnucash/trunk/src/app-utils/file-utils.h)
===================================================================
--- gnucash/trunk/src/app-utils/gnc-state.h	                        (rev 0)
+++ gnucash/trunk/src/app-utils/gnc-state.h	2013-10-29 19:26:02 UTC (rev 23350)
@@ -0,0 +1,91 @@
+/********************************************************************\
+ * gnc-state.h -- functions to manage gui state                     *
+ * Copyright (C) 1997 Robin D. Clark                                *
+ * Copyright (C) 1998 Linas Vepstas                                 *
+ * Copyright (C) 1998 Rob Browning                                  *
+ * Copyright (C) 2004 Derek Atkins <derek at ihtfp.com>                *
+ * Copyright (C) 2013 Geert Janssens <geert at kobaltwit.be>           *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, write to the Free Software      *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.        *
+ *                                                                  *
+ *   Author: Rob Clark                                              *
+ * Internet: rclark at cs.hmc.edu                                      *
+ *  Address: 609 8th Street                                         *
+ *           Huntington Beach, CA 92648-4632                        *
+\********************************************************************/
+
+/** @addtogroup Utils Utility functions
+    @{ */
+/** @addtogroup GncState State
+ * @{ */
+/** @file gnc-state.h
+ *  @brief  Functions to load, save and get gui state
+ *  @author Copyright (C) 1997 Robin D. Clark
+ *  @author Copyright (C) 1998 Linas Vepstas
+ *  @author Copyright (C) 1998 Rob Browning
+ *  @author Copyright (C) 2004 Derek Atkins <derek at ihtfp.com>
+ *  @author Copyright (C) 2013 Geert Janssens <geert at kobaltwit.be>
+ *
+ *  The state of the gui (number of open windows, number of open pages in each window,
+ *  window positions and sizes, page content of open pages,...) is loaded/saved
+ *  from to a file when opening/closing a book. These functions help
+ *  in loading/saving from/to the appropriate state file for a given session.
+ *
+ *  Note that each gui component is responsible itself to actually
+ *  add/read its state to/from the state file. The functions in
+ *  gnc-state only help to link the in-memory state file to an
+ *  actual file on disk.
+ */
+
+#ifndef GNC_STATE_H
+#define GNC_STATE_H
+
+#include "qof.h"
+
+/* Definitions shared by file-utils.c and gnc-main-window.c */
+#define STATE_FILE_TOP           "Top"
+#define STATE_FILE_BOOK_GUID     "BookGuid"
+#define STATE_FILE_EXT           ".gcm"
+
+/** Load the state from a state file on disk for the given session.
+ *
+ * @param session The session to load the state for.
+ *
+ * @return A pointer to a GKeyFile that holds the state information
+ *         for the given session. If no state file was found an
+ *         empty state is returned (not NULL!). This pointer should
+ *         never be freed, except when gnucash is exiting !
+ */
+GKeyFile *gnc_state_load (const QofSession *session);
+
+/** Save the state to a state file on disk for the given session.
+ *
+ * @param session The session to save the state for.
+ */
+void      gnc_state_save (const QofSession *session);
+
+/** Returns a pointer to the most recently loaded state.
+ *
+ * @return A pointer to a GKeyFile that holds the current state
+ *         information. If there is no current state, an
+ *         empty state is returned (not NULL!). This pointer should
+ *         never be freed, except when gnucash is exiting !
+ */
+GKeyFile *gnc_state_get_current (void);
+
+#endif /* GNC_STATE_H */
+/** @} */
+/** @} */
+

Modified: gnucash/trunk/src/gnome/top-level.c
===================================================================
--- gnucash/trunk/src/gnome/top-level.c	2013-10-29 19:25:52 UTC (rev 23349)
+++ gnucash/trunk/src/gnome/top-level.c	2013-10-29 19:26:02 UTC (rev 23350)
@@ -59,6 +59,7 @@
 #include "gnc-gnome-utils.h"
 #include "gnc-report.h"
 #include "gnc-split-reg.h"
+#include "gnc-state.h"
 #include "gnc-ui.h"
 #include "gnc-ui-util.h"
 #include "gnucash-color.h"
@@ -223,7 +224,7 @@
 }
 
 /** Restore all persistent program state.  This function finds the
- *  "new" state file associated with a specific book guid.  It then
+ *  "new" state file associated with the current session.  It then
  *  iterates through this state information, calling a helper function
  *  to recreate each open window.
  *
@@ -240,36 +241,13 @@
 gnc_restore_all_state (gpointer session, gpointer unused)
 {
     GKeyFile *keyfile = NULL;
-    QofBook *book;
-    const GncGUID *guid;
-    const gchar *url, *guid_string;
     gchar *file_guid;
     GError *error = NULL;
 
-    url = qof_session_get_url(session);
-    ENTER("session %p (%s)", session, url ? url : "(null)");
-    if (!url)
-    {
-        LEAVE("no url, nothing to do");
-        return;
-    }
+    keyfile = gnc_state_load (session);
 
-    /* Get the book GncGUID */
-    book = qof_session_get_book(session);
-    guid = qof_entity_get_guid(QOF_INSTANCE(book));
-    guid_string = guid_to_string(guid);
-
-    keyfile = gnc_find_state_file(url, guid_string, NULL);
-
-    if (!keyfile)
-    {
-        gnc_main_window_restore_default_state();
-        LEAVE("no state file");
-        return;
-    }
-
 #ifdef DEBUG
-    /*  Debugging: dump a copy to stdout and the trace log */
+    /*  Debugging: dump a copy to the trace log */
     {
         gchar *file_data;
         gsize file_length;
@@ -279,23 +257,18 @@
     }
 #endif
 
-    /* validate top level info */
+    /* If no state file was found, keyfile will be empty
+     * In that case, let's load the default state */
     file_guid = g_key_file_get_string(keyfile, STATE_FILE_TOP,
                                       STATE_FILE_BOOK_GUID, &error);
     if (error)
     {
+        gnc_main_window_restore_default_state();
         g_warning("error reading group %s key %s: %s",
                   STATE_FILE_TOP, STATE_FILE_BOOK_GUID, error->message);
-        LEAVE("can't read guid");
+        LEAVE("no guid in state file");
         goto cleanup;
     }
-    if (!file_guid || strcmp(guid_string, file_guid))
-    {
-        g_warning("guid mismatch: book guid %s, state file guid %s",
-                  guid_string, file_guid);
-        LEAVE("guid values do not match");
-        goto cleanup;
-    }
 
     gnc_main_window_restore_all_windows(keyfile);
 
@@ -306,7 +279,6 @@
         g_error_free(error);
     if (file_guid)
         g_free(file_guid);
-    g_key_file_free(keyfile);
 }
 
 
@@ -328,37 +300,36 @@
 gnc_save_all_state (gpointer session, gpointer unused)
 {
     QofBook *book;
-    const char *url, *guid_string;
-    gchar *filename;
+    const gchar *guid_string;
     const GncGUID *guid;
     GError *error = NULL;
     GKeyFile *keyfile = NULL;
 
-
-    url = qof_session_get_url(session);
-    ENTER("session %p (%s)", session, url ? url : "(null)");
-    if (!url)
+    keyfile = gnc_state_get_current ();
+    if (keyfile)
     {
-        LEAVE("no url, nothing to do");
-        return;
+        /* Remove existing Window and Page groups from the keyfile
+         * They will be regenerated.
+         */
+        gsize num_groups, curr;
+        gchar **groups = g_key_file_get_groups (keyfile, &num_groups);
+        gchar *group = NULL;
+        for (curr=0; curr < num_groups; curr++)
+        {
+            if (g_str_has_prefix (groups[curr], "Window ") ||
+                g_str_has_prefix (groups[curr], "Page "))
+            {
+                DEBUG ("Removing state group %s", groups[curr]);
+                g_key_file_remove_group (keyfile, groups[curr], NULL);
+            }
+        }
+        g_strfreev (groups);
     }
 
-    /* Get the book GncGUID */
+    /* Store the book's GncGUID in the top level group */
     book = qof_session_get_book(session);
     guid = qof_entity_get_guid(QOF_INSTANCE(book));
     guid_string = guid_to_string(guid);
-
-    /* Find the filename to use.  This returns the data from the
-     * file so its possible that we could reuse the data and
-     * maintain comments that were added to the data file, but
-     * that's not something we currently do. For now the existing
-     * data is dumped and completely regenerated.*/
-    keyfile = gnc_find_state_file(url, guid_string, &filename);
-    if (keyfile)
-        g_key_file_free(keyfile);
-
-    keyfile = g_key_file_new();
-    /* Store top level info in the data structure */
     g_key_file_set_string(keyfile, STATE_FILE_TOP, STATE_FILE_BOOK_GUID,
                           guid_string);
 
@@ -376,17 +347,7 @@
 #endif
 
     /* Write it all out to disk */
-    gnc_key_file_save_to_file(filename, keyfile, &error);
-    if (error)
-    {
-        g_critical(_("Error: Failure saving state file.\n  %s"),
-                   error->message);
-        g_error_free(error);
-    }
-    g_free(filename);
-
-    /* Clean up */
-    g_key_file_free(keyfile);
+    gnc_state_save (session);
     LEAVE("");
 }
 

Modified: gnucash/trunk/src/gnome-utils/gnc-main-window.c
===================================================================
--- gnucash/trunk/src/gnome-utils/gnc-main-window.c	2013-10-29 19:25:52 UTC (rev 23349)
+++ gnucash/trunk/src/gnome-utils/gnc-main-window.c	2013-10-29 19:26:02 UTC (rev 23350)
@@ -57,6 +57,7 @@
 #include "gnc-gui-query.h"
 #include "gnc-hooks.h"
 #include "gnc-session.h"
+#include "gnc-state.h"
 #include "gnc-ui.h"
 #include "gnc-ui-util.h"
 #include "gnc-uri-utils.h"



More information about the gnucash-changes mailing list