diff --git a/gnucash/gnome-utils/gnc-file.c b/gnucash/gnome-utils/gnc-file.c index 1b8f70145c..5b1d67c20f 100644 --- a/gnucash/gnome-utils/gnc-file.c +++ b/gnucash/gnome-utils/gnc-file.c @@ -651,7 +651,7 @@ gnc_file_query_save (GtkWindow *parent, gboolean can_cancel) static gboolean gnc_post_file_open (GtkWindow *parent, const char * filename, gboolean is_readonly) { - QofSession *current_session, *new_session; + QofSession *new_session; QofBook *new_book; GList *invalid_account_names; gboolean uh_oh = FALSE; @@ -726,7 +726,7 @@ RESTART: /* -- this code is almost identical in FileOpen and FileSaveAs -- */ if (gnc_current_session_exist()) { - current_session = gnc_get_current_session(); + QofSession *current_session = gnc_get_current_session(); gnc_hook_run(HOOK_BOOK_CLOSED, current_session); gnc_close_gui_component_by_session (current_session); gnc_state_save (current_session); @@ -735,7 +735,7 @@ RESTART: /* load the accounts from the users datafile */ /* but first, check to make sure we've got a session going. */ - new_session = qof_session_new (NULL); + new_session = qof_session_new (qof_book_new()); // Begin the new session. If we are in read-only mode, ignore the locks. qof_session_begin (new_session, newfile, is_readonly, FALSE, FALSE); @@ -1299,6 +1299,9 @@ gnc_file_save (GtkWindow *parent) QofSession *session; ENTER (" "); + if (!gnc_current_session_exist ()) + return; //No session means nothing to save. + /* hack alert -- Somehow make sure all in-progress edits get committed! */ /* If we don't have a filename/path to save to get one. */ @@ -1366,6 +1369,12 @@ gnc_file_save_as (GtkWindow *parent) ENTER(" "); + if (!gnc_current_session_exist ()) + { + LEAVE("No Session."); + return; + } + last = gnc_history_get_last(); if ( last && gnc_uri_targets_local_fs (last)) { diff --git a/gnucash/gnome/gnc-plugin-basic-commands.c b/gnucash/gnome/gnc-plugin-basic-commands.c index 51de834422..4deddcfe76 100644 --- a/gnucash/gnome/gnc-plugin-basic-commands.c +++ b/gnucash/gnome/gnc-plugin-basic-commands.c @@ -239,6 +239,14 @@ static const gchar *gnc_plugin_important_actions[] = NULL, }; +/** The following items should be made insensitive at startup time. The + * sensitivity will be changed by some later event. */ +static const gchar *gnc_plugin_initially_insensitive_actions[] = +{ + "FileSaveAction", + NULL, +}; + /** These actions are made not sensitive (i.e., * their toolbar and menu items are grayed out and do not send events * when clicked) when the current book is "Read Only". @@ -309,6 +317,11 @@ gnc_plugin_basic_commands_add_to_window (GncPlugin *plugin, GncMainWindow *window, GQuark type) { + GtkActionGroup *action_group = + gnc_main_window_get_action_group(window, PLUGIN_ACTIONS_NAME); + gnc_plugin_update_actions(action_group, + gnc_plugin_initially_insensitive_actions, + "sensitive", FALSE); g_signal_connect(window, "page_changed", G_CALLBACK(gnc_plugin_basic_commands_main_window_page_changed), plugin); diff --git a/gnucash/import-export/aqb/test/test-kvp.c b/gnucash/import-export/aqb/test/test-kvp.c index 4e13cebd89..62458e08bb 100644 --- a/gnucash/import-export/aqb/test/test-kvp.c +++ b/gnucash/import-export/aqb/test/test-kvp.c @@ -64,7 +64,8 @@ test_qofsession_aqb_kvp( void ) if (1) { // A file with no content at all, but a valid XML file - QofSession *new_session = qof_session_new (NULL); + QofBook *book = qof_book_new(); + QofSession *new_session = qof_session_new (book); char *newfile = g_strdup_printf("file://%s", file1); qof_session_begin (new_session, newfile, TRUE, FALSE, FALSE); @@ -92,7 +93,8 @@ test_qofsession_aqb_kvp( void ) { // A file with no content except for the book_template_list kvp // slot - QofSession *new_session = qof_session_new (NULL); + QofBook *book = qof_book_new(); + QofSession *new_session = qof_session_new (book); char *newfile = g_strdup_printf("file://%s", file2); qof_session_begin (new_session, newfile, TRUE, FALSE, FALSE); diff --git a/libgnucash/backend/dbi/test/test-backend-dbi-basic.cpp b/libgnucash/backend/dbi/test/test-backend-dbi-basic.cpp index c824959b34..994c5e8c42 100644 --- a/libgnucash/backend/dbi/test/test-backend-dbi-basic.cpp +++ b/libgnucash/backend/dbi/test/test-backend-dbi-basic.cpp @@ -91,7 +91,8 @@ setup (Fixture* fixture, gconstpointer pData) { gchar* url = (gchar*)pData; gnc_module_init_backend_dbi(); - fixture->session = qof_session_new (nullptr); + auto book = qof_book_new(); + fixture->session = qof_session_new (book); /* When running distcheck the source directory is read-only, which * prevents creating the lock file. Force the session to get * around that. @@ -391,7 +392,8 @@ test_dbi_store_and_reload (Fixture* fixture, gconstpointer pData) url = fixture->filename; // Save the session data - auto session_2 = qof_session_new (nullptr); + auto book2{qof_book_new()}; + auto session_2 = qof_session_new (book2); qof_session_begin (session_2, url, FALSE, TRUE, TRUE); g_assert (session_2 != NULL); g_assert_cmpint (qof_session_get_error (session_2), == , ERR_BACKEND_NO_ERR); @@ -402,7 +404,8 @@ test_dbi_store_and_reload (Fixture* fixture, gconstpointer pData) g_assert_cmpint (qof_session_get_error (session_2), == , ERR_BACKEND_NO_ERR); // Reload the session data - auto session_3 = qof_session_new (nullptr); + auto book3{qof_book_new()}; + auto session_3 = qof_session_new (book3); g_assert (session_3 != NULL); qof_session_begin (session_3, url, TRUE, FALSE, FALSE); g_assert (session_3 != NULL); @@ -442,7 +445,8 @@ test_dbi_safe_save (Fixture* fixture, gconstpointer pData) url = fixture->filename; // Load the session data - auto session_1 = qof_session_new (nullptr); + auto book1{qof_book_new()}; + auto session_1 = qof_session_new (book1); qof_session_begin (session_1, url, FALSE, TRUE, TRUE); if (session_1 && qof_session_get_error (session_1) != ERR_BACKEND_NO_ERR) @@ -468,7 +472,7 @@ test_dbi_safe_save (Fixture* fixture, gconstpointer pData) } /* Destroy the session and reload it */ - session_2 = qof_session_new (nullptr); + session_2 = qof_session_new (qof_book_new()); qof_session_begin (session_2, url, TRUE, FALSE, FALSE); if (session_2 && qof_session_get_error (session_2) != ERR_BACKEND_NO_ERR) @@ -508,7 +512,7 @@ static void test_dbi_version_control (Fixture* fixture, gconstpointer pData) { auto url = (gchar*)pData; - QofBook* book = nullptr; + QofBook* book{qof_book_new()}; QofBackendError err; gint ourversion = gnc_prefs_get_long_version (); GncSqlBackend* sql_be = nullptr; @@ -536,7 +540,7 @@ test_dbi_version_control (Fixture* fixture, gconstpointer pData) qof_book_commit_edit (book); qof_session_end (sess); qof_session_destroy (sess); - sess = qof_session_new (nullptr); + sess = qof_session_new (qof_book_new()); qof_session_begin (sess, url, TRUE, FALSE, FALSE); qof_session_load (sess, NULL); err = qof_session_pop_error (sess); @@ -549,7 +553,7 @@ test_dbi_version_control (Fixture* fixture, gconstpointer pData) qof_book_commit_edit (book); qof_session_end (sess); qof_session_destroy (sess); - sess = qof_session_new (nullptr); + sess = qof_session_new (qof_book_new()); qof_session_begin (sess, url, TRUE, FALSE, FALSE); qof_session_load (sess, NULL); qof_session_ensure_all_data_loaded (sess); @@ -578,14 +582,14 @@ test_dbi_business_store_and_reload (Fixture* fixture, gconstpointer pData) if (fixture->filename) url = fixture->filename; // Save the session data - auto session_2 = qof_session_new (nullptr); + auto session_2 = qof_session_new (qof_book_new()); qof_session_begin (session_2, url, FALSE, TRUE, TRUE); qof_session_swap_data (fixture->session, session_2); qof_book_mark_session_dirty (qof_session_get_book (session_2)); qof_session_save (session_2, NULL); // Reload the session data - auto session_3 = qof_session_new (nullptr); + auto session_3 = qof_session_new (qof_book_new()); qof_session_begin (session_3, url, TRUE, FALSE, FALSE); qof_session_load (session_3, NULL); diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp index a6ede7ac54..ed995fc352 100644 --- a/libgnucash/engine/Account.cpp +++ b/libgnucash/engine/Account.cpp @@ -1152,7 +1152,7 @@ gnc_book_get_root_account (QofBook *book) if (!book) return NULL; col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT); root = gnc_coll_get_root_account (col); - if (root == NULL) + if (root == NULL && !qof_book_shutting_down(book)) root = gnc_account_create_root(book); return root; } @@ -5875,6 +5875,8 @@ static void gnc_account_book_end(QofBook* book) { Account *root_account = gnc_book_get_root_account(book); + if (!root_account) + return; xaccAccountBeginEdit(root_account); xaccAccountDestroy(root_account); } diff --git a/libgnucash/engine/qofsession.cpp b/libgnucash/engine/qofsession.cpp index 1db4b6b54a..03a9c3001b 100644 --- a/libgnucash/engine/qofsession.cpp +++ b/libgnucash/engine/qofsession.cpp @@ -202,52 +202,49 @@ QofSessionImpl::load (QofPercentageFunc percentage_func) noexcept { /* We must have an empty book to load into or bad things will happen. */ g_return_if_fail(m_book && qof_book_empty(m_book)); - + if (!m_book_id.size ()) return; ENTER ("sess=%p book_id=%s", this, m_book_id.c_str ()); /* At this point, we should are supposed to have a valid book - * id and a lock on the file. */ + * id and a lock on the file. */ clear_error (); /* This code should be sufficient to initialize *any* backend, - * whether http, postgres, or anything else that might come along. - * Basically, the idea is that by now, a backend has already been - * created & set up. At this point, we only need to get the - * top-level account group out of the backend, and that is a - * generic, backend-independent operation. - */ - qof_book_set_backend (newbook, m_backend); + * whether http, postgres, or anything else that might come along. + * Basically, the idea is that by now, a backend has already been + * created & set up. At this point, we only need to get the + * top-level account group out of the backend, and that is a + * generic, backend-independent operation. + */ + qof_book_set_backend (m_book, m_backend); /* Starting the session should result in a bunch of accounts - * and currencies being downloaded, but probably no transactions; - * The GUI will need to do a query for that. - */ + * and currencies being downloaded, but probably no transactions; + * The GUI will need to do a query for that. + */ if (m_backend) { m_backend->set_percentage(percentage_func); - m_backend->load (newbook, LOAD_TYPE_INITIAL_LOAD); + m_backend->load (m_book, LOAD_TYPE_INITIAL_LOAD); push_error (m_backend->get_error(), {}); } auto err = get_error (); if ((err != ERR_BACKEND_NO_ERR) && - (err != ERR_FILEIO_FILE_TOO_OLD) && - (err != ERR_FILEIO_NO_ENCODING) && - (err != ERR_FILEIO_FILE_UPGRADE) && - (err != ERR_SQL_DB_TOO_OLD) && - (err != ERR_SQL_DB_TOO_NEW)) + (err != ERR_FILEIO_FILE_TOO_OLD) && + (err != ERR_FILEIO_NO_ENCODING) && + (err != ERR_FILEIO_FILE_UPGRADE) && + (err != ERR_SQL_DB_TOO_OLD) && + (err != ERR_SQL_DB_TOO_NEW)) { - /* Something broke, put back the old stuff */ - delete m_backend; - qof_book_set_backend (newbook, NULL); - qof_book_destroy (newbook); - m_book = oldbook; - m_backend = qof_book_get_backend (m_book); + // Something failed, delete and restore new ones. + destroy_backend(); + qof_book_destroy (m_book); + m_book = qof_book_new(); LEAVE ("error from backend %d", get_error ()); return; } - qof_book_destroy (oldbook); LEAVE ("sess = %p, book_id=%s", this, m_book_id.c_str ()); } diff --git a/libgnucash/engine/test/test-qofsession.cpp b/libgnucash/engine/test/test-qofsession.cpp index ae9b57311f..0179f830df 100644 --- a/libgnucash/engine/test/test-qofsession.cpp +++ b/libgnucash/engine/test/test-qofsession.cpp @@ -175,19 +175,25 @@ TEST (QofSessionTest, load) * and create a new one. */ qof_backend_register_provider (get_provider ()); - QofSession s; + QofSession s{qof_book_new()}; s.begin ("book1", false, false, false); - auto book = s.get_book (); + char *guidstr1 = guid_to_string(qof_instance_get_guid(s.get_book ())); s.load (nullptr); - EXPECT_NE (book, s.get_book ()); + char *guidstr2 = guid_to_string(qof_instance_get_guid(s.get_book ())); + EXPECT_STRNE (guidstr1, guidstr2); + g_free(guidstr1); + g_free(guidstr2); /* Now we'll do the load without returning an error from the backend, * and ensure that it's the new book from the previous test. */ load_error = false; - book = s.get_book(); + guidstr1 = guid_to_string(qof_instance_get_guid(s.get_book ())); s.load (nullptr); - EXPECT_EQ (book, s.get_book ()); + guidstr2 = guid_to_string(qof_instance_get_guid(s.get_book ())); + EXPECT_STREQ (guidstr1, guidstr2); + g_free(guidstr1); + g_free(guidstr2); EXPECT_EQ (s.get_error(), ERR_BACKEND_NO_ERR); //But it's still empty, to the book shouldn't need saving EXPECT_FALSE(qof_book_session_not_saved (s.get_book ()));