gnucash master: Multiple changes pushed

Geert Janssens gjanssens at code.gnucash.org
Tue Sep 12 08:20:56 EDT 2017


Updated	 via  https://github.com/Gnucash/gnucash/commit/0cdd7769 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/37ecf56f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6ec9aa36 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/65f18589 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2275603e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/240bc32e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/6c8275a7 (commit)
	from  https://github.com/Gnucash/gnucash/commit/ac86ca94 (commit)



commit 0cdd7769415d5af042a7d9026cb97e06a96637c6
Author: lmat <dartme18 at gmail.com>
Date:   Sun Sep 10 15:42:54 2017 -0400

    Adding python tests to cmake
    
    And removing some debugging comments

diff --git a/util/ci/arch-docker b/util/ci/arch-docker
index 4d9bcc6..a5d3120 100644
--- a/util/ci/arch-docker
+++ b/util/ci/arch-docker
@@ -1,6 +1,4 @@
 from base/archlinux
-#run pacman -Syu --quiet --noconfirm > /dev/null
-#run pacman -S --noconfirm archlinux-keyring
 run pacman -Syu --quiet --noconfirm gcc cmake make autoconf automake boost python2 pkg-config guile2.0 guile git ninja gtest gmock sqlite3 webkit2gtk swig gwenhywfar aqbanking intltool libxslt postgresql-libs libmariadbclient libdbi libdbi-drivers > /dev/null
 run echo en_US.UTF-8 UTF-8 >> /etc/locale.gen
 run echo en_GB.UTF-8 UTF-8 >> /etc/locale.gen
diff --git a/util/ci/commonbuild b/util/ci/commonbuild
index f94f9ff..3df33a5 100644
--- a/util/ci/commonbuild
+++ b/util/ci/commonbuild
@@ -11,7 +11,7 @@ if [[ "$BUILDTYPE" == "cmake-make" ]]; then
     make -j 4;
     make check || ../afterfailure;
 elif [[ "$BUILDTYPE" == "cmake-ninja" ]]; then
-    cmake ../gnucash -DCMAKE_BUILD_TYPE=debug -DENABLE_DEBUG=on -G Ninja
+    cmake ../gnucash -DWITH_PYTHON=ON -DCMAKE_BUILD_TYPE=debug -DENABLE_DEBUG=on -G Ninja
     ninja
     ninja check || ../afterfailure;
 elif [[ "$BUILDTYPE" == "autotools" ]]; then
diff --git a/util/ci/ubuntu-14.04-docker b/util/ci/ubuntu-14.04-docker
index 5609be4..9a92e69 100644
--- a/util/ci/ubuntu-14.04-docker
+++ b/util/ci/ubuntu-14.04-docker
@@ -1,6 +1,4 @@
 from ubuntu:14.04
-#sudo apt-get install -qq software-properties-common
-#sudo add-apt-repository -qq ppa:george-edison55/cmake-3.x
 run apt-get update -qq
 run apt-get build-dep -qq gnucash > /dev/null
 run apt-get install -qq git bash-completion cmake3 make swig xsltproc libdbd-sqlite3 texinfo ninja-build libboost-all-dev libgtk-3-dev libwebkit2gtk-3.0-dev > /dev/null

commit 37ecf56fbbd08746f1807750aa1a7ec9513abb53
Merge: ac86ca9 6ec9aa3
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Tue Sep 12 13:48:01 2017 +0200

    Merge branch 'maint'
    
    Resolved conflicts:
            bindings/python/tests/CMakeLists.txt

diff --cc bindings/python/CMakeLists.txt
index e13b295,0000000..341c298
mode 100644,000000..100644
--- a/bindings/python/CMakeLists.txt
+++ b/bindings/python/CMakeLists.txt
@@@ -1,120 -1,0 +1,109 @@@
 +ADD_SUBDIRECTORY(example_scripts)
 +ADD_SUBDIRECTORY(tests)
 +
 +IF (BUILDING_FROM_VCS)
 +  SET(SWIG_FILES ${CMAKE_CURRENT_SOURCE_DIR}/gnucash_core.i ${CMAKE_CURRENT_SOURCE_DIR}/timespec.i)
 +  SET(GNUCASH_CORE_C_INCLUDES
 +    ${CONFIG_H}
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/qofsession.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/qofbook.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/qofbackend.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/qoflog.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/qofutil.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/qofid.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/guid.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/gnc-module/gnc-module.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gnc-engine.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/Transaction.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/Split.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/Account.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gnc-commodity.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gnc-lot.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gnc-numeric.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gncCustomer.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gncEmployee.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gncVendor.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gncAddress.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gncBillTerm.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gncOwner.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gncInvoice.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gncJob.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gncEntry.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gncTaxTable.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gncIDSearch.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/gnc-pricedb.h
 +    ${CMAKE_SOURCE_DIR}/libgnucash/app-utils/gnc-prefs-utils.h
 +  )
 +
 +  SET (SWIG_GNUCASH_CORE_C ${CMAKE_CURRENT_BINARY_DIR}/gnucash_core.c)
 +
 +  GNC_ADD_SWIG_PYTHON_COMMAND (swig-gnucash-core ${SWIG_GNUCASH_CORE_C}
 +    ${SWIG_FILES}
 +    ${CMAKE_SOURCE_DIR}/common/base-typemaps.i
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine/engine-common.i
 +    ${GNUCASH_CORE_C_INCLUDES}
 +  )
 +ELSE()
 +  SET (SWIG_GNUCASH_CORE_C gnucash_core.c)
 +ENDIF()
 +
 +IF(WITH_PYTHON)
 +
 +  SET(gnucash_core_c_INCLUDE_DIRS
 +    ${CMAKE_SOURCE_DIR}/libgnucash
 +    ${CMAKE_SOURCE_DIR}/libgnucash/engine
 +    ${CMAKE_SOURCE_DIR}/gnucash/gnome-utils
 +    ${CMAKE_SOURCE_DIR}/libgnucash/app-utils
 +    ${CMAKE_SOURCE_DIR}/libgnucash/gnc-module
 +    ${CMAKE_SOURCE_DIR}/gnucash/gnome
 +    ${CMAKE_SOURCE_DIR}/libgnucash/core-utils
 +    ${CMAKE_SOURCE_DIR}/libgnucash/gnc-module
 +    ${GLIB_INCLUDE_DIRS}
 +    ${PYTHON_INCLUDE_DIRS}
 +  )
 +
 +  SET(PYEXEC_FILES  __init__.py function_class.py gnucash_business.py gnucash_core.py)
 +
 +  ADD_LIBRARY(gnucash_core_c MODULE ${SWIG_GNUCASH_CORE_C})
 +  TARGET_INCLUDE_DIRECTORIES(gnucash_core_c PRIVATE ${gnucash_core_c_INCLUDE_DIRS})
 +
 +  TARGET_LINK_LIBRARIES(gnucash_core_c gncmod-app-utils gncmod-engine gnc-module ${GLIB_LIBS} ${PYTHON_LIBRARIES})
 +  SET_TARGET_PROPERTIES(gnucash_core_c PROPERTIES PREFIX "_")
 +  TARGET_COMPILE_OPTIONS(gnucash_core_c PRIVATE -Wno-implicit -Wno-missing-prototypes -Wno-declaration-after-statement -Wno-missing-declarations)
 +
 +  ADD_EXECUTABLE(sqlite3test EXCLUDE_FROM_ALL sqlite3test.c ${SWIG_GNUCASH_CORE_C})
 +  TARGET_LINK_LIBRARIES(sqlite3test gncmod-app-utils gncmod-engine gnc-module ${GLIB_LIBS} ${PYTHON_LIBRARIES})
 +  TARGET_INCLUDE_DIRECTORIES(sqlite3test PRIVATE ${gnucash_core_c_INCLUDE_DIRS})
 +  TARGET_COMPILE_OPTIONS(sqlite3test PRIVATE -Wno-implicit -Wno-missing-prototypes -Wno-declaration-after-statement -Wno-missing-declarations)
 +
 +  ADD_TEST(NAME sqlite3test COMMAND sqlite3test)
 +  ADD_DEPENDENCIES(check sqlite3test)
 +
- 
-   # Determine where to install the python libraries.
-   EXECUTE_PROCESS(
-     COMMAND ${PYTHON_EXECUTABLE} -c "import sysconfig; print sysconfig.get_path('platlib', vars = { 'platbase' : '${CMAKE_INSTALL_PREFIX}' }  )"
-     RESULT_VARIABLE PYTHON_SYSCONFIG_RESULT
-     OUTPUT_VARIABLE PYTHON_SYSCONFIG_OUTPUT
-     ERROR_VARIABLE PYTHON_SYSCONFIG_ERROR
-     OUTPUT_STRIP_TRAILING_WHITESPACE
-     ERROR_STRIP_TRAILING_WHITESPACE
-   )
-   IF (PYTHON_SYSCONFIG_RESULT)
-     MESSAGE(SEND_ERROR "Could not determine Python site-package directory:\n${PYTHON_SYSCONFIG_ERROR}")
-   ENDIF()
- 
 +  INSTALL(TARGETS gnucash_core_c
 +    LIBRARY DESTINATION ${PYTHON_SYSCONFIG_OUTPUT}/gnucash
 +    ARCHIVE DESTINATION ${PYTHON_SYSCONFIG_OUTPUT}/gnucash
 +  )
-   INSTALL(FILES __init__.py function_class.py gnucash_business.py gnucash_core.py
-     ${CMAKE_CURRENT_BINARY_DIR}/gnucash_core_c.py
++  INSTALL(FILES ${PYEXEC_FILES} ${CMAKE_CURRENT_BINARY_DIR}/gnucash_core_c.py
 +    DESTINATION ${PYTHON_SYSCONFIG_OUTPUT}/gnucash
 +  )
 +
-   FILE(COPY ${PYEXEC_FILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/gnucash)
++  FILE(COPY ${PYEXEC_FILES} DESTINATION ${PYTHON_SYSCONFIG_BUILD}/gnucash)
 +
 +  ADD_CUSTOM_TARGET(gnucash-core-c-py ALL
-     COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/gnucash_core_c.py ${CMAKE_CURRENT_BINARY_DIR}/gnucash
++    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/gnucash_core_c.py ${PYTHON_SYSCONFIG_BUILD}/gnucash
 +    DEPENDS ${SWIG_GNUCASH_CORE_C})
 +
++  ADD_CUSTOM_TARGET(gnucash-core-c-build ALL
++    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/lib/gnucash/_gnucash_core_c${CMAKE_SHARED_LIBRARY_SUFFIX} ${PYTHON_SYSCONFIG_BUILD}/gnucash
++    DEPENDS gnucash_core_c)
++    
 +ENDIF()
 +
 +SET(python_bindings_DATA ${PYEXEC_FILES}
 +        gnucash_core.i
 +        sqlite3test.c
 +        timespec.i)
 +
 +SET_LOCAL_DIST(python_bindings_DIST_local CMakeLists.txt Makefile.am ${python_bindings_DATA})
 +
- SET(python_bindings_DIST ${python_bindings_DIST_local} ${test_python_bindings_DIST} ${example_scripts_DIST} PARENT_SCOPE)
++SET(python_bindings_DIST ${python_bindings_DIST_local} ${test_python_bindings_DIST} ${example_scripts_DIST} PARENT_SCOPE)
diff --cc bindings/python/tests/CMakeLists.txt
index 76058e4,0000000..d0a857a
mode 100644,000000..100644
--- a/bindings/python/tests/CMakeLists.txt
+++ b/bindings/python/tests/CMakeLists.txt
@@@ -1,18 -1,0 +1,18 @@@
 +
 +IF (WITH_PYTHON)
 +  ADD_TEST(python-bindings ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/runTests.py.in)
 +  SET_PROPERTY(TEST python-bindings PROPERTY ENVIRONMENT
 +    GNC_BUILDDIR=${CMAKE_BINARY_DIR}
-     PYTHONPATH=${CMAKE_BINARY_DIR}/bindings/python:${CMAKE_BINARY_DIR}/lib/gnucash:${CMAKE_BINARY_DIR}/common/test-core
++    PYTHONPATH=${PYTHON_SYSCONFIG_BUILD}:${CMAKE_BINARY_DIR}/lib/gnucash:${CMAKE_BINARY_DIR}/common/test-core
 +  )
 +ENDIF()
 +
 +SET(test_python_bindings_DATA
 +        runTests.py.in
 +        test_account.py
 +        test_book.py
 +        test_business.py
 +        test_split.py
 +        test_transaction.py)
 +
 +SET_DIST_LIST(test_python_bindings_DIST CMakeLists.txt Makefile.am ${test_python_bindings_DATA})
diff --cc gnucash/CMakeLists.txt-bin
index 33332c2,0000000..ac8705c
mode 100644,000000..100644
--- a/gnucash/CMakeLists.txt-bin
+++ b/gnucash/CMakeLists.txt-bin
@@@ -1,178 -1,0 +1,191 @@@
 +ADD_SUBDIRECTORY(overrides)
 +ADD_SUBDIRECTORY(test)
 +# Some settings are platform dependent. Let's define them per platform.
 +IF (WIN32)
 +  # Windows specific settings go here:
 +  SET (GNUCASH_RESOURCE_FILE gnucash.rc)
 +
 +ELSE (WIN32)
 +  # All other platforms use these settings:
 +  SET (PLATFORM_FILES gnucash-valgrind)
 +
 +ENDIF (WIN32)
 +
 +SET (gnucash_SOURCES
 +  gnucash-bin.c
 +  ${GNUCASH_RESOURCE_FILE}  
 +)
 +
 +ADD_EXECUTABLE (gnucash
 +  ${gnucash_SOURCES}
 +)
 +
 +TARGET_COMPILE_DEFINITIONS(gnucash PRIVATE -DG_LOG_DOMAIN=\"gnc.bin\")
 +
 +TARGET_LINK_LIBRARIES (gnucash
 +   gncmod-ledger-core gncmod-report-gnome gnc-gnome gncmod-gnome-utils gncmod-app-utils
 +   gncmod-engine gnc-module gnc-core-utils gncmod-report-system
 +   ${GUILE_LDFLAGS} ${GLIB2_LDFLAGS} ${GTK3_LDFLAGS} ${GTK_MAC_LDFLAGS}
 +)
 +
 +
 +IF (MAC_INTEGRATION)
 +  TARGET_COMPILE_OPTIONS(gnucash PRIVATE ${OSX_EXTRA_COMPILE_FLAGS})
 +  TARGET_LINK_LIBRARIES(gnucash ${OSX_EXTRA_LIBRARIES})
 +ENDIF(MAC_INTEGRATION)
 +
 +INSTALL(TARGETS gnucash DESTINATION bin)
 +# No headers to install.
 +
 +# Generate the gnucash-env script
 +SET(SCRIPT_LIST  "")
 +SET(SCRIPT_OUTPUT_DIR ${BINDIR_BUILD})
 +
 +FOREACH (script gnucash-env gnucash-make-guids)
 +  SET (GNUCASH_ENV_SCRIPT ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${script})
 +  LIST(APPEND SCRIPT_LIST ${SCRIPT_OUTPUT_DIR}/${script})
 +  SET (GNC_OVERRIDES_DIR ${CMAKE_INSTALL_PREFIX}/libexec/gnucash/overrides)
 +  FILE(WRITE ${GNUCASH_ENV_SCRIPT} "#!/bin/sh\n")
 +  FILE(APPEND ${GNUCASH_ENV_SCRIPT} "PATH=\"${GNC_OVERRIDES_DIR}:\${PATH}\"\n")
 +  FILE(APPEND ${GNUCASH_ENV_SCRIPT} "export PATH\n")
 +  FILE(APPEND ${GNUCASH_ENV_SCRIPT} "\nGUILE_WARN_DEPRECATED=\"no\"\n")
 +  FILE(APPEND ${GNUCASH_ENV_SCRIPT} "export GUILE_WARN_DEPRECATED\n")
 +  FILE(APPEND ${GNUCASH_ENV_SCRIPT} "\nexec \"${script}\" \"\$@\"\n")
 +  FILE(COPY ${GNUCASH_ENV_SCRIPT}
 +       DESTINATION ${SCRIPT_OUTPUT_DIR}
 +       FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
 +  )
 +ENDFOREACH(script)
 +
 +SET(TOP_SRC_DIR ${CMAKE_SOURCE_DIR})
 +SET(GNUCASH_BIN_INSTALL_NAME "gnucash")
 +
 +SET(VALGRIND_OUTDIR ${BINDIR_BUILD})
 +
 +CONFIGURE_FILE(gnucash.rc.in gnucash.rc @ONLY NEWLINE_STYLE WIN32)
 +GNC_CONFIGURE(gnucash-valgrind.in ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/gnucash-valgrind)
 +
 +FILE(COPY ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/gnucash-valgrind
 +          DESTINATION ${VALGRIND_OUTDIR}
 +          FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
 +)
 +
 +## Create the environment file
 +
 +FILE(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/environment.in ENV_STRINGS_IN)
 +
 +SET(ENV_STRINGS_LIST "")
 +
 +FOREACH(line ${ENV_STRINGS_IN})
 +  STRING(REGEX REPLACE "@-|-@" "@" line2 "${line}")
 +    STRING(REPLACE ";" "\;" line3 "${line2}")
 +  IF(NOT "${line3}" MATCHES "@NOTE")
 +    LIST(APPEND ENV_STRINGS_LIST "${line3}\n")
 +  ENDIF()
 +ENDFOREACH()
 +
 +STRING(CONCAT ENV_STRINGS ${ENV_STRINGS_LIST})
 +STRING(CONFIGURE "${ENV_STRINGS}" ENV_STRINGS_CONF @ONLY)
 +
 +SET(ENV_FILE_OUT ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/environment)
 +SET(BUILD_ENV_FILE_OUT ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/environment.build)
 +
 +FILE(WRITE ${ENV_FILE_OUT} "${ENV_STRINGS_CONF}")
 +FILE(WRITE ${BUILD_ENV_FILE_OUT} "${ENV_STRINGS_CONF}")
 +
 +SET(XDG_TEXT "
 +# GnuCash was not installed in the default location.
 +# XDG_DATA_DIRS will be set so that our documentation
 +# and gsettings schema are found.\n"
 +)
 +
 +IF (NOT(${GNC_DBD_DIR} STREQUAL "${CMAKE_PREFIX_PATH}/lib/dbd"))
 +  FILE(APPEND ${ENV_FILE_OUT} "GNC_DBD_DIR=${GNC_DBD_DIR}")
 +ENDIF()
 +
 +IF (NOT(${DATADIR} STREQUAL "/usr/share") AND NOT(${DATADIR} STREQUAL "/usr/local/share"))
 +  FILE(APPEND ${ENV_FILE_OUT} ${XDG_TEXT})
-   FILE(APPEND ${ENV_FILE_OUT} "XDG_DATA_DIRS=${DATADIR};{XDG_DATA_DIRS}" "${GNC_SYSTEM_XDG_DATA_DIRS}\n")
++  FILE(APPEND ${ENV_FILE_OUT} "XDG_DATA_DIRS=${DATADIR};{XDG_DATA_DIRS};" "${GNC_SYSTEM_XDG_DATA_DIRS}\n")
 +ENDIF()
 +
 +FILE(APPEND ${BUILD_ENV_FILE_OUT} "GNC_DBD_DIR=${LIBDBI_DRIVERS_DIR}/dbd")
 +
 +FILE(APPEND ${BUILD_ENV_FILE_OUT} ${XDG_TEXT})
 +FILE(APPEND ${BUILD_ENV_FILE_OUT} "XDG_DATA_DIRS=${DATADIR_BUILD};{XDG_DATA_DIRS};" "${GNC_SYSTEM_XDG_DATA_DIRS}\n")
 +
++SET(PYTHON_TEXT "
++# Define PYTHONPATH for non default installation path.\n"
++ )
++IF (NOT(${CMAKE_INSTALL_PREFIX} STREQUAL "/usr") AND NOT(${CMAKE_INSTALL_PREFIX} STREQUAL "/usr/local"))
++ 
++  FILE(APPEND ${ENV_FILE_OUT} ${PYTHON_TEXT})
++  FILE(APPEND ${ENV_FILE_OUT} "PYTHONPATH=${PYTHON_SYSCONFIG_OUTPUT};{PYTHONPATH}")
++ENDIF()
++
++FILE(APPEND ${BUILD_ENV_FILE_OUT} ${PYTHON_TEXT})
++FILE(APPEND ${BUILD_ENV_FILE_OUT} "PYTHONPATH=${PYTHON_SYSCONFIG_BUILD};{PYTHONPATH}")
++
++
 +FILE(COPY ${BUILD_ENV_FILE_OUT}
 +  DESTINATION ${SYSCONFDIR_BUILD}/gnucash
 +  FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
 +)
 +FILE(RENAME
 +  ${SYSCONFDIR_BUILD}/gnucash/environment.build
 +  ${SYSCONFDIR_BUILD}/gnucash/environment
 +)
 +
 +SET(ENVIRONMENT_FILE_DIR ${CMAKE_CURRENT_BINARY_DIR})
 +FILE(COPY ${ENV_FILE_OUT}
 +  DESTINATION ${ENVIRONMENT_FILE_DIR}
 +  FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
 +)
 +
 +INSTALL(FILES ${SCRIPT_LIST} ${VALGRIND_OUTDIR}/gnucash-valgrind DESTINATION bin)
 +INSTALL(FILES ${ENVIRONMENT_FILE_DIR}/environment DESTINATION etc/gnucash)
 +
 +SET_LOCAL_DIST(bin_DIST_local CMakeLists.txt environment.in generate-gnc-script gnucash-bin.c gnucash.rc.in gnucash-valgrind.in
 +        Makefile.am)
 +SET(bin_DIST ${bin_DIST_local} ${overrides_DIST} ${test_bin_DIST} PARENT_SCOPE)
 +
 +IF (WIN32)
 +  # Write out a command script for windows
 +  SET(lib_directories boost enchant libsoup mysql pgsql libxslt)
 +  SET(bin_directories mingw gnutls goffice libgsf pcre gnome guile webkit regex aqbanking gwenhywfar libofx opensp
 +    libdbi sqlite3 mysql pgsql enchant libsoup libxslt)
 +
 +  SET(CMD_LINES "")
 +  SET(BUILD_CMD_LINES "")
 +  FOREACH(dir bin lib lib/gnucash)
 +    FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX}/${dir} INSTALL_PATH_ITEM)
 +    FILE(TO_NATIVE_PATH ${CMAKE_BINARY_DIR}/${dir} BUILD_PATH_ITEM)
 +    LIST(APPEND CMD_LINES "set PATH=${INSTALL_PATH_ITEM}\;%PATH%\n")
 +    LIST(APPEND BUILD_CMD_LINES "set PATH=${BUILD_PATH_ITEM}\;%PATH%\n")
 +  ENDFOREACH(dir)
 +  IF (NOT ${MINGW64})
 +    FOREACH(dir ${lib_directories})
 +      FILE(TO_NATIVE_PATH ${CMAKE_PREFIX_PATH}/${dir}/lib PATH_ITEM)
 +      LIST(APPEND CMD_LINES "set PATH=${PATH_ITEM}\;%PATH%\n")
 +    ENDFOREACH(dir)
 +
 +    FOREACH(dir ${bin_directories})
 +      FILE(TO_NATIVE_PATH ${CMAKE_PREFIX_PATH}/${dir}/bin PATH_ITEM)
 +      LIST(APPEND CMD_LINES "set PATH=${PATH_ITEM}\;%PATH%\n")
 +    ENDFOREACH(dir)
 +  ENDIF (NOT ${MINGW64})
 +  SET(CMD_FILE ${CMAKE_CURRENT_BINARY_DIR}/gnucash-launcher.cmd)
 +  FILE(WRITE ${CMD_FILE} "@echo off\nsetlocal\n\n")
 +  FOREACH(line ${CMD_LINES})
 +    FILE(APPEND ${CMD_FILE} "${line}")
 +  ENDFOREACH(line)
 +  FILE(APPEND ${CMD_FILE} "\nstart gnucash %*\n")
 +
 +  SET(BUILD_CMD_FILE ${CMAKE_BINARY_DIR}/bin/gnucash-launcher.cmd)
 +  FILE(WRITE ${BUILD_CMD_FILE} "@echo off\nsetlocal\n\n")
 +  FOREACH(line ${CMD_LINES})
 +     FILE(APPEND ${BUILD_CMD_FILE} "${line}")
 +  ENDFOREACH(line)
 +  FILE(APPEND ${BUILD_CMD_FILE} "\nstart gnucash %*\n")
 +
 +  INSTALL(PROGRAMS ${CMD_FILE} DESTINATION bin)
 +ENDIF(WIN32)
diff --cc gnucash/gnome-utils/gnc-main-window.c
index b3861bb,0000000..2538e21
mode 100644,000000..100644
--- a/gnucash/gnome-utils/gnc-main-window.c
+++ b/gnucash/gnome-utils/gnc-main-window.c
@@@ -1,4839 -1,0 +1,4841 @@@
 +/*
 + * gnc-main-window.c -- GtkWindow which represents the
 + *	GnuCash main window.
 + *
 + * Copyright (C) 2003 Jan Arne Petersen <jpetersen at uni-bonn.de>
 + * Copyright (C) 2003,2005,2006 David Hampton <hampton at employees.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, contact:
 + *
 + * Free Software Foundation           Voice:  +1-617-542-5942
 + * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
 + * Boston, MA  02110-1301,  USA       gnu at gnu.org
 + */
 +
 +/** @addtogroup Windows
 +    @{ */
 +/** @addtogroup GncMainWindow Main Window functions.
 +    @{ */
 +/** @file gnc-main-window.c
 +    @brief Functions for adding content to a window.
 +    @author Copyright (C) 2003 Jan Arne Petersen <jpetersen at uni-bonn.de>
 +    @author Copyright (C) 2003,2005,2006 David Hampton <hampton at employees.org>
 +*/
 +#include "config.h"
 +
 +#include <glib/gi18n.h>
 +#include <gtk/gtk.h>
 +#include <gdk/gdk.h>
 +#include <gdk/gdkkeysyms.h>
 +
 +#include "gnc-plugin.h"
 +#include "gnc-plugin-manager.h"
 +#include "gnc-main-window.h"
 +
 +#include "dialog-options.h"
 +#include "dialog-preferences.h"
 +#include "dialog-reset-warnings.h"
 +#include "dialog-transfer.h"
 +#include "dialog-utils.h"
 +#include "file-utils.h"
 +#include "gnc-component-manager.h"
 +#include "gnc-engine.h"
 +#include "gnc-file.h"
 +#include "gnc-filepath-utils.h"
 +#include "gnc-gkeyfile-utils.h"
 +#include "gnc-gnome-utils.h"
 +#include "gnc-gobject-utils.h"
 +#include "gnc-gui-query.h"
 +#include "gnc-hooks.h"
 +#include "gnc-icons.h"
 +#include "gnc-session.h"
 +#include "gnc-state.h"
 +#include "gnc-ui.h"
 +#include "gnc-ui-util.h"
 +#include "gnc-uri-utils.h"
 +#include "gnc-version.h"
 +#include "gnc-window.h"
 +#include "gnc-prefs.h"
 +#include "option-util.h"
 +// +JSLED
 +//#include "gnc-html.h"
 +#include "gnc-autosave.h"
 +#include "print-session.h"
 +#ifdef MAC_INTEGRATION
 +#include <gtkmacintegration/gtkosxapplication.h>
 +#endif
 +#ifdef HAVE_SYS_STAT_H
 +# define __need_system_sys_stat_h //To block Guile-2.0's evil substitute
 +# include <sys/types.h>
 +# include <sys/stat.h> // for stat(2)
 +#endif
 +
 +/** Names of signals generated by the main window. */
 +enum
 +{
 +    PAGE_ADDED,
 +    PAGE_CHANGED,
 +    LAST_SIGNAL
 +};
 +
 +/** This label is used to provide a mapping from a visible page widget
 + *  back to the corresponding GncPluginPage object. */
 +#define PLUGIN_PAGE_LABEL "plugin-page"
 +
 +#define PLUGIN_PAGE_CLOSE_BUTTON "close-button"
 +#define PLUGIN_PAGE_TAB_LABEL    "label"
 +
 +#define GNC_PREF_SHOW_CLOSE_BUTTON    "tab-close-buttons"
 +#define GNC_PREF_TAB_NEXT_RECENT      "tab-next-recent"
 +#define GNC_PREF_TAB_POSITION_TOP     "tab-position-top"
 +#define GNC_PREF_TAB_POSITION_BOTTOM  "tab-position-bottom"
 +#define GNC_PREF_TAB_POSITION_LEFT    "tab-position-left"
 +#define GNC_PREF_TAB_POSITION_RIGHT   "tab-position-right"
 +#define GNC_PREF_TAB_WIDTH            "tab-width"
 +#define GNC_PREF_TAB_COLOR            "show-account-color-tabs"
 +#define GNC_PREF_SAVE_CLOSE_EXPIRES   "save-on-close-expires"
 +#define GNC_PREF_SAVE_CLOSE_WAIT_TIME "save-on-close-wait-time"
 +
 +#define GNC_MAIN_WINDOW_NAME "GncMainWindow"
 +
 +#define DIALOG_BOOK_OPTIONS_CM_CLASS "dialog-book-options"
 +
 +/* Static Globals *******************************************************/
 +
 +/** The debugging module that this .o belongs to.  */
 +static QofLogModule log_module = GNC_MOD_GUI;
 +/** A pointer to the parent class of an embedded window. */
 +static GObjectClass *parent_class = NULL;
 +/** An identifier that indicates a "main" window. */
 +static GQuark window_type = 0;
 +/** A list of all extant main windows. This is for convenience as the
 + *  same information can be obtained from the object tracking code. */
 +static GList *active_windows = NULL;
 +/** Count down timer for the save changes dialog. If the timer reaches zero
 + *  any changes will be saved and the save dialog closed automatically */
 +static guint secs_to_save = 0;
 +#define MSG_AUTO_SAVE _("Changes will be saved automatically in %u seconds")
 +
 +/* Declarations *********************************************************/
 +static void gnc_main_window_class_init (GncMainWindowClass *klass);
 +static void gnc_main_window_init (GncMainWindow *window, GncMainWindowClass *klass);
 +static void gnc_main_window_finalize (GObject *object);
 +static void gnc_main_window_destroy (GtkWidget *widget);
 +
 +static void gnc_main_window_setup_window (GncMainWindow *window);
 +static void gnc_window_main_window_init (GncWindowIface *iface);
 +#ifndef MAC_INTEGRATION
 +static void gnc_main_window_update_all_menu_items (void);
 +#endif
 +
 +/* Callbacks */
 +static void gnc_main_window_add_widget (GtkUIManager *merge, GtkWidget *widget, GncMainWindow *window);
 +static void gnc_main_window_switch_page (GtkNotebook *notebook, gpointer *notebook_page, gint pos, GncMainWindow *window);
 +static void gnc_main_window_page_reordered (GtkNotebook *notebook, GtkWidget *child, guint pos, GncMainWindow *window);
 +static void gnc_main_window_plugin_added (GncPlugin *manager, GncPlugin *plugin, GncMainWindow *window);
 +static void gnc_main_window_plugin_removed (GncPlugin *manager, GncPlugin *plugin, GncMainWindow *window);
 +static void gnc_main_window_engine_commit_error_callback( gpointer data, QofBackendError errcode );
 +
 +/* Command callbacks */
 +static void gnc_main_window_cmd_page_setup (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_file_properties (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_file_close (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_file_quit (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_edit_cut (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_edit_copy (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_edit_paste (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_edit_preferences (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_view_refresh (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_view_toolbar (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_view_summary (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_view_statusbar (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_actions_reset_warnings (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_actions_rename_page (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_window_new (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_window_move_page (GtkAction *action, GncMainWindow *window);
 +#ifndef MAC_INTEGRATION
 +static void gnc_main_window_cmd_window_raise (GtkAction *action, GtkRadioAction *current, GncMainWindow *window);
 +#endif
 +static void gnc_main_window_cmd_help_tutorial (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_help_contents (GtkAction *action, GncMainWindow *window);
 +static void gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window);
 +
 +static void do_popup_menu(GncPluginPage *page, GdkEventButton *event);
 +static gboolean gnc_main_window_popup_menu_cb (GtkWidget *widget, GncPluginPage *page);
 +static GtkWidget *gnc_main_window_get_statusbar (GncWindow *window_in);
 +static void statusbar_notification_lastmodified(void);
 +
 +#ifdef MAC_INTEGRATION
 +static void gnc_quartz_shutdown(GtkosxApplication *theApp, gpointer data);
 +static gboolean gnc_quartz_should_quit(GtkosxApplication *theApp, GncMainWindow *window);
 +static void gnc_quartz_set_menu(GncMainWindow* window);
 +#endif
 +
 +/** The instance private data structure for an embedded window
 + *  object. */
 +typedef struct GncMainWindowPrivate
 +{
 +    /** The dock (vbox) at the top of the window containing the
 +     *  menubar and toolbar.  These items are generated by the UI
 +     *  manager and stored here when the UI manager provides them
 +     *  to the main window. */
 +    GtkWidget *menu_dock;
 +    /** The toolbar created by the UI manager.  This pointer
 +     * provides easy access for showing/hiding the toolbar. */
 +    GtkWidget *toolbar;
 +    /** The notebook containing all the pages in this window. */
 +    GtkWidget *notebook;
 +    /** Show account color as background on tabs */
 +    gboolean show_color_tabs;
 +    /** A pointer to the status bar at the bottom edge of the
 +     *  window.  This pointer provides easy access for
 +     *  updating/showing/hiding the status bar. */
 +    GtkWidget *statusbar;
 +    /** A pointer to the progress bar at the bottom right of the
 +     *  window that is contained in the status bar.  This pointer
 +     *  provides easy access for updating the progressbar. */
 +    GtkWidget *progressbar;
 +    /** Pointer to the about dialog.  We need this so that we create
 +     *  only one, can attach to its activate-link signal, and can
 +     *  destroy it with the main window.
 +     */
 +    GtkWidget *about_dialog;
 +
 +    /** The group of all actions provided by the main window
 +     *  itself.  This does not include any action provided by menu
 +     *  or content plugins. */
 +    GtkActionGroup *action_group;
 +
 +    /** A list of all pages that are installed in this window. */
 +    GList *installed_pages;
 +    /** A list of pages in order of use (most recent -> least recent) */
 +    GList *usage_order;
 +    /** The currently selected page. */
 +    GncPluginPage *current_page;
 +    /** The identifier for this window's engine event handler. */
 +    gint event_handler_id;
 +
 +    /** A hash table of all action groups that have been installed
 +     *  into this window. The keys are the name of an action
 +     *  group, the values are structures of type
 +     *  MergedActionEntry. */
 +    GHashTable *merged_actions_table;
 +} GncMainWindowPrivate;
 +
 +#define GNC_MAIN_WINDOW_GET_PRIVATE(o)  \
 +   (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_MAIN_WINDOW, GncMainWindowPrivate))
 +
 +/** This data structure maintains information about one action groups
 + *  that has been installed in this window. */
 +typedef struct
 +{
 +    /** The merge identifier for this action group.  This number
 +     *  is provided by the UI manager. */
 +    guint merge_id;
 +    /** The action group itself.  This contains all actions added
 +     *  by a single menu or content plugin. */
 +    GtkActionGroup *action_group;
 +} MergedActionEntry;
 +
 +/** A holding place for all the signals generated by the main window
 + *  code. */
 +static guint main_window_signals[LAST_SIGNAL] = { 0 };
 +
 +
 +/** An array of all of the actions provided by the main window code.
 + *  This includes some placeholder actions for the menus that are
 + *  visible in the menu bar but have no action associated with
 + *  them. */
 +static GtkActionEntry gnc_menu_actions [] =
 +{
 +    /* Toplevel */
 +
 +    { "FileAction", NULL, N_("_File"), NULL, NULL, NULL, },
 +    { "EditAction", NULL, N_("_Edit"), NULL, NULL, NULL },
 +    { "ViewAction", NULL, N_("_View"), NULL, NULL, NULL },
 +    { "ActionsAction", NULL, N_("_Actions"), NULL, NULL, NULL },
 +    { "TransactionAction", NULL, N_("Tra_nsaction"), NULL, NULL, NULL },
 +    { "ReportsAction", NULL, N_("_Reports"), NULL, NULL, NULL },
 +    { "ToolsAction", NULL, N_("_Tools"), NULL, NULL, NULL },
 +    { "ExtensionsAction", NULL, N_("E_xtensions"), NULL, NULL, NULL },
 +    { "WindowsAction", NULL, N_("_Windows"), NULL, NULL, NULL },
 +    { "HelpAction", NULL, N_("_Help"), NULL, NULL, NULL },
 +
 +    /* File menu */
 +
 +    { "FileImportAction", NULL, N_("_Import"), NULL, NULL, NULL },
 +    { "FileExportAction", NULL, N_("_Export"), NULL, NULL, NULL },
 +    {
 +        "FilePrintAction", "document-print", N_("_Print..."), "<primary>p",
 +        N_("Print the currently active page"), NULL
 +    },
 +#ifndef GTK_STOCK_PAGE_SETUP
 +#    define GTK_STOCK_PAGE_SETUP NULL
 +#endif
 +    {
 +        "FilePageSetupAction", "document-page-setup", N_("Pa_ge Setup..."), "<primary><shift>p",
 +        N_("Specify the page size and orientation for printing"),
 +        G_CALLBACK (gnc_main_window_cmd_page_setup)
 +    },
 +    {
 +        "FilePropertiesAction", "document-properties", N_("Proper_ties"), "<Alt>Return",
 +        N_("Edit the properties of the current file"),
 +        G_CALLBACK (gnc_main_window_cmd_file_properties)
 +    },
 +    {
 +        "FileCloseAction", "window-close", N_("_Close"), "<primary>W",
 +        N_("Close the currently active page"),
 +        G_CALLBACK (gnc_main_window_cmd_file_close)
 +    },
 +    {
 +        "FileQuitAction", "application-exit", N_("_Quit"), "<primary>Q",
 +        N_("Quit this application"),
 +        G_CALLBACK (gnc_main_window_cmd_file_quit)
 +    },
 +
 +    /* Edit menu */
 +
 +    {
 +        "EditCutAction", "edit-cut", N_("Cu_t"), "<primary>X",
 +        N_("Cut the current selection and copy it to clipboard"),
 +        G_CALLBACK (gnc_main_window_cmd_edit_cut)
 +    },
 +    {
 +        "EditCopyAction", "edit-copy", N_("_Copy"), "<primary>C",
 +        N_("Copy the current selection to clipboard"),
 +        G_CALLBACK (gnc_main_window_cmd_edit_copy)
 +    },
 +    {
 +        "EditPasteAction", "edit-paste", N_("_Paste"), "<primary>V",
 +        N_("Paste the clipboard content at the cursor position"),
 +        G_CALLBACK (gnc_main_window_cmd_edit_paste)
 +    },
 +    {
 +        "EditPreferencesAction", "preferences-system", N_("Pr_eferences"), NULL,
 +        N_("Edit the global preferences of GnuCash"),
 +        G_CALLBACK (gnc_main_window_cmd_edit_preferences)
 +    },
 +
 +    /* View menu */
 +
 +    {
 +        "ViewSortByAction", NULL, N_("_Sort By..."), NULL,
 +        N_("Select sorting criteria for this page view"), NULL
 +    },
 +    {
 +        "ViewFilterByAction", NULL, N_("_Filter By..."), NULL,
 +        N_("Select the account types that should be displayed."), NULL
 +    },
 +    {
 +        "ViewRefreshAction", "view-refresh", N_("_Refresh"), "<primary>r",
 +        N_("Refresh this window"),
 +        G_CALLBACK (gnc_main_window_cmd_view_refresh)
 +    },
 +
 +    /* Actions menu */
 +
 +    { "ScrubMenuAction", NULL, N_("_Check & Repair"), NULL, NULL, NULL },
 +    {
 +        "ActionsForgetWarningsAction", NULL, N_("Reset _Warnings..."), NULL,
 +        N_("Reset the state of all warning messages so they will be shown again."),
 +        G_CALLBACK (gnc_main_window_cmd_actions_reset_warnings)
 +    },
 +    {
 +        "ActionsRenamePageAction", NULL, N_("Re_name Page"), NULL,
 +        N_("Rename this page."),
 +        G_CALLBACK (gnc_main_window_cmd_actions_rename_page)
 +    },
 +
 +    /* Windows menu */
 +
 +    {
 +        "WindowNewAction", NULL, N_("_New Window"), NULL,
 +        N_("Open a new top-level GnuCash window."),
 +        G_CALLBACK (gnc_main_window_cmd_window_new)
 +    },
 +    {
 +        "WindowMovePageAction", NULL, N_("New Window with _Page"), NULL,
 +        N_("Move the current page to a new top-level GnuCash window."),
 +        G_CALLBACK (gnc_main_window_cmd_window_move_page)
 +    },
 +
 +    /* Help menu */
 +
 +    {
 +        "HelpTutorialAction", "help-browser", N_("Tutorial and Concepts _Guide"), "<primary>H",
 +        N_("Open the GnuCash Tutorial"),
 +        G_CALLBACK (gnc_main_window_cmd_help_tutorial)
 +    },
 +    {
 +        "HelpContentsAction", "help-browser", N_("_Contents"), "F1",
 +        N_("Open the GnuCash Help"),
 +        G_CALLBACK (gnc_main_window_cmd_help_contents)
 +    },
 +    {
 +        "HelpAboutAction", "help-about", N_("_About"), NULL,
 +        N_("About GnuCash"),
 +        G_CALLBACK (gnc_main_window_cmd_help_about)
 +    },
 +};
 +/** The number of actions provided by the main window. */
 +static guint gnc_menu_n_actions = G_N_ELEMENTS (gnc_menu_actions);
 +
 +/** An array of all of the toggle action provided by the main window
 + *  code. */
 +static GtkToggleActionEntry toggle_actions [] =
 +{
 +    {
 +        "ViewToolbarAction", NULL, N_("_Toolbar"), NULL,
 +        N_("Show/hide the toolbar on this window"),
 +        G_CALLBACK (gnc_main_window_cmd_view_toolbar), TRUE
 +    },
 +    {
 +        "ViewSummaryAction", NULL, N_("Su_mmary Bar"), NULL,
 +        N_("Show/hide the summary bar on this window"),
 +        G_CALLBACK (gnc_main_window_cmd_view_summary), TRUE
 +    },
 +    {
 +        "ViewStatusbarAction", NULL, N_("Stat_us Bar"), NULL,
 +        N_("Show/hide the status bar on this window"),
 +        G_CALLBACK (gnc_main_window_cmd_view_statusbar), TRUE
 +    },
 +};
 +/** The number of toggle actions provided by the main window. */
 +static guint n_toggle_actions = G_N_ELEMENTS (toggle_actions);
 +
 +#ifndef MAC_INTEGRATION
 +/** An array of all of the radio action provided by the main window
 + *  code. */
 +static GtkRadioActionEntry radio_entries [] =
 +{
 +    { "Window0Action", NULL, N_("Window _1"), NULL, NULL, 0 },
 +    { "Window1Action", NULL, N_("Window _2"), NULL, NULL, 1 },
 +    { "Window2Action", NULL, N_("Window _3"), NULL, NULL, 2 },
 +    { "Window3Action", NULL, N_("Window _4"), NULL, NULL, 3 },
 +    { "Window4Action", NULL, N_("Window _5"), NULL, NULL, 4 },
 +    { "Window5Action", NULL, N_("Window _6"), NULL, NULL, 5 },
 +    { "Window6Action", NULL, N_("Window _7"), NULL, NULL, 6 },
 +    { "Window7Action", NULL, N_("Window _8"), NULL, NULL, 7 },
 +    { "Window8Action", NULL, N_("Window _9"), NULL, NULL, 8 },
 +    { "Window9Action", NULL, N_("Window _0"), NULL, NULL, 9 },
 +};
 +
 +/** The number of radio actions provided by the main window. */
 +static guint n_radio_entries = G_N_ELEMENTS (radio_entries);
 +#endif
 +
 +/** These are the "important" actions provided by the main window.
 + *  Their labels will appear when the toolbar is set to "Icons and
 + *  important text" (e.g. GTK_TOOLBAR_BOTH_HORIZ) mode. */
 +static const gchar *gnc_menu_important_actions[] =
 +{
 +    "FileCloseAction",
 +    NULL,
 +};
 +
 +
 +/** The following are in the main window so they will always be
 + *  present in the menu structure, but they are never sensitive.
 + *  These actions should be overridden in child windows where they
 + *  have meaning. */
 +static const gchar *always_insensitive_actions[] =
 +{
 +    "FilePrintAction",
 +    NULL
 +};
 +
 +
 +/** The following items in the main window should be made insensitive
 + *  at startup time.  The sensitivity will be changed by some later
 + *  event. */
 +static const gchar *initially_insensitive_actions[] =
 +{
 +    "FileCloseAction",
 +    NULL
 +};
 +
 +
 +/** The following are in the main window so they will always be
 + *  present in the menu structure, but they are always hidden.
 + *  These actions should be overridden in child windows where they
 + *  have meaning. */
 +static const gchar *always_hidden_actions[] =
 +{
 +    "ViewSortByAction",
 +    "ViewFilterByAction",
 +    NULL
 +};
 +
 +
 +/** If a page is flagged as immutable, then the following actions
 + *  cannot be performed on that page. */
 +static const gchar *immutable_page_actions[] =
 +{
 +    "FileCloseAction",
 +    NULL
 +};
 +
 +
 +/** The following actions can only be performed if there are multiple
 + *  pages in a window. */
 +static const gchar *multiple_page_actions[] =
 +{
 +    "WindowMovePageAction",
 +    NULL
 +};
 +
 +
 +/************************************************************
 + *                                                          *
 + ************************************************************/
 +#define WINDOW_COUNT            "WindowCount"
 +#define WINDOW_STRING           "Window %d"
 +#define WINDOW_GEOMETRY         "WindowGeometry"
 +#define WINDOW_POSITION         "WindowPosition"
 +#define WINDOW_MAXIMIZED        "WindowMaximized"
 +#define TOOLBAR_VISIBLE         "ToolbarVisible"
 +#define STATUSBAR_VISIBLE       "StatusbarVisible"
 +#define SUMMARYBAR_VISIBLE      "SummarybarVisible"
 +#define WINDOW_FIRSTPAGE        "FirstPage"
 +#define WINDOW_PAGECOUNT        "PageCount"
 +#define WINDOW_PAGEORDER        "PageOrder"
 +#define PAGE_TYPE               "PageType"
 +#define PAGE_NAME               "PageName"
 +#define PAGE_STRING             "Page %d"
 +
 +typedef struct
 +{
 +    GKeyFile *key_file;
 +    const gchar *group_name;
 +    gint window_num;
 +    gint page_num;
 +    gint page_offset;
 +} GncMainWindowSaveData;
 +
 +
 +/*  Iterator function to walk all pages in all windows, calling the
 + *  specified function for each page. */
 +void
 +gnc_main_window_foreach_page (GncMainWindowPageFunc fn, gpointer user_data)
 +{
 +    GncMainWindowPrivate *priv;
 +    GncMainWindow *window;
 +    GncPluginPage *page;
 +    GList *w, *p;
 +
 +    ENTER(" ");
 +    for (w = active_windows; w; w = g_list_next(w))
 +    {
 +        window = w->data;
 +        priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +        for (p = priv->installed_pages; p; p = g_list_next(p))
 +        {
 +            page = p->data;
 +            fn(page, user_data);
 +        }
 +    }
 +    LEAVE(" ");
 +}
 +
 +
 +/** Restore a single page to a window.  This function calls a page
 + *  specific function to create the actual page.  It then handles all
 + *  the common tasks such as insuring the page is installed into a
 + *  window, updating the page name, and anything else that might be
 + *  common to all pages.
 + *
 + *  @param window The GncMainWindow where the new page will be
 + *  installed.
 + *
 + *  @param data A data structure containing state about the
 + *  window/page restoration process. */
 +static void
 +gnc_main_window_restore_page (GncMainWindow *window,
 +                              GncMainWindowSaveData *data)
 +{
 +    GncMainWindowPrivate *priv;
 +    GncPluginPage *page;
 +    gchar *page_group, *page_type = NULL, *name = NULL;
 +    const gchar *class_type;
 +    GError *error = NULL;
 +
 +    ENTER("window %p, data %p (key file %p, window %d, page start %d, page num %d)",
 +          window, data, data->key_file, data->window_num, data->page_offset,
 +          data->page_num);
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    page_group = g_strdup_printf(PAGE_STRING,
 +                                 data->page_offset + data->page_num);
 +    page_type = g_key_file_get_string(data->key_file, page_group,
 +                                      PAGE_TYPE, &error);
 +    if (error)
 +    {
 +        g_warning("error reading group %s key %s: %s",
 +                  page_group, PAGE_TYPE, error->message);
 +        goto cleanup;
 +    }
 +
 +    /* See if the page already exists. */
 +    page = g_list_nth_data(priv->installed_pages, data->page_num);
 +    if (page)
 +    {
 +        class_type = GNC_PLUGIN_PAGE_GET_CLASS(page)->plugin_name;
 +        if (strcmp(page_type, class_type) != 0)
 +        {
 +            g_warning("error: page types don't match: state %s, existing page %s",
 +                      page_type, class_type);
 +            goto cleanup;
 +        }
 +    }
 +    else
 +    {
 +        /* create and install the page */
 +        page = gnc_plugin_page_recreate_page(GTK_WIDGET(window), page_type,
 +                                             data->key_file, page_group);
 +        if (page)
 +        {
 +            /* Does the page still need to be installed into the window? */
 +            if (page->window == NULL)
 +            {
 +                gnc_plugin_page_set_use_new_window(page, FALSE);
 +                gnc_main_window_open_page(window, page);
 +            }
 +
 +            /* Restore the page name */
 +            name = g_key_file_get_string(data->key_file, page_group,
 +                                         PAGE_NAME, &error);
 +            if (error)
 +            {
 +                g_warning("error reading group %s key %s: %s",
 +                          page_group, PAGE_NAME, error->message);
 +                /* Fall through and still show the page. */
 +            }
 +            else
 +            {
 +                DEBUG("updating page name for %p to %s.", page, name);
 +                main_window_update_page_name(page, name);
 +                g_free(name);
 +            }
 +        }
 +    }
 +
 +    LEAVE("ok");
 +cleanup:
 +    if (error)
 +        g_error_free(error);
 +    if (page_type)
 +        g_free(page_type);
 +    g_free(page_group);
 +}
 +
 +
 +/** Restore all the pages in a given window.  This function restores
 + *  all the window specific attributes, then calls a helper function
 + *  to restore all the pages that are contained in the window.
 + *
 + *  @param window The GncMainWindow whose pages should be restored.
 + *
 + *  @param data A data structure containing state about the
 + *  window/page restoration process. */
 +static void
 +gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *data)
 +{
 +    GncMainWindowPrivate *priv;
 +    GtkAction *action;
 +    gint *pos, *geom, *order;
 +    gsize length;
 +    gboolean max, visible, desired_visibility;
 +    gchar *window_group;
 +    gint page_start, page_count, i;
 +    GError *error = NULL;
 +
 +    /* Setup */
 +    ENTER("window %p, data %p (key file %p, window %d)",
 +          window, data, data->key_file, data->window_num);
 +    window_group = g_strdup_printf(WINDOW_STRING, data->window_num + 1);
 +
 +    /* Deal with the uncommon case that the state file defines a window
 +     * but no pages. An example to get in such a situation can be found
 +     * here: https://bugzilla.gnome.org/show_bug.cgi?id=436479#c3
 +     * If this happens on the first window, we will open an account hierarchy
 +     * to avoid confusing the user by presenting a completely empty window.
 +     * If it happens on a later window, we'll just skip restoring that window.
 +     */
 +    if (!g_key_file_has_group (data->key_file, window_group) ||
 +        !g_key_file_has_key (data->key_file, window_group, WINDOW_PAGECOUNT, &error))
 +    {
 +        if (window)
 +        {
 +            gnc_main_window_restore_default_state (window);
 +            PINFO ("saved state had an empty first main window\n"
 +                   "an account hierarchy page was added automatically to avoid confusion");
 +        }
 +        else
 +            PINFO ("saved state had an empty main window, skipping restore");
 +
 +        goto cleanup;
 +    }
 +
 +
 +    /* Get this window's notebook info */
 +    page_count = g_key_file_get_integer(data->key_file,
 +                                        window_group, WINDOW_PAGECOUNT, &error);
 +    if (error)
 +    {
 +        g_warning("error reading group %s key %s: %s",
 +                  window_group, WINDOW_PAGECOUNT, error->message);
 +        goto cleanup;
 +    }
 +    if (page_count == 0)
 +    {
 +        /* Should never happen, but has during alpha testing. Having this
 +         * check doesn't hurt anything. */
 +        goto cleanup;
 +    }
 +    page_start = g_key_file_get_integer(data->key_file,
 +                                        window_group, WINDOW_FIRSTPAGE, &error);
 +    if (error)
 +    {
 +        g_warning("error reading group %s key %s: %s",
 +                  window_group, WINDOW_FIRSTPAGE, error->message);
 +        goto cleanup;
 +    }
 +
 +    /* Build a window if we don't already have one */
 +    if (window == NULL)
 +    {
 +        DEBUG("Window %d doesn't exist. Creating new window.", data->window_num);
 +        DEBUG("active_windows %p.", active_windows);
 +        if (active_windows)
 +            DEBUG("first window %p.", active_windows->data);
 +        window = gnc_main_window_new();
 +        gtk_widget_show(GTK_WIDGET(window));
 +    }
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +
 +    /* Get the window coordinates, etc. */
 +    geom = g_key_file_get_integer_list(data->key_file, window_group,
 +                                       WINDOW_GEOMETRY, &length, &error);
 +    if (error)
 +    {
 +        g_warning("error reading group %s key %s: %s",
 +                  window_group, WINDOW_GEOMETRY, error->message);
 +        g_error_free(error);
 +        error = NULL;
 +    }
 +    else if (length != 2)
 +    {
 +        g_warning("invalid number of values for group %s key %s",
 +                  window_group, WINDOW_GEOMETRY);
 +    }
 +    else
 +    {
 +        gtk_window_resize(GTK_WINDOW(window), geom[0], geom[1]);
 +        DEBUG("window (%p) size %dx%d", window, geom[0], geom[1]);
 +    }
 +    /* keep the geometry for a test whether the windows position
 +       is offscreen */
 +
 +    pos = g_key_file_get_integer_list(data->key_file, window_group,
 +                                      WINDOW_POSITION, &length, &error);
 +    if (error)
 +    {
 +        g_warning("error reading group %s key %s: %s",
 +                  window_group, WINDOW_POSITION, error->message);
 +        g_error_free(error);
 +        error = NULL;
 +    }
 +    else if (length != 2)
 +    {
 +        g_warning("invalid number of values for group %s key %s",
 +                  window_group, WINDOW_POSITION);
 +    }
 +// This does not do any thing ?
 +//    else if ((pos[0] + (geom ? geom[0] : 0) < 0) ||
 +//             (pos[0] > gdk_screen_width()) ||
 +//             (pos[1] + (geom ? geom[1] : 0) < 0) ||
 +//             (pos[1] > gdk_screen_height()))
 +//    {
 +//    g_debug("position %dx%d, size%dx%d is offscreen; will not move",
 +//	    pos[0], pos[1], geom[0], geom[1]);
 +//    }
 +    else
 +    {
 +        gtk_window_move(GTK_WINDOW(window), pos[0], pos[1]);
 +        DEBUG("window (%p) position %dx%d", window, pos[0], pos[1]);
 +    }
 +    if (geom)
 +    {
 +        g_free(geom);
 +    }
 +    if (pos)
 +    {
 +        g_free(pos);
 +    }
 +
 +    max = g_key_file_get_boolean(data->key_file, window_group,
 +                                 WINDOW_MAXIMIZED, &error);
 +    if (error)
 +    {
 +        g_warning("error reading group %s key %s: %s",
 +                  window_group, WINDOW_MAXIMIZED, error->message);
 +        g_error_free(error);
 +        error = NULL;
 +    }
 +    else if (max)
 +    {
 +        gtk_window_maximize(GTK_WINDOW(window));
 +    }
 +
 +    /* Common view menu items */
 +    action = gnc_main_window_find_action(window, "ViewToolbarAction");
 +    visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
 +    desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
 +                         TOOLBAR_VISIBLE, &error);
 +    if (error)
 +    {
 +        g_warning("error reading group %s key %s: %s",
 +                  window_group, TOOLBAR_VISIBLE, error->message);
 +        g_error_free(error);
 +        error = NULL;
 +    }
 +    else if (visible != desired_visibility)
 +    {
 +        gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
 +    }
 +
 +    action = gnc_main_window_find_action(window, "ViewSummaryAction");
 +    visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
 +    desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
 +                         SUMMARYBAR_VISIBLE, &error);
 +    if (error)
 +    {
 +        g_warning("error reading group %s key %s: %s",
 +                  window_group, TOOLBAR_VISIBLE, error->message);
 +        g_error_free(error);
 +        error = NULL;
 +    }
 +    else if (visible != desired_visibility)
 +    {
 +        gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
 +    }
 +
 +    action = gnc_main_window_find_action(window, "ViewStatusbarAction");
 +    visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
 +    desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
 +                         STATUSBAR_VISIBLE, &error);
 +    if (error)
 +    {
 +        g_warning("error reading group %s key %s: %s",
 +                  window_group, TOOLBAR_VISIBLE, error->message);
 +        g_error_free(error);
 +        error = NULL;
 +    }
 +    else if (visible != desired_visibility)
 +    {
 +        gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
 +    }
 +
 +    /* Now populate the window with pages. */
 +    for (i = 0; i < page_count; i++)
 +    {
 +        data->page_offset = page_start;
 +        data->page_num = i;
 +        gnc_main_window_restore_page(window, data);
 +
 +        /* give the page a chance to display */
 +        while (gtk_events_pending ())
 +            gtk_main_iteration ();
 +    }
 +
 +    /* Restore page ordering within the notebook. Use +1 notation so the
 +     * numbers in the page order match the page sections, at least for
 +     * the one window case. */
 +    order = g_key_file_get_integer_list(data->key_file, window_group,
 +                                        WINDOW_PAGEORDER, &length, &error);
 +    if (error)
 +    {
 +        g_warning("error reading group %s key %s: %s",
 +                  window_group, WINDOW_PAGEORDER, error->message);
 +        g_error_free(error);
 +        error = NULL;
 +    }
 +    else if (length != page_count)
 +    {
 +        g_warning("%s key %s length %" G_GSIZE_FORMAT " differs from window page count %d",
 +                  window_group, WINDOW_PAGEORDER, length, page_count);
 +    }
 +    else
 +    {
 +        /* Dump any list that might exist */
 +        g_list_free(priv->usage_order);
 +        priv->usage_order = NULL;
 +        /* Now rebuild the list from the key file. */
 +        for (i = 0; i < length; i++)
 +        {
 +            gpointer page = g_list_nth_data(priv->installed_pages, order[i] - 1);
 +            if (page)
 +            {
 +                priv->usage_order = g_list_append(priv->usage_order, page);
 +            }
 +        }
 +        gtk_notebook_set_current_page (GTK_NOTEBOOK(priv->notebook),
 +                                       order[0] - 1);
 +    }
 +    if (order)
 +    {
 +        g_free(order);
 +    }
 +
 +    LEAVE("window %p", window);
 +cleanup:
 +    if (error)
 +        g_error_free(error);
 +    g_free(window_group);
 +}
 +
 +void
 +gnc_main_window_restore_all_windows(const GKeyFile *keyfile)
 +{
 +    gint i, window_count;
 +    GError *error = NULL;
 +    GncMainWindowSaveData data;
 +    GncMainWindow *window;
 +
 +    /* We use the same struct for reading and for writing, so we cast
 +       away the const. */
 +    data.key_file = (GKeyFile *) keyfile;
 +    window_count = g_key_file_get_integer(data.key_file, STATE_FILE_TOP,
 +                                          WINDOW_COUNT, &error);
 +    if (error)
 +    {
 +        g_warning("error reading group %s key %s: %s",
 +                  STATE_FILE_TOP, WINDOW_COUNT, error->message);
 +        g_error_free(error);
 +        LEAVE("can't read count");
 +        return;
 +    }
 +
 +    /* Restore all state information on the open windows.  Window
 +       numbers in state file are 1-based. GList indices are 0-based. */
 +    gnc_set_busy_cursor (NULL, TRUE);
 +    for (i = 0; i < window_count; i++)
 +    {
 +        data.window_num = i;
 +        window = g_list_nth_data(active_windows, i);
 +        gnc_main_window_restore_window(window, &data);
 +    }
 +    gnc_unset_busy_cursor (NULL);
 +
 +    statusbar_notification_lastmodified();
 +}
 +
 +void
 +gnc_main_window_restore_default_state(GncMainWindow *window)
 +{
 +    GtkAction *action;
 +
 +    /* The default state should be to have an Account Tree page open
 +     * in the window. */
 +    DEBUG("no saved state file");
 +    if (!window)
 +        window = g_list_nth_data(active_windows, 0);
 +    action = gnc_main_window_find_action(window, "ViewAccountTreeAction");
 +    gtk_action_activate(action);
 +}
 +
 +/** Save the state of a single page to a disk.  This function handles
 + *  all the common tasks such as saving the page type and name, and
 + *  anything else that might be common to all pages.  It then calls a
 + *  page specific function to save the actual page.
 + *
 + *  @param page The GncPluginPage whose state should be saved.
 + *
 + *  @param data A data structure containing state about the
 + *  window/page saving process. */
 +static void
 +gnc_main_window_save_page (GncPluginPage *page, GncMainWindowSaveData *data)
 +{
 +    gchar *page_group;
 +    const gchar *plugin_name, *page_name;
 +
 +    ENTER("page %p, data %p (key file %p, window %d, page %d)",
 +          page, data, data->key_file, data->window_num, data->page_num);
 +    plugin_name = gnc_plugin_page_get_plugin_name(page);
 +    page_name = gnc_plugin_page_get_page_name(page);
 +    if (!plugin_name || !page_name)
 +    {
 +        LEAVE("not saving invalid page");
 +        return;
 +    }
 +    page_group = g_strdup_printf(PAGE_STRING, data->page_num++);
 +    g_key_file_set_string(data->key_file, page_group, PAGE_TYPE, plugin_name);
 +    g_key_file_set_string(data->key_file, page_group, PAGE_NAME, page_name);
 +
 +    gnc_plugin_page_save_page(page, data->key_file, page_group);
 +    g_free(page_group);
 +    LEAVE(" ");
 +}
 +
 +
 +/** Saves all the pages in a single window to a disk.  This function
 + *  saves all the window specific attributes, then calls a helper
 + *  function to save all the pages that are contained in the window.
 + *
 + *  @param window The GncMainWindow whose pages should be saved.
 + *
 + *  @param data A data structure containing state about the
 + *  window/page saving process. */
 +static void
 +gnc_main_window_save_window (GncMainWindow *window, GncMainWindowSaveData *data)
 +{
 +    GncMainWindowPrivate *priv;
 +    GtkAction *action;
 +    gint i, num_pages, coords[4], *order;
 +    gboolean maximized, visible;
 +    gchar *window_group;
 +
 +    /* Setup */
 +    ENTER("window %p, data %p (key file %p, window %d)",
 +          window, data, data->key_file, data->window_num);
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +
 +    /* Check for bogus window structures. */
 +    num_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook));
 +    if (0 == num_pages)
 +    {
 +        LEAVE("empty window %p", window);
 +        return;
 +    }
 +
 +    /* Save this window's notebook info */
 +    window_group = g_strdup_printf(WINDOW_STRING, data->window_num++);
 +    g_key_file_set_integer(data->key_file, window_group,
 +                           WINDOW_PAGECOUNT, num_pages);
 +    g_key_file_set_integer(data->key_file, window_group,
 +                           WINDOW_FIRSTPAGE, data->page_num);
 +
 +    /* Save page ordering within the notebook. Use +1 notation so the
 +     * numbers in the page order match the page sections, at least for
 +     * the one window case. */
 +    order = g_malloc(sizeof(gint) * num_pages);
 +    for (i = 0; i < num_pages; i++)
 +    {
 +        gpointer page = g_list_nth_data(priv->usage_order, i);
 +        order[i] = g_list_index(priv->installed_pages, page) + 1;
 +    }
 +    g_key_file_set_integer_list(data->key_file, window_group,
 +                                WINDOW_PAGEORDER, order, num_pages);
 +    g_free(order);
 +
 +    /* Save the window coordinates, etc. */
 +    gtk_window_get_position(GTK_WINDOW(window), &coords[0], &coords[1]);
 +    gtk_window_get_size(GTK_WINDOW(window), &coords[2], &coords[3]);
 +    maximized = (gdk_window_get_state(gtk_widget_get_window ((GTK_WIDGET(window))))
 +                 & GDK_WINDOW_STATE_MAXIMIZED) != 0;
 +    g_key_file_set_integer_list(data->key_file, window_group,
 +                                WINDOW_POSITION, &coords[0], 2);
 +    g_key_file_set_integer_list(data->key_file, window_group,
 +                                WINDOW_GEOMETRY, &coords[2], 2);
 +    g_key_file_set_boolean(data->key_file, window_group,
 +                           WINDOW_MAXIMIZED, maximized);
 +    DEBUG("window (%p) position %dx%d, size %dx%d, %s", window,  coords[0], coords[1],
 +          coords[2], coords[3],
 +          maximized ? "maximized" : "not maximized");
 +
 +    /* Common view menu items */
 +    action = gnc_main_window_find_action(window, "ViewToolbarAction");
 +    visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
 +    g_key_file_set_boolean(data->key_file, window_group,
 +                           TOOLBAR_VISIBLE, visible);
 +    action = gnc_main_window_find_action(window, "ViewSummaryAction");
 +    visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
 +    g_key_file_set_boolean(data->key_file, window_group,
 +                           SUMMARYBAR_VISIBLE, visible);
 +    action = gnc_main_window_find_action(window, "ViewStatusbarAction");
 +    visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
 +    g_key_file_set_boolean(data->key_file, window_group,
 +                           STATUSBAR_VISIBLE, visible);
 +
 +    /* Save individual pages in this window */
 +    g_list_foreach(priv->installed_pages, (GFunc)gnc_main_window_save_page, data);
 +
 +    g_free(window_group);
 +    LEAVE("window %p", window);
 +}
 +
 +void
 +gnc_main_window_save_all_windows(GKeyFile *keyfile)
 +{
 +    GncMainWindowSaveData data;
 +
 +    /* Set up the iterator data structures */
 +    data.key_file = keyfile;
 +    data.window_num = 1;
 +    data.page_num = 1;
 +
 +    g_key_file_set_integer(data.key_file,
 +                           STATE_FILE_TOP, WINDOW_COUNT,
 +                           g_list_length(active_windows));
 +    /* Dump all state information on the open windows */
 +    g_list_foreach(active_windows, (GFunc)gnc_main_window_save_window, &data);
 +}
 +
 +
 +gboolean
 +gnc_main_window_finish_pending (GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +    GList *item;
 +
 +    g_return_val_if_fail(GNC_IS_MAIN_WINDOW(window), TRUE);
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    for (item = priv->installed_pages; item; item = g_list_next(item))
 +    {
 +        if (!gnc_plugin_page_finish_pending(item->data))
 +        {
 +            return FALSE;
 +        }
 +    }
 +    return TRUE;
 +}
 +
 +
 +gboolean
 +gnc_main_window_all_finish_pending (void)
 +{
 +    const GList *windows, *item;
 +
 +    windows = gnc_gobject_tracking_get_list(GNC_MAIN_WINDOW_NAME);
 +    for (item = windows; item; item = g_list_next(item))
 +    {
 +        if (!gnc_main_window_finish_pending(item->data))
 +        {
 +            return FALSE;
 +        }
 +    }
 +    return TRUE;
 +}
 +
 +
 +/** See if the page already exists.  For each open window, look
 + *  through the list of pages installed in that window and see if the
 + *  specified page is there.
 + *
 + *  @internal
 + *
 + *  @param page The page to search for.
 + *
 + *  @return TRUE if the page is present in the window, FALSE otherwise.
 + */
 +static gboolean
 +gnc_main_window_page_exists (GncPluginPage *page)
 +{
 +    GncMainWindow *window;
 +    GncMainWindowPrivate *priv;
 +    GList *walker;
 +
 +    for (walker = active_windows; walker; walker = g_list_next(walker))
 +    {
 +        window = walker->data;
 +        priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +        if (g_list_find(priv->installed_pages, page))
 +        {
 +            return TRUE;
 +        }
 +    }
 +    return FALSE;
 +}
 +
 +static gboolean auto_save_countdown (GtkWidget *dialog)
 +{
 +    GtkWidget *label;
 +    gchar *timeoutstr = NULL;
 +
 +    /* Stop count down if user closed the dialog since the last time we were called */
 +    if (!GTK_IS_DIALOG (dialog))
 +        return FALSE; /* remove timer */
 +
 +    /* Stop count down if count down text can't be updated */
 +    label = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), "count-down-label"));
 +    if (!GTK_IS_LABEL (label))
 +        return FALSE; /* remove timer */
 +
 +    secs_to_save--;
 +    DEBUG ("Counting down: %d seconds", secs_to_save);
 +
 +    timeoutstr = g_strdup_printf (MSG_AUTO_SAVE, secs_to_save);
 +    gtk_label_set_text (GTK_LABEL (label), timeoutstr);
 +    g_free (timeoutstr);
 +
 +    /* Count down reached 0. Save and close dialog */
 +    if (!secs_to_save)
 +    {
 +        gtk_dialog_response (GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);
 +        return FALSE; /* remove timer */
 +    }
 +
 +    /* Run another cycle */
 +    return TRUE;
 +}
 +
 +
 +/** This function prompts the user to save the file with a dialog that
 + *  follows the HIG guidelines.
 + *
 + *  @internal
 + *
 + *  @returns This function returns TRUE if the user clicked the Cancel
 + *  button.  It returns FALSE if the closing of the window should
 + *  continue.
 + */
 +static gboolean
 +gnc_main_window_prompt_for_save (GtkWidget *window)
 +{
 +    QofSession *session;
 +    QofBook *book;
 +    GtkWidget *dialog, *msg_area, *label;
 +    gint response;
 +    const gchar *filename, *tmp;
 +    const gchar *title = _("Save changes to file %s before closing?");
 +    /* This should be the same message as in gnc-file.c */
 +    const gchar *message_hours =
 +        _("If you don't save, changes from the past %d hours and %d minutes will be discarded.");
 +    const gchar *message_days =
 +        _("If you don't save, changes from the past %d days and %d hours will be discarded.");
 +    time64 oldest_change;
 +    gint minutes, hours, days;
 +
 +    session = gnc_get_current_session();
 +    book = qof_session_get_book(session);
 +    filename = qof_session_get_url(session);
 +    if (!strlen (filename))
 +        filename = _("<unknown>");
 +    if ((tmp = strrchr(filename, '/')) != NULL)
 +        filename = tmp + 1;
 +
 +    /* Remove any pending auto-save timeouts */
 +    gnc_autosave_remove_timer(book);
 +
 +    dialog = gtk_message_dialog_new(GTK_WINDOW(window),
 +                                    GTK_DIALOG_MODAL,
 +                                    GTK_MESSAGE_WARNING,
 +                                    GTK_BUTTONS_NONE,
 +                                    title,
 +                                    filename);
 +    oldest_change = qof_book_get_session_dirty_time(book);
 +    minutes = (gnc_time (NULL) - oldest_change) / 60 + 1;
 +    hours = minutes / 60;
 +    minutes = minutes % 60;
 +    days = hours / 24;
 +    hours = hours % 24;
 +    if (days > 0)
 +    {
 +        gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
 +                message_days, days, hours);
 +    }
 +    else if (hours > 0)
 +    {
 +        gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
 +                message_hours, hours, minutes);
 +    }
 +    else
 +    {
 +        gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
 +                ngettext("If you don't save, changes from the past %d minute will be discarded.",
 +                         "If you don't save, changes from the past %d minutes will be discarded.",
 +                         minutes), minutes);
 +    }
 +    gtk_dialog_add_buttons(GTK_DIALOG(dialog),
 +                           _("Close _Without Saving"), GTK_RESPONSE_CLOSE,
 +                           _("_Cancel"), GTK_RESPONSE_CANCEL,
 +                           _("_Save"), GTK_RESPONSE_APPLY,
 +                           NULL);
 +    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);
 +
 +    /* If requested by the user, add a timeout to the question to save automatically
 +     * if the user doesn't answer after a chosen number of seconds.
 +     */
 +    if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_CLOSE_EXPIRES))
 +    {
 +        gchar *timeoutstr = NULL;
 +
 +        secs_to_save = gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_CLOSE_WAIT_TIME);
 +        timeoutstr = g_strdup_printf (MSG_AUTO_SAVE, secs_to_save);
 +        label = GTK_WIDGET(gtk_label_new (timeoutstr));
 +        g_free (timeoutstr);
 +        gtk_widget_show (label);
 +
 +        msg_area = gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG(dialog));
 +        gtk_box_pack_end (GTK_BOX(msg_area), label, TRUE, TRUE, 0);
 +        g_object_set (G_OBJECT (label), "xalign", 0.0, NULL);
 +
 +        g_object_set_data (G_OBJECT (dialog), "count-down-label", label);
 +        g_timeout_add_seconds (1, (GSourceFunc)auto_save_countdown, dialog);
 +    }
 +
 +    response = gtk_dialog_run (GTK_DIALOG (dialog));
 +    gtk_widget_destroy(dialog);
 +
 +    switch (response)
 +    {
 +    case GTK_RESPONSE_APPLY:
 +        gnc_file_save();
 +        return FALSE;
 +
 +    case GTK_RESPONSE_CLOSE:
 +        qof_book_mark_session_saved(book);
 +        return FALSE;
 +
 +    default:
 +        return TRUE;
 +    }
 +}
 +
 +
 +static void
 +gnc_main_window_add_plugin (gpointer plugin,
 +                            gpointer window)
 +{
 +    g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
 +    g_return_if_fail (GNC_IS_PLUGIN (plugin));
 +
 +    ENTER(" ");
 +    gnc_plugin_add_to_window (GNC_PLUGIN (plugin),
 +                              GNC_MAIN_WINDOW (window),
 +                              window_type);
 +    LEAVE(" ");
 +}
 +
 +static void
 +gnc_main_window_remove_plugin (gpointer plugin,
 +                               gpointer window)
 +{
 +    g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
 +    g_return_if_fail (GNC_IS_PLUGIN (plugin));
 +
 +    ENTER(" ");
 +    gnc_plugin_remove_from_window (GNC_PLUGIN (plugin),
 +                                   GNC_MAIN_WINDOW (window),
 +                                   window_type);
 +    LEAVE(" ");
 +}
 +
 +
 +static gboolean
 +gnc_main_window_timed_quit (gpointer dummy)
 +{
 +    if (gnc_file_save_in_progress())
 +        return TRUE;
 +
 +    gnc_shutdown (0);
 +    return FALSE;
 +}
 +
 +static gboolean
 +gnc_main_window_quit(GncMainWindow *window)
 +{
 +    QofSession *session;
 +    gboolean needs_save, do_shutdown;
 +
 +    session = gnc_get_current_session();
 +    needs_save = qof_book_session_not_saved(qof_session_get_book(session)) &&
 +                 !gnc_file_save_in_progress();
 +    do_shutdown = !needs_save ||
 +                  (needs_save && !gnc_main_window_prompt_for_save(GTK_WIDGET(window)));
 +
 +    if (do_shutdown)
 +    {
 +        g_timeout_add(250, gnc_main_window_timed_quit, NULL);
 +        return TRUE;
 +    }
 +    return FALSE;
 +}
 +
 +static gboolean
 +gnc_main_window_delete_event (GtkWidget *window,
 +                              GdkEvent *event,
 +                              gpointer user_data)
 +{
 +    static gboolean already_dead = FALSE;
 +
 +    if (already_dead)
 +        return TRUE;
 +
 +    if (!gnc_main_window_finish_pending(GNC_MAIN_WINDOW(window)))
 +    {
 +        /* Don't close the window. */
 +        return TRUE;
 +    }
 +
 +    if (g_list_length(active_windows) > 1)
 +        return FALSE;
 +
 +    already_dead = gnc_main_window_quit(GNC_MAIN_WINDOW(window));
 +    return TRUE;
 +}
 +
 +
 +/** This function handles any event notifications from the engine.
 + *  The only event it currently cares about is the deletion of a book.
 + *  When a book is deleted, it runs through all installed pages
 + *  looking for pages that reference the just (about to be?) deleted
 + *  book.  It closes any page it finds so there are no dangling
 + *  references to the book.
 + *
 + *  @internal
 + *
 + *  @param entity     The guid the item being added, deleted, etc.
 + *
 + *  @param type       The type of the item being added, deleted, etc. This
 + *                    function only cares about a type of GNC_ID_BOOK.
 + *
 + *  @param event_type The type of the event.  This function only cares
 + *                    about an event type of QOF_EVENT_DESTROY.
 + *
 + *  @param user_data  A pointer to the window data structure.
 + */
 +static void
 +gnc_main_window_event_handler (QofInstance *entity,  QofEventId event_type,
 +                               gpointer user_data, gpointer event_data)
 +{
 +    GncMainWindow *window;
 +    GncMainWindowPrivate *priv;
 +    GncPluginPage *page;
 +    GList *item, *next;
 +
 +    /* hard failures */
 +    g_return_if_fail(GNC_IS_MAIN_WINDOW(user_data));
 +
 +    /* soft failures */
 +    if (!QOF_CHECK_TYPE(entity, QOF_ID_BOOK))
 +        return;
 +    if (event_type !=  QOF_EVENT_DESTROY)
 +        return;
 +
 +    ENTER("entity %p, event %d, window %p, event data %p",
 +          entity, event_type, user_data, event_data);
 +    window = GNC_MAIN_WINDOW(user_data);
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +
 +    /* This is not a typical list iteration.  We're removing while
 +     * we iterate, so we have to cache the 'next' pointer before
 +     * executing any code in the loop. */
 +    for (item = priv->installed_pages; item; item = next)
 +    {
 +        next = g_list_next(item);
 +        page = GNC_PLUGIN_PAGE(item->data);
 +        if (gnc_plugin_page_has_book (page, (QofBook *)entity))
 +            gnc_main_window_close_page (page);
 +    }
 +    LEAVE(" ");
 +}
 +
 +
 +/** Generate a title for this window based upon the Gnome Human
 + *  Interface Guidelines, v2.0.  This title will be used as both the
 + *  window title and the title of the "Window" menu item associated
 + *  with the window.
 + *
 + *  As a side-effect, the save action is set sensitive iff the book
 + *  is dirty, and the immutable_page_actions are set sensitive iff the page is
 + *  mutable.
 + *
 + *  @param window The window whose title should be generated.
 + *
 + *  @return The title for the window.  It is the callers
 + *  responsibility to free this string.
 + *
 + *  @internal
 + */
 +static gchar *
 +gnc_main_window_generate_title (GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +    GncPluginPage *page;
 +    QofBook *book;
 +    gboolean immutable;
 +    gchar *filename = NULL;
 +    const gchar *book_id = NULL;
 +    const gchar *dirty = "";
 +    const gchar *readonly_text = NULL;
 +    gchar *readonly;
 +    gchar *title;
 +
 +    if (gnc_current_session_exist())
 +    {
 +        book_id = qof_session_get_url (gnc_get_current_session ());
 +        book = gnc_get_current_book();
 +        if (qof_book_session_not_saved (book))
 +            dirty = "*";
 +        if (qof_book_is_readonly(book))
 +        {
 +            /* Translators: This string is shown in the window title if this
 +            document is, well, read-only. */
 +            readonly_text = _("(read-only)");
 +        }
 +    }
 +    readonly = (readonly_text != NULL)
 +               ? g_strdup_printf(" %s", readonly_text)
 +               : g_strdup("");
 +
 +    if (!book_id || g_strcmp0 (book_id, "") == 0)
 +        filename = g_strdup(_("Unsaved Book"));
 +    else
 +    {
 +        if ( gnc_uri_is_file_uri ( book_id ) )
 +        {
 +            /* The filename is a true file.
 +             * The Gnome HIG 2.0 recommends only the file name (no path) be used. (p15) */
 +            gchar *path = gnc_uri_get_path ( book_id );
 +            filename = g_path_get_basename ( path );
 +            g_free ( path );
 +        }
 +        else
 +        {
 +            /* The filename is composed of database connection parameters.
 +             * For this we will show access_method://username@database[:port] */
 +            filename = gnc_uri_normalize_uri (book_id, FALSE);
 +        }
 +    }
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    page = priv->current_page;
 +    if (page)
 +    {
 +        /* The Gnome HIG 2.0 recommends the application name not be used. (p16)
 +         * but several developers prefer to use it anyway. */
 +        title = g_strdup_printf("%s%s%s - %s - GnuCash", dirty, filename, readonly,
 +                                gnc_plugin_page_get_page_name(page));
 +    }
 +    else
 +    {
 +        title = g_strdup_printf("%s%s%s - GnuCash", dirty, filename, readonly);
 +    }
 +    /* Update the menus based upon whether this is an "immutable" page. */
 +    immutable = page &&
 +                g_object_get_data (G_OBJECT (page), PLUGIN_PAGE_IMMUTABLE);
 +    gnc_plugin_update_actions(priv->action_group,
 +                              immutable_page_actions,
 +                              "sensitive", !immutable);
 +    /* Trigger sensitivity updtates of other actions such as Save/Revert */
 +    g_signal_emit_by_name (window, "page_changed", page);
 +    g_free( filename );
 +    g_free(readonly);
 +
 +    return title;
 +}
 +
 +
 +/** Update the title bar on the specified window.  This routine uses
 + *  the gnc_main_window_generate_title() function to create the title.
 + *  It is called whenever the user switched pages in a window, as the
 + *  title includes the name of the current page.
 + *
 + *  @param window The window whose title should be updated.
 + *
 + *  @internal
 + */
 +static void
 +gnc_main_window_update_title (GncMainWindow *window)
 +{
 +    gchar *title;
 +
 +    title = gnc_main_window_generate_title(window);
 +    gtk_window_set_title(GTK_WINDOW(window), title);
 +    g_free(title);
 +}
 +
 +static void
 +gnc_main_window_update_all_titles (void)
 +{
 +    g_list_foreach(active_windows,
 +                   (GFunc)gnc_main_window_update_title,
 +                   NULL);
 +}
 +
 +static void
 +gnc_main_window_book_dirty_cb (QofBook *book,
 +                               gboolean dirty,
 +                               gpointer user_data)
 +{
 +    gnc_main_window_update_all_titles();
 +
 +    /* Auto-save feature */
 +    gnc_autosave_dirty_handler(book, dirty);
 +}
 +
 +static void
 +gnc_main_window_attach_to_book (QofSession *session)
 +{
 +    QofBook *book;
 +
 +    g_return_if_fail(session);
 +
 +    book = qof_session_get_book(session);
 +    qof_book_set_dirty_cb(book, gnc_main_window_book_dirty_cb, NULL);
 +    gnc_main_window_update_all_titles();
 +#ifndef MAC_INTEGRATION
 +    gnc_main_window_update_all_menu_items();
 +#endif
 +}
 +
 +static guint gnc_statusbar_notification_messageid = 0;
 +//#define STATUSBAR_NOTIFICATION_AUTOREMOVAL
 +#ifdef STATUSBAR_NOTIFICATION_AUTOREMOVAL
 +/* Removes the statusbar notification again that has been pushed to the
 + * statusbar by generate_statusbar_lastmodified_message. */
 +static gboolean statusbar_notification_off(gpointer user_data_unused)
 +{
 +    GtkWidget *widget = gnc_ui_get_toplevel();
 +    //g_warning("statusbar_notification_off\n");
 +    if (gnc_statusbar_notification_messageid == 0)
 +        return FALSE;
 +
 +    if (widget && GNC_IS_MAIN_WINDOW(widget))
 +    {
 +        GncMainWindow *mainwindow = GNC_MAIN_WINDOW(widget);
 +        GtkWidget *statusbar = gnc_main_window_get_statusbar(GNC_WINDOW(mainwindow));
 +        gtk_statusbar_remove(GTK_STATUSBAR(statusbar), 0, gnc_statusbar_notification_messageid);
 +        gnc_statusbar_notification_messageid = 0;
 +    }
 +    else
 +    {
 +        g_warning("oops, no GncMainWindow obtained\n");
 +    }
 +    return FALSE; // should not be called again
 +}
 +#endif // STATUSBAR_NOTIFICATION_AUTOREMOVAL
 +
 +/* Creates a statusbar message stating the last modification time of the opened
 + * data file. */
 +static gchar *generate_statusbar_lastmodified_message()
 +{
 +    gchar *message = NULL;
 +    const gchar *book_id = NULL;
 +
 +    if (gnc_current_session_exist())
 +    {
 +        book_id = qof_session_get_url (gnc_get_current_session ());
 +    }
 +
 +    if (!strlen (book_id))
 +        return NULL;
 +    else
 +    {
 +        if ( gnc_uri_is_file_uri ( book_id ) )
 +        {
 +#ifdef HAVE_SYS_STAT_H
 +            /* The filename is a true file. */
 +            gchar *filepath = gnc_uri_get_path ( book_id );
 +            gchar *filename = g_path_get_basename ( filepath );
 +            {
 +                // Access the mtime information through stat(2)
 +                struct stat statbuf;
 +                int r = stat(filepath, &statbuf);
 +                if (r == 0)
 +                {
 +                    /* Translators: This is the date and time that is shown in
 +                    the status bar after opening a file: The date and time of
 +                    last modification. The string is a format string using
 +                    boost::date_time's format flags, see the boost docs for an
 +                    explanation of the modifiers. */
 +                    char *time_string =
 +			gnc_print_time64(statbuf.st_mtime,
 +					 _("Last modified on %a, %b %d, %Y at %I:%M %p"));
 +                    //g_warning("got time %ld, str=%s\n", mtime, time_string);
 +                    /* Translators: This message appears in the status bar after opening the file. */
 +                    message = g_strdup_printf(_("File %s opened. %s"),
 +                                              filename, time_string);
 +                    free(time_string);
 +                }
 +                else
 +                {
 +                    g_warning("Unable to read mtime for file %s\n", filepath);
 +                    // message is still NULL
 +                }
 +            }
 +            g_free(filename);
 +            g_free(filepath);
 +#else
 +            return NULL;
 +#endif
 +        }
 +        // If the URI is not a file but a database, we can maybe also show
 +        // something useful, but I have no idea how to obtain this information.
 +    }
 +    return message;
 +}
 +
 +static void
 +statusbar_notification_lastmodified()
 +{
 +    // First look up the first GncMainWindow to set the statusbar there
 +    GList *iter;
 +    GtkWidget *widget = NULL;
 +    for (iter = active_windows; iter && !(widget && GNC_IS_MAIN_WINDOW(widget));
 +            iter = g_list_next(iter))
 +    {
 +        widget = iter->data;
 +    }
 +    if (widget && GNC_IS_MAIN_WINDOW(widget))
 +    {
 +        // Ok, we found a mainwindow where we can set a statusbar message
 +        GncMainWindow *mainwindow = GNC_MAIN_WINDOW(widget);
 +        GtkWidget *statusbar = gnc_main_window_get_statusbar(GNC_WINDOW(mainwindow));
 +
 +        gchar *msg = generate_statusbar_lastmodified_message();
 +        if (msg)
 +        {
 +            gnc_statusbar_notification_messageid = gtk_statusbar_push(GTK_STATUSBAR(statusbar), 0, msg);
 +        }
 +        g_free(msg);
 +
 +#ifdef STATUSBAR_NOTIFICATION_AUTOREMOVAL
 +        // Also register a timeout callback to remove that statusbar
 +        // notification again after 10 seconds
 +        g_timeout_add(10 * 1000, statusbar_notification_off, NULL); // maybe not needed anyway?
 +#endif
 +    }
 +    else
 +    {
 +        g_warning("uh oh, no GNC_IS_MAIN_WINDOW\n");
 +    }
 +}
 +
 +
 +/** This data structure is used to describe the requested state of a
 + *  GtkRadioAction, and us used to pass data among several
 + *  functions. */
 +struct menu_update
 +{
 +    /** The name of the GtkRadioAction to be updated. */
 +    gchar    *action_name;
 +
 +    /** The new label for this GtkRadioAction. */
 +    gchar    *label;
 +
 +    /** Whether or not the GtkRadioAction should be visible. */
 +    gboolean  visible;
 +};
 +
 +#ifndef MAC_INTEGRATION
 +/** Update the label on the specified GtkRadioAction in the specified
 + *  window.  This action is displayed as a menu item in the "Windows"
 + *  menu.  This function will end up being called whenever the front
 + *  page is changed in any window, or whenever a window is added or
 + *  deleted.
 + *
 + *  @param window The window whose menu item should be updated.
 + *
 + *  @param data A data structure containing the name of the
 + *  GtkRadioAction, and describing the new state for this action.
 + *
 + *  @internal
 + */
 +static void
 +gnc_main_window_update_one_menu_action (GncMainWindow *window,
 +                                        struct menu_update *data)
 +{
 +    GncMainWindowPrivate *priv;
 +    GtkAction* action;
 +
 +    ENTER("window %p, action %s, label %s, visible %d", window,
 +          data->action_name, data->label, data->visible);
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    action = gtk_action_group_get_action(priv->action_group, data->action_name);
 +    if (action)
 +        g_object_set(G_OBJECT(action),
 +                     "label", data->label,
 +                     "visible", data->visible,
 +                     (char *)NULL);
 +    LEAVE(" ");
 +}
 +
 +/** Update the window selection GtkRadioAction for a specific window.
 + *  This is fairly simple since the windows are listed in the same
 + *  order that they appear in the active_windows list, so the index
 + *  from the window list is used to generate the name of the action.
 + *  If the code is ever changed to allow more than ten open windows in
 + *  the menu, then the actions in the menu will need to be dynamically
 + *  generated/deleted and it gets harder.
 + *
 + *  @param window The window whose menu item should be updated.
 + *
 + *  @internal
 + */
 +static void
 +gnc_main_window_update_radio_button (GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +    GtkAction *action, *first_action;
 +    GSList *action_list;
 +    gchar *action_name;
 +    gint index;
 +
 +    ENTER("window %p", window);
 +
 +    /* Show the new entry in all windows. */
 +    index = g_list_index(active_windows, window);
 +    if (index >= n_radio_entries)
 +    {
 +        LEAVE("window %d, only %d actions", index, n_radio_entries);
 +        return;
 +    }
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    action_name = g_strdup_printf("Window%dAction", index);
 +    action = gtk_action_group_get_action(priv->action_group, action_name);
 +
 +    /* Block the signal so as not to affect window ordering (top to
 +     * bottom) on the screen */
 +    action_list = gtk_radio_action_get_group(GTK_RADIO_ACTION(action));
 +    if (action_list)
 +    {
 +        first_action = g_slist_last(action_list)->data;
 +        g_signal_handlers_block_by_func(G_OBJECT(first_action),
 +                                        G_CALLBACK(gnc_main_window_cmd_window_raise),
 +                                        window);
 +        DEBUG("blocked signal on %p, set %p active, window %p", first_action,
 +              action, window);
 +        gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
 +        g_signal_handlers_unblock_by_func(G_OBJECT(first_action),
 +                                          G_CALLBACK(gnc_main_window_cmd_window_raise),
 +                                          window);
 +    }
 +    g_free(action_name);
 +    LEAVE(" ");
 +}
 +
 +/** In every window that the user has open, update the "Window" menu
 + *  item that points to the specified window.  This keeps the "Window"
 + *  menu items consistent across all open windows.  (These items
 + *  cannot be shared because of the way the GtkUIManager code works.)
 + *
 + *  This function is called whenever the user switches pages in a
 + *  window, or whenever a window is added or deleted.
 + *
 + *  @param window The window whose menu item should be updated in all
 + *  open windows.
 + *
 + *  @internal
 + */
 +static void
 +gnc_main_window_update_menu_item (GncMainWindow *window)
 +{
 +    struct menu_update data;
 +    gchar **strings, *title, *expanded;
 +    gint index;
 +
 +    ENTER("window %p", window);
 +    index = g_list_index(active_windows, window);
 +    if (index > n_radio_entries)
 +    {
 +        LEAVE("skip window %d (only %d entries)", index, n_radio_entries);
 +        return;
 +    }
 +
 +    /* Figure out the label name. Add the accelerator if possible. */
 +    title = gnc_main_window_generate_title(window);
 +    strings = g_strsplit(title, "_", 0);
 +    g_free(title);
 +    expanded = g_strjoinv("__", strings);
 +    if (index < 10)
 +    {
 +        data.label = g_strdup_printf("_%d %s", (index + 1) % 10, expanded);
 +        g_free(expanded);
 +    }
 +    else
 +    {
 +        data.label = expanded;
 +    }
 +    g_strfreev(strings);
 +
 +    data.visible = TRUE;
 +    data.action_name = g_strdup_printf("Window%dAction", index);
 +    g_list_foreach(active_windows,
 +                   (GFunc)gnc_main_window_update_one_menu_action,
 +                   &data);
 +    g_free(data.action_name);
 +    g_free(data.label);
 +
 +    LEAVE(" ");
 +}
 +#endif /* !MAC_INTEGRATION */
 +
 +/** Update all menu entries for all window menu items in all windows.
 + *  This function is called whenever a window is added or deleted.
 + *  The worst case scenario is where the user has deleted the first
 + *  window, so every single visible item needs to be updated.
 + *
 + *  @internal
 + */
 +
 +#ifndef MAC_INTEGRATION
 +static void
 +gnc_main_window_update_all_menu_items (void)
 +{
 +    struct menu_update data;
 +    gchar *label;
 +    gint i;
 +
 +    ENTER("");
 +    /* First update the entries for all existing windows */
 +    g_list_foreach(active_windows,
 +                   (GFunc)gnc_main_window_update_menu_item,
 +                   NULL);
 +    g_list_foreach(active_windows,
 +                   (GFunc)gnc_main_window_update_radio_button,
 +                   NULL);
 +
 +    /* Now hide any entries that aren't being used. */
 +    data.visible = FALSE;
 +    for (i = g_list_length(active_windows); i < n_radio_entries; i++)
 +    {
 +        data.action_name = g_strdup_printf("Window%dAction", i);
 +        label = g_strdup_printf("Window _%d", (i - 1) % 10);
 +        data.label = gettext(label);
 +
 +        g_list_foreach(active_windows,
 +                       (GFunc)gnc_main_window_update_one_menu_action,
 +                       &data);
 +
 +        g_free(data.action_name);
 +        g_free(label);
 +    }
 +    LEAVE(" ");
 +}
 +#endif /* !MAC_INTEGRATION */
 +
 +/** Show/hide the close box on the tab of a notebook page.  This
 + *  function first checks to see if the specified page has a close
 + *  box, and if so, sets its visibility to the requested state.
 + *
 + *  @internal
 + *
 + *  @param page The GncPluginPage whose notebook tab should be updated.
 + *
 + *  @param new_value A pointer to the boolean that indicates whether
 + *  or not the close button should be visible.
 + */
 +static void
 +gnc_main_window_update_tab_close_one_page (GncPluginPage *page,
 +        gpointer user_data)
 +{
 +    gboolean *new_value = user_data;
 +    GtkWidget * close_button;
 +
 +    ENTER("page %p, visible %d", page, *new_value);
 +    close_button = g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON);
 +    if (!close_button)
 +    {
 +        LEAVE("no close button");
 +        return;
 +    }
 +
 +    if (*new_value)
 +        gtk_widget_show (close_button);
 +    else
 +        gtk_widget_hide (close_button);
 +    LEAVE(" ");
 +}
 +
 +
 +/** Show/hide the close box on all pages in all windows.  This function
 + *  calls gnc_main_window_update_tab_close() for each plugin page in the
 + *  application.
 + *
 + *  @internal
 + *
 + *  @param prefs Unused.
 + *
 + *  @param pref Unused.
 + *
 + *  @param user_data Unused.
 + */
 +static void
 +gnc_main_window_update_tab_close (gpointer prefs, gchar *pref, gpointer user_data)
 +{
 +    gboolean new_value;
 +
 +    ENTER(" ");
 +    new_value = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SHOW_CLOSE_BUTTON);
 +    gnc_main_window_foreach_page(
 +        gnc_main_window_update_tab_close_one_page,
 +        &new_value);
 +    LEAVE(" ");
 +}
 +
 +
 +/** Show/hide the account color on the tab of a notebook page.
 + *
 + *  @internal
 + *
 + *  @param page The GncPluginPage whose notebook tab should be updated.
 + *
 + *  @param user_data GncMainWindow.
 + */
 +static void
 +gnc_main_window_update_tab_color_one_page (GncPluginPage *page,
 +        gpointer user_data)
 +{
 +    const gchar          *color_string;
 +
 +    ENTER("page %p", page);
 +    color_string = gnc_plugin_page_get_page_color(page);
 +    main_window_update_page_color (page, color_string);
 +    LEAVE(" ");
 +}
 +
 +
 +/** Show/hide the account color on tabs.
 + *
 + *  @internal
 + *
 + *  @param prefs Unused.
 + *
 + *  @param pref Name of the preference that was changed.
 + *
 + *  @param user_data GncMainWindow.
 + */
 +static void
 +gnc_main_window_update_tab_color (gpointer gsettings, gchar *pref, gpointer user_data)
 +{
 +    GncMainWindowPrivate *priv;
 +    GncMainWindow        *window;
 +
 +    ENTER(" ");
 +    g_return_if_fail(GNC_IS_MAIN_WINDOW(user_data));
 +    window = user_data;
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    if (g_strcmp0 (GNC_PREF_TAB_COLOR, pref) == 0)
 +        priv->show_color_tabs = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_COLOR);
 +    gnc_main_window_foreach_page (gnc_main_window_update_tab_color_one_page, window);
 +    LEAVE(" ");
 +}
 +
 +
 +/** Update the width of the label in the tab of a notebook page.  This
 + *  function adjusts both the width and the ellipsize mode so that the tab
 + *  label looks correct.  The special check for a zero value handles the
 + *  case where a user hasn't set a tab width and the preference default isn't
 + *  detected.
 + *
 + *  @internal
 + *
 + *  @param page The GncPluginPage whose notebook tab should be updated.
 + *
 + *  @param new_value The new width of the label in the tab.
 + */
 +static void
 +gnc_main_window_update_tab_width_one_page (GncPluginPage *page,
 +        gpointer user_data)
 +{
 +    gint *new_value = user_data;
 +    GtkWidget *label;
 +    const gchar *lab_text;
 +
 +    ENTER("page %p, visible %d", page, *new_value);
 +    label = g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_TAB_LABEL);
 +    if (!label)
 +    {
 +        LEAVE("no label");
 +        return;
 +    }
 +
 +    lab_text = gtk_label_get_text (GTK_LABEL(label));
 +
 +    if (*new_value != 0)
 +    {
 +        if (g_utf8_strlen (lab_text, -1) < *new_value)
 +            gtk_label_set_width_chars (GTK_LABEL(label), strlen (lab_text));
 +        else
 +            gtk_label_set_width_chars (GTK_LABEL(label), *new_value);
 +
 +        gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
 +    }
 +    else
 +    {
 +        gtk_label_set_width_chars (GTK_LABEL(label), 15);
 +        gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_NONE);
 +    }
 +    LEAVE(" ");
 +}
 +
 +
 +/** Update the tab label width in all pages in all windows.  This function
 + *  calls gnc_main_window_update_tab_width() for each plugin page in the
 + *  application.
 + *
 + *  @internal
 + *
 + *  @param prefs Unused.
 + *
 + *  @param pref Unused.
 + *
 + *  @param user_data Unused.
 + */
 +static void
 +gnc_main_window_update_tab_width (gpointer prefs, gchar *pref, gpointer user_data)
 +{
 +    gint new_value;
 +
 +    ENTER(" ");
 +    new_value = gnc_prefs_get_float (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_WIDTH);
 +    gnc_main_window_foreach_page(
 +        gnc_main_window_update_tab_width_one_page,
 +        &new_value);
 +    LEAVE(" ");
 +}
 +
 +
 +/************************************************************
 + *                 Tab Label Implementation                 *
 + ************************************************************/
 +static gboolean
 +main_window_find_tab_items (GncMainWindow *window,
 +                            GncPluginPage *page,
 +                            GtkWidget **label_p,
 +                            GtkWidget **entry_p)
 +{
 +    GncMainWindowPrivate *priv;
 +    GtkWidget *tab_hbox, *widget, *tab_widget;
 +    GList *children, *tmp;
 +
 +    ENTER("window %p, page %p, label_p %p, entry_p %p",
 +          window, page, label_p, entry_p);
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    *label_p = *entry_p = NULL;
 +
 +    if (!page->notebook_page)
 +    {
 +        LEAVE("invalid notebook_page");
 +        return FALSE;
 +    }
 +
 +    tab_widget = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
 +                                           page->notebook_page);
 +    if (GTK_IS_EVENT_BOX (tab_widget))
 +        tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
 +    else if (GTK_IS_BOX (tab_widget))
 +        tab_hbox = tab_widget;
 +    else
 +    {
 +        PWARN ("Unknown widget for tab label %p", tab_widget);
 +        return FALSE;
 +    }
 +
 +    children = gtk_container_get_children(GTK_CONTAINER(tab_hbox));
 +    for (tmp = children; tmp; tmp = g_list_next(tmp))
 +    {
 +        widget = tmp->data;
 +        if (GTK_IS_LABEL(widget))
 +        {
 +            *label_p = widget;
 +        }
 +        else if (GTK_IS_ENTRY(widget))
 +        {
 +            *entry_p = widget;
 +        }
 +    }
 +    g_list_free(children);
 +
 +    LEAVE("label %p, entry %p", *label_p, *entry_p);
 +    return (*label_p && *entry_p);
 +}
 +
 +static gboolean
 +main_window_find_tab_widget (GncMainWindow *window,
 +                             GncPluginPage *page,
 +                             GtkWidget **widget_p)
 +{
 +    GncMainWindowPrivate *priv;
 +
 +    ENTER("window %p, page %p, widget %p",
 +          window, page, widget_p);
 +    *widget_p = NULL;
 +
 +    if (!page->notebook_page)
 +    {
 +        LEAVE("invalid notebook_page");
 +        return FALSE;
 +    }
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    *widget_p = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
 +                                           page->notebook_page);
 +
 +    LEAVE("widget %p", *widget_p);
 +    return TRUE;
 +}
 +
 +void
 +main_window_update_page_name (GncPluginPage *page,
 +                              const gchar *name_in)
 +{
 +    GncMainWindow *window;
 +    GncMainWindowPrivate *priv;
 +    GtkWidget *label, *entry;
 +    gchar *name, *old_page_name, *old_page_long_name;
 +    gint lab_width;
 +
 +    ENTER(" ");
 +
 +    if ((name_in == NULL) || (*name_in == '\0'))
 +    {
 +        LEAVE("no string");
 +        return;
 +    }
 +    name = g_strstrip(g_strdup(name_in));
 +
 +    /* Optimization, if the name hasn't changed, don't update X. */
 +    if (*name == '\0' || 0 == strcmp(name, gnc_plugin_page_get_page_name(page)))
 +    {
 +        g_free(name);
 +        LEAVE("empty string or name unchanged");
 +        return;
 +    }
 +
 +    old_page_name = g_strdup( gnc_plugin_page_get_page_name(page));
 +    old_page_long_name = g_strdup( gnc_plugin_page_get_page_long_name(page));
 +
 +    /* Update the plugin */
 +    gnc_plugin_page_set_page_name(page, name);
 +
 +    /* Update the notebook tab */
 +    window = GNC_MAIN_WINDOW(page->window);
 +    if (!window)
 +    {
 +        g_free(old_page_name);
 +        g_free(old_page_long_name);
 +        g_free(name);
 +        LEAVE("no window widget available");
 +        return;
 +    }
 +
 +    if (main_window_find_tab_items(window, page, &label, &entry))
 +        gtk_label_set_text(GTK_LABEL(label), name);
 +
 +    /* Adjust the label width for new text */
 +    lab_width = gnc_prefs_get_float (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_WIDTH);
 +    gnc_main_window_update_tab_width_one_page (page, &lab_width);
 +
 +    /* Update Tooltip on notebook Tab */
 +    if (old_page_long_name && old_page_name
 +            && g_strrstr(old_page_long_name, old_page_name) != NULL)
 +    {
 +        gchar *new_page_long_name;
 +        gint string_position;
 +        GtkWidget *tab_widget;
 +
 +        string_position = strlen(old_page_long_name) - strlen(old_page_name);
 +        new_page_long_name = g_strconcat(g_strndup(old_page_long_name, string_position), name, NULL);
 +
 +        gnc_plugin_page_set_page_long_name(page, new_page_long_name);
 +
 +        if (main_window_find_tab_widget(window, page, &tab_widget))
 +            gtk_widget_set_tooltip_text(tab_widget, new_page_long_name);
 +
 +        g_free(new_page_long_name);
 +    }
 +
 +    /* Update the notebook menu */
 +    if (page->notebook_page)
 +    {
 +        priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +        label = gtk_notebook_get_menu_label (GTK_NOTEBOOK(priv->notebook),
 +                                             page->notebook_page);
 +        gtk_label_set_text(GTK_LABEL(label), name);
 +    }
 +
 +    /* Force an update of the window title */
 +    gnc_main_window_update_title(window);
 +    g_free(old_page_long_name);
 +    g_free(old_page_name);
 +    g_free(name);
 +    LEAVE("done");
 +}
 +
 +
 +void
 +main_window_update_page_color (GncPluginPage *page,
 +                               const gchar *color_in)
 +{
 +    GncMainWindow *window;
 +    GncMainWindowPrivate *priv;
 +    GtkWidget *tab_widget;
 +    GdkRGBA tab_color;
 +    gchar *color_string = NULL;
 +    gboolean want_color = FALSE;
 +
 +    ENTER(" ");
 +    if (color_in)
 +        color_string = g_strstrip(g_strdup(color_in));
 +
 +    if (color_string && *color_string != '\0')
 +        want_color = TRUE;
 +
 +    /* Update the plugin */
 +    window = GNC_MAIN_WINDOW(page->window);
 +    if (want_color)
 +        gnc_plugin_page_set_page_color(page, color_string);
 +    else
 +        gnc_plugin_page_set_page_color(page, NULL);
 +
 +    /* Update the notebook tab */
 +    main_window_find_tab_widget (window, page, &tab_widget);
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +
 +    if (want_color && gdk_rgba_parse(&tab_color, color_string) && priv->show_color_tabs)
 +    {
 +        GtkCssProvider *provider = gtk_css_provider_new();
 +        GtkStyleContext *stylectxt;
 +        gchar *col_str, *widget_css;
 +
 +        if (!GTK_IS_EVENT_BOX (tab_widget))
 +        {
 +            GtkWidget *event_box = gtk_event_box_new ();
 +            g_object_ref (tab_widget);
 +            gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
 +                                        page->notebook_page, event_box);
 +            gtk_container_add (GTK_CONTAINER(event_box), tab_widget);
 +            g_object_unref (tab_widget);
 +            tab_widget = event_box;
 +        }
 +
 +        stylectxt = gtk_widget_get_style_context (GTK_WIDGET (tab_widget));
 +        col_str = gdk_rgba_to_string (&tab_color);
 +        widget_css = g_strconcat ("*{\n  background-color:", col_str, ";\n}\n", NULL);
 +
 +        gtk_css_provider_load_from_data (provider, widget_css, -1, NULL);
 +        gtk_style_context_add_provider (stylectxt, GTK_STYLE_PROVIDER (provider),
 +                                        GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
 +        g_object_unref (provider);
 +        g_free (col_str);
 +        g_free (widget_css);
 +    }
 +    else
 +    {
 +        if (GTK_IS_EVENT_BOX (tab_widget))
 +        {
 +            GtkWidget *tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
 +            g_object_ref (tab_hbox);
 +            gtk_container_remove (GTK_CONTAINER(tab_widget), tab_hbox);
 +            gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
 +                                        page->notebook_page, tab_hbox);
 +            g_object_unref (tab_hbox);
 +        }
 +    }
 +    g_free(color_string);
 +    LEAVE("done");
 +}
 +
 +
 +static void
 +gnc_main_window_tab_entry_activate (GtkWidget *entry,
 +                                    GncPluginPage *page)
 +{
 +    GtkWidget *label, *entry2;
 +
 +    g_return_if_fail(GTK_IS_ENTRY(entry));
 +    g_return_if_fail(GNC_IS_PLUGIN_PAGE(page));
 +
 +    ENTER("");
 +    if (!main_window_find_tab_items(GNC_MAIN_WINDOW(page->window),
 +                                    page, &label, &entry2))
 +    {
 +        LEAVE("can't find required widgets");
 +        return;
 +    }
 +
 +    main_window_update_page_name(page, gtk_entry_get_text(GTK_ENTRY(entry)));
 +
 +    gtk_widget_hide(entry);
 +    gtk_widget_show(label);
 +    LEAVE("");
 +}
 +
 +
 +static gboolean
 +gnc_main_window_tab_entry_editing_done (GtkWidget *entry,
 +                                        GncPluginPage *page)
 +{
 +    ENTER("");
 +    gnc_main_window_tab_entry_activate(entry, page);
 +    LEAVE("");
 +    return FALSE;
 +}
 +
 +static gboolean
 +gnc_main_window_tab_entry_focus_out_event (GtkWidget *entry,
 +        GdkEvent *event,
 +        GncPluginPage *page)
 +{
 +    ENTER("");
 +    gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
 +    LEAVE("");
 +    return FALSE;
 +}
 +
 +static gboolean
 +gnc_main_window_tab_entry_key_press_event (GtkWidget *entry,
 +        GdkEventKey *event,
 +        GncPluginPage *page)
 +{
 +    if (event->keyval == GDK_KEY_Escape)
 +    {
 +        GtkWidget *label, *entry2;
 +
 +        g_return_val_if_fail(GTK_IS_ENTRY(entry), FALSE);
 +        g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
 +
 +        ENTER("");
 +        if (!main_window_find_tab_items(GNC_MAIN_WINDOW(page->window),
 +                                        page, &label, &entry2))
 +        {
 +            LEAVE("can't find required widgets");
 +            return FALSE;
 +        }
 +
 +        gtk_entry_set_text(GTK_ENTRY(entry), gtk_label_get_text(GTK_LABEL(label)));
 +        gtk_widget_hide(entry);
 +        gtk_widget_show(label);
 +        LEAVE("");
 +    }
 +    return FALSE;
 +}
 +
 +/************************************************************
 + *                   Widget Implementation                  *
 + ************************************************************/
 +
 +/*  Get the type of a gnc main window.
 + */
 +GType
 +gnc_main_window_get_type (void)
 +{
 +    static GType gnc_main_window_type = 0;
 +
 +    if (gnc_main_window_type == 0)
 +    {
 +        static const GTypeInfo our_info =
 +        {
 +            sizeof (GncMainWindowClass),
 +            NULL,
 +            NULL,
 +            (GClassInitFunc) gnc_main_window_class_init,
 +            NULL,
 +            NULL,
 +            sizeof (GncMainWindow),
 +            0,
 +            (GInstanceInitFunc) gnc_main_window_init
 +        };
 +
 +        static const GInterfaceInfo plugin_info =
 +        {
 +            (GInterfaceInitFunc) gnc_window_main_window_init,
 +            NULL,
 +            NULL
 +        };
 +
 +        gnc_main_window_type = g_type_register_static (GTK_TYPE_WINDOW,
 +                               GNC_MAIN_WINDOW_NAME,
 +                               &our_info, 0);
 +        g_type_add_interface_static (gnc_main_window_type,
 +                                     GNC_TYPE_WINDOW,
 +                                     &plugin_info);
 +    }
 +
 +    return gnc_main_window_type;
 +}
 +
 +
 +/** Initialize the class for a new gnucash main window.  This will set
 + *  up any function pointers that override functions in the parent
 + *  class, and also initialize the signals that this class of widget
 + *  can generate.
 + *
 + *  @param klass The new class structure created by the object system.
 + */
 +static void
 +gnc_main_window_class_init (GncMainWindowClass *klass)
 +{
 +    GObjectClass *object_class = G_OBJECT_CLASS (klass);
 +    GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS(klass);
 +
 +    parent_class = g_type_class_peek_parent (klass);
 +
 +    window_type = g_quark_from_static_string ("gnc-main-window");
 +
 +    object_class->finalize = gnc_main_window_finalize;
 +
 +    /* GtkWidget signals */
 +    gtkwidget_class->destroy = gnc_main_window_destroy;
 +
 +    g_type_class_add_private(klass, sizeof(GncMainWindowPrivate));
 +
 +    /**
 +     * GncMainWindow::page_added:
 +     * @param window: the #GncMainWindow
 +     * @param page: the #GncPluginPage
 +     *
 +     * The "page_added" signal is emitted when a new page is added
 +     * to the notebook of a GncMainWindow.  This can be used to
 +     * attach a signal from the page so that menu actions can be
 +     * adjusted based upon events that occur within the page
 +     * (e.g. an account is selected.)
 +     */
 +    main_window_signals[PAGE_ADDED] =
 +        g_signal_new ("page_added",
 +                      G_OBJECT_CLASS_TYPE (object_class),
 +                      G_SIGNAL_RUN_FIRST,
 +                      G_STRUCT_OFFSET (GncMainWindowClass, page_added),
 +                      NULL, NULL,
 +                      g_cclosure_marshal_VOID__OBJECT,
 +                      G_TYPE_NONE, 1,
 +                      G_TYPE_OBJECT);
 +
 +    /**
 +     * GncMainWindow::page_changed:
 +     * @param window: the #GncMainWindow
 +     * @param page: the #GncPluginPage
 +     *
 +     * The "page_changed" signal is emitted when a new page is
 +     * selected in the notebook of a GncMainWindow.  This can be
 +     * used to to adjust menu actions based upon which page is
 +     * currently displayed in a window.
 +     */
 +    main_window_signals[PAGE_CHANGED] =
 +        g_signal_new ("page_changed",
 +                      G_OBJECT_CLASS_TYPE (object_class),
 +                      G_SIGNAL_RUN_FIRST,
 +                      G_STRUCT_OFFSET (GncMainWindowClass, page_changed),
 +                      NULL, NULL,
 +                      g_cclosure_marshal_VOID__OBJECT,
 +                      G_TYPE_NONE, 1,
 +                      G_TYPE_OBJECT);
 +
 +    gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
 +                           GNC_PREF_SHOW_CLOSE_BUTTON,
 +                           gnc_main_window_update_tab_close,
 +                           NULL);
 +    gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
 +                           GNC_PREF_TAB_WIDTH,
 +                           gnc_main_window_update_tab_width,
 +                           NULL);
 +
 +    gnc_hook_add_dangler(HOOK_BOOK_SAVED,
 +                         (GFunc)gnc_main_window_update_all_titles, NULL);
 +    gnc_hook_add_dangler(HOOK_BOOK_OPENED,
 +                         (GFunc)gnc_main_window_attach_to_book, NULL);
 +
 +}
 +
 +
 +/** Initialize a new instance of a gnucash main window.  This function
 + *  initializes the object private storage space.  It also adds the
 + *  new object to a list (for memory tracking purposes).
 + *
 + *  @param window The new object instance created by the object system.
 + *
 + *  @param klass A pointer to the class data structure for this
 + *  object. */
 +static void
 +gnc_main_window_init (GncMainWindow *window,
 +                      GncMainWindowClass *klass)
 +{
 +    GncMainWindowPrivate *priv;
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    priv->merged_actions_table =
 +        g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 +
 +    // Set the style context for this widget so it can be easily manipulated with css
 +    gnc_widget_set_style_context (GTK_WIDGET(window), "GncMainWindow");
 +
 +    priv->event_handler_id =
 +        qof_event_register_handler(gnc_main_window_event_handler, window);
 +
 +    /* Get the show_color_tabs value preference */
 +    priv->show_color_tabs = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_COLOR);
 +    priv->about_dialog = NULL;
 +
 +    gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
 +                           GNC_PREF_TAB_COLOR,
 +                           gnc_main_window_update_tab_color,
 +                           window);
 +
 +    gnc_main_window_setup_window (window);
 +    gnc_gobject_tracking_remember(G_OBJECT(window),
 +                                  G_OBJECT_CLASS(klass));
 +}
 +
 +
 +/** Finalize the GncMainWindow object.  This function is called from
 + *  the G_Object level to complete the destruction of the object.  It
 + *  should release any memory not previously released by the destroy
 + *  function (i.e. the private data structure), then chain up to the
 + *  parent's destroy function.
 + *
 + *  @param object The object being destroyed.
 + *
 + *  @internal
 + */
 +static void
 +gnc_main_window_finalize (GObject *object)
 +{
 +    g_return_if_fail (object != NULL);
 +    g_return_if_fail (GNC_IS_MAIN_WINDOW (object));
 +
 +    if (active_windows == NULL)
 +    {
 +        /* Oops. User killed last window and we didn't catch it. */
 +        g_idle_add((GSourceFunc)gnc_shutdown, 0);
 +    }
 +
 +    gnc_gobject_tracking_forget(object);
 +    G_OBJECT_CLASS (parent_class)->finalize (object);
 +}
 +
 +
 +static void
 +gnc_main_window_destroy (GtkWidget *widget)
 +{
 +    GncMainWindow *window;
 +    GncMainWindowPrivate *priv;
 +    GncPluginManager *manager;
 +    GList *plugins;
 +
 +    g_return_if_fail (widget != NULL);
 +    g_return_if_fail (GNC_IS_MAIN_WINDOW (widget));
 +
 +    window = GNC_MAIN_WINDOW (widget);
 +
 +    active_windows = g_list_remove (active_windows, window);
 +
 +    /* Do these things once */
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    if (priv->merged_actions_table)
 +    {
 +
 +        /* Close any pages in this window */
 +        while (priv->current_page)
 +            gnc_main_window_close_page(priv->current_page);
 +
 +        if (gnc_window_get_progressbar_window() == GNC_WINDOW(window))
 +            gnc_window_set_progressbar_window(NULL);
 +#ifndef MAC_INTEGRATION
 +        /* Update the "Windows" menu in all other windows */
 +        gnc_main_window_update_all_menu_items();
 +#endif
 +        gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
 +                                     GNC_PREF_TAB_COLOR,
 +                                     gnc_main_window_update_tab_color,
 +                                     window);
 +
 +        qof_event_unregister_handler(priv->event_handler_id);
 +        priv->event_handler_id = 0;
 +
 +        g_hash_table_destroy (priv->merged_actions_table);
 +        priv->merged_actions_table = NULL;
 +
 +        /* GncPluginManager stuff */
 +        manager = gnc_plugin_manager_get ();
 +        plugins = gnc_plugin_manager_get_plugins (manager);
 +        g_list_foreach (plugins, gnc_main_window_remove_plugin, window);
 +        g_list_free (plugins);
 +    }
 +    if (priv->about_dialog)
 +	g_object_unref (priv->about_dialog);
 +    GTK_WIDGET_CLASS (parent_class)->destroy (widget);
 +}
 +
 +
 +/*  Create a new gnc main window plugin.
 + */
 +GncMainWindow *
 +gnc_main_window_new (void)
 +{
 +    GncMainWindow *window;
 +    GtkWidget *old_window;
 +
 +    window = g_object_new (GNC_TYPE_MAIN_WINDOW, NULL);
 +    gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
 +
 +    old_window = gnc_ui_get_toplevel();
 +    if (old_window)
 +    {
 +        gint width, height;
 +        gtk_window_get_size (GTK_WINDOW (old_window), &width, &height);
 +        gtk_window_resize (GTK_WINDOW (window), width, height);
 +        if ((gdk_window_get_state((gtk_widget_get_window (GTK_WIDGET(old_window))))
 +                & GDK_WINDOW_STATE_MAXIMIZED) != 0)
 +        {
 +            gtk_window_maximize (GTK_WINDOW (window));
 +        }
 +    }
 +    active_windows = g_list_append (active_windows, window);
 +    gnc_main_window_update_title(window);
 +#ifdef MAC_INTEGRATION
 +    gnc_quartz_set_menu(window);
 +#else
 +    gnc_main_window_update_all_menu_items();
 +#endif
 +    gnc_engine_add_commit_error_callback( gnc_main_window_engine_commit_error_callback, window );
 +
 +    return window;
 +}
 +
 +/************************************************************
 + *                     Utility Functions                    *
 + ************************************************************/
 +
 +static void
 +gnc_main_window_engine_commit_error_callback( gpointer data,
 +        QofBackendError errcode )
 +{
 +    GncMainWindow* window = GNC_MAIN_WINDOW(data);
 +    GtkWidget* dialog;
 +    const gchar *reason = _("Unable to save to database.");
 +    if ( errcode == ERR_BACKEND_READONLY )
 +        reason = _("Unable to save to database: Book is marked read-only.");
 +    dialog = gtk_message_dialog_new( GTK_WINDOW(window),
 +                                     GTK_DIALOG_DESTROY_WITH_PARENT,
 +                                     GTK_MESSAGE_ERROR,
 +                                     GTK_BUTTONS_CLOSE,
 +                                     "%s",
 +                                     reason );
 +    gtk_dialog_run(GTK_DIALOG (dialog));
 +    gtk_widget_destroy(dialog);
 +
 +}
 +
 +/** Connect a GncPluginPage to the window.  This function will insert
 + *  the page in to the window's notebook and its list of active pages.
 + *  It will also emit the "inserted" signal on the page, and the
 + *  "add_page" signal on the window.
 + *
 + *  @param window The window where the new page should be added.
 + *
 + *  @param page The GncPluginPage that should be added to the window.
 + *  The visible widget for this plugin must have already been created.
 + *
 + *  @param tab_hbox The widget that should be added into the notebook
 + *  tab for this page.  Generally this is a GtkLabel, but could also
 + *  be a GtkHBox containing an icon and a label.
 + *
 + *  @param menu_label The widget that should be added into the
 + *  notebook popup menu for this page.  This should be a GtkLabel.
 + */
 +static void
 +gnc_main_window_connect (GncMainWindow *window,
 +                         GncPluginPage *page,
 +                         GtkWidget *tab_hbox,
 +                         GtkWidget *menu_label)
 +{
 +    GncMainWindowPrivate *priv;
 +    GtkNotebook *notebook;
 +
 +    page->window = GTK_WIDGET(window);
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    notebook = GTK_NOTEBOOK (priv->notebook);
 +    priv->installed_pages = g_list_append (priv->installed_pages, page);
 +    priv->usage_order = g_list_prepend (priv->usage_order, page);
 +    gtk_notebook_append_page_menu (notebook, page->notebook_page,
 +                                   tab_hbox, menu_label);
 +    gtk_notebook_set_tab_reorderable (notebook, page->notebook_page, TRUE);
 +    gnc_plugin_page_inserted (page);
 +    gtk_notebook_set_current_page (notebook, -1);
 +    if (GNC_PLUGIN_PAGE_GET_CLASS(page)->window_changed)
 +        (GNC_PLUGIN_PAGE_GET_CLASS(page)->window_changed)(page, GTK_WIDGET(window));
 +    g_signal_emit (window, main_window_signals[PAGE_ADDED], 0, page);
 +
 +    g_signal_connect(G_OBJECT(page->notebook_page), "popup-menu",
 +                     G_CALLBACK(gnc_main_window_popup_menu_cb), page);
 +    g_signal_connect_after(G_OBJECT(page->notebook_page), "button-press-event",
 +                           G_CALLBACK(gnc_main_window_button_press_cb), page);
 +}
 +
 +
 +/** Disconnect a GncPluginPage page from the window.  If this page is
 + *  currently foremost in the window's notebook, its user interface
 + *  actions will be disconnected and the page's summarybar widget (if
 + *  any) will be removed.  The page is then removed from the window's
 + *  notebook and its list of active pages.
 + *
 + *  @param window The window the page should be removed from.
 + *
 + *  @param page The GncPluginPage that should be removed from the
 + *  window.
 + *
 + *  @internal
 + */
 +static void
 +gnc_main_window_disconnect (GncMainWindow *window,
 +                            GncPluginPage *page)
 +{
 +    GncMainWindowPrivate *priv;
 +    GtkNotebook *notebook;
 +    GncPluginPage *new_page;
 +    gint page_num;
 +
 +    /* Disconnect the callbacks */
 +    g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
 +                                         G_CALLBACK(gnc_main_window_popup_menu_cb), page);
 +    g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
 +                                         G_CALLBACK(gnc_main_window_button_press_cb), page);
 +
 +    /* Disconnect the page and summarybar from the window */
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    if (priv->current_page == page)
 +    {
 +        gnc_plugin_page_unmerge_actions (page, window->ui_merge);
 +        gnc_plugin_page_unselected (page);
 +        priv->current_page = NULL;
 +    }
 +
 +    /* Remove it from the list of pages in the window */
 +    priv->installed_pages = g_list_remove (priv->installed_pages, page);
 +    priv->usage_order = g_list_remove (priv->usage_order, page);
 +
 +    /* Switch to the last recently used page */
 +    notebook = GTK_NOTEBOOK (priv->notebook);
 +    if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_NEXT_RECENT))
 +    {
 +        new_page = g_list_nth_data (priv->usage_order, 0);
 +        if (new_page)
 +        {
 +            page_num =  gtk_notebook_page_num(notebook, new_page->notebook_page);
 +            gtk_notebook_set_current_page(notebook, page_num);
 +            /* This may have caused WebKit to schedule  a timer interrupt which it
 +               sometimes  forgets to cancel before deleting the object.  See
 +               <https://bugs.webkit.org/show_bug.cgi?id=119003>.   Get around this
 +               by flushing all events to get rid of the timer interrupt. */
 +            while (gtk_events_pending())
 +                gtk_main_iteration();
 +        }
 +    }
 +
 +    /* Remove the page from the notebook */
 +    page_num =  gtk_notebook_page_num(notebook, page->notebook_page);
 +    gtk_notebook_remove_page (notebook, page_num);
 +
 +    if ( gtk_notebook_get_current_page(notebook) == -1)
 +    {
 +        /* Need to synthesize a page changed signal when the last
 +         * page is removed.  The notebook doesn't generate a signal
 +         * for this, therefore the switch_page code in this file
 +         * never gets called to generate this signal. */
 +        gnc_main_window_switch_page(notebook, NULL, -1, window);
 +        //g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, NULL);
 +    }
 +
 +    gnc_plugin_page_removed (page);
 +
 +    gtk_ui_manager_ensure_update (window->ui_merge);
 +    gnc_window_set_status (GNC_WINDOW(window), page, NULL);
 +}
 +
 +
 +/************************************************************
 + *                                                          *
 + ************************************************************/
 +
 +
 +void
 +gnc_main_window_display_page (GncPluginPage *page)
 +{
 +    GncMainWindow *window;
 +    GncMainWindowPrivate *priv;
 +    GtkNotebook *notebook;
 +    gint page_num;
 +
 +    window = GNC_MAIN_WINDOW (page->window);
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    notebook = GTK_NOTEBOOK (priv->notebook);
 +    page_num = gtk_notebook_page_num(notebook, page->notebook_page);
 +    gtk_notebook_set_current_page (notebook, page_num);
 +    gtk_window_present(GTK_WINDOW(window));
 +}
 +
 +
 +/*  Display a data plugin page in a window.  If the page already
 + *  exists in any window, then that window will be brought to the
 + *  front and the notebook switch to display the specified page.  If
 + *  the page is new then it will be added to the specified window.  If
 + *  the window is NULL, the new page will be added to the first
 + *  window.
 + */
 +void
 +gnc_main_window_open_page (GncMainWindow *window,
 +                           GncPluginPage *page)
 +{
 +    GncMainWindowPrivate *priv;
 +    GtkWidget *tab_hbox;
 +    GtkWidget *label, *entry;
 +    const gchar *icon, *text, *color_string, *lab_text;
 +    GtkWidget *image;
 +    GList *tmp;
 +    gint width;
 +
 +    ENTER("window %p, page %p", window, page);
 +    if (window)
 +        g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
 +    g_return_if_fail (GNC_IS_PLUGIN_PAGE (page));
 +    g_return_if_fail (gnc_plugin_page_has_books(page));
 +
 +    if (gnc_main_window_page_exists(page))
 +    {
 +        gnc_main_window_display_page(page);
 +        return;
 +    }
 +
 +    /* Does the page want to be in a new window? */
 +    if (gnc_plugin_page_get_use_new_window(page))
 +    {
 +        /* See if there's a blank window. If so, use that. */
 +        for (tmp = active_windows; tmp; tmp = g_list_next(tmp))
 +        {
 +            window = GNC_MAIN_WINDOW(tmp->data);
 +            priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +            if (priv->installed_pages == NULL)
 +            {
 +                break;
 +            }
 +        }
 +        if (tmp == NULL)
 +            window = gnc_main_window_new ();
 +        gtk_widget_show(GTK_WIDGET(window));
 +    }
 +    else if ((window == NULL) && active_windows)
 +    {
 +        window = active_windows->data;
 +    }
 +
 +    page->window = GTK_WIDGET(window);
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    page->notebook_page = gnc_plugin_page_create_widget (page);
 +    g_object_set_data (G_OBJECT (page->notebook_page),
 +                       PLUGIN_PAGE_LABEL, page);
 +
 +    /*
 +     * The page tab.
 +     */
 +    width = gnc_prefs_get_float(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_WIDTH);
 +    icon = GNC_PLUGIN_PAGE_GET_CLASS(page)->tab_icon;
 +    lab_text = gnc_plugin_page_get_page_name(page);
 +    label = gtk_label_new (lab_text);
 +    g_object_set_data (G_OBJECT (page), PLUGIN_PAGE_TAB_LABEL, label);
 +
 +    if (width != 0)
 +    {
 +        if (g_utf8_strlen (lab_text, -1) < width)
 +            gtk_label_set_width_chars (GTK_LABEL(label), strlen (lab_text));
 +        else
 +            gtk_label_set_width_chars (GTK_LABEL(label), width);
 +
 +        gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
 +    }
 +    gtk_widget_show (label);
 +
 +    tab_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 +    gtk_box_set_homogeneous (GTK_BOX (tab_hbox), FALSE);
 +    gtk_widget_show (tab_hbox);
 +
 +    if (icon != NULL)
 +    {
 +        image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU);
 +        gtk_widget_show (image);
 +        gtk_box_pack_start (GTK_BOX (tab_hbox), image, FALSE, FALSE, 0);
 +#if GTK_CHECK_VERSION(3,12,0)
 +        gtk_widget_set_margin_start (GTK_WIDGET(image), 5);
 +#else
 +        gtk_widget_set_margin_left (GTK_WIDGET(image), 5);
 +#endif
 +        gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0);
 +    }
 +    else
 +        gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0);
 +
 +    text = gnc_plugin_page_get_page_long_name(page);
 +    if (text)
 +    {
 +        gtk_widget_set_tooltip_text(tab_hbox, text);
 +    }
 +
 +    entry = gtk_entry_new();
 +    gtk_widget_hide (entry);
 +    gtk_box_pack_start (GTK_BOX (tab_hbox), entry, TRUE, TRUE, 0);
 +    g_signal_connect(G_OBJECT(entry), "activate",
 +                     G_CALLBACK(gnc_main_window_tab_entry_activate), page);
 +    g_signal_connect(G_OBJECT(entry), "focus-out-event",
 +                     G_CALLBACK(gnc_main_window_tab_entry_focus_out_event),
 +                     page);
 +    g_signal_connect(G_OBJECT(entry), "key-press-event",
 +                     G_CALLBACK(gnc_main_window_tab_entry_key_press_event),
 +                     page);
 +    g_signal_connect(G_OBJECT(entry), "editing-done",
 +                     G_CALLBACK(gnc_main_window_tab_entry_editing_done),
 +                     page);
 +
 +    /* Add close button - Not for immutable pages */
 +    if (!g_object_get_data (G_OBJECT (page), PLUGIN_PAGE_IMMUTABLE))
 +    {
 +        GtkWidget *close_image, *close_button;
 +        GtkRequisition requisition;
 +
 +        close_button = gtk_button_new();
 +        gtk_button_set_relief(GTK_BUTTON(close_button), GTK_RELIEF_NONE);
 +        close_image = gtk_image_new_from_icon_name ("window-close", GTK_ICON_SIZE_MENU);
 +        gtk_widget_show(close_image);
 +        gtk_widget_get_preferred_size (close_image, &requisition, NULL);
 +        gtk_widget_set_size_request(close_button, requisition.width + 4,
 +                                    requisition.height + 2);
 +        gtk_container_add(GTK_CONTAINER(close_button), close_image);
 +        if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_SHOW_CLOSE_BUTTON))
 +            gtk_widget_show (close_button);
 +        else
 +            gtk_widget_hide (close_button);
 +
 +        g_signal_connect_swapped (G_OBJECT (close_button), "clicked",
 +                                  G_CALLBACK(gnc_main_window_close_page), page);
 +
 +        gtk_box_pack_start (GTK_BOX (tab_hbox), close_button, FALSE, FALSE, 0);
 +#if GTK_CHECK_VERSION(3,12,0)
 +        gtk_widget_set_margin_end (GTK_WIDGET(close_button), 5);
 +#else
 +        gtk_widget_set_margin_right (GTK_WIDGET(close_button), 5);
 +#endif
 +        g_object_set_data (G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON, close_button);
 +    }
 +
 +    /*
 +     * The popup menu
 +     */
 +    label = gtk_label_new (gnc_plugin_page_get_page_name(page));
 +
 +    /*
 +     * Now install it all in the window.
 +     */
 +    gnc_main_window_connect(window, page, tab_hbox, label);
 +
 +    color_string = gnc_plugin_page_get_page_color(page);
 +    main_window_update_page_color (page, color_string);
 +    LEAVE("");
 +}
 +
 +
 +/*  Remove a data plugin page from a window and display the previous
 + *  page.  If the page removed was the last page in the window, and
 + *  there is more than one window open, then the entire window will be
 + *  destroyed.
 + */
 +void
 +gnc_main_window_close_page (GncPluginPage *page)
 +{
 +    GncMainWindow *window;
 +    GncMainWindowPrivate *priv;
 +
 +    if (!page || !page->notebook_page)
 +        return;
 +
 +    if (!gnc_plugin_page_finish_pending(page))
 +        return;
 +
 +    if (!GNC_IS_MAIN_WINDOW (page->window))
 +        return;
 +
 +    window = GNC_MAIN_WINDOW (page->window);
 +    if (!window)
 +    {
 +        g_warning("Page is not in a window.");
 +        return;
 +    }
 +
 +    gnc_main_window_disconnect(window, page);
 +    gnc_plugin_page_destroy_widget (page);
 +    g_object_unref(page);
 +
 +    /* If this isn't the last window, go ahead and destroy the window. */
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    if (priv->installed_pages == NULL)
 +    {
 +        if (g_list_length(active_windows) > 1)
 +        {
 +            gtk_widget_destroy(GTK_WIDGET(window));
 +        }
 +    }
 +}
 +
 +
 +/*  Retrieve a pointer to the page that is currently at the front of
 + *  the specified window.  Any plugin that needs to manipulate its
 + *  menus based upon the currently selected menu page should connect
 + *  to the "page_changed" signal on a window.  The callback function
 + *  from that signal can then call this function to obtain a pointer
 + *  to the current page.
 + */
 +GncPluginPage *
 +gnc_main_window_get_current_page (GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    return priv->current_page;
 +}
 +
 +
 +/*  Manually add a set of actions to the specified window.  Plugins
 + *  whose user interface is not hard coded (e.g. the menu-additions
 + *  plugin) must create their actions at run time, then use this
 + *  function to install them into the window.
 + */
 +void
 +gnc_main_window_manual_merge_actions (GncMainWindow *window,
 +                                      const gchar *group_name,
 +                                      GtkActionGroup *group,
 +                                      guint merge_id)
 +{
 +    GncMainWindowPrivate *priv;
 +    MergedActionEntry *entry;
 +
 +    g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
 +    g_return_if_fail (group_name != NULL);
 +    g_return_if_fail (GTK_IS_ACTION_GROUP(group));
 +    g_return_if_fail (merge_id > 0);
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    entry = g_new0 (MergedActionEntry, 1);
 +    entry->action_group = group;
 +    entry->merge_id = merge_id;
 +    gtk_ui_manager_ensure_update (window->ui_merge);
 +    g_hash_table_insert (priv->merged_actions_table, g_strdup (group_name), entry);
 +}
 +
 +
 +/*  Add a set of actions to the specified window.  This function
 + *  should not need to be called directly by plugin implementors.
 + *  Correctly assigning values to the GncPluginClass fields during
 + *  plugin initialization will cause this routine to be automatically
 + *  called.
 + */
 +void
 +gnc_main_window_merge_actions (GncMainWindow *window,
 +                               const gchar *group_name,
 +                               GtkActionEntry *actions,
 +                               guint n_actions,
 +                               GtkToggleActionEntry *toggle_actions,
 +                               guint n_toggle_actions,
 +                               const gchar *filename,
 +                               gpointer user_data)
 +{
 +    GncMainWindowPrivate *priv;
 +    GncMainWindowActionData *data;
 +    MergedActionEntry *entry;
 +    GError *error = NULL;
 +    gchar *pathname;
 +
 +    g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
 +    g_return_if_fail (group_name != NULL);
 +    g_return_if_fail (actions != NULL);
 +    g_return_if_fail (n_actions > 0);
 +    g_return_if_fail (filename != NULL);
 +
 +    pathname = gnc_filepath_locate_ui_file (filename);
 +    if (pathname == NULL)
 +        return;
 +
 +    data = g_new0 (GncMainWindowActionData, 1);
 +    data->window = window;
 +    data->data = user_data;
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    entry = g_new0 (MergedActionEntry, 1);
 +    entry->action_group = gtk_action_group_new (group_name);
 +    gnc_gtk_action_group_set_translation_domain (entry->action_group, GETTEXT_PACKAGE);
 +    gtk_action_group_add_actions (entry->action_group, actions, n_actions, data);
 +    if (toggle_actions != NULL && n_toggle_actions > 0)
 +    {
 +        gtk_action_group_add_toggle_actions (entry->action_group,
 +                                             toggle_actions, n_toggle_actions,
 +                                             data);
 +    }
 +    gtk_ui_manager_insert_action_group (window->ui_merge, entry->action_group, 0);
 +    entry->merge_id = gtk_ui_manager_add_ui_from_file (window->ui_merge, pathname, &error);
 +    g_assert(entry->merge_id || error);
 +    if (entry->merge_id)
 +    {
 +        gtk_ui_manager_ensure_update (window->ui_merge);
 +        g_hash_table_insert (priv->merged_actions_table, g_strdup (group_name), entry);
 +    }
 +    else
 +    {
 +        g_critical("Failed to load ui file.\n  Filename %s\n  Error %s",
 +                   filename, error->message);
 +        g_error_free(error);
 +        g_free(entry);
 +    }
 +    g_free(pathname);
 +}
 +
 +
 +/*  Remove a set of actions from the specified window.  This function
 + *  should not need to be called directly by plugin implementors.  It
 + *  will automatically be called when a plugin is removed from a
 + *  window.
 + */
 +void
 +gnc_main_window_unmerge_actions (GncMainWindow *window,
 +                                 const gchar *group_name)
 +{
 +    GncMainWindowPrivate *priv;
 +    MergedActionEntry *entry;
 +
 +    g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
 +    g_return_if_fail (group_name != NULL);
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    if (priv->merged_actions_table == NULL)
 +        return;
 +    entry = g_hash_table_lookup (priv->merged_actions_table, group_name);
 +
 +    if (entry == NULL)
 +        return;
 +
 +    gtk_ui_manager_remove_action_group (window->ui_merge, entry->action_group);
 +    gtk_ui_manager_remove_ui (window->ui_merge, entry->merge_id);
 +    gtk_ui_manager_ensure_update (window->ui_merge);
 +
 +    g_hash_table_remove (priv->merged_actions_table, group_name);
 +}
 +
 +
 +/*  Force a full update of the user interface for the specified
 + *  window.  This can be an expensive function, but is needed because
 + *  the gtk ui manager doesn't always seem to update properly when
 + *  actions are changed.
 + */
 +void
 +gnc_main_window_actions_updated (GncMainWindow *window)
 +{
 +    GtkActionGroup *force;
 +
 +    g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
 +
 +    /* Unfortunately gtk_ui_manager_ensure_update doesn't work
 +     * here.  Force a full update by adding and removing an empty
 +     * action group.
 +     */
 +    force = gtk_action_group_new("force_update");
 +    gtk_ui_manager_insert_action_group (window->ui_merge, force, 0);
 +    gtk_ui_manager_ensure_update (window->ui_merge);
 +    gtk_ui_manager_remove_action_group (window->ui_merge, force);
 +    g_object_unref(force);
 +}
 +
 +
 +GtkAction *
 +gnc_main_window_find_action (GncMainWindow *window, const gchar *name)
 +{
 +    GtkAction *action = NULL;
 +    const GList *groups, *tmp;
 +
 +    groups = gtk_ui_manager_get_action_groups(window->ui_merge);
 +    for (tmp = groups; tmp; tmp = g_list_next(tmp))
 +    {
 +        action = gtk_action_group_get_action(GTK_ACTION_GROUP(tmp->data), name);
 +        if (action)
 +            break;
 +    }
 +    return action;
 +}
 +
 +
 +/*  Retrieve a specific set of user interface actions from a window.
 + *  This function can be used to get an group of action to be
 + *  manipulated when the front page of a window has changed.
 + */
 +GtkActionGroup *
 +gnc_main_window_get_action_group (GncMainWindow *window,
 +                                  const gchar *group_name)
 +{
 +    GncMainWindowPrivate *priv;
 +    MergedActionEntry *entry;
 +
 +    g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), NULL);
 +    g_return_val_if_fail (group_name != NULL, NULL);
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    if (priv->merged_actions_table == NULL)
 +        return NULL;
 +    entry = g_hash_table_lookup (priv->merged_actions_table, group_name);
 +
 +    if (entry == NULL)
 +        return NULL;
 +
 +    return entry->action_group;
 +}
 +
 +static void
 +gnc_main_window_update_tab_position (gpointer prefs, gchar *pref, gpointer user_data)
 +{
 +    GncMainWindow *window;
 +    GtkPositionType position = GTK_POS_TOP;
 +    GncMainWindowPrivate *priv;
 +
 +    window = GNC_MAIN_WINDOW(user_data);
 +
 +    ENTER ("window %p", window);
 +    if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_BOTTOM))
 +        position = GTK_POS_BOTTOM;
 +    else if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT))
 +        position = GTK_POS_LEFT;
 +    else if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT))
 +        position = GTK_POS_RIGHT;
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE (window);
 +    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (priv->notebook), position);
 +
 +    LEAVE ("");
 +}
 +
 +/*
 + * Based on code from Epiphany (src/ephy-window.c)
 + */
 +static void
 +gnc_main_window_update_edit_actions_sensitivity (GncMainWindow *window, gboolean hide)
 +{
 +    GncMainWindowPrivate *priv;
 +    GncPluginPage *page;
 +    GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
 +    GtkAction *action;
 +    gboolean can_copy = FALSE, can_cut = FALSE, can_paste = FALSE;
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    page = priv->current_page;
 +    if (page && GNC_PLUGIN_PAGE_GET_CLASS(page)->update_edit_menu_actions)
 +    {
 +        (GNC_PLUGIN_PAGE_GET_CLASS(page)->update_edit_menu_actions)(page, hide);
 +        return;
 +    }
 +
 +    if (GTK_IS_EDITABLE (widget))
 +    {
 +        gboolean has_selection;
 +
 +        has_selection = gtk_editable_get_selection_bounds
 +                        (GTK_EDITABLE (widget), NULL, NULL);
 +
 +        can_copy = has_selection;
 +        can_cut = has_selection;
 +        can_paste = TRUE;
 +    }
 +    else if (GTK_IS_TEXT_VIEW (widget))
 +    {
 +        gboolean has_selection;
 +        GtkTextBuffer *text_buffer;
 +
 +        text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
 +        has_selection = gtk_text_buffer_get_selection_bounds
 +                        (text_buffer, NULL, NULL);
 +
 +        can_copy = has_selection;
 +        can_cut = has_selection;
 +        can_paste = TRUE;
 +    }
 +    else
 +    {
 +#ifdef ORIGINAL_EPIPHANY_CODE
 +        /* For now we assume all actions are possible */
 +        can_copy = can_cut = can_paste = TRUE;
 +#else
 +        /* If its not a GtkEditable, we don't know what to do
 +         * with it. */
 +        can_copy = can_cut = can_paste = FALSE;
 +#endif
 +    }
 +
 +    action = gnc_main_window_find_action (window, "EditCopyAction");
 +    gtk_action_set_sensitive (action, can_copy);
 +    gtk_action_set_visible (action, !hide || can_copy);
 +    action = gnc_main_window_find_action (window, "EditCutAction");
 +    gtk_action_set_sensitive (action, can_cut);
 +    gtk_action_set_visible (action, !hide || can_cut);
 +    action = gnc_main_window_find_action (window, "EditPasteAction");
 +    gtk_action_set_sensitive (action, can_paste);
 +    gtk_action_set_visible (action,  !hide || can_paste);
 +}
 +
 +static void
 +gnc_main_window_enable_edit_actions_sensitivity (GncMainWindow *window)
 +{
 +    GtkAction *action;
 +
 +    action = gnc_main_window_find_action (window, "EditCopyAction");
 +    gtk_action_set_sensitive (action, TRUE);
 +    gtk_action_set_visible (action, TRUE);
 +    action = gnc_main_window_find_action (window, "EditCutAction");
 +    gtk_action_set_sensitive (action, TRUE);
 +    gtk_action_set_visible (action, TRUE);
 +    action = gnc_main_window_find_action (window, "EditPasteAction");
 +    gtk_action_set_sensitive (action, TRUE);
 +    gtk_action_set_visible (action, TRUE);
 +}
 +
 +static void
 +gnc_main_window_edit_menu_show_cb (GtkWidget *menu,
 +                                   GncMainWindow *window)
 +{
 +    gnc_main_window_update_edit_actions_sensitivity (window, FALSE);
 +}
 +
 +static void
 +gnc_main_window_edit_menu_hide_cb (GtkWidget *menu,
 +                                   GncMainWindow *window)
 +{
 +    gnc_main_window_enable_edit_actions_sensitivity (window);
 +}
 +
 +static void
 +gnc_main_window_init_menu_updaters (GncMainWindow *window)
 +{
 +    GtkWidget *edit_menu_item, *edit_menu;
 +
 +    edit_menu_item = gtk_ui_manager_get_widget
 +                     (window->ui_merge, "/menubar/Edit");
 +    edit_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (edit_menu_item));
 +
 +    g_signal_connect (edit_menu, "show",
 +                      G_CALLBACK (gnc_main_window_edit_menu_show_cb), window);
 +    g_signal_connect (edit_menu, "hide",
 +                      G_CALLBACK (gnc_main_window_edit_menu_hide_cb), window);
 +}
 +
 +/* CS: This callback functions will set the statusbar text to the
 + * "tooltip" property of the currently selected GtkAction.
 + *
 + * This code is directly copied from gtk+/test/testmerge.c.
 + * Thanks to (L)GPL! */
 +typedef struct _ActionStatus ActionStatus;
 +struct _ActionStatus
 +{
 +    GtkAction *action;
 +    GtkWidget *statusbar;
 +};
 +
 +static void
 +action_status_destroy (gpointer data)
 +{
 +    ActionStatus *action_status = data;
 +
 +    g_object_unref (action_status->action);
 +    g_object_unref (action_status->statusbar);
 +
 +    g_free (action_status);
 +}
 +
 +static void
 +set_tip (GtkWidget *widget)
 +{
 +    ActionStatus *data;
 +    gchar *tooltip;
 +
 +    data = g_object_get_data (G_OBJECT (widget), "action-status");
 +
 +    if (data)
 +    {
 +        g_object_get (data->action, "tooltip", &tooltip, NULL);
 +
 +        gtk_statusbar_push (GTK_STATUSBAR (data->statusbar), 0,
 +                            tooltip ? tooltip : "");
 +
 +        g_free (tooltip);
 +    }
 +}
 +
 +static void
 +unset_tip (GtkWidget *widget)
 +{
 +    ActionStatus *data;
 +
 +    data = g_object_get_data (G_OBJECT (widget), "action-status");
 +
 +    if (data)
 +        gtk_statusbar_pop (GTK_STATUSBAR (data->statusbar), 0);
 +}
 +
 +static void
 +connect_proxy (GtkUIManager *merge,
 +               GtkAction    *action,
 +               GtkWidget    *proxy,
 +               GtkWidget    *statusbar)
 +{
 +    if (GTK_IS_MENU_ITEM (proxy))
 +    {
 +        ActionStatus *data;
 +
 +        data = g_object_get_data (G_OBJECT (proxy), "action-status");
 +        if (data)
 +        {
 +            g_object_unref (data->action);
 +            g_object_unref (data->statusbar);
 +
 +            data->action = g_object_ref (action);
 +            data->statusbar = g_object_ref (statusbar);
 +        }
 +        else
 +        {
 +            data = g_new0 (ActionStatus, 1);
 +
 +            data->action = g_object_ref (action);
 +            data->statusbar = g_object_ref (statusbar);
 +
 +            g_object_set_data_full (G_OBJECT (proxy), "action-status",
 +                                    data, action_status_destroy);
 +
 +            g_signal_connect (proxy, "select",  G_CALLBACK (set_tip), NULL);
 +            g_signal_connect (proxy, "deselect", G_CALLBACK (unset_tip), NULL);
 +        }
 +    }
 +}
 +/* CS: end copied code from gtk+/test/testmerge.c */
 +
 +static void
 +gnc_main_window_window_menu (GncMainWindow *window)
 +{
 +    guint merge_id;
 +#ifdef MAC_INTEGRATION
 +    gchar *filename = gnc_filepath_locate_ui_file("gnc-windows-menu-ui-quartz.xml");
 +#else
 +    gchar *filename = gnc_filepath_locate_ui_file("gnc-windows-menu-ui.xml");
 +    GncMainWindowPrivate *priv;
 +#endif
 +    GError *error = NULL;
 +    g_assert(filename);
 +    merge_id = gtk_ui_manager_add_ui_from_file(window->ui_merge, filename,
 +               &error);
 +    g_free(filename);
 +    g_assert(merge_id);
 +#ifndef MAC_INTEGRATION
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    gtk_action_group_add_radio_actions (priv->action_group,
 +                                        radio_entries, n_radio_entries,
 +                                        0,
 +                                        G_CALLBACK(gnc_main_window_cmd_window_raise),
 +                                        window);
 +#endif
 +};
 +
 +static void
 +gnc_main_window_setup_window (GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +    GtkWidget *main_vbox;
 +    guint merge_id;
 +    GncPluginManager *manager;
 +    GList *plugins;
 +    GError *error = NULL;
 +    gchar *filename;
 +
 +    ENTER(" ");
 +
 +    /* Catch window manager delete signal */
 +    g_signal_connect (G_OBJECT (window), "delete-event",
 +                      G_CALLBACK (gnc_main_window_delete_event), window);
 +
 +    /* Create widgets and add them to the window */
 +    main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
 +    gtk_box_set_homogeneous (GTK_BOX (main_vbox), FALSE);
 +    gtk_widget_show (main_vbox);
 +    gtk_container_add (GTK_CONTAINER (window), main_vbox);
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    priv->menu_dock = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
 +    gtk_box_set_homogeneous (GTK_BOX (priv->menu_dock), FALSE);
 +    gtk_widget_show (priv->menu_dock);
 +    gtk_box_pack_start (GTK_BOX (main_vbox), priv->menu_dock,
 +                        FALSE, TRUE, 0);
 +
 +    priv->notebook = gtk_notebook_new ();
 +    g_object_set(G_OBJECT(priv->notebook),
 +                 "scrollable", TRUE,
 +                 "enable-popup", TRUE,
 +                 (char *)NULL);
 +    gtk_widget_show (priv->notebook);
 +    g_signal_connect (G_OBJECT (priv->notebook), "switch-page",
 +                      G_CALLBACK (gnc_main_window_switch_page), window);
 +    g_signal_connect (G_OBJECT (priv->notebook), "page-reordered",
 +                      G_CALLBACK (gnc_main_window_page_reordered), window);
 +    gtk_box_pack_start (GTK_BOX (main_vbox), priv->notebook,
 +                        TRUE, TRUE, 0);
 +
 +    priv->statusbar = gtk_statusbar_new ();
 +    gtk_widget_show (priv->statusbar);
 +    gtk_box_pack_start (GTK_BOX (main_vbox), priv->statusbar,
 +                        FALSE, TRUE, 0);
 +
 +    priv->progressbar = gtk_progress_bar_new ();
 +    gtk_progress_bar_set_text(GTK_PROGRESS_BAR(priv->progressbar), " ");
 +    gtk_widget_show (priv->progressbar);
 +    gtk_box_pack_start (GTK_BOX (priv->statusbar), priv->progressbar,
 +                        FALSE, TRUE, 0);
 +    gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(priv->progressbar),
 +                                    0.01);
 +
 +    window->ui_merge = gtk_ui_manager_new ();
 +
 +    /* Create menu and toolbar information */
 +    priv->action_group = gtk_action_group_new ("MainWindowActions");
 +    gnc_gtk_action_group_set_translation_domain (priv->action_group, GETTEXT_PACKAGE);
 +    gtk_action_group_add_actions (priv->action_group, gnc_menu_actions,
 +                                  gnc_menu_n_actions, window);
 +    gtk_action_group_add_toggle_actions (priv->action_group,
 +                                         toggle_actions, n_toggle_actions,
 +                                         window);
 +    gnc_plugin_update_actions(priv->action_group,
 +                              initially_insensitive_actions,
 +                              "sensitive", FALSE);
 +    gnc_plugin_update_actions(priv->action_group,
 +                              always_insensitive_actions,
 +                              "sensitive", FALSE);
 +    gnc_plugin_update_actions(priv->action_group,
 +                              always_hidden_actions,
 +                              "visible", FALSE);
 +    gnc_plugin_set_important_actions (priv->action_group,
 +                                      gnc_menu_important_actions);
 +    gtk_ui_manager_insert_action_group (window->ui_merge, priv->action_group, 0);
 +
 +    g_signal_connect (G_OBJECT (window->ui_merge), "add_widget",
 +                      G_CALLBACK (gnc_main_window_add_widget), window);
 +    /* Use the "connect-proxy" signal for tooltip display in the
 +       status bar */
 +    g_signal_connect (G_OBJECT (window->ui_merge), "connect-proxy",
 +                      G_CALLBACK (connect_proxy), priv->statusbar);
 +
 +    filename = gnc_filepath_locate_ui_file("gnc-main-window-ui.xml");
 +
 +    /* Can't do much without a ui. */
 +    g_assert (filename);
 +
 +    merge_id = gtk_ui_manager_add_ui_from_file (window->ui_merge,
 +               filename, &error);
 +    g_assert(merge_id || error);
 +    if (merge_id)
 +    {
 +        gtk_window_add_accel_group (GTK_WINDOW (window),
 +                                    gtk_ui_manager_get_accel_group(window->ui_merge));
 +        gtk_ui_manager_ensure_update (window->ui_merge);
 +    }
 +    else
 +    {
 +        g_critical("Failed to load ui file.\n  Filename %s\n  Error %s",
 +                   filename, error->message);
 +        g_error_free(error);
 +        g_assert(merge_id != 0);
 +    }
 +    g_free(filename);
 +    gnc_main_window_window_menu(window);
 +    gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
 +                           GNC_PREF_TAB_POSITION_TOP,
 +                           gnc_main_window_update_tab_position,
 +                           window);
 +    gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
 +                           GNC_PREF_TAB_POSITION_BOTTOM,
 +                           gnc_main_window_update_tab_position,
 +                           window);
 +    gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
 +                           GNC_PREF_TAB_POSITION_LEFT,
 +                           gnc_main_window_update_tab_position,
 +                           window);
 +    gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
 +                           GNC_PREF_TAB_POSITION_RIGHT,
 +                           gnc_main_window_update_tab_position,
 +                           window);
 +    gnc_main_window_update_tab_position(NULL, NULL, window);
 +
 +    gnc_main_window_init_menu_updaters(window);
 +
 +    /* Testing */
 +    /* Now update the "eXtensions" menu */
 +    if (!gnc_prefs_is_extra_enabled())
 +    {
 +        GtkAction*  action;
 +
 +        action = gtk_action_group_get_action(priv->action_group,
 +                                             "ExtensionsAction");
 +        gtk_action_set_visible(action, FALSE);
 +    }
 +
 +    /* GncPluginManager stuff */
 +    manager = gnc_plugin_manager_get ();
 +    plugins = gnc_plugin_manager_get_plugins (manager);
 +    g_list_foreach (plugins, gnc_main_window_add_plugin, window);
 +    g_list_free (plugins);
 +
 +    g_signal_connect (G_OBJECT (manager), "plugin-added",
 +                      G_CALLBACK (gnc_main_window_plugin_added), window);
 +    g_signal_connect (G_OBJECT (manager), "plugin-removed",
 +                      G_CALLBACK (gnc_main_window_plugin_removed), window);
 +
 +    LEAVE(" ");
 +}
 +
 +#ifdef MAC_INTEGRATION
 +/* Event handlers for the shutdown process.  Gnc_quartz_shutdown is
 + * connected to NSApplicationWillTerminate, the last chance to do
 + * anything before quitting. The problem is that it's launched from a
 + * CFRunLoop, not a g_main_loop, and if we call anything that would
 + * affect the main_loop we get an assert that we're in a subidiary
 + * loop.
 + */
 +static void
 +gnc_quartz_shutdown (GtkosxApplication *theApp, gpointer data)
 +{
 +    /* Do Nothing. It's too late. */
 +}
 +/* Should quit responds to NSApplicationBlockTermination; returning
 + * TRUE means "don't terminate", FALSE means "do terminate". If we
 + * decide that it's OK to terminate, then we queue a gnc_shutdown for
 + * the next idle time (because we're not running in the main loop) and
 + * then tell the OS not to terminate. That gives the gnc_shutdown an
 + * opportunity to shut down.
 + */
 +static gboolean
 +gnc_quartz_should_quit (GtkosxApplication *theApp, GncMainWindow *window)
 +{
 +    QofSession *session;
 +    gboolean needs_save;
 +
 +    if (!gnc_main_window_all_finish_pending() ||
 +            gnc_file_save_in_progress())
 +    {
 +        return TRUE;
 +    }
 +    session = gnc_get_current_session();
 +    needs_save = qof_book_session_not_saved(qof_session_get_book(session)) &&
 +                 !gnc_file_save_in_progress();
 +    if (needs_save && gnc_main_window_prompt_for_save(GTK_WIDGET(window)))
 +        return TRUE;
 +
 +    g_timeout_add(250, gnc_main_window_timed_quit, NULL);
 +    return TRUE;
 +}
 +
 +static void
 +gnc_quartz_set_menu(GncMainWindow* window)
 +{
 +    GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
 +    GtkWidget       *menu;
 +    GtkWidget       *item;
 +
 +    menu = gtk_ui_manager_get_widget (window->ui_merge, "/menubar");
 +    if (GTK_IS_MENU_ITEM (menu))
 +        menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
 +    gtk_widget_hide(menu);
 +    gtkosx_application_set_menu_bar (theApp, GTK_MENU_SHELL (menu));
 +
 +    item = gtk_ui_manager_get_widget (window->ui_merge,
 +                                      "/menubar/File/FileQuit");
 +    if (GTK_IS_MENU_ITEM (item))
 +        gtk_widget_hide (GTK_WIDGET (item));
 +
 +    item = gtk_ui_manager_get_widget (window->ui_merge,
 +                                      "/menubar/Help/HelpAbout");
 +    if (GTK_IS_MENU_ITEM (item))
 +    {
 +        gtkosx_application_insert_app_menu_item (theApp, GTK_WIDGET (item), 0);
 +    }
 +
 +    item = gtk_ui_manager_get_widget (window->ui_merge,
 +                                      "/menubar/Edit/EditPreferences");
 +    if (GTK_IS_MENU_ITEM (item))
 +    {
 +        gtkosx_application_insert_app_menu_item (theApp,
 +                gtk_separator_menu_item_new (), 1);
 +        gtkosx_application_insert_app_menu_item (theApp, GTK_WIDGET (item), 2);
 +    }
 +
 +    item = gtk_ui_manager_get_widget (window->ui_merge,
 +                                      "/menubar/Help");
 +    gtkosx_application_set_help_menu(theApp, GTK_MENU_ITEM(item));
 +    item = gtk_ui_manager_get_widget (window->ui_merge,
 +                                      "/menubar/Windows");
 +    gtkosx_application_set_window_menu(theApp, GTK_MENU_ITEM(item));
 +    g_signal_connect(theApp, "NSApplicationBlockTermination",
 +                     G_CALLBACK(gnc_quartz_should_quit), window);
 +    gtkosx_application_set_use_quartz_accelerators (theApp, FALSE);
 +    g_object_unref (theApp);
 +
 +}
 +#endif //MAC_INTEGRATION
 +
 +/* Callbacks */
 +static void
 +gnc_main_window_add_widget (GtkUIManager *merge,
 +                            GtkWidget *widget,
 +                            GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    if (GTK_IS_TOOLBAR (widget))
 +    {
 +        priv->toolbar = widget;
 +    }
 +
 +    gtk_box_pack_start (GTK_BOX (priv->menu_dock), widget, FALSE, FALSE, 0);
 +    gtk_widget_show (widget);
 +}
 +
 +/** Should a summary bar be visible in this window?  In order to
 + *  prevent synchronization issues, the "ViewSummaryBar"
 + *  GtkToggleAction is the sole source of information for whether or
 + *  not any summary bar should be visible in a window.
 + *
 + *  @param window A pointer to the window in question.
 + *
 + *  @param action If known, a pointer to the "ViewSummaryBar"
 + *  GtkToggleAction.  If NULL, the function will look up this action.
 + *
 + *  @return TRUE if the summarybar should be visible.
 + */
 +static gboolean
 +gnc_main_window_show_summarybar (GncMainWindow *window, GtkAction *action)
 +{
 +    GncMainWindowPrivate *priv;
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    if (action == NULL)
 +        action = gtk_action_group_get_action(priv->action_group,
 +                                             "ViewSummaryAction");
 +    if (action == NULL)
 +        return TRUE;
 +    return gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
 +}
 +
 +/** This function is invoked when the GtkNotebook switches pages.  It
 + *  is responsible for updating the rest of the window contents
 + *  outside of the notebook.  I.E. Updating the user interface, the
 + *  summary bar, etc.  This function also emits the "page_changed"
 + *  signal from the window so that any plugin can also learn about the
 + *  fact that the page has changed.
 + *
 + *  @internal
 + */
 +static void
 +gnc_main_window_switch_page (GtkNotebook *notebook,
 +                             gpointer *notebook_page,
 +                             gint pos,
 +                             GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +    GtkWidget *child;
 +    GncPluginPage *page;
 +    gboolean visible;
 +
 +    ENTER("Notebook %p, page, %p, index %d, window %p",
 +          notebook, notebook_page, pos, window);
 +    g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    if (priv->current_page != NULL)
 +    {
 +        page = priv->current_page;
 +        gnc_plugin_page_unmerge_actions (page, window->ui_merge);
 +        gnc_plugin_page_unselected (page);
 +    }
 +
 +    child = gtk_notebook_get_nth_page (notebook, pos);
 +    if (child)
 +    {
 +        page = g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL);
 +    }
 +    else
 +    {
 +        page = NULL;
 +    }
 +
 +    priv->current_page = page;
 +
 +    if (page != NULL)
 +    {
 +        /* Update the user interface (e.g. menus and toolbars */
 +        gnc_plugin_page_merge_actions (page, window->ui_merge);
 +        visible = gnc_main_window_show_summarybar(window, NULL);
 +        gnc_plugin_page_show_summarybar (page, visible);
 +
 +        /* Allow page specific actions */
 +        gnc_plugin_page_selected (page);
 +        gnc_window_update_status (GNC_WINDOW(window), page);
 +
 +        /* Update the page reference info */
 +        priv->usage_order = g_list_remove (priv->usage_order, page);
 +        priv->usage_order = g_list_prepend (priv->usage_order, page);
 +    }
 +
 +    gnc_plugin_update_actions(priv->action_group,
 +                              multiple_page_actions,
 +                              "sensitive",
 +                              g_list_length(priv->installed_pages) > 1);
 +
 +    gnc_main_window_update_title(window);
 +#ifndef MAC_INTEGRATION
 +    gnc_main_window_update_menu_item(window);
 +#endif
 +    g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, page);
 +    LEAVE(" ");
 +}
 +
 +/** This function is invoked when a GtkNotebook tab gets reordered by
 + *  drag and drop. It adjusts the list installed_pages to reflect the new
 + *  ordering so that GnuCash saves and restores the tabs correctly.
 + *
 + *  @internal
 + */
 +static void
 +gnc_main_window_page_reordered (GtkNotebook *notebook,
 +                                GtkWidget *child,
 +                                guint pos,
 +                                GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +    GncPluginPage *page;
 +    GList *old_link;
 +
 +    ENTER("Notebook %p, child %p, index %d, window %p",
 +          notebook, child, pos, window);
 +    g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
 +
 +    if (!child) return;
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +
 +    page = g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL);
 +    if (!page) return;
 +
 +    old_link = g_list_find (priv->installed_pages, page);
 +    if (!old_link) return;
 +
 +    priv->installed_pages = g_list_delete_link (priv->installed_pages,
 +                            old_link);
 +    priv->installed_pages = g_list_insert (priv->installed_pages,
 +                                           page, pos);
 +
 +    LEAVE(" ");
 +}
 +
 +static void
 +gnc_main_window_plugin_added (GncPlugin *manager,
 +                              GncPlugin *plugin,
 +                              GncMainWindow *window)
 +{
 +    g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
 +    g_return_if_fail (GNC_IS_PLUGIN (plugin));
 +
 +    gnc_plugin_add_to_window (plugin, window, window_type);
 +}
 +
 +static void
 +gnc_main_window_plugin_removed (GncPlugin *manager,
 +                                GncPlugin *plugin,
 +                                GncMainWindow *window)
 +{
 +    g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
 +    g_return_if_fail (GNC_IS_PLUGIN (plugin));
 +
 +    gnc_plugin_remove_from_window (plugin, window, window_type);
 +}
 +
 +
 +/* Command callbacks */
 +static void
 +gnc_main_window_cmd_page_setup (GtkAction *action,
 +                                GncMainWindow *window)
 +{
 +    GtkWindow *gtk_window;
 +
 +    g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
 +
 +    gtk_window = gnc_window_get_gtk_window(GNC_WINDOW(window));
 +    gnc_ui_page_setup(gtk_window);
 +}
 +
 +gboolean
 +gnc_book_options_dialog_apply_helper(GNCOptionDB * options)
 +{
 +    QofBook *book = gnc_get_current_book ();
 +    gboolean use_split_action_for_num_before =
 +        qof_book_use_split_action_for_num_field (book);
 +    gboolean use_book_currency_before =
 +        gnc_book_use_book_currency (book);
 +    gboolean use_split_action_for_num_after;
 +    gboolean use_book_currency_after;
 +    gboolean return_val = FALSE;
 +    GList *results = NULL, *iter;
 +
 +    if (!options) return return_val;
 +
 +    results = gnc_option_db_commit (options);
 +    for (iter = results; iter; iter = iter->next)
 +    {
 +        GtkWidget *dialog = gtk_message_dialog_new(NULL,
 +                                                   0,
 +                                                   GTK_MESSAGE_ERROR,
 +                                                   GTK_BUTTONS_OK,
 +                                                   "%s",
 +                                                   (char*)iter->data);
 +        gtk_dialog_run(GTK_DIALOG(dialog));
 +        gtk_widget_destroy(dialog);
 +        g_free (iter->data);
 +    }
 +    g_list_free (results);
 +    qof_book_begin_edit (book);
 +    qof_book_save_options (book, gnc_option_db_save, options, TRUE);
 +    use_split_action_for_num_after =
 +        qof_book_use_split_action_for_num_field (book);
 +    use_book_currency_after = gnc_book_use_book_currency (book);
 +    if (use_split_action_for_num_before != use_split_action_for_num_after)
 +    {
 +        gnc_book_option_num_field_source_change_cb (
 +                                                use_split_action_for_num_after);
 +        return_val = TRUE;
 +    }
 +    if (use_book_currency_before != use_book_currency_after)
 +    {
 +        gnc_book_option_book_currency_selected_cb (use_book_currency_after);
 +        return_val = TRUE;
 +    }
 +    qof_book_commit_edit (book);
 +    return return_val;
 +}
 +
 +static void
 +gnc_book_options_dialog_apply_cb(GNCOptionWin * optionwin,
 +                                 gpointer user_data)
 +{
 +    GNCOptionDB * options = user_data;
 +
 +    if (!options) return;
 +
 +    if (gnc_book_options_dialog_apply_helper (options))
 +        gnc_gui_refresh_all ();
 +}
 +
 +static void
 +gnc_book_options_dialog_close_cb(GNCOptionWin * optionwin,
 +                                 gpointer user_data)
 +{
 +    GNCOptionDB * options = user_data;
 +
 +    gnc_options_dialog_destroy(optionwin);
 +    gnc_option_db_destroy(options);
 +}
 +
 +static gboolean
 +show_handler (const char *class_name, gint component_id,
 +              gpointer user_data, gpointer iter_data)
 +{
 +    GtkWidget *dialog;
 +
 +    dialog = GTK_WIDGET(user_data);
 +    gtk_window_present(GTK_WINDOW(dialog));
 +    return(TRUE);
 +}
 +
 +GtkWidget *
 +gnc_book_options_dialog_cb (gboolean modal, gchar *title)
 +{
 +    QofBook *book = gnc_get_current_book ();
 +    GNCOptionDB *options;
 +    GNCOptionWin *optionwin;
 +
 +    options = gnc_option_db_new_for_type (QOF_ID_BOOK);
 +    qof_book_load_options (book, gnc_option_db_load, options);
 +    gnc_option_db_clean (options);
 +
 +    /* Only allow one Book Options dialog if called from file->properties
 +       menu */
 +    if (gnc_forall_gui_components(DIALOG_BOOK_OPTIONS_CM_CLASS,
 +                                  show_handler, NULL))
 +    {
 +        return NULL;
 +    }
 +    optionwin = gnc_options_dialog_new_modal (modal,
 +                (title ? title : _( "Book Options")),
 +                DIALOG_BOOK_OPTIONS_CM_CLASS);
 +    gnc_options_dialog_build_contents (optionwin, options);
 +
 +    gnc_options_dialog_set_book_options_help_cb (optionwin);
 +
 +    gnc_options_dialog_set_apply_cb (optionwin,
 +                                     gnc_book_options_dialog_apply_cb,
 +                                     (gpointer)options);
 +    gnc_options_dialog_set_close_cb (optionwin,
 +                                     gnc_book_options_dialog_close_cb,
 +                                     (gpointer)options);
 +    if (modal)
 +        gnc_options_dialog_set_new_book_option_values (options);
 +    return gnc_options_dialog_widget (optionwin);
 +}
 +
 +static void
 +gnc_main_window_cmd_file_properties (GtkAction *action, GncMainWindow *window)
 +{
 +    gnc_book_options_dialog_cb (FALSE, NULL);
 +}
 +
 +static void
 +gnc_main_window_cmd_file_close (GtkAction *action, GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +    GncPluginPage *page;
 +
 +    g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    page = priv->current_page;
 +    gnc_main_window_close_page(page);
 +}
 +
 +static void
 +gnc_main_window_cmd_file_quit (GtkAction *action, GncMainWindow *window)
 +{
 +    if (!gnc_main_window_all_finish_pending())
 +        return;
 +
 +    gnc_main_window_quit(window);
 +}
 +
 +static void
 +gnc_main_window_cmd_edit_cut (GtkAction *action, GncMainWindow *window)
 +{
 +    GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
 +    GtkTextBuffer *text_buffer;
 +    GtkClipboard *clipboard;
 +    gboolean editable;
 +
 +    if (GTK_IS_EDITABLE (widget))
 +    {
 +        gtk_editable_cut_clipboard (GTK_EDITABLE (widget));
 +    }
 +    else if (GTK_IS_TEXT_VIEW (widget))
 +    {
 +        text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
 +        clipboard = gtk_widget_get_clipboard (GTK_WIDGET(text_buffer),
 +                                              GDK_SELECTION_CLIPBOARD);
 +        editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (widget));
 +        gtk_text_buffer_cut_clipboard (text_buffer, clipboard, editable);
 +    }
 +}
 +
 +static void
 +gnc_main_window_cmd_edit_copy (GtkAction *action, GncMainWindow *window)
 +{
 +    GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
 +    GtkTextBuffer *text_buffer;
 +    GtkClipboard *clipboard;
 +
 +    if (GTK_IS_EDITABLE (widget))
 +    {
 +        gtk_editable_copy_clipboard (GTK_EDITABLE (widget));
 +    }
 +    else if (GTK_IS_TEXT_VIEW (widget))
 +    {
 +        text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
 +        clipboard = gtk_widget_get_clipboard (GTK_WIDGET(text_buffer),
 +                                              GDK_SELECTION_CLIPBOARD);
 +        gtk_text_buffer_copy_clipboard (text_buffer, clipboard);
 +    }
 +}
 +
 +static void
 +gnc_main_window_cmd_edit_paste (GtkAction *action, GncMainWindow *window)
 +{
 +    GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
 +    GtkTextBuffer *text_buffer;
 +    GtkClipboard *clipboard;
 +
 +    if (GTK_IS_EDITABLE (widget))
 +    {
 +        gtk_editable_paste_clipboard (GTK_EDITABLE (widget));
 +    }
 +    else if (GTK_IS_TEXT_VIEW (widget))
 +    {
 +        text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
 +        clipboard = gtk_widget_get_clipboard (GTK_WIDGET(text_buffer),
 +                                              GDK_SELECTION_CLIPBOARD);
 +        gtk_text_buffer_paste_clipboard (text_buffer, clipboard, NULL, FALSE);
 +    }
 +}
 +
 +static void
 +gnc_main_window_cmd_edit_preferences (GtkAction *action, GncMainWindow *window)
 +{
 +    gnc_preferences_dialog ();
 +}
 +
 +static void
 +gnc_main_window_cmd_view_refresh (GtkAction *action, GncMainWindow *window)
 +{
 +}
 +
 +static void
 +gnc_main_window_cmd_actions_reset_warnings (GtkAction *action, GncMainWindow *window)
 +{
 +    gnc_reset_warnings_dialog(GTK_WINDOW(window));
 +}
 +
 +static void
 +gnc_main_window_cmd_actions_rename_page (GtkAction *action, GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +    GncPluginPage *page;
 +    GtkWidget *label, *entry;
 +
 +    ENTER(" ");
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    page = priv->current_page;
 +    if (!page)
 +    {
 +        LEAVE("No current page");
 +        return;
 +    }
 +
 +    if (!main_window_find_tab_items(window, page, &label, &entry))
 +    {
 +        LEAVE("can't find required widgets");
 +        return;
 +    }
 +
 +    gtk_entry_set_text(GTK_ENTRY(entry), gtk_label_get_text(GTK_LABEL(label)));
 +    gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
 +    gtk_widget_hide(label);
 +    gtk_widget_show(entry);
 +    gtk_widget_grab_focus(entry);
 +    LEAVE("opened for editing");
 +}
 +
 +static void
 +gnc_main_window_cmd_view_toolbar (GtkAction *action, GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)))
 +    {
 +        gtk_widget_show (priv->toolbar);
 +    }
 +    else
 +    {
 +        gtk_widget_hide (priv->toolbar);
 +    }
 +}
 +
 +static void
 +gnc_main_window_cmd_view_summary (GtkAction *action, GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +    GList *item;
 +    gboolean visible;
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    visible = gnc_main_window_show_summarybar(window, action);
 +    for (item = priv->installed_pages; item; item = g_list_next(item))
 +    {
 +        gnc_plugin_page_show_summarybar(item->data, visible);
 +    }
 +}
 +
 +static void
 +gnc_main_window_cmd_view_statusbar (GtkAction *action, GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)))
 +    {
 +        gtk_widget_show (priv->statusbar);
 +    }
 +    else
 +    {
 +        gtk_widget_hide (priv->statusbar);
 +    }
 +}
 +
 +static void
 +gnc_main_window_cmd_window_new (GtkAction *action, GncMainWindow *window)
 +{
 +    GncMainWindow *new_window;
 +
 +    /* Create the new window */
 +    ENTER(" ");
 +    new_window = gnc_main_window_new ();
 +    gtk_widget_show(GTK_WIDGET(new_window));
 +    LEAVE(" ");
 +}
 +
 +static void
 +gnc_main_window_cmd_window_move_page (GtkAction *action, GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +    GncMainWindow *new_window;
 +    GncPluginPage *page;
 +    GtkNotebook *notebook;
 +    GtkWidget *tab_widget, *menu_widget;
 +
 +    ENTER("action %p,window %p", action, window);
 +
 +    /* Setup */
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    page = priv->current_page;
 +    if (!page)
 +    {
 +        LEAVE("invalid page");
 +        return;
 +    }
 +    if (!page->notebook_page)
 +    {
 +        LEAVE("invalid notebook_page");
 +        return;
 +    }
 +
 +    notebook = GTK_NOTEBOOK (priv->notebook);
 +    tab_widget = gtk_notebook_get_tab_label (notebook, page->notebook_page);
 +    menu_widget = gtk_notebook_get_menu_label (notebook, page->notebook_page);
 +
 +    /* Ref the page components, then remove it from its old window */
 +    g_object_ref(page);
 +    g_object_ref(tab_widget);
 +    g_object_ref(menu_widget);
 +    g_object_ref(page->notebook_page);
 +    gnc_main_window_disconnect(window, page);
 +
 +    /* Create the new window */
 +    new_window = gnc_main_window_new ();
 +    gtk_widget_show(GTK_WIDGET(new_window));
 +
 +    /* Now add the page to the new window */
 +    gnc_main_window_connect (new_window, page, tab_widget, menu_widget);
 +
 +    /* Unref the page components now that we're done */
 +    g_object_unref(page->notebook_page);
 +    g_object_unref(menu_widget);
 +    g_object_unref(tab_widget);
 +    g_object_unref(page);
 +
 +    /* just a little debugging. :-) */
 +    DEBUG("Moved page %p from window %p to new window %p",
 +          page, window, new_window);
 +    DEBUG("Old window current is %p, new window current is %p",
 +          priv->current_page, priv->current_page);
 +
 +    LEAVE("page moved");
 +}
 +
 +#ifndef MAC_INTEGRATION
 +static void
 +gnc_main_window_cmd_window_raise (GtkAction *action,
 +                                  GtkRadioAction *current,
 +                                  GncMainWindow *old_window)
 +{
 +    GncMainWindow *new_window;
 +    gint value;
 +
 +    g_return_if_fail(GTK_IS_ACTION(action));
 +    g_return_if_fail(GTK_IS_RADIO_ACTION(current));
 +    g_return_if_fail(GNC_IS_MAIN_WINDOW(old_window));
 +
 +    ENTER("action %p, current %p, window %p", action, current, old_window);
 +    value = gtk_radio_action_get_current_value(current);
 +    new_window = g_list_nth_data(active_windows, value);
 +    gtk_window_present(GTK_WINDOW(new_window));
 +    /* revert the change in the radio group
 +     * impossible while handling "changed" (G_SIGNAL_NO_RECURSE) */
 +    g_idle_add((GSourceFunc)gnc_main_window_update_radio_button, old_window);
 +    LEAVE(" ");
 +}
 +#endif /* !MAC_INTEGRATION */
 +
 +static void
 +gnc_main_window_cmd_help_tutorial (GtkAction *action, GncMainWindow *window)
 +{
 +    gnc_gnome_help (HF_GUIDE, NULL);
 +}
 +
 +static void
 +gnc_main_window_cmd_help_contents (GtkAction *action, GncMainWindow *window)
 +{
 +    gnc_gnome_help (HF_HELP, NULL);
 +}
 +
 +/** This is a helper function to find a data file and suck it into
 + *  memory.
 + *
 + *  @param partial The name of the file relative to the gnucash
 + *  specific shared data directory.
 + *
 + *  @return The text of the file or NULL. The caller is responsible
 + *  for freeing this string.
 + */
 +static gchar *
 +get_file (const gchar *partial)
 +{
 +    gchar *filename, *text = NULL;
 +    gsize length;
 +
 +    filename = gnc_filepath_locate_doc_file(partial);
 +    if (filename && g_file_get_contents(filename, &text, &length, NULL))
 +    {
 +	if (length)
 +	{
 +	    g_free(filename);
 +	    return text;
 +	}
 +        g_free(text);
 +    }
 +    g_free (filename);
 +    return NULL;
 +}
 +
 +
 +/** This is a helper function to find a data file, suck it into
 + *  memory, and split it into an array of strings.
 + *
 + *  @param partial The name of the file relative to the gnucash
 + *  specific shared data directory.
 + *
 + *  @return The text of the file as an array of strings, or NULL. The
 + *  caller is responsible for freeing all the strings and the array.
 + */
 +static gchar **
 +get_file_strsplit (const gchar *partial)
 +{
 +    gchar *text, **lines;
 +
 +    text = get_file(partial);
 +    if (!text)
 +        return NULL;
 +
 +    lines = g_strsplit_set(text, "\r\n", -1);
 +    g_free(text);
 +    return lines;
 +}
 +/** URL activation callback.
 + *  Use our own function to activate the URL in the users browser
 + *  instead of gtk_show_uri(), which requires gvfs.
 + *  Signature described in gtk docs at GtkAboutDialog activate-link signal.
 + */
 +
 +static gboolean
 +url_signal_cb (GtkAboutDialog *dialog, gchar *uri, gpointer data)
 +{
 +    gnc_launch_assoc (uri);
 +    return TRUE;
 +}
 +
 +/** Create and display the "about" dialog for gnucash.
 + *
 + *  @param action The GtkAction for the "about" menu item.
 + *
 + *  @param window The main window whose menu item was activated.
 + */
 +static void
 +gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window)
 +{
 +    GncMainWindowPrivate *priv;
 +
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +
 +    if (priv->about_dialog == NULL)
 +    {
 +	const gchar *fixed_message = _("The GnuCash personal finance manager. "
 +                                   "The GNU way to manage your money!");
- 	const gchar *copyright = _("© 1997-2017 Contributors");
++	gchar *copyright = g_strdup_printf(_("© 1997-%s Contributors"),
++			                             GNUCASH_BUILD_YEAR);
 +	gchar **authors = get_file_strsplit("AUTHORS");
 +	gchar **documenters = get_file_strsplit("DOCUMENTERS");
 +	gchar *license = get_file("LICENSE");
 +	gchar *message;
 +        GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
 +	GdkPixbuf *logo = gtk_icon_theme_load_icon (icon_theme,
 +                                                    GNC_ICON_APP,
 +                                                    48,
 +                                                    GTK_ICON_LOOKUP_USE_BUILTIN,
 +                                                    NULL);
 +
 +
 +#ifdef GNUCASH_SCM
 +    /* Development version */
 +    /* Translators: 1st %s is a fixed message, which is translated independently;
 +                    2nd %s is the scm type (svn/svk/git/bzr);
 +                    3rd %s is the scm revision number;
 +                    4th %s is the build date */
 +	message = g_strdup_printf(_("%s\nThis copy was built from %s rev %s on %s."),
 +				  fixed_message, GNUCASH_SCM, GNUCASH_SCM_REV,
 +				  GNUCASH_BUILD_DATE);
 +#else
 +    /* Translators: 1st %s is a fixed message, which is translated independently;
 +                    2nd %s is the scm (svn/svk/git/bzr) revision number;
 +                    3rd %s is the build date */
 +	message = g_strdup_printf(_("%s\nThis copy was built from rev %s on %s."),
 +				  fixed_message, GNUCASH_SCM_REV,
 +				  GNUCASH_BUILD_DATE);
 +#endif
 +	priv->about_dialog = gtk_about_dialog_new ();
 +	g_object_set (priv->about_dialog,
 +		      "authors", authors,
 +		      "documenters", documenters,
 +		      "comments", message,
 +		      "copyright", copyright,
 +		      "license", license,
 +		      "logo", logo,
 +		      "name", "GnuCash",
 +     /* Translators: the following string will be shown in Help->About->Credits
 +      * Enter your name or that of your team and an email contact for feedback.
 +      * The string can have multiple rows, so you can also add a list of
 +      * contributors. */
 +		      "translator-credits", _("translator_credits"),
 +		      "version", VERSION,
 +		      "website", "http://www.gnucash.org",
 +		      NULL);
 +
 +	g_free(message);
++	g_free(copyright);
 +	if (license)     g_free(license);
 +	if (documenters) g_strfreev(documenters);
 +	if (authors)     g_strfreev(authors);
 +	g_object_unref (logo);
 +	g_signal_connect (priv->about_dialog, "activate-link",
 +			  G_CALLBACK (url_signal_cb), NULL);
 +	g_signal_connect (priv->about_dialog, "response",
 +			  G_CALLBACK (gtk_widget_hide), NULL);
 +
 +        /* Set dialog to resize. */
 +        gtk_window_set_resizable(GTK_WINDOW(priv->about_dialog), TRUE);
 +
 +	gtk_window_set_transient_for (GTK_WINDOW (priv->about_dialog),
 +				      GTK_WINDOW (window));
 +    }
 +    gtk_dialog_run (GTK_DIALOG (priv->about_dialog));
 +}
 +
 +
 +/************************************************************
 + *                                                          *
 + ************************************************************/
 +
 +void
 +gnc_main_window_show_all_windows(void)
 +{
 +    GList *window_iter;
 +#ifdef MAC_INTEGRATION
 +    GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
 +#endif
 +    for (window_iter = active_windows; window_iter != NULL; window_iter = window_iter->next)
 +    {
 +        gtk_widget_show(GTK_WIDGET(window_iter->data));
 +    }
 +#ifdef MAC_INTEGRATION
 +    g_signal_connect(theApp, "NSApplicationWillTerminate",
 +                     G_CALLBACK(gnc_quartz_shutdown), NULL);
 +    gtkosx_application_ready(theApp);
 +    g_object_unref (theApp);
 +#endif
 +}
 +
 +/** Get a pointer to the first active top level window or NULL
 + *  if there is none.
 + *
 + *  @return A pointer to a GtkWindow object. */
 +GtkWidget *
 +gnc_ui_get_toplevel (void)
 +{
 +    GList *window;
 +
 +    for (window = active_windows; window; window = window->next)
 +        if (gtk_window_is_active (GTK_WINDOW (window->data)))
 +            return window->data;
 +
 +    return NULL;
 +}
 +
 +
 +/** Retrieve the gtk window associated with a main window object.
 + *  This function is called via a vector off a generic window
 + *  interface.
 + *
 + *  @param window A pointer to a generic window. */
 +static GtkWindow *
 +gnc_main_window_get_gtk_window (GncWindow *window)
 +{
 +    g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), NULL);
 +    return GTK_WINDOW(window);
 +}
 +
 +
 +/** Retrieve the status bar associated with a main window object.
 + *  This function is called via a vector off a generic window
 + *  interface.
 + *
 + *  @param window_in A pointer to a generic window. */
 +static GtkWidget *
 +gnc_main_window_get_statusbar (GncWindow *window_in)
 +{
 +    GncMainWindowPrivate *priv;
 +    GncMainWindow *window;
 +
 +    g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), NULL);
 +
 +    window = GNC_MAIN_WINDOW(window_in);
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    return priv->statusbar;
 +}
 +
 +
 +/** Retrieve the progress bar associated with a main window object.
 + *  This function is called via a vector off a generic window
 + *  interface.
 + *
 + *  @param window_in A pointer to a generic window. */
 +static GtkWidget *
 +gnc_main_window_get_progressbar (GncWindow *window_in)
 +{
 +    GncMainWindowPrivate *priv;
 +    GncMainWindow *window;
 +
 +    g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), NULL);
 +
 +    window = GNC_MAIN_WINDOW(window_in);
 +    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +    return priv->progressbar;
 +}
 +
 +
 +static void
 +gnc_main_window_all_ui_set_sensitive (GncWindow *unused, gboolean sensitive)
 +{
 +    GncMainWindow *window;
 +    GncMainWindowPrivate *priv;
 +    GList *groupp, *groups, *winp, *tmp;
 +    GtkWidget *close_button;
 +
 +    for (winp = active_windows; winp; winp = g_list_next(winp))
 +    {
 +        window = winp->data;
 +        priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
 +
 +        groups = gtk_ui_manager_get_action_groups(window->ui_merge);
 +        for (groupp = groups; groupp; groupp = g_list_next(groupp))
 +        {
 +            gtk_action_group_set_sensitive(GTK_ACTION_GROUP(groupp->data), sensitive);
 +        }
 +
 +        for (tmp = priv->installed_pages; tmp; tmp = g_list_next(tmp))
 +        {
 +            close_button = g_object_get_data(tmp->data, PLUGIN_PAGE_CLOSE_BUTTON);
 +            if (!close_button)
 +                continue;
 +            gtk_widget_set_sensitive (close_button, sensitive);
 +        }
 +    }
 +}
 +
 +
 +/** Initialize the generic window interface for a main window.
 + *
 + *  @param iface A pointer to the interface data structure to
 + *  populate. */
 +static void
 +gnc_window_main_window_init (GncWindowIface *iface)
 +{
 +    iface->get_gtk_window  = gnc_main_window_get_gtk_window;
 +    iface->get_statusbar   = gnc_main_window_get_statusbar;
 +    iface->get_progressbar = gnc_main_window_get_progressbar;
 +    iface->ui_set_sensitive = gnc_main_window_all_ui_set_sensitive;
 +}
 +
 +
 +/*  Set the window where all progressbar updates should occur.  This
 + *  is a wrapper around the gnc_window_set_progressbar_window()
 + *  function.
 + */
 +void
 +gnc_main_window_set_progressbar_window (GncMainWindow *window)
 +{
 +    GncWindow *gncwin;
 +    gncwin = GNC_WINDOW(window);
 +    gnc_window_set_progressbar_window(gncwin);
 +}
 +
 +
 +/** Popup a contextual menu.  This function ends up being called when
 + *  the user right-clicks in the context of a window, or uses the
 + *  keyboard context-menu request key combination (Shift-F10 by
 + *  default).
 + *
 + *  @param page This is the GncPluginPage corresponding to the visible
 + *  page.
 + *
 + *  @param event The event parameter passed to the "button-press"
 + *  callback.  May be null if there was no event (aka keyboard
 + *  request).
 + */
 +static void
 +do_popup_menu(GncPluginPage *page, GdkEventButton *event)
 +{
 +    GtkUIManager *ui_merge;
 +    GtkWidget *menu;
 +    int button, event_time;
 +
 +    g_return_if_fail(GNC_IS_PLUGIN_PAGE(page));
 +
 +    ENTER("page %p, event %p", page, event);
 +    ui_merge = gnc_plugin_page_get_ui_merge(page);
 +    if (ui_merge == NULL)
 +    {
 +        LEAVE("no ui merge");
 +        return;
 +    }
 +
 +    menu = gtk_ui_manager_get_widget(ui_merge, "/MainPopup");
 +    if (!menu)
 +    {
 +        LEAVE("no menu");
 +        return;
 +    }
 +
 +    if (event)
 +    {
 +        button = event->button;
 +        event_time = event->time;
 +    }
 +    else
 +    {
 +        button = 0;
 +        event_time = gtk_get_current_event_time ();
 +    }
 +#if GTK_CHECK_VERSION(3,22,0)
 +    gtk_menu_popup_at_pointer (GTK_MENU(menu), (GdkEvent *) event);
 +#else
 +    gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button, event_time);
 +#endif
 +    LEAVE(" ");
 +}
 +
 +
 +/** Callback function invoked when the user requests that Gnucash
 + *  popup the contextual menu via the keyboard context-menu request
 + *  key combination (Shift-F10 by default).
 + *
 + *  @param page This is the GncPluginPage corresponding to the visible
 + *  page.
 + *
 + *  @param widget Whatever widget had focus when the user issued the
 + *  keyboard context-menu request.
 + *
 + *  @return Always returns TRUE to indicate that the menu request was
 + *  handled.
 + */
 +static gboolean
 +gnc_main_window_popup_menu_cb (GtkWidget *widget,
 +                               GncPluginPage *page)
 +{
 +    ENTER("widget %p, page %p", widget, page);
 +    do_popup_menu(page, NULL);
 +    LEAVE(" ");
 +    return TRUE;
 +}
 +
 +
 +/*  Callback function invoked when the user clicks in the content of
 + *  any Gnucash window.  If this was a "right-click" then Gnucash will
 + *  popup the contextual menu.
 + */
 +gboolean
 +gnc_main_window_button_press_cb (GtkWidget *whatever,
 +                                 GdkEventButton *event,
 +                                 GncPluginPage *page)
 +{
 +    g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
 +
 +    ENTER("widget %p, event %p, page %p", whatever, event, page);
 +    /* Ignore double-clicks and triple-clicks */
 +    if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
 +    {
 +        do_popup_menu(page, event);
 +        LEAVE("menu shown");
 +        return TRUE;
 +    }
 +
 +    LEAVE("other click");
 +    return FALSE;
 +}
 +
 +
 +/* CS: Code copied from gtk/gtkactiongroup.c */
 +static gchar *
 +dgettext_swapped (const gchar *msgid,
 +                  const gchar *domainname)
 +{
 +    /* CS: Pass this through dgettext if and only if msgid is
 +       nonempty. */
 +    return (msgid && *msgid) ? dgettext (domainname, msgid) : (gchar*) msgid;
 +}
 +
 +/*
 + * This is copied into GnuCash from Gtk in order to fix problems when
 + * empty msgids were passed through gettext().
 + *
 + * See http://bugzilla.gnome.org/show_bug.cgi?id=326200 . If that bug
 + * is fixed in the gtk that we can rely open, then
 + * gnc_gtk_action_group_set_translation_domain can be replaced by
 + * gtk_action_group_set_translation_domain again.
 + */
 +void
 +gnc_gtk_action_group_set_translation_domain (GtkActionGroup *action_group,
 +        const gchar    *domain)
 +{
 +    g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
 +
 +    gtk_action_group_set_translate_func (action_group,
 +                                         (GtkTranslateFunc)dgettext_swapped,
 +                                         g_strdup (domain),
 +                                         g_free);
 +}
 +/* CS: End of code copied from gtk/gtkactiongroup.c */
 +
 +void
 +gnc_main_window_all_action_set_sensitive (const gchar *action_name,
 +        gboolean sensitive)
 +{
 +    GList *tmp;
 +    GtkAction *action;
 +
 +    for (tmp = active_windows; tmp; tmp = g_list_next(tmp))
 +    {
 +        action = gnc_main_window_find_action (tmp->data, action_name);
 +        gtk_action_set_sensitive (action, sensitive);
 +    }
 +}
 +
 +GtkUIManager *gnc_main_window_get_uimanager (GncMainWindow *window)
 +{
 +    g_assert(window);
 +    return window->ui_merge;
 +}
 +
 +/** @} */
 +/** @} */
diff --cc libgnucash/core-utils/CMakeLists.txt
index 710d39a,0000000..37b72d9
mode 100644,000000..100644
--- a/libgnucash/core-utils/CMakeLists.txt
+++ b/libgnucash/core-utils/CMakeLists.txt
@@@ -1,202 -1,0 +1,204 @@@
 +# CMakeLists.txt for libgnucash/core-utils
 +
 +ADD_SUBDIRECTORY(test)
 +
 +IF (BUILDING_FROM_VCS)
 +  SET (SWIG_CORE_UTILS_GUILE_C ${CMAKE_CURRENT_BINARY_DIR}/swig-core-utils-guile.c)
 +  GNC_ADD_SWIG_COMMAND (swig-core-utils-guile-c ${SWIG_CORE_UTILS_GUILE_C} ${CMAKE_CURRENT_SOURCE_DIR}/core-utils.i)
 +  SET (SWIG_CORE_UTILS_PYTHON_C ${CMAKE_CURRENT_BINARY_DIR}/swig-core-utils-python.c)
 +  GNC_ADD_SWIG_PYTHON_COMMAND (swig-core-utils-python ${SWIG_CORE_UTILS_PYTHON_C} ${CMAKE_CURRENT_SOURCE_DIR}/core-utils.i)
 +ELSE()
 +  SET(SWIG_CORE_UTILS_GUILE_C swig-core-utils-guile.c)
 +  SET(SWIG_CORE_UTILS_PYTHON_C swig-core-utils-python.c)
 +ENDIF()
 +
 +SET (core_utils_SOURCES
 +  binreloc.c
 +  gnc-prefs.c
 +  gnc-environment.c
 +  gnc-filepath-utils.cpp
 +  gnc-gkeyfile-utils.c
 +  gnc-glib-utils.c
 +  gnc-guile-utils.c
 +  gnc-jalali.c
 +  gnc-locale-utils.c
 +  gnc-path.c
 +)
 +
 +# Add dependency on config.h
 +SET_SOURCE_FILES_PROPERTIES (${core_utils_SOURCES} PROPERTIES OBJECT_DEPENDS ${CONFIG_H})
 +
 +IF (BUILDING_FROM_VCS)
 +  # Command to generate the swig-runtime.h header
 +  ADD_CUSTOM_COMMAND (
 +    OUTPUT ${SWIG_RUNTIME_H}
 +    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt
 +    COMMAND ${SWIG_EXECUTABLE} -guile -external-runtime ${SWIG_RUNTIME_H}
 +  )
 +ENDIF()
 +
 +ADD_CUSTOM_TARGET(swig-runtime-h DEPENDS ${SWIG_RUNTIME_H})
 +
 +# Add dependency on swig-runtime.h
 +SET_SOURCE_FILES_PROPERTIES (gnc-guile-utils.c PROPERTIES OBJECT_DEPENDS ${SWIG_RUNTIME_H})
 +
 +SET(prefix ${CMAKE_INSTALL_PREFIX})
 +SET(datadir ${DATADIR})
 +SET(bindir ${BINDIR})
 +SET(libdir ${LIBDIR})
 +SET(sysconfdir ${SYSCONFDIR})
 +GNC_CONFIGURE(gncla-dir.h.in gncla-dir.h)
 +
 +### Create gnc-version.h ###
 +
 +SET (GNC_VERSION_H_IN
 +"/* Autogenerated. Do not change. */
 +#ifndef GNC_VERSION_H
 +#define GNC_VERSION_H
 +
 +#define GNUCASH_SCM \"git\"
 +#define GNUCASH_BUILD_DATE \"@GNUCASH_BUILD_DATE@\"
++#define GNUCASH_BUILD_YEAR \"@GNUCASH_BUILD_YEAR@\"
 +#include \"gnc-vcs-info.h\"
 +#endif
 +")
 +
 +STRING(TIMESTAMP GNUCASH_BUILD_DATE "%Y-%m-%d")
++STRING(TIMESTAMP GNUCASH_BUILD_YEAR "%Y")
 +
 +STRING(CONFIGURE ${GNC_VERSION_H_IN} GNC_VERSION_H_CONTENT)
 +
 +FILE (WRITE ${CMAKE_CURRENT_BINARY_DIR}/gnc-version.h ${GNC_VERSION_H_CONTENT})
 +
 +### Create gnc-vcs-info.h
 +# The meta-cmake gymnastics here come from https://cmake.org/pipermail/cmake/2010-July/038015.html
 +
 +FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/gnc-vcs-info.h.in
 +   "/* Autogenerated. Do not change. */\n#define GNUCASH_SCM_REV \"@GNUCASH_SCM_REV@\"\n"
 +)
 +SET(SCM_REV_COMMAND "${CMAKE_SOURCE_DIR}/util/gnc-vcs-info -r")
 +FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/version.cmake
 +  "EXECUTE_PROCESS(
 +    COMMAND ${SHELL} ${SCM_REV_COMMAND} ${CMAKE_SOURCE_DIR}
 +    OUTPUT_VARIABLE GNUCASH_SCM_REV
 +    OUTPUT_STRIP_TRAILING_WHITESPACE
 +  )
 +  CONFIGURE_FILE(\${SRC} \${DST} @ONLY)
 +")
 +
 +IF (${CMAKE_VERSION} VERSION_GREATER "3.1")
 +  ADD_CUSTOM_TARGET(gnc-vcs-info ALL
 +    BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/gnc-vcs-info.h
 +    COMMAND ${CMAKE_COMMAND} -D SRC=${CMAKE_CURRENT_BINARY_DIR}/gnc-vcs-info.h.in
 +                     -D DST=${CMAKE_CURRENT_BINARY_DIR}/gnc-vcs-info.h
 +                     -P ${CMAKE_CURRENT_BINARY_DIR}/version.cmake
 +  )
 +ELSE()
 +  # BYPRODUCTS keyword not supported below 3.2
 +  ADD_CUSTOM_TARGET(gnc-vcs-info ALL
 +    COMMAND ${CMAKE_COMMAND} -D SRC=${CMAKE_CURRENT_BINARY_DIR}/gnc-vcs-info.h.in
 +                     -D DST=${CMAKE_CURRENT_BINARY_DIR}/gnc-vcs-info.h
 +                     -P ${CMAKE_CURRENT_BINARY_DIR}/version.cmake
 +  )
 +ENDIF()
 +
 +### Compile library
 +
 +SET(core_utils_noinst_HEADERS
 +  binreloc.h
 +  gnc-prefs.h
 +  gnc-prefs-p.h
 +  gnc-environment.h
 +  gnc-filepath-utils.h
 +  gnc-gkeyfile-utils.h
 +  gnc-glib-utils.h
 +  gnc-guile-utils.h
 +  gnc-jalali.h
 +  gnc-locale-utils.h
 +  gnc-path.h
 +)
 +
 +SET(core_utils_ALL_SOURCES ${core_utils_SOURCES} ${core_utils_noinst_HEADERS})
 +SET(core_utils_ALL_LIBRARIES ${Boost_LIBRARIES} ${GUILE_LDFLAGS} ${GLIB2_LDFLAGS} ${GOBJECT_LDFLAGS} ${GTK_MAC_LDFLAGS})
 +SET(core_utils_ALL_INCLUDES
 +  ${CMAKE_SOURCE_DIR}/common
 +  ${CMAKE_BINARY_DIR}/common
 +  ${CMAKE_CURRENT_BINARY_DIR}
 +  ${CMAKE_CURRENT_SOURCE_DIR}
 +  ${GLIB2_INCLUDE_DIRS}
 +  ${GUILE_INCLUDE_DIRS}
 +  ${GTK_MAC_INCLUDE_DIRS})
 +
 +SET_LOCAL_DIST(core_utils_DIST_local ${core_utils_ALL_SOURCES} CMakeLists.txt Makefile.am core-utils.i core-utils.scm gncla-dir.h.in)
 +SET(core_utils_DIST ${core_utils_DIST_local} ${test_core_utils_DIST} PARENT_SCOPE)
 +
 +IF (MAC_INTEGRATION)
 +  LIST(APPEND core_utils_ALL_LIBRARIES ${OSX_EXTRA_LIBRARIES})
 +ENDIF()
 +
 +ADD_LIBRARY	(gnc-core-utils ${core_utils_ALL_SOURCES} ${SWIG_CORE_UTILS_GUILE_C})
 +ADD_DEPENDENCIES(gnc-core-utils gnc-vcs-info)
 +
 +TARGET_LINK_LIBRARIES(gnc-core-utils ${core_utils_ALL_LIBRARIES})
 +
 +TARGET_COMPILE_DEFINITIONS(gnc-core-utils
 +    PRIVATE -DG_LOG_DOMAIN=\"gnc.core-utils\" ${GTK_MAC_CFLAGS_OTHER})
 +
 +TARGET_INCLUDE_DIRECTORIES(gnc-core-utils PUBLIC ${core_utils_ALL_INCLUDES})
 +
 +IF (MAC_INTEGRATION)
 +  TARGET_COMPILE_OPTIONS(gnc-core-utils PRIVATE ${OSX_EXTRA_COMPILE_FLAGS})
 +ENDIF(MAC_INTEGRATION)
 +
 +INSTALL(TARGETS gnc-core-utils
 +  LIBRARY DESTINATION lib
 +  ARCHIVE DESTINATION lib
 +  RUNTIME DESTINATION bin
 +)
 +
 +IF (WITH_PYTHON)
 +  ADD_LIBRARY	(gnc-core-utils-python ${core_utils_ALL_SOURCES} ${SWIG_CORE_UTILS_PYTHON_C})
 +  ADD_DEPENDENCIES(gnc-core-utils-python gnc-vcs-info)
 +
 +  TARGET_LINK_LIBRARIES(gnc-core-utils-python ${core_utils_ALL_LIBRARIES} ${PYTHON_LIBRARIES})
 +
 +  TARGET_COMPILE_DEFINITIONS(gnc-core-utils-python
 +    PRIVATE -DG_LOG_DOMAIN=\"gnc.core-utils\" ${GTK_MAC_CFLAGS_OTHER}
 +  )
 +
 +  TARGET_INCLUDE_DIRECTORIES(gnc-core-utils-python PUBLIC ${core_utils_ALL_INCLUDES} ${PYTHON_INCLUDE_DIRS})
 +
 +  IF (MAC_INTEGRATION)
 +    TARGET_COMPILE_OPTIONS(gnc-core-utils-python PRIVATE ${OSX_EXTRA_COMPILE_FLAGS})
 +  ENDIF(MAC_INTEGRATION)
 +
 +  INSTALL(TARGETS gnc-core-utils-python
 +    LIBRARY DESTINATION lib
 +    ARCHIVE DESTINATION lib
 +    RUNTIME DESTINATION bin
 +  )
 +
 +ENDIF()
 +
 +# No headers to install
 +
 +# Scheme
 +
 +SET (core_utils_SCHEME core-utils.scm)
 +
 +SET(GUILE_OUTPUT_DIR   gnucash)
 +SET(GUILE_MODULES      "")
 +SET(GUILE_LOAD_DIRS    libgnucash/core-utils)
 +SET(GUILE_LIBRARY_DIRS libgnucash/core-utils)
 +SET(GUILE_DEPENDS      gnc-core-utils)
 +
 +GNC_ADD_SCHEME_TARGETS(scm-core-utils
 +    "${core_utils_SCHEME}"
 +    ${GUILE_OUTPUT_DIR}
 +    "${GUILE_MODULES}"
 +    "${GUILE_LOAD_DIRS}"
 +    "${GUILE_LIBRARY_DIRS}"
 +    "${GUILE_DEPENDS}"
 +    FALSE
 +)
 +
diff --cc libgnucash/core-utils/Makefile.am
index c9830f6,0000000..b54f2dd
mode 100644,000000..100644
--- a/libgnucash/core-utils/Makefile.am
+++ b/libgnucash/core-utils/Makefile.am
@@@ -1,196 -1,0 +1,197 @@@
 +# No SUBDIR variable here...
 +# The 'test' subdir is added via src/Makefile to handle
 +# it's dependency on src/engine
 +
 +lib_LTLIBRARIES = libgnc-core-utils.la
 +
 +libgnc_core_utils_la_SOURCES = \
 +  binreloc.c \
 +  gnc-prefs.c \
 +  gnc-environment.c \
 +  gnc-filepath-utils.cpp \
 +  gnc-gkeyfile-utils.c \
 +  gnc-glib-utils.c \
 +  gnc-guile-utils.c \
 +  gnc-jalali.c \
 +  gnc-locale-utils.c \
 +  gnc-path.c \
 +  swig-core-utils-guile.c
 +
 +libgnc_core_utils_la_LIBADD = \
 +  ${GUILE_LIBS} \
 +  ${GLIB_LIBS} \
 +  ${BINRELOC_LIBS} \
 +  ${GTK_MAC_LIBS} \
 +  ${BOOST_LDFLAGS} -lboost_filesystem
 +
 +
 +noinst_HEADERS = \
 +  binreloc.h \
 +  gnc-prefs.h \
 +  gnc-prefs-p.h \
 +  gnc-environment.h \
 +  gnc-filepath-utils.h \
 +  gnc-gkeyfile-utils.h \
 +  gnc-glib-utils.h \
 +  gnc-guile-utils.h \
 +  gnc-jalali.h \
 +  gnc-locale-utils.h \
 +  gnc-path.h
 +
 +if BUILDING_FROM_VCS
 +swig-core-utils-guile.c: core-utils.i ${top_srcdir}/common/base-typemaps.i
 +	$(SWIG) -guile $(SWIG_ARGS) -Linkage module \
 +	-I${top_srcdir}/common -o $@ $<
 +if ! OS_WIN32
 +if ! SWIG_DIST_FAIL
 +	if ! `grep "define scm_from_utf8_string" $@ > /dev/null 2>&1`; then \
 +	  patch $@ $(top_srcdir)/common/swig-utf8.patch; \
 +	fi
 +endif
 +endif
 +swig-core-utils-python.c: core-utils.i ${top_srcdir}/common/base-typemaps.i
 +	$(SWIG) -python -Wall -Werror $(SWIG_ARGS) \
 +	-I${top_srcdir}/common -o $@ $<
 +endif
 +
 +AM_CPPFLAGS = \
 +  ${GUILE_CFLAGS} \
 +  ${GLIB_CFLAGS} \
 +  ${GTK_MAC_CFLAGS} \
 +  -I${top_builddir}/common \
 +  -I${top_srcdir}/common \
 +  $(BOOST_CPPFLAGS)
 +
 +gncscmmoddir = ${GNC_SCM_INSTALL_DIR}/gnucash
 +gncscmmod_DATA = core-utils.scm
 +
 +if GNUCASH_SEPARATE_BUILDDIR
 +#Only needed when srcdir and builddir are different
 +#for running
 +SCM_FILE_LINKS = ${gncscmmod_DATA}
 +endif
 +
 +if WITH_PYTHON
 +
 +lib_LTLIBRARIES += libgnc-core-utils-python.la
 +
 +libgnc_core_utils_python_la_SOURCES = \
 +	swig-core-utils-python.c
 +
 +libgnc_core_utils_python_la_LIBADD = \
 +	${PYTHON_LIBS} \
 +	libgnc-core-utils.la
 +
 +libgnc_core_utils_python_la_CPPFLAGS = \
 +  ${GLIB_CFLAGS} \
 +  ${GTK_MAC_CFLAGS} \
 +  ${PYTHON_CPPFLAGS} \
 +  -I${top_builddir}/common \
 +  -I${top_srcdir}/common
 +
 +endif
 +
 +.scm-links: 
 +	$(RM) -rf gnucash
 +	mkdir -p  gnucash
 +if GNUCASH_SEPARATE_BUILDDIR
 +	for X in ${SCM_FILE_LINKS} ; do \
 +	  $(LN_S) -f ${srcdir}/$$X . ; \
 +	done
 +endif
 +	( cd gnucash; for A in $(gncscmmod_DATA) ; do $(LN_S) -f ../$$A . ; done )
 +if ! OS_WIN32
 +# Windows knows no "ln -s" but uses "cp": must copy every time (see bug #566567).
 +	touch .scm-links
 +endif
 +
 +if GNC_HAVE_GUILE_2
 +GUILE_COMPILE_ENV = \
 +  --library-dir    ${top_builddir}/libgnucash/core-utils
 +
 +%.go : %.scm .scm-links $(lib_LTLIBRARIES)
 +	GNC_UNINSTALLED=yes \
 +	GNC_BUILDDIR=${top_builddir} \
 +	$(shell ${abs_top_srcdir}/common/gnc-test-env.pl --noexports ${GUILE_COMPILE_ENV})  \
 +	$(GUILD) compile -o $@ $<
 +
 +gncscmmodcachedir = ${pkglibdir}/scm/ccache/@GUILE_EFFECTIVE_VERSION@/gnucash
 +gncscmmodcache_DATA = $(gncscmmod_DATA:.scm=.go)
 +endif
 +
 +noinst_DATA = .scm-links
 +BUILT_SOURCES = gncla-dir.h gnc-version.h
 +EXTRA_DIST = \
 +  $(gncscmmod_DATA) \
 +  core-utils.i \
 +  gncla-dir.h.in \
 +  gnc-vcs-info.h \
 +  CMakeLists.txt
 +
 +clean-local:
 +	rm -rf gnucash
 +
 +CLEANFILES = $(BUILT_SOURCES) .scm-links ${gncscmmodcache_DATA}
 +
 +MAINTAINERCLEANFILES = swig-core-utils-guile.c swig-core-utils-python.c
 +
 +AM_CPPFLAGS += -DG_LOG_DOMAIN=\"gnc.core-utils\" -DGNC_SCM_INSTALL_DIR="\"${GNC_SCM_INSTALL_DIR}\""
 +
 +gncla-dir.h: gncla-dir.h.in ${top_builddir}/config.status Makefile
 +	rm -f $@.tmp
 +	sed < $< > $@.tmp \
 +		-e 's#@-DATADIRNAME-@#${DATADIRNAME}#g' \
 +		-e 's#@-libdir-@#${libdir}#g' \
 +		-e 's#@-bindir-@#${bindir}#g' \
 +		-e 's#@-sysconfdir-@#${sysconfdir}#g' \
 +		-e 's#@-datadir-@#${datadir}#g' \
 +		-e 's#@-prefix-@#${prefix}#g'
 +	mv $@.tmp $@
 +
 +## We borrow guile's convention and use @-...-@ as the substitution
 +## brackets here, instead of the usual @... at .  This prevents autoconf
 +## from substituting the values directly into the left-hand sides of
 +## the sed substitutions.
 +gnc-version.h: _gnc-version.h
 +	-if [ ! -f gnc-version.h ]; then cp _gnc-version.h gnc-version.h; fi
 +	-cmp -s _gnc-version.h gnc-version.h || cp _gnc-version.h gnc-version.h
 +	-rm -f _gnc-version.h
 +
 +_gnc-version.h: gnc-vcs-info.h Makefile
 +	@echo "/* Autogenerated. Do not change. */"   > _gnc-version.h
 +	@echo "#ifndef GNC_VERSION_H"                >> _gnc-version.h
 +	@echo "#define GNC_VERSION_H"                >> _gnc-version.h
 +	@echo ""                                     >> _gnc-version.h
 +	@scm_info=`${top_srcdir}/util/gnc-vcs-info -t ${top_srcdir}` ; \
 +	if [ $$? = 0 ] ; then \
 +		echo "#define GNUCASH_SCM \"$$scm_info\"" >> _gnc-version.h ; \
 +	fi
 +	@echo "#define GNUCASH_BUILD_DATE \"`date +%Y-%m-%d`\"" >> _gnc-version.h
++	@echo "#define GNUCASH_BUILD_YEAR \"`date +%Y`\"" >> _gnc-version.h
 +	@echo "#include \"gnc-vcs-info.h\""          >> _gnc-version.h
 +	@echo "#endif"                               >> _gnc-version.h
 +
 +gnc-vcs-info.h: _gnc-vcs-info.h
 +	-if [ ! -f gnc-vcs-info.h ]; then cp _gnc-vcs-info.h gnc-vcs-info.h; fi
 +	-cmp -s _gnc-vcs-info.h gnc-vcs-info.h || cp _gnc-vcs-info.h gnc-vcs-info.h
 +	-rm -f _gnc-vcs-info.h
 +
 +_gnc-vcs-info.h: Makefile
 +	@scm_info=`${top_srcdir}/util/gnc-vcs-info -r ${top_srcdir}` ; \
 +	if [ $$? = 0 ] ; then \
 +		if [ -z "$$scm_info" ] ; then \
 +			echo "gnc-vcs-info failed.  figure out why." ; \
 +			echo "can't determine svn/svk/git/bzr revision from ${top_srcdir}." ; \
 +			exit 1 ; \
 +		fi ; \
 +		echo "/* Autogenerated. Do not change. */"    > _gnc-vcs-info.h ; \
 +		echo "#define GNUCASH_SCM_REV \"$$scm_info\"" >> _gnc-vcs-info.h ; \
 +	else \
 +		if [ -r $(srcdir)/gnc-vcs-info.h ] ; then \
 +			cp $(srcdir)/gnc-vcs-info.h _gnc-vcs-info.h ; \
 +		else \
 +			echo "You're building from svn/svk/git/bzr...  But your build system is broken" ; \
 +			echo "Don't do that.   Complain to your build-system creator." ; \
 +			exit 1 ; \
 +		fi ; \
 +	fi

commit 6ec9aa362b647f6d24fd3836e3fee7117f6613e7
Author: Rob Gowin <robgowin at gmail.com>
Date:   Mon Sep 11 15:44:41 2017 -0500

    Fix issues with loading and running init.py

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 70d3380..94f74c1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -383,6 +383,20 @@ IF (WITH_PYTHON)
   IF (NOT PYTHONLIBS_FOUND)
     MESSAGE(SEND_ERROR "Python support enabled, but Python libraries not found.")
   ENDIF()
+
+  # Determine where to install the python libraries.
+  EXECUTE_PROCESS(
+    COMMAND ${PYTHON_EXECUTABLE} -c "import sysconfig; print sysconfig.get_path('platlib', vars = { 'platbase' : '${CMAKE_INSTALL_PREFIX}' }  )"
+    RESULT_VARIABLE PYTHON_SYSCONFIG_RESULT
+    OUTPUT_VARIABLE PYTHON_SYSCONFIG_OUTPUT
+    ERROR_VARIABLE PYTHON_SYSCONFIG_ERROR
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    ERROR_STRIP_TRAILING_WHITESPACE
+  )
+  IF (PYTHON_SYSCONFIG_RESULT)
+    MESSAGE(SEND_ERROR "Could not determine Python site-package directory:\n${PYTHON_SYSCONFIG_ERROR}")
+  ENDIF()
+  STRING(REPLACE ${CMAKE_INSTALL_PREFIX} ${CMAKE_BINARY_DIR} PYTHON_SYSCONFIG_BUILD ${PYTHON_SYSCONFIG_OUTPUT})  
 ENDIF()
 
 
diff --git a/src/bin/CMakeLists.txt b/src/bin/CMakeLists.txt
index 9599994..c552e96 100644
--- a/src/bin/CMakeLists.txt
+++ b/src/bin/CMakeLists.txt
@@ -105,7 +105,7 @@ ENDIF()
 
 IF (NOT(${DATADIR} STREQUAL "/usr/share") AND NOT(${DATADIR} STREQUAL "/usr/local/share"))
   FILE(APPEND ${ENV_FILE_OUT} ${XDG_TEXT})
-  FILE(APPEND ${ENV_FILE_OUT} "XDG_DATA_DIRS=${DATADIR};{XDG_DATA_DIRS}" "${GNC_SYSTEM_XDG_DATA_DIRS}\n")
+  FILE(APPEND ${ENV_FILE_OUT} "XDG_DATA_DIRS=${DATADIR};{XDG_DATA_DIRS};" "${GNC_SYSTEM_XDG_DATA_DIRS}\n")
 ENDIF()
 
 FILE(APPEND ${BUILD_ENV_FILE_OUT} "GNC_DBD_DIR=${LIBDBI_DRIVERS_DIR}/dbd")
@@ -113,6 +113,19 @@ FILE(APPEND ${BUILD_ENV_FILE_OUT} "GNC_DBD_DIR=${LIBDBI_DRIVERS_DIR}/dbd")
 FILE(APPEND ${BUILD_ENV_FILE_OUT} ${XDG_TEXT})
 FILE(APPEND ${BUILD_ENV_FILE_OUT} "XDG_DATA_DIRS=${DATADIR_BUILD};{XDG_DATA_DIRS};" "${GNC_SYSTEM_XDG_DATA_DIRS}\n")
 
+SET(PYTHON_TEXT "
+# Define PYTHONPATH for non default installation path.\n"
+ )
+IF (NOT(${CMAKE_INSTALL_PREFIX} STREQUAL "/usr") AND NOT(${CMAKE_INSTALL_PREFIX} STREQUAL "/usr/local"))
+ 
+  FILE(APPEND ${ENV_FILE_OUT} ${PYTHON_TEXT})
+  FILE(APPEND ${ENV_FILE_OUT} "PYTHONPATH=${PYTHON_SYSCONFIG_OUTPUT};{PYTHONPATH}")
+ENDIF()
+
+FILE(APPEND ${BUILD_ENV_FILE_OUT} ${PYTHON_TEXT})
+FILE(APPEND ${BUILD_ENV_FILE_OUT} "PYTHONPATH=${PYTHON_SYSCONFIG_BUILD};{PYTHONPATH}")
+
+
 FILE(COPY ${BUILD_ENV_FILE_OUT}
   DESTINATION ${SYSCONFDIR_BUILD}/gnucash
   FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
diff --git a/src/optional/python-bindings/CMakeLists.txt b/src/optional/python-bindings/CMakeLists.txt
index eb1e6e9..92ea1d8 100644
--- a/src/optional/python-bindings/CMakeLists.txt
+++ b/src/optional/python-bindings/CMakeLists.txt
@@ -80,35 +80,24 @@ IF(WITH_PYTHON)
   ADD_TEST(NAME sqlite3test COMMAND sqlite3test)
   ADD_DEPENDENCIES(check sqlite3test)
 
-
-  # Determine where to install the python libraries.
-  EXECUTE_PROCESS(
-    COMMAND ${PYTHON_EXECUTABLE} -c "import sysconfig; print sysconfig.get_path('platlib', vars = { 'platbase' : '${CMAKE_INSTALL_PREFIX}' }  )"
-    RESULT_VARIABLE PYTHON_SYSCONFIG_RESULT
-    OUTPUT_VARIABLE PYTHON_SYSCONFIG_OUTPUT
-    ERROR_VARIABLE PYTHON_SYSCONFIG_ERROR
-    OUTPUT_STRIP_TRAILING_WHITESPACE
-    ERROR_STRIP_TRAILING_WHITESPACE
-  )
-  IF (PYTHON_SYSCONFIG_RESULT)
-    MESSAGE(SEND_ERROR "Could not determine Python site-package directory:\n${PYTHON_SYSCONFIG_ERROR}")
-  ENDIF()
-
   INSTALL(TARGETS gnucash_core_c
     LIBRARY DESTINATION ${PYTHON_SYSCONFIG_OUTPUT}/gnucash
     ARCHIVE DESTINATION ${PYTHON_SYSCONFIG_OUTPUT}/gnucash
   )
-  INSTALL(FILES __init__.py function_class.py gnucash_business.py gnucash_core.py
-    ${CMAKE_CURRENT_BINARY_DIR}/gnucash_core_c.py
+  INSTALL(FILES ${PYEXEC_FILES} ${CMAKE_CURRENT_BINARY_DIR}/gnucash_core_c.py
     DESTINATION ${PYTHON_SYSCONFIG_OUTPUT}/gnucash
   )
 
-  FILE(COPY ${PYEXEC_FILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/gnucash)
+  FILE(COPY ${PYEXEC_FILES} DESTINATION ${PYTHON_SYSCONFIG_BUILD}/gnucash)
 
   ADD_CUSTOM_TARGET(gnucash-core-c-py ALL
-    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/gnucash_core_c.py ${CMAKE_CURRENT_BINARY_DIR}/gnucash
+    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/gnucash_core_c.py ${PYTHON_SYSCONFIG_BUILD}/gnucash
     DEPENDS ${SWIG_GNUCASH_CORE_C})
 
+  ADD_CUSTOM_TARGET(gnucash-core-c-build ALL
+    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/lib/gnucash/_gnucash_core_c${CMAKE_SHARED_LIBRARY_SUFFIX} ${PYTHON_SYSCONFIG_BUILD}/gnucash
+    DEPENDS gnucash_core_c)
+    
 ENDIF()
 
 SET(python_bindings_DATA ${PYEXEC_FILES}
@@ -118,4 +107,4 @@ SET(python_bindings_DATA ${PYEXEC_FILES}
 
 SET_LOCAL_DIST(python_bindings_DIST_local CMakeLists.txt Makefile.am ${python_bindings_DATA})
 
-SET(python_bindings_DIST ${python_bindings_DIST_local} ${test_python_bindings_DIST} ${example_scripts_DIST} PARENT_SCOPE)
\ No newline at end of file
+SET(python_bindings_DIST ${python_bindings_DIST_local} ${test_python_bindings_DIST} ${example_scripts_DIST} PARENT_SCOPE)
diff --git a/src/optional/python-bindings/tests/CMakeLists.txt b/src/optional/python-bindings/tests/CMakeLists.txt
index f900fa1..78b244d 100644
--- a/src/optional/python-bindings/tests/CMakeLists.txt
+++ b/src/optional/python-bindings/tests/CMakeLists.txt
@@ -3,7 +3,7 @@ IF (WITH_PYTHON)
   ADD_TEST(python-bindings ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/runTests.py.in)
   SET_PROPERTY(TEST python-bindings PROPERTY ENVIRONMENT
     GNC_BUILDDIR=${CMAKE_BINARY_DIR}
-    PYTHONPATH=${CMAKE_BINARY_DIR}/src/optional/python-bindings:${CMAKE_BINARY_DIR}/lib/gnucash:${CMAKE_BINARY_DIR}/src/test-core
+    PYTHONPATH=${PYTHON_SYSCONFIG_BUILD}:${CMAKE_BINARY_DIR}/lib/gnucash:${CMAKE_BINARY_DIR}/src/test-core
   )
 ENDIF()
 
@@ -15,4 +15,4 @@ SET(test_python_bindings_DATA
         test_split.py
         test_transaction.py)
 
-SET_DIST_LIST(test_python_bindings_DIST CMakeLists.txt Makefile.am ${test_python_bindings_DATA})
\ No newline at end of file
+SET_DIST_LIST(test_python_bindings_DIST CMakeLists.txt Makefile.am ${test_python_bindings_DATA})
diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt
index 6698f3f..79d27ba 100644
--- a/src/python/CMakeLists.txt
+++ b/src/python/CMakeLists.txt
@@ -16,7 +16,11 @@ IF (WITH_PYTHON)
     RUNTIME DESTINATION bin
   )
 
-  INSTALL(FILES init.py DESTINATION share/python)
+  INSTALL(DIRECTORY pycons DESTINATION share/gnucash/python)
+  INSTALL(FILES init.py DESTINATION share/gnucash/python)
+
+  FILE(COPY init.py DESTINATION ${CMAKE_BINARY_DIR}/share/gnucash/python)
+  FILE(COPY pycons DESTINATION ${CMAKE_BINARY_DIR}/share/gnucash/python)
 ENDIF(WITH_PYTHON)
 
 SET_LOCAL_DIST(python_DIST_local CMakeLists.txt Makefile.am gncmod-python.c init.py)

commit 65f18589da0f9be5749469ab73895fdf6917e7be
Author: fell <frank.h.ellenberger at gmail.com>
Date:   Mon Sep 11 04:40:22 2017 +0200

    Update changed string in de.po

diff --git a/po/de.po b/po/de.po
index 85cf949..a5d6b6f 100644
--- a/po/de.po
+++ b/po/de.po
@@ -15,8 +15,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: GnuCash 2.6.15\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-09-10 20:53+0200\n"
-"PO-Revision-Date: 2017-09-09 21:33+0200\n"
+"POT-Creation-Date: 2017-09-11 02:57+0200\n"
+"PO-Revision-Date: 2017-09-11 04:33+0200\n"
 "Last-Translator: Mechtilde <ooo at mechtilde.de>\n"
 "Language-Team: GnuCash-de <gnucash-de at gnucash.org>\n"
 "Language: de\n"
@@ -1205,7 +1205,7 @@ msgstr "GnuCash Entwicklungsversion %s"
 #. 2nd %s is the scm type (svn/svk/git/bzr);
 #. 3rd %s is the scm revision number;
 #. 4th %s is the build date
-#: ../src/bin/gnucash-bin.c:452 ../src/gnome-utils/gnc-main-window.c:4385
+#: ../src/bin/gnucash-bin.c:452 ../src/gnome-utils/gnc-main-window.c:4386
 #, c-format
 msgid ""
 "%s\n"
@@ -1222,7 +1222,7 @@ msgstr "GnuCash %s"
 #. Translators: 1st %s is a fixed message, which is translated independently;
 #. 2nd %s is the scm (svn/svk/git/bzr) revision number;
 #. 3rd %s is the build date
-#: ../src/bin/gnucash-bin.c:463 ../src/gnome-utils/gnc-main-window.c:4392
+#: ../src/bin/gnucash-bin.c:463 ../src/gnome-utils/gnc-main-window.c:4393
 #, c-format
 msgid ""
 "%s\n"
@@ -7145,9 +7145,9 @@ msgid ""
 "manual page of strftime by \"man 3 strftime\"."
 msgstr ""
 "Wenn das Datumsformat auf Benutzerdefiniert gesetzt ist, kann in dieser "
-"Ausdruck als Argument an das strftime-Format übergegeben werden. Es kann eine "
-"beliebige gültige strftime Zeichenkette sein. Für mehr Informationen lesen "
-"Sie die Ausgabe von »man 3 strftime«."
+"Ausdruck als Argument an das strftime-Format übergegeben werden. Es kann "
+"eine beliebige gültige strftime Zeichenkette sein. Für mehr Informationen "
+"lesen Sie die Ausgabe von »man 3 strftime«."
 
 #: ../src/gnome/gschemas/org.gnucash.dialogs.checkprinting.gschema.xml.in.in.h:10
 msgid "Units in which the custom coordinates are expressed"
@@ -12133,14 +12133,15 @@ msgstr ""
 "GnuCash: Ihr privater Finanzmanager. Die freie Lösung zur Finanzverwaltung."
 
 #: ../src/gnome-utils/gnc-main-window.c:4372
-msgid "© 1997-2017 Contributors"
-msgstr "© 1997-2017 Mitwirkende"
+#, c-format
+msgid "© 1997-%s Contributors"
+msgstr "© 1997-%s Mitwirkende"
 
 #. Translators: the following string will be shown in Help->About->Credits
 #. * Enter your name or that of your team and an email contact for feedback.
 #. * The string can have multiple rows, so you can also add a list of
 #. * contributors.
-#: ../src/gnome-utils/gnc-main-window.c:4409
+#: ../src/gnome-utils/gnc-main-window.c:4410
 msgid "translator_credits"
 msgstr "Christian Stimming <christian at cstimming.de> et. al."
 

commit 2275603eb4cb3725cac79e2955a9c68b0617f318
Author: fell <frank.h.ellenberger at gmail.com>
Date:   Mon Sep 11 04:34:02 2017 +0200

    Fix error in recent commit

diff --git a/src/core-utils/CMakeLists.txt b/src/core-utils/CMakeLists.txt
index b379fe3..26089f0 100644
--- a/src/core-utils/CMakeLists.txt
+++ b/src/core-utils/CMakeLists.txt
@@ -67,7 +67,7 @@ SET (GNC_VERSION_H_IN
 ")
 
 STRING(TIMESTAMP GNUCASH_BUILD_DATE "%Y-%m-%d")
-STRING(TIMESTAMP GNUCASH_BUILD_DATE "%Y")
+STRING(TIMESTAMP GNUCASH_BUILD_YEAR "%Y")
 
 STRING(CONFIGURE ${GNC_VERSION_H_IN} GNC_VERSION_H_CONTENT)
 

commit 240bc32e57098344a979791b38c4517e337a0fb0
Author: fell <frank.h.ellenberger at gmail.com>
Date:   Mon Sep 11 02:40:03 2017 +0200

    Remove build_year from translatable copyright string

diff --git a/src/core-utils/CMakeLists.txt b/src/core-utils/CMakeLists.txt
index c48f7ee..b379fe3 100644
--- a/src/core-utils/CMakeLists.txt
+++ b/src/core-utils/CMakeLists.txt
@@ -61,11 +61,13 @@ SET (GNC_VERSION_H_IN
 
 #define GNUCASH_SCM \"git\"
 #define GNUCASH_BUILD_DATE \"@GNUCASH_BUILD_DATE@\"
+#define GNUCASH_BUILD_YEAR \"@GNUCASH_BUILD_YEAR@\"
 #include \"gnc-vcs-info.h\"
 #endif
 ")
 
 STRING(TIMESTAMP GNUCASH_BUILD_DATE "%Y-%m-%d")
+STRING(TIMESTAMP GNUCASH_BUILD_DATE "%Y")
 
 STRING(CONFIGURE ${GNC_VERSION_H_IN} GNC_VERSION_H_CONTENT)
 
diff --git a/src/core-utils/Makefile.am b/src/core-utils/Makefile.am
index 514eb9d..8f2f70b 100644
--- a/src/core-utils/Makefile.am
+++ b/src/core-utils/Makefile.am
@@ -176,6 +176,7 @@ _gnc-version.h: gnc-vcs-info.h Makefile
 		echo "#define GNUCASH_SCM \"$$scm_info\"" >> _gnc-version.h ; \
 	fi
 	@echo "#define GNUCASH_BUILD_DATE \"`date +%Y-%m-%d`\"" >> _gnc-version.h
+	@echo "#define GNUCASH_BUILD_YEAR \"`date +%Y`\"" >> _gnc-version.h
 	@echo "#include \"gnc-vcs-info.h\""          >> _gnc-version.h
 	@echo "#endif"                               >> _gnc-version.h
 
diff --git a/src/gnome-utils/gnc-main-window.c b/src/gnome-utils/gnc-main-window.c
index ee37483..347805a 100644
--- a/src/gnome-utils/gnc-main-window.c
+++ b/src/gnome-utils/gnc-main-window.c
@@ -4369,7 +4369,8 @@ gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window)
     {
 	const gchar *fixed_message = _("The GnuCash personal finance manager. "
                                    "The GNU way to manage your money!");
-	const gchar *copyright = _("© 1997-2017 Contributors");
+	gchar *copyright = g_strdup_printf(_("© 1997-%s Contributors"),
+			                             GNUCASH_BUILD_YEAR);
 	gchar **authors = get_file_strsplit("AUTHORS");
 	gchar **documenters = get_file_strsplit("DOCUMENTERS");
 	gchar *license = get_file("LICENSE");
@@ -4412,6 +4413,7 @@ gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window)
 		      NULL);
 
 	g_free(message);
+	g_free(copyright);
 	if (license)     g_free(license);
 	if (documenters) g_strfreev(documenters);
 	if (authors)     g_strfreev(authors);

commit 6c8275a7ffcd9d9a7e9c4daac155a45a418ced0a
Author: fell <frank.h.ellenberger at gmail.com>
Date:   Sun Sep 10 21:49:26 2017 +0200

    Review of PR #183
    
    4612 translated messages, 25 fuzzy translations, 48 untranslated
    messages.

diff --git a/po/de.po b/po/de.po
index e8a618b..85cf949 100644
--- a/po/de.po
+++ b/po/de.po
@@ -15,7 +15,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: GnuCash 2.6.15\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-09-04 00:02+0200\n"
+"POT-Creation-Date: 2017-09-10 20:53+0200\n"
 "PO-Revision-Date: 2017-09-09 21:33+0200\n"
 "Last-Translator: Mechtilde <ooo at mechtilde.de>\n"
 "Language-Team: GnuCash-de <gnucash-de at gnucash.org>\n"
@@ -7145,7 +7145,7 @@ msgid ""
 "manual page of strftime by \"man 3 strftime\"."
 msgstr ""
 "Wenn das Datumsformat auf Benutzerdefiniert gesetzt ist, kann in dieser "
-"Ausdruckals Argument an das strftime-Format übergegeben werden. Es kann eine "
+"Ausdruck als Argument an das strftime-Format übergegeben werden. Es kann eine "
 "beliebige gültige strftime Zeichenkette sein. Für mehr Informationen lesen "
 "Sie die Ausgabe von »man 3 strftime«."
 
@@ -8322,7 +8322,8 @@ msgid ""
 msgstr ""
 "Auf hochauflösenden Bildschirmen können Berichte schwer zu lesen sein.\n"
 "Diese Option erlaubt das Skalieren der Berichte um den gewählten Faktor.\n"
-"Z.B. wird ein Wert von 2 die Berichte mit dem Doppelten ihrer ursprünglichen Größe darstellen."
+"Z.B. wird ein Wert von 2 die Berichte mit dem Doppelten ihrer ursprünglichen "
+"Größe darstellen."
 
 #: ../src/gnome/gschemas/org.gnucash.gschema.xml.in.in.h:113
 msgid "PDF export file name format"
@@ -13417,7 +13418,7 @@ msgstr "Be_merkung:"
 msgid "Ta_x related"
 msgstr "Steuer_relevant"
 
-#. Translators: use the same words here as in
+#. Translators: use the same words here as in 'Ta_x Report Options'.
 #: ../src/gnome-utils/gtkbuilder/dialog-account.glade.h:21
 msgid ""
 "Use Edit->Tax Report Options to set the tax-related flag and assign a tax "
@@ -14809,6 +14810,26 @@ msgstr "Nicht vorgemerkt"
 msgid "Select occurrence date above."
 msgstr "Wählen Sie oben ein Datum des Auftretens."
 
+#: ../src/gnome-utils/gtkbuilder/gnc-frequency.glade.h:7
+msgctxt "Daily"
+msgid "Every"
+msgstr "Alle"
+
+#: ../src/gnome-utils/gtkbuilder/gnc-frequency.glade.h:8
+msgctxt "Daily"
+msgid "days."
+msgstr "Tage."
+
+#: ../src/gnome-utils/gtkbuilder/gnc-frequency.glade.h:10
+msgctxt "Weekly"
+msgid "Every"
+msgstr "Alle"
+
+#: ../src/gnome-utils/gtkbuilder/gnc-frequency.glade.h:11
+msgctxt "Weekly"
+msgid "weeks."
+msgstr "Wochen."
+
 #: ../src/gnome-utils/gtkbuilder/gnc-frequency.glade.h:12
 #: ../src/report/standard-reports/daily-reports.scm:355
 msgid "Saturday"
@@ -14850,6 +14871,16 @@ msgstr "Montag"
 msgid "Tuesday"
 msgstr "Dienstag"
 
+#: ../src/gnome-utils/gtkbuilder/gnc-frequency.glade.h:21
+msgctxt "Semimonthly"
+msgid "Every"
+msgstr "Alle"
+
+#: ../src/gnome-utils/gtkbuilder/gnc-frequency.glade.h:22
+msgctxt "Semimonthly"
+msgid "months."
+msgstr "Monate."
+
 #: ../src/gnome-utils/gtkbuilder/gnc-frequency.glade.h:23
 msgid "First on the:"
 msgstr "Zuerst am:"
@@ -14866,6 +14897,16 @@ msgstr "dann am:"
 msgid "Semi-Monthly"
 msgstr "Halbmonatlich"
 
+#: ../src/gnome-utils/gtkbuilder/gnc-frequency.glade.h:27
+msgctxt "Monthly"
+msgid "Every"
+msgstr "Alle"
+
+#: ../src/gnome-utils/gtkbuilder/gnc-frequency.glade.h:28
+msgctxt "Monthly"
+msgid "months."
+msgstr "Monate."
+
 #: ../src/gnome-utils/gtkbuilder/gnc-frequency.glade.h:29
 msgid "On the"
 msgstr "Am"
@@ -26180,38 +26221,6 @@ msgstr ""
 #~ "Sie müssen eine Devise/Wertpapier auswählen. Wenn Sie eine neue anlegen "
 #~ "möchten, klicken Sie auf »Neu«."
 
-#~ msgctxt "Daily"
-#~ msgid "Every"
-#~ msgstr "Alle"
-
-#~ msgctxt "Daily"
-#~ msgid "days."
-#~ msgstr "Tage."
-
-#~ msgctxt "Weekly"
-#~ msgid "Every"
-#~ msgstr "Alle"
-
-#~ msgctxt "Weekly"
-#~ msgid "weeks."
-#~ msgstr "Wochen."
-
-#~ msgctxt "Semimonthly"
-#~ msgid "Every"
-#~ msgstr "Alle"
-
-#~ msgctxt "Semimonthly"
-#~ msgid "months."
-#~ msgstr "Monate."
-
-#~ msgctxt "Monthly"
-#~ msgid "Every"
-#~ msgstr "Alle"
-
-#~ msgctxt "Monthly"
-#~ msgid "months."
-#~ msgstr "Monate."
-
 #~ msgid "set true"
 #~ msgstr "auf wahr setzen"
 



Summary of changes:
 CMakeLists.txt                        | 14 +++++
 bindings/python/CMakeLists.txt        | 27 +++-------
 bindings/python/tests/CMakeLists.txt  |  2 +-
 gnucash/CMakeLists.txt-bin            | 15 +++++-
 gnucash/gnome-utils/gnc-main-window.c |  4 +-
 gnucash/python/CMakeLists.txt         |  6 ++-
 libgnucash/core-utils/CMakeLists.txt  |  2 +
 libgnucash/core-utils/Makefile.am     |  1 +
 po/de.po                              | 98 +++++++++++++++++++----------------
 util/ci/arch-docker                   |  2 -
 util/ci/commonbuild                   |  2 +-
 util/ci/ubuntu-14.04-docker           |  2 -
 12 files changed, 103 insertions(+), 72 deletions(-)



More information about the gnucash-changes mailing list