mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-26 19:00:18 -06:00
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 git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@23350 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
df299589d4
commit
de58942794
@ -58,6 +58,7 @@ libgncmod_app_utils_la_SOURCES = \
|
||||
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 @@ gncinclude_HEADERS = \
|
||||
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 \
|
||||
|
@ -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;
|
||||
|
||||
/********************************************************************\
|
||||
\********************************************************************/
|
||||
|
@ -76,29 +76,6 @@ int gncReadFile (const char * filename, char ** data);
|
||||
*/
|
||||
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 */
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
256
src/app-utils/gnc-state.c
Normal file
256
src/app-utils/gnc-state.c
Normal file
@ -0,0 +1,256 @@
|
||||
/********************************************************************\
|
||||
* file-utils.c -- simple file utilities *
|
||||
* Copyright (C) 1997 Robin D. Clark <rclark@cs.hmc.edu> *
|
||||
* Copyright (C) 1998 Rob Browning *
|
||||
* Copyright (C) 1998-2000 Linas Vepstas <linas@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;
|
||||
|
||||
}
|
||||
|
91
src/app-utils/gnc-state.h
Normal file
91
src/app-utils/gnc-state.h
Normal file
@ -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@ihtfp.com> *
|
||||
* Copyright (C) 2013 Geert Janssens <geert@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@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@ihtfp.com>
|
||||
* @author Copyright (C) 2013 Geert Janssens <geert@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 */
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
@ -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"
|
||||
|
@ -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 @@ gnc_html_price_url_cb (const char *location, const char *label,
|
||||
}
|
||||
|
||||
/** 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 @@ static void
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
keyfile = gnc_state_load (session);
|
||||
|
||||
#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,21 +257,16 @@ gnc_restore_all_state (gpointer session, gpointer unused)
|
||||
}
|
||||
#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");
|
||||
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");
|
||||
LEAVE("no guid in state file");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@ -306,7 +279,6 @@ cleanup:
|
||||
g_error_free(error);
|
||||
if (file_guid)
|
||||
g_free(file_guid);
|
||||
g_key_file_free(keyfile);
|
||||
}
|
||||
|
||||
|
||||
@ -328,37 +300,36 @@ static void
|
||||
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 @@ gnc_save_all_state (gpointer session, gpointer unused)
|
||||
#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("");
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user