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