r20682 - gnucash/trunk/src/gnome-utils - Rename gtk_quartz_shutdown, gtk_quartz_should_quit, and gtk_quartz_set_menu to gnc_quartz... for better naming consistency.

John Ralls jralls at code.gnucash.org
Mon May 23 15:27:31 EDT 2011


Author: jralls
Date: 2011-05-23 15:27:31 -0400 (Mon, 23 May 2011)
New Revision: 20682
Trac: http://svn.gnucash.org/trac/changeset/20682

Added:
   gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/
   gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/
   gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/ui/
   gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/ui/Makefile.am100644 94c76a9e4c6a8f6ceaacfff0c1ed4c9e3d4128e1	src/
   gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/ui/Makefile.am100644 94c76a9e4c6a8f6ceaacfff0c1ed4c9e3d4128e1	src/gnome-utils/
   gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/ui/Makefile.am100644 94c76a9e4c6a8f6ceaacfff0c1ed4c9e3d4128e1	src/gnome-utils/ui/
   gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/ui/Makefile.am100644 94c76a9e4c6a8f6ceaacfff0c1ed4c9e3d4128e1	src/gnome-utils/ui/gnc-main-window-ui.xml0 0000000000000000000000000000000000000000	src/
   gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/ui/Makefile.am100644 94c76a9e4c6a8f6ceaacfff0c1ed4c9e3d4128e1	src/gnome-utils/ui/gnc-main-window-ui.xml0 0000000000000000000000000000000000000000	src/gnome-utils/
   gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/ui/Makefile.am100644 94c76a9e4c6a8f6ceaacfff0c1ed4c9e3d4128e1	src/gnome-utils/ui/gnc-main-window-ui.xml0 0000000000000000000000000000000000000000	src/gnome-utils/ui/
   gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/ui/Makefile.am100644 94c76a9e4c6a8f6ceaacfff0c1ed4c9e3d4128e1	src/gnome-utils/ui/gnc-main-window-ui.xml0 0000000000000000000000000000000000000000	src/gnome-utils/ui/gnc-windows-menu-ui-quartz.xml0 0000000000000000000000000000000000000000	src/
   gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/ui/Makefile.am100644 94c76a9e4c6a8f6ceaacfff0c1ed4c9e3d4128e1	src/gnome-utils/ui/gnc-main-window-ui.xml0 0000000000000000000000000000000000000000	src/gnome-utils/ui/gnc-windows-menu-ui-quartz.xml0 0000000000000000000000000000000000000000	src/gnome-utils/
   gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/ui/Makefile.am100644 94c76a9e4c6a8f6ceaacfff0c1ed4c9e3d4128e1	src/gnome-utils/ui/gnc-main-window-ui.xml0 0000000000000000000000000000000000000000	src/gnome-utils/ui/gnc-windows-menu-ui-quartz.xml0 0000000000000000000000000000000000000000	src/gnome-utils/ui/
   gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/ui/Makefile.am100644 94c76a9e4c6a8f6ceaacfff0c1ed4c9e3d4128e1	src/gnome-utils/ui/gnc-main-window-ui.xml0 0000000000000000000000000000000000000000	src/gnome-utils/ui/gnc-windows-menu-ui-quartz.xml0 0000000000000000000000000000000000000000	src/gnome-utils/ui/gnc-windows-menu-ui.xml
Modified:
   gnucash/trunk/src/gnome-utils/gnc-main-window.c
Log:
Rename gtk_quartz_shutdown, gtk_quartz_should_quit, and gtk_quartz_set_menu to gnc_quartz... for better naming consistency.

Modified: gnucash/trunk/src/gnome-utils/gnc-main-window.c
===================================================================
--- gnucash/trunk/src/gnome-utils/gnc-main-window.c	2011-05-23 19:23:06 UTC (rev 20681)
+++ gnucash/trunk/src/gnome-utils/gnc-main-window.c	2011-05-23 19:27:31 UTC (rev 20682)
@@ -151,9 +151,9 @@
 static gboolean gnc_main_window_popup_menu_cb (GtkWidget *widget, GncPluginPage *page);
 
 #ifdef MAC_INTEGRATION
-static void gtk_quartz_shutdown(GtkOSXApplication *theApp, gpointer data);
-static gboolean gtk_quartz_should_quit(GtkOSXApplication *theApp, GncMainWindow *window);
-static void gtk_quartz_set_menu(GncMainWindow* window);
+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
@@ -3373,13 +3373,13 @@
 
 #ifdef MAC_INTEGRATION
 static void
-gtk_quartz_shutdown (GtkOSXApplication *theApp, gpointer data)
+gnc_quartz_shutdown (GtkOSXApplication *theApp, gpointer data)
 {
     gnc_shutdown(0);
 }
 
 static gboolean
-gtk_quartz_should_quit (GtkOSXApplication *theApp, GncMainWindow *window)
+gnc_quartz_should_quit (GtkOSXApplication *theApp, GncMainWindow *window)
 {
     QofSession *session;
     gboolean needs_save, do_shutdown;
@@ -3397,7 +3397,7 @@
 }
 
 static void
-gtk_quartz_set_menu(GncMainWindow* window)
+gnc_quartz_set_menu(GncMainWindow* window)
 {
     GtkOSXApplicationMenuGroup *group;
     GtkOSXApplication *theApp = g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
@@ -3440,7 +3440,7 @@
                                       "/menubar/Windows");
     gtk_osxapplication_set_window_menu(theApp, GTK_MENU_ITEM(item));
     g_signal_connect(theApp, "NSApplicationBlockTermination",
-                     G_CALLBACK(gtk_quartz_should_quit), window);
+                     G_CALLBACK(gnc_quartz_should_quit), window);
 
 }
 #endif //MAC_INTEGRATION
@@ -4057,12 +4057,12 @@
     {
         gtk_widget_show(GTK_WIDGET(window_iter->data));
 #ifdef MAC_INTEGRATION
-        gtk_quartz_set_menu(window_iter->data);
+        gnc_quartz_set_menu(window_iter->data);
 #endif
     }
 #ifdef MAC_INTEGRATION
     g_signal_connect(theApp, "NSApplicationWillTerminate",
-                     G_CALLBACK(gtk_quartz_shutdown), NULL);
+                     G_CALLBACK(gnc_quartz_shutdown), NULL);
     gtk_osxapplication_ready(theApp);
 #endif
 }

Copied: gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/ui/Makefile.am100644 94c76a9e4c6a8f6ceaacfff0c1ed4c9e3d4128e1	src/gnome-utils/ui/gnc-main-window-ui.xml0 0000000000000000000000000000000000000000	src/gnome-utils/ui/gnc-windows-menu-ui-quartz.xml0 0000000000000000000000000000000000000000	src/gnome-utils/ui/gnc-windows-menu-ui.xml (from rev 20671, gnucash/trunk/src/gnome-utils/gnc-main-window.c)
===================================================================
--- gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/ui/Makefile.am100644 94c76a9e4c6a8f6ceaacfff0c1ed4c9e3d4128e1	src/gnome-utils/ui/gnc-main-window-ui.xml0 0000000000000000000000000000000000000000	src/gnome-utils/ui/gnc-windows-menu-ui-quartz.xml0 0000000000000000000000000000000000000000	src/gnome-utils/ui/gnc-windows-menu-ui.xml	                        (rev 0)
+++ gnucash/trunk/src/gnome-utils/gnc-main-window.c100644 01901c6c8752987f426cff1e6534ba0b27867fae	src/gnome-utils/ui/Makefile.am100644 94c76a9e4c6a8f6ceaacfff0c1ed4c9e3d4128e1	src/gnome-utils/ui/gnc-main-window-ui.xml0 0000000000000000000000000000000000000000	src/gnome-utils/ui/gnc-windows-menu-ui-quartz.xml0 0000000000000000000000000000000000000000	src/gnome-utils/ui/gnc-windows-menu-ui.xml	2011-05-23 19:27:31 UTC (rev 20682)
@@ -0,0 +1,4349 @@
+/*
+ * 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 <gnome.h>
+#include <glib/gi18n.h>
+#include <libguile.h>
+#include "guile-mappings.h"
+
+#include "gnc-plugin.h"
+#include "gnc-plugin-manager.h"
+#include "gnc-main-window.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-gkeyfile-utils.h"
+#include "gnc-gnome-utils.h"
+#include "gnc-gobject-utils.h"
+#include "gnc-gui-query.h"
+#include "gnc-hooks.h"
+#include "gnc-session.h"
+#include "gnc-ui.h"
+#include "gnc-ui-util.h"
+#include "gnc-uri-utils.h"
+#include "core-utils/gnc-version.h"
+#include "gnc-window.h"
+#include "gnc-main.h"
+#include "gnc-gconf-utils.h"
+// +JSLED
+//#include "gnc-html.h"
+#include "gnc-autosave.h"
+#include "print-session.h"
+#ifdef MAC_INTEGRATION
+#include <igemacintegration/gtkosxapplication.h>
+#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 KEY_SHOW_CLOSE_BUTTON	"tab_close_buttons"
+#define KEY_TAB_NEXT_RECENT	"tab_next_recent"
+#define KEY_TAB_POSITION	"tab_position"
+#define KEY_TAB_WIDTH           "tab_width"
+
+#define GNC_MAIN_WINDOW_NAME "GncMainWindow"
+
+
+/* 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;
+
+/* 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 (GtkObject *object);
+
+static void gnc_main_window_setup_window (GncMainWindow *window);
+static void gnc_window_main_window_init (GncWindowIface *iface);
+static void gnc_main_window_update_all_menu_items (void);
+
+/* Callbacks */
+static void gnc_main_window_add_widget (GtkUIManager *merge, GtkWidget *widget, GncMainWindow *window);
+static void gnc_main_window_switch_page (GtkNotebook *notebook, GtkNotebookPage *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);
+static void gnc_main_window_cmd_window_raise (GtkAction *action, GtkRadioAction *current, GncMainWindow *window);
+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);
+
+#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 bu 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;
+    /** 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;
+
+    /** 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", GTK_STOCK_PRINT, N_("_Print..."), "<control>p",
+        N_("Print the currently active page"), NULL
+    },
+#ifndef GTK_STOCK_PAGE_SETUP
+#    define GTK_STOCK_PAGE_SETUP NULL
+#endif
+    {
+        "FilePageSetupAction", GTK_STOCK_PAGE_SETUP, N_("Pa_ge Setup..."), "<control><shift>p",
+        N_("Specify the page size and orientation for printing"),
+        G_CALLBACK (gnc_main_window_cmd_page_setup)
+    },
+    {
+        "FilePropertiesAction", GTK_STOCK_PROPERTIES, N_("Proper_ties"), "<Alt>Return",
+        N_("Edit the properties of the current file"),
+        G_CALLBACK (gnc_main_window_cmd_file_properties)
+    },
+    {
+        "FileCloseAction", GTK_STOCK_CLOSE, N_("_Close"), NULL,
+        N_("Close the currently active page"),
+        G_CALLBACK (gnc_main_window_cmd_file_close)
+    },
+    {
+        "FileQuitAction", GTK_STOCK_QUIT, N_("_Quit"), NULL,
+        N_("Quit this application"),
+        G_CALLBACK (gnc_main_window_cmd_file_quit)
+    },
+
+    /* Edit menu */
+
+    {
+        "EditCutAction", GTK_STOCK_CUT, N_("Cu_t"), NULL,
+        N_("Cut the current selection and copy it to clipboard"),
+        G_CALLBACK (gnc_main_window_cmd_edit_cut)
+    },
+    {
+        "EditCopyAction", GTK_STOCK_COPY, N_("_Copy"), NULL,
+        N_("Copy the current selection to clipboard"),
+        G_CALLBACK (gnc_main_window_cmd_edit_copy)
+    },
+    {
+        "EditPasteAction", GTK_STOCK_PASTE, N_("_Paste"), NULL,
+        N_("Paste the clipboard content at the cursor position"),
+        G_CALLBACK (gnc_main_window_cmd_edit_paste)
+    },
+    {
+        "EditPreferencesAction", GTK_STOCK_PREFERENCES, 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", GTK_STOCK_REFRESH, N_("_Refresh"), "<control>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", GNOME_STOCK_BOOK_BLUE, N_("Tutorial and Concepts _Guide"), NULL,
+        N_("Open the GnuCash Tutorial"),
+        G_CALLBACK (gnc_main_window_cmd_help_tutorial)
+    },
+    {
+        "HelpContentsAction", GTK_STOCK_HELP, N_("_Contents"), "F1",
+        N_("Open the GnuCash Help"),
+        G_CALLBACK (gnc_main_window_cmd_help_contents)
+    },
+    {
+        "HelpAboutAction", GNOME_STOCK_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);
+
+/** 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);
+
+
+/** 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
+};
+
+
+/* This data structure holds the tooltops for all notebook tabs.
+ * Typically these are used to provide the full path of a register
+ * page. */
+static GtkTooltips *tips = 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);
+
+    /* 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)
+    {
+        /* Shound 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);
+    }
+    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);
+}
+
+void
+gnc_main_window_restore_default_state(void)
+{
+    GtkAction *action;
+    GncMainWindow *window;
+
+    /* The default state should be to have an Account Tree page open
+     * in the window. */
+    DEBUG("no saved state file");
+    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(window))->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;
+}
+
+
+/** 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;
+    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_mins =
+        _("If you don't save, changes from the past %d minutes will be discarded.");
+    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.");
+    time_t oldest_change;
+    gint minutes, hours, days;
+
+    session = gnc_get_current_session();
+    book = qof_session_get_book(session);
+    filename = qof_session_get_url(session);
+    if (filename == NULL)
+        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_dirty_time(book);
+    minutes = (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),
+                message_mins, minutes);
+    }
+    gtk_dialog_add_buttons(GTK_DIALOG(dialog),
+                           _("Close _Without Saving"), GTK_RESPONSE_CLOSE,
+                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                           GTK_STOCK_SAVE, GTK_RESPONSE_APPLY,
+                           NULL);
+    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);
+    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_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_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.
+ *
+ *  @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;
+    gchar *filename = NULL;
+    const gchar *book_id = NULL;
+    const gchar *dirty = "";
+    gchar *title;
+    GtkAction* action;
+
+    /* The save action is sensitive if the book is dirty */
+    action = gnc_main_window_find_action (window, "FileSaveAction");
+    if (action != NULL)
+    {
+        gtk_action_set_sensitive(action, FALSE);
+    }
+    if (gnc_current_session_exist())
+    {
+        book_id = qof_session_get_url (gnc_get_current_session ());
+        book = gnc_get_current_book();
+        if (qof_instance_is_dirty(QOF_INSTANCE(book)))
+        {
+            dirty = "*";
+            if (action != NULL)
+            {
+                gtk_action_set_sensitive(action, TRUE);
+            }
+        }
+    }
+
+    if (!book_id)
+        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 - GnuCash", dirty, filename,
+                                gnc_plugin_page_get_page_name(page));
+    }
+    else
+    {
+        title = g_strdup_printf("%s%s - GnuCash", dirty, filename);
+    }
+    g_free( filename );
+
+    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
+}
+
+
+/** 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;
+};
+
+
+/** 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);
+#ifdef MAC_INTEGRATION
+    {
+        GtkOSXApplication *theApp =
+            g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
+        gtk_osxapplication_sync_menubar(theApp);
+    }
+#endif
+    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(" ");
+}
+
+/** 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
+ */
+static void
+gnc_main_window_update_all_menu_items (void)
+{
+    struct menu_update data;
+    gchar *label;
+    gint i;
+
+    ENTER("");
+#ifndef MAC_INTEGRATION
+    /* 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);
+    }
+#endif
+    LEAVE(" ");
+}
+
+
+/** 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 entry A pointer to the GConfEntry which describes the new
+ *  state of whether close buttons should be visible on notebook tabs.
+ *
+ *  @param user_data Unused.
+ */
+static void
+gnc_main_window_update_tab_close (GConfEntry *entry, gpointer user_data)
+{
+    gboolean new_value;
+
+    ENTER(" ");
+    new_value = gconf_value_get_bool(entry->value);
+    gnc_main_window_foreach_page(
+        gnc_main_window_update_tab_close_one_page,
+        &new_value);
+    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 gconf 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;
+
+    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;
+    }
+
+    if (*new_value != 0)
+    {
+        gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
+        gtk_label_set_max_width_chars(GTK_LABEL(label), *new_value);
+    }
+    else
+    {
+        gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_NONE);
+        gtk_label_set_max_width_chars(GTK_LABEL(label), 100);
+    }
+    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 entry A pointer to the GConfEntry which describes the new
+ *  size of the tab label width.
+ *
+ *  @param user_data Unused.
+ */
+static void
+gnc_main_window_update_tab_width (GConfEntry *entry, gpointer user_data)
+{
+    gint new_value;
+
+    ENTER(" ");
+    new_value = gconf_value_get_float(entry->value);
+    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, *event_box;
+    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;
+    }
+
+    event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
+                                           page->notebook_page);
+
+    tab_hbox = gtk_bin_get_child(GTK_BIN(event_box));
+
+    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_event (GncMainWindow *window,
+                            GncPluginPage *page,
+                            GtkWidget **event_p)
+{
+    GncMainWindowPrivate *priv;
+    GtkWidget *event_box;
+
+    ENTER("window %p, page %p, event %p",
+          window, page, event_p);
+    *event_p = NULL;
+
+    if (!page->notebook_page)
+    {
+        LEAVE("invalid notebook_page");
+        return FALSE;
+    }
+
+    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
+    event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
+                                           page->notebook_page);
+    if (GTK_IS_EVENT_BOX(event_box))
+    {
+        *event_p = event_box;
+        LEAVE("event %p", *event_p);
+        return (TRUE);
+    }
+
+    LEAVE("event %p", *event_p);
+    return (FALSE);
+}
+
+void
+main_window_update_page_name (GncPluginPage *page,
+                              const gchar *name_in)
+{
+    GncMainWindow *window;
+    GncMainWindowPrivate *priv;
+    GtkWidget *label, *entry, *event_box;
+    gchar *name, *old_page_name, *old_page_long_name;
+
+    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)
+    {
+        LEAVE("no window widget available");
+        return;
+    }
+
+    if (main_window_find_tab_items(window, page, &label, &entry))
+        gtk_label_set_text(GTK_LABEL(label), name);
+
+    /* 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;
+
+        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_event(window, page, &event_box))
+            gtk_tooltips_set_tip(GTK_TOOLTIPS(tips), event_box, new_page_long_name, NULL);
+
+        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 *event_box;
+    GdkColor tab_color;
+    gchar *color_string;
+
+
+    ENTER(" ");
+
+    if ((color_in == NULL) || (*color_in == '\0'))
+    {
+        LEAVE("no string");
+        return;
+    }
+    color_string = g_strstrip(g_strdup(color_in));
+
+    /* Optimization, if the color hasn't changed, don't update. */
+    if (*color_string == '\0' || 0 == safe_strcmp(color_string, gnc_plugin_page_get_page_color(page)))
+    {
+        g_free(color_string);
+        LEAVE("empty string or color unchanged");
+        return;
+    }
+
+    /* Update the plugin */
+    window = GNC_MAIN_WINDOW(page->window);
+    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
+    gnc_plugin_page_set_page_color(page, color_string);
+
+    /* Update the notebook tab */
+    main_window_find_tab_event(window, page, &event_box);
+
+    if (gdk_color_parse(color_string, &tab_color))
+    {
+        gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, &tab_color);
+        gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, &tab_color);
+    }
+    else
+    {
+        gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, NULL);
+        gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, NULL);
+    }
+    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_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);
+    GtkObjectClass *gtkobject_class = GTK_OBJECT_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;
+
+    /* GtkObject signals */
+    gtkobject_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_gconf_general_register_cb (KEY_SHOW_CLOSE_BUTTON,
+                                   gnc_main_window_update_tab_close,
+                                   NULL);
+    gnc_gconf_general_register_cb (KEY_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);
+
+    tips = gtk_tooltips_new();
+}
+
+
+/** 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);
+
+    priv->event_handler_id =
+        qof_event_register_handler(gnc_main_window_event_handler, 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)
+{
+    GncMainWindow *window;
+    GncMainWindowPrivate *priv;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (GNC_IS_MAIN_WINDOW (object));
+
+    window = GNC_MAIN_WINDOW (object);
+    priv = GNC_MAIN_WINDOW_GET_PRIVATE (window);
+
+    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 (GtkObject *object)
+{
+    GncMainWindow *window;
+    GncMainWindowPrivate *priv;
+    GncPluginManager *manager;
+    GList *plugins;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (GNC_IS_MAIN_WINDOW (object));
+
+    window = GNC_MAIN_WINDOW (object);
+
+    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);
+
+        /* Update the "Windows" menu in all other windows */
+        gnc_main_window_update_all_menu_items();
+
+        gnc_gconf_remove_notification(G_OBJECT(window), DESKTOP_GNOME_INTERFACE,
+                                      GNC_MAIN_WINDOW_NAME);
+        gnc_gconf_remove_notification(G_OBJECT(window), GCONF_GENERAL,
+                                      GNC_MAIN_WINDOW_NAME);
+
+        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);
+    }
+    GTK_OBJECT_CLASS (parent_class)->destroy (object);
+}
+
+
+/*  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(old_window))->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);
+    gnc_main_window_update_all_menu_items();
+
+    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_gconf_get_bool(GCONF_GENERAL, KEY_TAB_NEXT_RECENT, NULL))
+    {
+        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);
+        }
+    }
+
+    /* 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, *event_box;
+    const gchar *icon, *text, *color_string;
+    GtkWidget *image;
+    GList *tmp;
+    gint width;
+    GdkColor tab_color;
+
+    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);
+    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_gconf_get_float(GCONF_GENERAL, KEY_TAB_WIDTH, NULL);
+    icon = GNC_PLUGIN_PAGE_GET_CLASS(page)->tab_icon;
+    label = gtk_label_new (gnc_plugin_page_get_page_name(page));
+    if (width != 0)
+    {
+        gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
+        gtk_label_set_max_width_chars(GTK_LABEL(label), width);
+    }
+    gtk_widget_show (label);
+
+    tab_hbox = gtk_hbox_new (FALSE, 6);
+    gtk_widget_show (tab_hbox);
+
+    if (icon != NULL)
+    {
+        image = gtk_image_new_from_stock (icon, GTK_ICON_SIZE_MENU);
+        gtk_widget_show (image);
+        gtk_box_pack_start (GTK_BOX (tab_hbox), image, FALSE, FALSE, 0);
+        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);
+
+    event_box = gtk_event_box_new();
+    /* Note: this doesn't work properly on Windows with gtk+2.18.x (last
+     * with 2.18.7). Setting the eventbox visible with that version results
+     * in the tab's text being invisible. See bug #610675 for more on this.
+     */
+    gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box), TRUE);
+    gtk_widget_show(event_box);
+    gtk_container_add(GTK_CONTAINER(event_box), tab_hbox);
+    color_string = gnc_plugin_page_get_page_color(page);
+    if (color_string == NULL) color_string = "";
+    if (gdk_color_parse(color_string, &tab_color))
+    {
+        gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, &tab_color);
+        gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, &tab_color);
+    }
+    else
+    {
+        gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, NULL);
+        gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, NULL);
+    }
+
+    text = gnc_plugin_page_get_page_long_name(page);
+    if (text)
+    {
+        gtk_tooltips_set_tip(tips, event_box, text, NULL);
+    }
+
+    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_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
+        gtk_widget_show(close_image);
+        gtk_widget_size_request(close_image, &requisition);
+        gtk_widget_set_size_request(close_button, requisition.width + 4,
+                                    requisition.height + 2);
+        gtk_button_set_alignment(GTK_BUTTON(close_button), 0.5, 0.5);
+        gtk_container_add(GTK_CONTAINER(close_button), close_image);
+        if (gnc_gconf_get_bool(GCONF_GENERAL, KEY_SHOW_CLOSE_BUTTON, NULL))
+            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);
+
+        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, event_box, label);
+    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);
+
+    data = g_new0 (GncMainWindowActionData, 1);
+    data->window = window;
+    data->data = user_data;
+
+    pathname = gnc_gnome_locate_ui_file (filename);
+    if (pathname == NULL)
+        return;
+
+    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_toolbar (GncMainWindow *window)
+{
+    GtkToolbarStyle style;
+    GSList *list;
+
+    ENTER("window %p", window);
+
+    style = gnc_get_toolbar_style();
+    list = gtk_ui_manager_get_toplevels(window->ui_merge, GTK_UI_MANAGER_TOOLBAR);
+    g_slist_foreach(list, (GFunc)gtk_toolbar_set_style, GINT_TO_POINTER(style));
+    g_slist_free(list);
+    LEAVE("");
+}
+
+static void
+gnc_main_window_update_tab_position (GncMainWindow *window)
+{
+    GtkPositionType position = GTK_POS_TOP;
+    gchar *conf_string;
+    GncMainWindowPrivate *priv;
+
+    ENTER ("window %p", window);
+    conf_string = gnc_gconf_get_string (GCONF_GENERAL,
+                                        KEY_TAB_POSITION, NULL);
+    if (conf_string)
+    {
+        position = gnc_enum_from_nick (GTK_TYPE_POSITION_TYPE,
+                                       conf_string, GTK_POS_TOP);
+        g_free (conf_string);
+    }
+
+    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);
+}
+
+static void
+gnc_main_window_gconf_changed (GConfClient *client,
+                               guint cnxn_id,
+                               GConfEntry *entry,
+                               gpointer user_data)
+{
+    GncMainWindow *window;
+    GConfValue *value;
+    const gchar *key, *key_tail;
+
+    window = GNC_MAIN_WINDOW(user_data);
+
+    key = gconf_entry_get_key(entry);
+    value = gconf_entry_get_value(entry);
+    if (!key || !value)
+        return;
+
+    key_tail = strrchr(key, '/');
+    if (key_tail != NULL)
+        key_tail++;
+    if (strcmp(key_tail, KEY_TOOLBAR_STYLE) == 0)
+    {
+        gnc_main_window_update_toolbar(window);
+    }
+    else if (strcmp(key_tail, KEY_TAB_POSITION) == 0)
+    {
+        gnc_main_window_update_tab_position(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_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_vbox_new (FALSE, 0);
+    gtk_widget_show (main_vbox);
+    gtk_container_add (GTK_CONTAINER (window), main_vbox);
+
+    priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
+    priv->menu_dock = gtk_vbox_new (FALSE, 0);
+    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);
+    gtk_statusbar_set_has_resize_grip( GTK_STATUSBAR(priv->statusbar), TRUE );
+
+    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);
+#ifndef MAC_INTEGRATION
+    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
+    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_gnome_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_gconf_add_notification(G_OBJECT(window), GCONF_GENERAL,
+                               gnc_main_window_gconf_changed,
+                               GNC_MAIN_WINDOW_NAME);
+    gnc_gconf_add_notification(G_OBJECT(window), DESKTOP_GNOME_INTERFACE,
+                               gnc_main_window_gconf_changed,
+                               GNC_MAIN_WINDOW_NAME);
+    gnc_main_window_update_toolbar(window);
+    gnc_main_window_update_tab_position(window);
+
+    gnc_main_window_init_menu_updaters(window);
+
+    /* Testing */
+    /* Now update the "eXtensions" menu */
+    if (!gnc_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
+static void
+gnc_quartz_shutdown (GtkOSXApplication *theApp, gpointer data)
+{
+    gnc_shutdown(0);
+}
+
+static gboolean
+gnc_quartz_should_quit (GtkOSXApplication *theApp, GncMainWindow *window)
+{
+    QofSession *session;
+    gboolean needs_save, do_shutdown;
+
+    gboolean finished = gnc_main_window_all_finish_pending();
+    if (!finished)
+        return TRUE;
+    session = gnc_get_current_session();
+    needs_save = qof_book_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;
+    gnc_shutdown(0);
+    return FALSE;
+}
+
+static void
+gnc_quartz_set_menu(GncMainWindow* window)
+{
+    GtkOSXApplicationMenuGroup *group;
+    GtkOSXApplication *theApp = g_object_new(GTK_TYPE_OSX_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);
+    gtk_osxapplication_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));
+
+    /*  the about group  */
+    group = gtk_osxapplication_add_app_menu_group (theApp);
+
+    item = gtk_ui_manager_get_widget (window->ui_merge,
+                                      "/menubar/Help/HelpAbout");
+    if (GTK_IS_MENU_ITEM (item))
+        gtk_osxapplication_add_app_menu_item (theApp, group,
+                                              GTK_MENU_ITEM (item));
+
+    /*  the preferences group  */
+    group = gtk_osxapplication_add_app_menu_group (theApp);
+
+    item = gtk_ui_manager_get_widget (window->ui_merge,
+                                      "/menubar/Edit/EditPreferences");
+    if (GTK_IS_MENU_ITEM (item))
+        gtk_osxapplication_add_app_menu_item (theApp, group,
+                                              GTK_MENU_ITEM (item));
+
+    item = gtk_ui_manager_get_widget (window->ui_merge,
+                                      "/menubar/Help");
+    gtk_osxapplication_set_help_menu(theApp, GTK_MENU_ITEM(item));
+    item = gtk_ui_manager_get_widget (window->ui_merge,
+                                      "/menubar/Windows");
+    gtk_osxapplication_set_window_menu(theApp, GTK_MENU_ITEM(item));
+    g_signal_connect(theApp, "NSApplicationBlockTermination",
+                     G_CALLBACK(gnc_quartz_should_quit), window);
+
+}
+#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,
+                             GtkNotebookPage *notebook_page,
+                             gint pos,
+                             GncMainWindow *window)
+{
+    GncMainWindowPrivate *priv;
+    GtkWidget *child;
+    GncPluginPage *page;
+    gboolean immutable, 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);
+    }
+
+    /* 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);
+    gnc_plugin_update_actions(priv->action_group,
+                              multiple_page_actions,
+                              "sensitive",
+                              g_list_length(priv->installed_pages) > 1);
+
+    gnc_main_window_update_title(window);
+    gnc_main_window_update_menu_item(window);
+
+    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);
+}
+
+static void
+gnc_main_window_cmd_file_properties (GtkAction *action, GncMainWindow *window)
+{
+    SCM func = scm_c_eval_string("gnc:main-window-properties-cb");
+    if (!scm_is_procedure (func))
+    {
+        PERR ("not a procedure\n");
+        return;
+    }
+    scm_call_0(func);
+}
+
+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;
+    gboolean editable;
+
+    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);
+        editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (widget));
+        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_WIDGET(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, *new_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. :-) */
+    new_priv = GNC_MAIN_WINDOW_GET_PRIVATE(new_window);
+    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");
+}
+
+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(" ");
+}
+
+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;
+
+    filename = gnc_gnome_locate_data_file(partial);
+    g_file_get_contents(filename, &text, NULL, NULL);
+    g_free(filename);
+
+    /* Anything there? */
+    if (text && *text)
+        return text;
+
+    /* Just a empty string or no string at all. */
+    if (text)
+        g_free(text);
+    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;
+}
+
+
+/** 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)
+{
+    const gchar *fixed_message = _("The GnuCash personal finance manager. "
+                                   "The GNU way to manage your money!");
+    const gchar *copyright = "© 1997-2010 Contributors";
+    gchar **authors, **documenters, *license, *message;
+    GdkPixbuf *logo;
+
+    logo = gnc_gnome_get_gdkpixbuf ("gnucash-icon-48x48.png");
+
+    authors = get_file_strsplit("doc/AUTHORS");
+    documenters = get_file_strsplit("doc/DOCUMENTERS");
+    license = get_file("doc/LICENSE");
+#ifdef GNUCASH_SVN
+    /* Development version */
+    message = g_strdup_printf(_("%s  This copy was built from svn r%s on %s."),
+                              fixed_message, GNUCASH_SVN_REV, GNUCASH_BUILD_DATE);
+#else
+    message = g_strdup_printf(_("%s  This copy was built from r%s on %s."),
+                              fixed_message, GNUCASH_SVN_REV, GNUCASH_BUILD_DATE);
+#endif
+    gtk_show_about_dialog
+    (GTK_WINDOW (window),
+     "authors", authors,
+     "documenters", documenters,
+     "comments", message,
+     "copyright", copyright,
+     "license", license,
+     "logo", logo,
+     "name", "GnuCash",
+     "translator-credits", _("translator_credits"),
+     "version", VERSION,
+     "website", "http://www.gnucash.org",
+     (gchar *)NULL);
+
+    g_free(message);
+    if (license)     g_free(license);
+    if (documenters) g_strfreev(documenters);
+    if (authors)     g_strfreev(authors);
+    g_object_unref (logo);
+}
+
+
+/************************************************************
+ *                                                          *
+ ************************************************************/
+
+void
+gnc_main_window_show_all_windows(void)
+{
+    GList *window_iter;
+#ifdef MAC_INTEGRATION
+    GtkOSXApplication *theApp = g_object_new(GTK_TYPE_OSX_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
+        gnc_quartz_set_menu(window_iter->data);
+#endif
+    }
+#ifdef MAC_INTEGRATION
+    g_signal_connect(theApp, "NSApplicationWillTerminate",
+                     G_CALLBACK(gnc_quartz_shutdown), NULL);
+    gtk_osxapplication_ready(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 ();
+    }
+
+    gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button, event_time);
+    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;
+}
+
+/** @} */
+/** @} */



More information about the gnucash-changes mailing list