GnuCash 2.4.99
gnc-main-window.c
Go to the documentation of this file.
00001 /*
00002  * gnc-main-window.c -- GtkWindow which represents the
00003  *      GnuCash main window.
00004  *
00005  * Copyright (C) 2003 Jan Arne Petersen <jpetersen@uni-bonn.de>
00006  * Copyright (C) 2003,2005,2006 David Hampton <hampton@employees.org>
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU General Public License as
00010  * published by the Free Software Foundation; either version 2 of
00011  * the License, or (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, contact:
00020  *
00021  * Free Software Foundation           Voice:  +1-617-542-5942
00022  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
00023  * Boston, MA  02110-1301,  USA       gnu@gnu.org
00024  */
00025 
00035 #include "config.h"
00036 
00037 #include <gnome.h>
00038 #include <glib/gi18n.h>
00039 #include <libguile.h>
00040 #include "guile-mappings.h"
00041 
00042 #include "gnc-plugin.h"
00043 #include "gnc-plugin-manager.h"
00044 #include "gnc-main-window.h"
00045 
00046 #include "dialog-preferences.h"
00047 #include "dialog-reset-warnings.h"
00048 #include "dialog-transfer.h"
00049 #include "dialog-utils.h"
00050 #include "file-utils.h"
00051 #include "gnc-component-manager.h"
00052 #include "gnc-engine.h"
00053 #include "gnc-file.h"
00054 #include "gnc-gkeyfile-utils.h"
00055 #include "gnc-gnome-utils.h"
00056 #include "gnc-gobject-utils.h"
00057 #include "gnc-gui-query.h"
00058 #include "gnc-hooks.h"
00059 #include "gnc-session.h"
00060 #include "gnc-ui.h"
00061 #include "gnc-ui-util.h"
00062 #include "gnc-uri-utils.h"
00063 #include "core-utils/gnc-version.h"
00064 #include "gnc-window.h"
00065 #include "gnc-main.h"
00066 #include "gnc-gconf-utils.h"
00067 // +JSLED
00068 //#include "gnc-html.h"
00069 #include "gnc-autosave.h"
00070 #include "print-session.h"
00071 #ifdef MAC_INTEGRATION
00072 #include <gtkmacintegration/gtkosxapplication.h>
00073 #endif
00074 
00076 enum
00077 {
00078     PAGE_ADDED,
00079     PAGE_CHANGED,
00080     LAST_SIGNAL
00081 };
00082 
00085 #define PLUGIN_PAGE_LABEL "plugin-page"
00086 
00087 #define PLUGIN_PAGE_CLOSE_BUTTON "close-button"
00088 #define PLUGIN_PAGE_TAB_LABEL    "label"
00089 
00090 #define KEY_SHOW_CLOSE_BUTTON   "tab_close_buttons"
00091 #define KEY_TAB_NEXT_RECENT     "tab_next_recent"
00092 #define KEY_TAB_POSITION        "tab_position"
00093 #define KEY_TAB_WIDTH           "tab_width"
00094 
00095 #define GNC_MAIN_WINDOW_NAME "GncMainWindow"
00096 
00097 
00098 /* Static Globals *******************************************************/
00099 
00101 static QofLogModule log_module = GNC_MOD_GUI;
00103 static GObjectClass *parent_class = NULL;
00105 static GQuark window_type = 0;
00108 static GList *active_windows = NULL;
00109 
00110 /* Declarations *********************************************************/
00111 static void gnc_main_window_class_init (GncMainWindowClass *klass);
00112 static void gnc_main_window_init (GncMainWindow *window, GncMainWindowClass *klass);
00113 static void gnc_main_window_finalize (GObject *object);
00114 static void gnc_main_window_destroy (GtkObject *object);
00115 
00116 static void gnc_main_window_setup_window (GncMainWindow *window);
00117 static void gnc_window_main_window_init (GncWindowIface *iface);
00118 static void gnc_main_window_update_all_menu_items (void);
00119 
00120 /* Callbacks */
00121 static void gnc_main_window_add_widget (GtkUIManager *merge, GtkWidget *widget, GncMainWindow *window);
00122 static void gnc_main_window_switch_page (GtkNotebook *notebook, GtkNotebookPage *notebook_page, gint pos, GncMainWindow *window);
00123 static void gnc_main_window_page_reordered (GtkNotebook *notebook, GtkWidget *child, guint pos, GncMainWindow *window);
00124 static void gnc_main_window_plugin_added (GncPlugin *manager, GncPlugin *plugin, GncMainWindow *window);
00125 static void gnc_main_window_plugin_removed (GncPlugin *manager, GncPlugin *plugin, GncMainWindow *window);
00126 static void gnc_main_window_engine_commit_error_callback( gpointer data, QofBackendError errcode );
00127 
00128 /* Command callbacks */
00129 static void gnc_main_window_cmd_page_setup (GtkAction *action, GncMainWindow *window);
00130 static void gnc_main_window_cmd_file_properties (GtkAction *action, GncMainWindow *window);
00131 static void gnc_main_window_cmd_file_close (GtkAction *action, GncMainWindow *window);
00132 static void gnc_main_window_cmd_file_quit (GtkAction *action, GncMainWindow *window);
00133 static void gnc_main_window_cmd_edit_cut (GtkAction *action, GncMainWindow *window);
00134 static void gnc_main_window_cmd_edit_copy (GtkAction *action, GncMainWindow *window);
00135 static void gnc_main_window_cmd_edit_paste (GtkAction *action, GncMainWindow *window);
00136 static void gnc_main_window_cmd_edit_preferences (GtkAction *action, GncMainWindow *window);
00137 static void gnc_main_window_cmd_view_refresh (GtkAction *action, GncMainWindow *window);
00138 static void gnc_main_window_cmd_view_toolbar (GtkAction *action, GncMainWindow *window);
00139 static void gnc_main_window_cmd_view_summary (GtkAction *action, GncMainWindow *window);
00140 static void gnc_main_window_cmd_view_statusbar (GtkAction *action, GncMainWindow *window);
00141 static void gnc_main_window_cmd_actions_reset_warnings (GtkAction *action, GncMainWindow *window);
00142 static void gnc_main_window_cmd_actions_rename_page (GtkAction *action, GncMainWindow *window);
00143 static void gnc_main_window_cmd_window_new (GtkAction *action, GncMainWindow *window);
00144 static void gnc_main_window_cmd_window_move_page (GtkAction *action, GncMainWindow *window);
00145 static void gnc_main_window_cmd_window_raise (GtkAction *action, GtkRadioAction *current, GncMainWindow *window);
00146 static void gnc_main_window_cmd_help_tutorial (GtkAction *action, GncMainWindow *window);
00147 static void gnc_main_window_cmd_help_contents (GtkAction *action, GncMainWindow *window);
00148 static void gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window);
00149 
00150 static void do_popup_menu(GncPluginPage *page, GdkEventButton *event);
00151 static gboolean gnc_main_window_popup_menu_cb (GtkWidget *widget, GncPluginPage *page);
00152 
00153 #ifdef MAC_INTEGRATION
00154 static void gnc_quartz_shutdown(GtkOSXApplication *theApp, gpointer data);
00155 static gboolean gnc_quartz_should_quit(GtkOSXApplication *theApp, GncMainWindow *window);
00156 static void gnc_quartz_set_menu(GncMainWindow* window);
00157 #endif
00158 
00161 typedef struct GncMainWindowPrivate
00162 {
00167     GtkWidget *menu_dock;
00170     GtkWidget *toolbar;
00172     GtkWidget *notebook;
00176     GtkWidget *statusbar;
00180     GtkWidget *progressbar;
00181 
00185     GtkActionGroup *action_group;
00186 
00188     GList *installed_pages;
00190     GList *usage_order;
00192     GncPluginPage *current_page;
00194     gint event_handler_id;
00195 
00200     GHashTable *merged_actions_table;
00201 } GncMainWindowPrivate;
00202 
00203 #define GNC_MAIN_WINDOW_GET_PRIVATE(o)  \
00204    (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_MAIN_WINDOW, GncMainWindowPrivate))
00205 
00208 typedef struct
00209 {
00212     guint merge_id;
00215     GtkActionGroup *action_group;
00216 } MergedActionEntry;
00217 
00220 static guint main_window_signals[LAST_SIGNAL] = { 0 };
00221 
00222 
00227 static GtkActionEntry gnc_menu_actions [] =
00228 {
00229     /* Toplevel */
00230 
00231     { "FileAction", NULL, N_("_File"), NULL, NULL, NULL, },
00232     { "EditAction", NULL, N_("_Edit"), NULL, NULL, NULL },
00233     { "ViewAction", NULL, N_("_View"), NULL, NULL, NULL },
00234     { "ActionsAction", NULL, N_("_Actions"), NULL, NULL, NULL },
00235     { "TransactionAction", NULL, N_("Tra_nsaction"), NULL, NULL, NULL },
00236     { "ReportsAction", NULL, N_("_Reports"), NULL, NULL, NULL },
00237     { "ToolsAction", NULL, N_("_Tools"), NULL, NULL, NULL },
00238     { "ExtensionsAction", NULL, N_("E_xtensions"), NULL, NULL, NULL },
00239     { "WindowsAction", NULL, N_("_Windows"), NULL, NULL, NULL },
00240     { "HelpAction", NULL, N_("_Help"), NULL, NULL, NULL },
00241 
00242     /* File menu */
00243 
00244     { "FileImportAction", NULL, N_("_Import"), NULL, NULL, NULL },
00245     { "FileExportAction", NULL, N_("_Export"), NULL, NULL, NULL },
00246     {
00247         "FilePrintAction", GTK_STOCK_PRINT, N_("_Print..."), "<control>p",
00248         N_("Print the currently active page"), NULL
00249     },
00250 #ifndef GTK_STOCK_PAGE_SETUP
00251 #    define GTK_STOCK_PAGE_SETUP NULL
00252 #endif
00253     {
00254         "FilePageSetupAction", GTK_STOCK_PAGE_SETUP, N_("Pa_ge Setup..."), "<control><shift>p",
00255         N_("Specify the page size and orientation for printing"),
00256         G_CALLBACK (gnc_main_window_cmd_page_setup)
00257     },
00258     {
00259         "FilePropertiesAction", GTK_STOCK_PROPERTIES, N_("Proper_ties"), "<Alt>Return",
00260         N_("Edit the properties of the current file"),
00261         G_CALLBACK (gnc_main_window_cmd_file_properties)
00262     },
00263     {
00264         "FileCloseAction", GTK_STOCK_CLOSE, N_("_Close"), NULL,
00265         N_("Close the currently active page"),
00266         G_CALLBACK (gnc_main_window_cmd_file_close)
00267     },
00268     {
00269         "FileQuitAction", GTK_STOCK_QUIT, N_("_Quit"), NULL,
00270         N_("Quit this application"),
00271         G_CALLBACK (gnc_main_window_cmd_file_quit)
00272     },
00273 
00274     /* Edit menu */
00275 
00276     {
00277         "EditCutAction", GTK_STOCK_CUT, N_("Cu_t"), NULL,
00278         N_("Cut the current selection and copy it to clipboard"),
00279         G_CALLBACK (gnc_main_window_cmd_edit_cut)
00280     },
00281     {
00282         "EditCopyAction", GTK_STOCK_COPY, N_("_Copy"), NULL,
00283         N_("Copy the current selection to clipboard"),
00284         G_CALLBACK (gnc_main_window_cmd_edit_copy)
00285     },
00286     {
00287         "EditPasteAction", GTK_STOCK_PASTE, N_("_Paste"), NULL,
00288         N_("Paste the clipboard content at the cursor position"),
00289         G_CALLBACK (gnc_main_window_cmd_edit_paste)
00290     },
00291     {
00292         "EditPreferencesAction", GTK_STOCK_PREFERENCES, N_("Pr_eferences"), NULL,
00293         N_("Edit the global preferences of GnuCash"),
00294         G_CALLBACK (gnc_main_window_cmd_edit_preferences)
00295     },
00296 
00297     /* View menu */
00298 
00299     {
00300         "ViewSortByAction", NULL, N_("_Sort By..."), NULL,
00301         N_("Select sorting criteria for this page view"), NULL
00302     },
00303     {
00304         "ViewFilterByAction", NULL, N_("_Filter By..."), NULL,
00305         N_("Select the account types that should be displayed."), NULL
00306     },
00307     {
00308         "ViewRefreshAction", GTK_STOCK_REFRESH, N_("_Refresh"), "<control>r",
00309         N_("Refresh this window"),
00310         G_CALLBACK (gnc_main_window_cmd_view_refresh)
00311     },
00312 
00313     /* Actions menu */
00314 
00315     { "ScrubMenuAction", NULL, N_("_Check & Repair"), NULL, NULL, NULL },
00316     {
00317         "ActionsForgetWarningsAction", NULL, N_("Reset _Warnings..."), NULL,
00318         N_("Reset the state of all warning messages so they will be shown again."),
00319         G_CALLBACK (gnc_main_window_cmd_actions_reset_warnings)
00320     },
00321     {
00322         "ActionsRenamePageAction", NULL, N_("Re_name Page"), NULL,
00323         N_("Rename this page."),
00324         G_CALLBACK (gnc_main_window_cmd_actions_rename_page)
00325     },
00326 
00327     /* Windows menu */
00328 
00329     {
00330         "WindowNewAction", NULL, N_("_New Window"), NULL,
00331         N_("Open a new top-level GnuCash window."),
00332         G_CALLBACK (gnc_main_window_cmd_window_new)
00333     },
00334     {
00335         "WindowMovePageAction", NULL, N_("New Window with _Page"), NULL,
00336         N_("Move the current page to a new top-level GnuCash window."),
00337         G_CALLBACK (gnc_main_window_cmd_window_move_page)
00338     },
00339 
00340     /* Help menu */
00341 
00342     {
00343         "HelpTutorialAction", GNOME_STOCK_BOOK_BLUE, N_("Tutorial and Concepts _Guide"), NULL,
00344         N_("Open the GnuCash Tutorial"),
00345         G_CALLBACK (gnc_main_window_cmd_help_tutorial)
00346     },
00347     {
00348         "HelpContentsAction", GTK_STOCK_HELP, N_("_Contents"), "F1",
00349         N_("Open the GnuCash Help"),
00350         G_CALLBACK (gnc_main_window_cmd_help_contents)
00351     },
00352     {
00353         "HelpAboutAction", GNOME_STOCK_ABOUT, N_("_About"), NULL,
00354         N_("About GnuCash"),
00355         G_CALLBACK (gnc_main_window_cmd_help_about)
00356     },
00357 };
00359 static guint gnc_menu_n_actions = G_N_ELEMENTS (gnc_menu_actions);
00360 
00363 static GtkToggleActionEntry toggle_actions [] =
00364 {
00365     {
00366         "ViewToolbarAction", NULL, N_("_Toolbar"), NULL,
00367         N_("Show/hide the toolbar on this window"),
00368         G_CALLBACK (gnc_main_window_cmd_view_toolbar), TRUE
00369     },
00370     {
00371         "ViewSummaryAction", NULL, N_("Su_mmary Bar"), NULL,
00372         N_("Show/hide the summary bar on this window"),
00373         G_CALLBACK (gnc_main_window_cmd_view_summary), TRUE
00374     },
00375     {
00376         "ViewStatusbarAction", NULL, N_("Stat_us Bar"), NULL,
00377         N_("Show/hide the status bar on this window"),
00378         G_CALLBACK (gnc_main_window_cmd_view_statusbar), TRUE
00379     },
00380 };
00382 static guint n_toggle_actions = G_N_ELEMENTS (toggle_actions);
00383 
00386 static GtkRadioActionEntry radio_entries [] =
00387 {
00388     { "Window0Action", NULL, N_("Window _1"), NULL, NULL, 0 },
00389     { "Window1Action", NULL, N_("Window _2"), NULL, NULL, 1 },
00390     { "Window2Action", NULL, N_("Window _3"), NULL, NULL, 2 },
00391     { "Window3Action", NULL, N_("Window _4"), NULL, NULL, 3 },
00392     { "Window4Action", NULL, N_("Window _5"), NULL, NULL, 4 },
00393     { "Window5Action", NULL, N_("Window _6"), NULL, NULL, 5 },
00394     { "Window6Action", NULL, N_("Window _7"), NULL, NULL, 6 },
00395     { "Window7Action", NULL, N_("Window _8"), NULL, NULL, 7 },
00396     { "Window8Action", NULL, N_("Window _9"), NULL, NULL, 8 },
00397     { "Window9Action", NULL, N_("Window _0"), NULL, NULL, 9 },
00398 };
00400 static guint n_radio_entries = G_N_ELEMENTS (radio_entries);
00401 
00402 
00406 static const gchar *gnc_menu_important_actions[] =
00407 {
00408     "FileCloseAction",
00409     NULL,
00410 };
00411 
00412 
00417 static const gchar *always_insensitive_actions[] =
00418 {
00419     "FilePrintAction",
00420     NULL
00421 };
00422 
00423 
00427 static const gchar *initially_insensitive_actions[] =
00428 {
00429     "FileCloseAction",
00430     NULL
00431 };
00432 
00433 
00438 static const gchar *always_hidden_actions[] =
00439 {
00440     "ViewSortByAction",
00441     "ViewFilterByAction",
00442     NULL
00443 };
00444 
00445 
00448 static const gchar *immutable_page_actions[] =
00449 {
00450     "FileCloseAction",
00451     NULL
00452 };
00453 
00454 
00457 static const gchar *multiple_page_actions[] =
00458 {
00459     "WindowMovePageAction",
00460     NULL
00461 };
00462 
00463 
00464 /************************************************************
00465  *                                                          *
00466  ************************************************************/
00467 #define WINDOW_COUNT            "WindowCount"
00468 #define WINDOW_STRING           "Window %d"
00469 #define WINDOW_GEOMETRY         "WindowGeometry"
00470 #define WINDOW_POSITION         "WindowPosition"
00471 #define WINDOW_MAXIMIZED        "WindowMaximized"
00472 #define TOOLBAR_VISIBLE         "ToolbarVisible"
00473 #define STATUSBAR_VISIBLE       "StatusbarVisible"
00474 #define SUMMARYBAR_VISIBLE      "SummarybarVisible"
00475 #define WINDOW_FIRSTPAGE        "FirstPage"
00476 #define WINDOW_PAGECOUNT        "PageCount"
00477 #define WINDOW_PAGEORDER        "PageOrder"
00478 #define PAGE_TYPE               "PageType"
00479 #define PAGE_NAME               "PageName"
00480 #define PAGE_STRING             "Page %d"
00481 
00482 typedef struct
00483 {
00484     GKeyFile *key_file;
00485     const gchar *group_name;
00486     gint window_num;
00487     gint page_num;
00488     gint page_offset;
00489 } GncMainWindowSaveData;
00490 
00491 
00492 /*  Iterator function to walk all pages in all windows, calling the
00493  *  specified function for each page. */
00494 void
00495 gnc_main_window_foreach_page (GncMainWindowPageFunc fn, gpointer user_data)
00496 {
00497     GncMainWindowPrivate *priv;
00498     GncMainWindow *window;
00499     GncPluginPage *page;
00500     GList *w, *p;
00501 
00502     ENTER(" ");
00503     for (w = active_windows; w; w = g_list_next(w))
00504     {
00505         window = w->data;
00506         priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
00507         for (p = priv->installed_pages; p; p = g_list_next(p))
00508         {
00509             page = p->data;
00510             fn(page, user_data);
00511         }
00512     }
00513     LEAVE(" ");
00514 }
00515 
00516 
00528 static void
00529 gnc_main_window_restore_page (GncMainWindow *window,
00530                               GncMainWindowSaveData *data)
00531 {
00532     GncMainWindowPrivate *priv;
00533     GncPluginPage *page;
00534     gchar *page_group, *page_type = NULL, *name = NULL;
00535     const gchar *class_type;
00536     GError *error = NULL;
00537 
00538     ENTER("window %p, data %p (key file %p, window %d, page start %d, page num %d)",
00539           window, data, data->key_file, data->window_num, data->page_offset,
00540           data->page_num);
00541 
00542     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
00543     page_group = g_strdup_printf(PAGE_STRING,
00544                                  data->page_offset + data->page_num);
00545     page_type = g_key_file_get_string(data->key_file, page_group,
00546                                       PAGE_TYPE, &error);
00547     if (error)
00548     {
00549         g_warning("error reading group %s key %s: %s",
00550                   page_group, PAGE_TYPE, error->message);
00551         goto cleanup;
00552     }
00553 
00554     /* See if the page already exists. */
00555     page = g_list_nth_data(priv->installed_pages, data->page_num);
00556     if (page)
00557     {
00558         class_type = GNC_PLUGIN_PAGE_GET_CLASS(page)->plugin_name;
00559         if (strcmp(page_type, class_type) != 0)
00560         {
00561             g_warning("error: page types don't match: state %s, existing page %s",
00562                       page_type, class_type);
00563             goto cleanup;
00564         }
00565     }
00566     else
00567     {
00568         /* create and install the page */
00569         page = gnc_plugin_page_recreate_page(GTK_WIDGET(window), page_type,
00570                                              data->key_file, page_group);
00571         if (page)
00572         {
00573             /* Does the page still need to be installed into the window? */
00574             if (page->window == NULL)
00575             {
00576                 gnc_plugin_page_set_use_new_window(page, FALSE);
00577                 gnc_main_window_open_page(window, page);
00578             }
00579 
00580             /* Restore the page name */
00581             name = g_key_file_get_string(data->key_file, page_group,
00582                                          PAGE_NAME, &error);
00583             if (error)
00584             {
00585                 g_warning("error reading group %s key %s: %s",
00586                           page_group, PAGE_NAME, error->message);
00587                 /* Fall through and still show the page. */
00588             }
00589             else
00590             {
00591                 DEBUG("updating page name for %p to %s.", page, name);
00592                 main_window_update_page_name(page, name);
00593                 g_free(name);
00594             }
00595         }
00596     }
00597 
00598     LEAVE("ok");
00599 cleanup:
00600     if (error)
00601         g_error_free(error);
00602     if (page_type)
00603         g_free(page_type);
00604     g_free(page_group);
00605 }
00606 
00607 
00616 static void
00617 gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *data)
00618 {
00619     GncMainWindowPrivate *priv;
00620     GtkAction *action;
00621     gint *pos, *geom, *order;
00622     gsize length;
00623     gboolean max, visible, desired_visibility;
00624     gchar *window_group;
00625     gint page_start, page_count, i;
00626     GError *error = NULL;
00627 
00628     /* Setup */
00629     ENTER("window %p, data %p (key file %p, window %d)",
00630           window, data, data->key_file, data->window_num);
00631     window_group = g_strdup_printf(WINDOW_STRING, data->window_num + 1);
00632 
00633     /* Get this window's notebook info */
00634     page_count = g_key_file_get_integer(data->key_file,
00635                                         window_group, WINDOW_PAGECOUNT, &error);
00636     if (error)
00637     {
00638         g_warning("error reading group %s key %s: %s",
00639                   window_group, WINDOW_PAGECOUNT, error->message);
00640         goto cleanup;
00641     }
00642     if (page_count == 0)
00643     {
00644         /* Shound never happen, but has during alpha testing. Having this
00645          * check doesn't hurt anything. */
00646         goto cleanup;
00647     }
00648     page_start = g_key_file_get_integer(data->key_file,
00649                                         window_group, WINDOW_FIRSTPAGE, &error);
00650     if (error)
00651     {
00652         g_warning("error reading group %s key %s: %s",
00653                   window_group, WINDOW_FIRSTPAGE, error->message);
00654         goto cleanup;
00655     }
00656 
00657     /* Build a window if we don't already have one */
00658     if (window == NULL)
00659     {
00660         DEBUG("Window %d doesn't exist. Creating new window.", data->window_num);
00661         DEBUG("active_windows %p.", active_windows);
00662         if (active_windows)
00663             DEBUG("first window %p.", active_windows->data);
00664         window = gnc_main_window_new();
00665         gtk_widget_show(GTK_WIDGET(window));
00666     }
00667 
00668     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
00669 
00670     /* Get the window coordinates, etc. */
00671     geom = g_key_file_get_integer_list(data->key_file, window_group,
00672                                        WINDOW_GEOMETRY, &length, &error);
00673     if (error)
00674     {
00675         g_warning("error reading group %s key %s: %s",
00676                   window_group, WINDOW_GEOMETRY, error->message);
00677         g_error_free(error);
00678         error = NULL;
00679     }
00680     else if (length != 2)
00681     {
00682         g_warning("invalid number of values for group %s key %s",
00683                   window_group, WINDOW_GEOMETRY);
00684     }
00685     else
00686     {
00687         gtk_window_resize(GTK_WINDOW(window), geom[0], geom[1]);
00688         DEBUG("window (%p) size %dx%d", window, geom[0], geom[1]);
00689     }
00690     /* keep the geometry for a test whether the windows position
00691        is offscreen */
00692 
00693     pos = g_key_file_get_integer_list(data->key_file, window_group,
00694                                       WINDOW_POSITION, &length, &error);
00695     if (error)
00696     {
00697         g_warning("error reading group %s key %s: %s",
00698                   window_group, WINDOW_POSITION, error->message);
00699         g_error_free(error);
00700         error = NULL;
00701     }
00702     else if (length != 2)
00703     {
00704         g_warning("invalid number of values for group %s key %s",
00705                   window_group, WINDOW_POSITION);
00706     }
00707     else if ((pos[0] + (geom ? geom[0] : 0) < 0) ||
00708              (pos[0] > gdk_screen_width()) ||
00709              (pos[1] + (geom ? geom[1] : 0) < 0) ||
00710              (pos[1] > gdk_screen_height()))
00711     {
00712 //    g_debug("position %dx%d, size%dx%d is offscreen; will not move",
00713 //          pos[0], pos[1], geom[0], geom[1]);
00714     }
00715     else
00716     {
00717         gtk_window_move(GTK_WINDOW(window), pos[0], pos[1]);
00718         DEBUG("window (%p) position %dx%d", window, pos[0], pos[1]);
00719     }
00720     if (geom)
00721     {
00722         g_free(geom);
00723     }
00724     if (pos)
00725     {
00726         g_free(pos);
00727     }
00728 
00729     max = g_key_file_get_boolean(data->key_file, window_group,
00730                                  WINDOW_MAXIMIZED, &error);
00731     if (error)
00732     {
00733         g_warning("error reading group %s key %s: %s",
00734                   window_group, WINDOW_MAXIMIZED, error->message);
00735         g_error_free(error);
00736         error = NULL;
00737     }
00738     else if (max)
00739     {
00740         gtk_window_maximize(GTK_WINDOW(window));
00741     }
00742 
00743     /* Common view menu items */
00744     action = gnc_main_window_find_action(window, "ViewToolbarAction");
00745     visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
00746     desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
00747                          TOOLBAR_VISIBLE, &error);
00748     if (error)
00749     {
00750         g_warning("error reading group %s key %s: %s",
00751                   window_group, TOOLBAR_VISIBLE, error->message);
00752         g_error_free(error);
00753         error = NULL;
00754     }
00755     else if (visible != desired_visibility)
00756     {
00757         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
00758     }
00759 
00760     action = gnc_main_window_find_action(window, "ViewSummaryAction");
00761     visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
00762     desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
00763                          SUMMARYBAR_VISIBLE, &error);
00764     if (error)
00765     {
00766         g_warning("error reading group %s key %s: %s",
00767                   window_group, TOOLBAR_VISIBLE, error->message);
00768         g_error_free(error);
00769         error = NULL;
00770     }
00771     else if (visible != desired_visibility)
00772     {
00773         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
00774     }
00775 
00776     action = gnc_main_window_find_action(window, "ViewStatusbarAction");
00777     visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
00778     desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
00779                          STATUSBAR_VISIBLE, &error);
00780     if (error)
00781     {
00782         g_warning("error reading group %s key %s: %s",
00783                   window_group, TOOLBAR_VISIBLE, error->message);
00784         g_error_free(error);
00785         error = NULL;
00786     }
00787     else if (visible != desired_visibility)
00788     {
00789         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
00790     }
00791 
00792     /* Now populate the window with pages. */
00793     for (i = 0; i < page_count; i++)
00794     {
00795         data->page_offset = page_start;
00796         data->page_num = i;
00797         gnc_main_window_restore_page(window, data);
00798 
00799         /* give the page a chance to display */
00800         while (gtk_events_pending ())
00801             gtk_main_iteration ();
00802     }
00803 
00804     /* Restore page ordering within the notebook. Use +1 notation so the
00805      * numbers in the page order match the page sections, at least for
00806      * the one window case. */
00807     order = g_key_file_get_integer_list(data->key_file, window_group,
00808                                         WINDOW_PAGEORDER, &length, &error);
00809     if (error)
00810     {
00811         g_warning("error reading group %s key %s: %s",
00812                   window_group, WINDOW_PAGEORDER, error->message);
00813         g_error_free(error);
00814         error = NULL;
00815     }
00816     else if (length != page_count)
00817     {
00818         g_warning("%s key %s length %" G_GSIZE_FORMAT " differs from window page count %d",
00819                   window_group, WINDOW_PAGEORDER, length, page_count);
00820     }
00821     else
00822     {
00823         /* Dump any list that might exist */
00824         g_list_free(priv->usage_order);
00825         priv->usage_order = NULL;
00826         /* Now rebuild the list from the key file. */
00827         for (i = 0; i < length; i++)
00828         {
00829             gpointer page = g_list_nth_data(priv->installed_pages, order[i] - 1);
00830             if (page)
00831             {
00832                 priv->usage_order = g_list_append(priv->usage_order, page);
00833             }
00834         }
00835         gtk_notebook_set_current_page (GTK_NOTEBOOK(priv->notebook),
00836                                        order[0] - 1);
00837     }
00838     if (order)
00839     {
00840         g_free(order);
00841     }
00842 
00843     LEAVE("window %p", window);
00844 cleanup:
00845     if (error)
00846         g_error_free(error);
00847     g_free(window_group);
00848 }
00849 
00850 void
00851 gnc_main_window_restore_all_windows(const GKeyFile *keyfile)
00852 {
00853     gint i, window_count;
00854     GError *error = NULL;
00855     GncMainWindowSaveData data;
00856     GncMainWindow *window;
00857 
00858     /* We use the same struct for reading and for writing, so we cast
00859        away the const. */
00860     data.key_file = (GKeyFile *) keyfile;
00861     window_count = g_key_file_get_integer(data.key_file, STATE_FILE_TOP,
00862                                           WINDOW_COUNT, &error);
00863     if (error)
00864     {
00865         g_warning("error reading group %s key %s: %s",
00866                   STATE_FILE_TOP, WINDOW_COUNT, error->message);
00867         g_error_free(error);
00868         LEAVE("can't read count");
00869         return;
00870     }
00871 
00872     /* Restore all state information on the open windows.  Window
00873        numbers in state file are 1-based. GList indices are 0-based. */
00874     gnc_set_busy_cursor (NULL, TRUE);
00875     for (i = 0; i < window_count; i++)
00876     {
00877         data.window_num = i;
00878         window = g_list_nth_data(active_windows, i);
00879         gnc_main_window_restore_window(window, &data);
00880     }
00881     gnc_unset_busy_cursor (NULL);
00882 }
00883 
00884 void
00885 gnc_main_window_restore_default_state(void)
00886 {
00887     GtkAction *action;
00888     GncMainWindow *window;
00889 
00890     /* The default state should be to have an Account Tree page open
00891      * in the window. */
00892     DEBUG("no saved state file");
00893     window = g_list_nth_data(active_windows, 0);
00894     action = gnc_main_window_find_action(window, "ViewAccountTreeAction");
00895     gtk_action_activate(action);
00896 }
00897 
00907 static void
00908 gnc_main_window_save_page (GncPluginPage *page, GncMainWindowSaveData *data)
00909 {
00910     gchar *page_group;
00911     const gchar *plugin_name, *page_name;
00912 
00913     ENTER("page %p, data %p (key file %p, window %d, page %d)",
00914           page, data, data->key_file, data->window_num, data->page_num);
00915     plugin_name = gnc_plugin_page_get_plugin_name(page);
00916     page_name = gnc_plugin_page_get_page_name(page);
00917     if (!plugin_name || !page_name)
00918     {
00919         LEAVE("not saving invalid page");
00920         return;
00921     }
00922     page_group = g_strdup_printf(PAGE_STRING, data->page_num++);
00923     g_key_file_set_string(data->key_file, page_group, PAGE_TYPE, plugin_name);
00924     g_key_file_set_string(data->key_file, page_group, PAGE_NAME, page_name);
00925 
00926     gnc_plugin_page_save_page(page, data->key_file, page_group);
00927     g_free(page_group);
00928     LEAVE(" ");
00929 }
00930 
00931 
00940 static void
00941 gnc_main_window_save_window (GncMainWindow *window, GncMainWindowSaveData *data)
00942 {
00943     GncMainWindowPrivate *priv;
00944     GtkAction *action;
00945     gint i, num_pages, coords[4], *order;
00946     gboolean maximized, visible;
00947     gchar *window_group;
00948 
00949     /* Setup */
00950     ENTER("window %p, data %p (key file %p, window %d)",
00951           window, data, data->key_file, data->window_num);
00952     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
00953 
00954     /* Check for bogus window structures. */
00955     num_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook));
00956     if (0 == num_pages)
00957     {
00958         LEAVE("empty window %p", window);
00959         return;
00960     }
00961 
00962     /* Save this window's notebook info */
00963     window_group = g_strdup_printf(WINDOW_STRING, data->window_num++);
00964     g_key_file_set_integer(data->key_file, window_group,
00965                            WINDOW_PAGECOUNT, num_pages);
00966     g_key_file_set_integer(data->key_file, window_group,
00967                            WINDOW_FIRSTPAGE, data->page_num);
00968 
00969     /* Save page ordering within the notebook. Use +1 notation so the
00970      * numbers in the page order match the page sections, at least for
00971      * the one window case. */
00972     order = g_malloc(sizeof(gint) * num_pages);
00973     for (i = 0; i < num_pages; i++)
00974     {
00975         gpointer page = g_list_nth_data(priv->usage_order, i);
00976         order[i] = g_list_index(priv->installed_pages, page) + 1;
00977     }
00978     g_key_file_set_integer_list(data->key_file, window_group,
00979                                 WINDOW_PAGEORDER, order, num_pages);
00980     g_free(order);
00981 
00982     /* Save the window coordinates, etc. */
00983     gtk_window_get_position(GTK_WINDOW(window), &coords[0], &coords[1]);
00984     gtk_window_get_size(GTK_WINDOW(window), &coords[2], &coords[3]);
00985     maximized = (gdk_window_get_state((GTK_WIDGET(window))->window)
00986                  & GDK_WINDOW_STATE_MAXIMIZED) != 0;
00987     g_key_file_set_integer_list(data->key_file, window_group,
00988                                 WINDOW_POSITION, &coords[0], 2);
00989     g_key_file_set_integer_list(data->key_file, window_group,
00990                                 WINDOW_GEOMETRY, &coords[2], 2);
00991     g_key_file_set_boolean(data->key_file, window_group,
00992                            WINDOW_MAXIMIZED, maximized);
00993     DEBUG("window (%p) position %dx%d, size %dx%d, %s", window,  coords[0], coords[1],
00994           coords[2], coords[3],
00995           maximized ? "maximized" : "not maximized");
00996 
00997     /* Common view menu items */
00998     action = gnc_main_window_find_action(window, "ViewToolbarAction");
00999     visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
01000     g_key_file_set_boolean(data->key_file, window_group,
01001                            TOOLBAR_VISIBLE, visible);
01002     action = gnc_main_window_find_action(window, "ViewSummaryAction");
01003     visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
01004     g_key_file_set_boolean(data->key_file, window_group,
01005                            SUMMARYBAR_VISIBLE, visible);
01006     action = gnc_main_window_find_action(window, "ViewStatusbarAction");
01007     visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
01008     g_key_file_set_boolean(data->key_file, window_group,
01009                            STATUSBAR_VISIBLE, visible);
01010 
01011     /* Save individual pages in this window */
01012     g_list_foreach(priv->installed_pages, (GFunc)gnc_main_window_save_page, data);
01013 
01014     g_free(window_group);
01015     LEAVE("window %p", window);
01016 }
01017 
01018 void
01019 gnc_main_window_save_all_windows(GKeyFile *keyfile)
01020 {
01021     GncMainWindowSaveData data;
01022 
01023     /* Set up the iterator data structures */
01024     data.key_file = keyfile;
01025     data.window_num = 1;
01026     data.page_num = 1;
01027 
01028     g_key_file_set_integer(data.key_file,
01029                            STATE_FILE_TOP, WINDOW_COUNT,
01030                            g_list_length(active_windows));
01031     /* Dump all state information on the open windows */
01032     g_list_foreach(active_windows, (GFunc)gnc_main_window_save_window, &data);
01033 }
01034 
01035 
01036 gboolean
01037 gnc_main_window_finish_pending (GncMainWindow *window)
01038 {
01039     GncMainWindowPrivate *priv;
01040     GList *item;
01041 
01042     g_return_val_if_fail(GNC_IS_MAIN_WINDOW(window), TRUE);
01043 
01044     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
01045     for (item = priv->installed_pages; item; item = g_list_next(item))
01046     {
01047         if (!gnc_plugin_page_finish_pending(item->data))
01048         {
01049             return FALSE;
01050         }
01051     }
01052     return TRUE;
01053 }
01054 
01055 
01056 gboolean
01057 gnc_main_window_all_finish_pending (void)
01058 {
01059     const GList *windows, *item;
01060 
01061     windows = gnc_gobject_tracking_get_list(GNC_MAIN_WINDOW_NAME);
01062     for (item = windows; item; item = g_list_next(item))
01063     {
01064         if (!gnc_main_window_finish_pending(item->data))
01065         {
01066             return FALSE;
01067         }
01068     }
01069     return TRUE;
01070 }
01071 
01072 
01083 static gboolean
01084 gnc_main_window_page_exists (GncPluginPage *page)
01085 {
01086     GncMainWindow *window;
01087     GncMainWindowPrivate *priv;
01088     GList *walker;
01089 
01090     for (walker = active_windows; walker; walker = g_list_next(walker))
01091     {
01092         window = walker->data;
01093         priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
01094         if (g_list_find(priv->installed_pages, page))
01095         {
01096             return TRUE;
01097         }
01098     }
01099     return FALSE;
01100 }
01101 
01102 
01112 static gboolean
01113 gnc_main_window_prompt_for_save (GtkWidget *window)
01114 {
01115     QofSession *session;
01116     QofBook *book;
01117     GtkWidget *dialog;
01118     gint response;
01119     const gchar *filename, *tmp;
01120     const gchar *title = _("Save changes to file %s before closing?");
01121     /* This should be the same message as in gnc-file.c */
01122     const gchar *message_hours =
01123         _("If you don't save, changes from the past %d hours and %d minutes will be discarded.");
01124     const gchar *message_days =
01125         _("If you don't save, changes from the past %d days and %d hours will be discarded.");
01126     time_t oldest_change;
01127     gint minutes, hours, days;
01128 
01129     session = gnc_get_current_session();
01130     book = qof_session_get_book(session);
01131     filename = qof_session_get_url(session);
01132     if (filename == NULL)
01133         filename = _("<unknown>");
01134     if ((tmp = strrchr(filename, '/')) != NULL)
01135         filename = tmp + 1;
01136 
01137     /* Remove any pending auto-save timeouts */
01138     gnc_autosave_remove_timer(book);
01139 
01140     dialog = gtk_message_dialog_new(GTK_WINDOW(window),
01141                                     GTK_DIALOG_MODAL,
01142                                     GTK_MESSAGE_WARNING,
01143                                     GTK_BUTTONS_NONE,
01144                                     title,
01145                                     filename);
01146     oldest_change = qof_book_get_session_dirty_time(book);
01147     minutes = (time(NULL) - oldest_change) / 60 + 1;
01148     hours = minutes / 60;
01149     minutes = minutes % 60;
01150     days = hours / 24;
01151     hours = hours % 24;
01152     if (days > 0)
01153     {
01154         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
01155                 message_days, days, hours);
01156     }
01157     else if (hours > 0)
01158     {
01159         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
01160                 message_hours, hours, minutes);
01161     }
01162     else
01163     {
01164         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
01165                 ngettext("If you don't save, changes from the past %d minute will be discarded.",
01166                          "If you don't save, changes from the past %d minutes will be discarded.",
01167                          minutes), minutes);
01168     }
01169     gtk_dialog_add_buttons(GTK_DIALOG(dialog),
01170                            _("Close _Without Saving"), GTK_RESPONSE_CLOSE,
01171                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
01172                            GTK_STOCK_SAVE, GTK_RESPONSE_APPLY,
01173                            NULL);
01174     gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);
01175     response = gtk_dialog_run (GTK_DIALOG (dialog));
01176     gtk_widget_destroy(dialog);
01177 
01178     switch (response)
01179     {
01180     case GTK_RESPONSE_APPLY:
01181         gnc_file_save();
01182         return FALSE;
01183 
01184     case GTK_RESPONSE_CLOSE:
01185         qof_book_mark_session_saved(book);
01186         return FALSE;
01187 
01188     default:
01189         return TRUE;
01190     }
01191 }
01192 
01193 
01194 static void
01195 gnc_main_window_add_plugin (gpointer plugin,
01196                             gpointer window)
01197 {
01198     g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
01199     g_return_if_fail (GNC_IS_PLUGIN (plugin));
01200 
01201     ENTER(" ");
01202     gnc_plugin_add_to_window (GNC_PLUGIN (plugin),
01203                               GNC_MAIN_WINDOW (window),
01204                               window_type);
01205     LEAVE(" ");
01206 }
01207 
01208 static void
01209 gnc_main_window_remove_plugin (gpointer plugin,
01210                                gpointer window)
01211 {
01212     g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
01213     g_return_if_fail (GNC_IS_PLUGIN (plugin));
01214 
01215     ENTER(" ");
01216     gnc_plugin_remove_from_window (GNC_PLUGIN (plugin),
01217                                    GNC_MAIN_WINDOW (window),
01218                                    window_type);
01219     LEAVE(" ");
01220 }
01221 
01222 
01223 static gboolean
01224 gnc_main_window_timed_quit (gpointer dummy)
01225 {
01226     if (gnc_file_save_in_progress())
01227         return TRUE;
01228 
01229     gnc_shutdown (0);
01230     return FALSE;
01231 }
01232 
01233 static gboolean
01234 gnc_main_window_quit(GncMainWindow *window)
01235 {
01236     QofSession *session;
01237     gboolean needs_save, do_shutdown;
01238 
01239     session = gnc_get_current_session();
01240     needs_save = qof_book_session_not_saved(qof_session_get_book(session)) &&
01241                  !gnc_file_save_in_progress();
01242     do_shutdown = !needs_save ||
01243                   (needs_save && !gnc_main_window_prompt_for_save(GTK_WIDGET(window)));
01244 
01245     if (do_shutdown)
01246     {
01247         g_timeout_add(250, gnc_main_window_timed_quit, NULL);
01248         return TRUE;
01249     }
01250     return FALSE;
01251 }
01252 
01253 static gboolean
01254 gnc_main_window_delete_event (GtkWidget *window,
01255                               GdkEvent *event,
01256                               gpointer user_data)
01257 {
01258     static gboolean already_dead = FALSE;
01259 
01260     if (already_dead)
01261         return TRUE;
01262 
01263     if (!gnc_main_window_finish_pending(GNC_MAIN_WINDOW(window)))
01264     {
01265         /* Don't close the window. */
01266         return TRUE;
01267     }
01268 
01269     if (g_list_length(active_windows) > 1)
01270         return FALSE;
01271 
01272     already_dead = gnc_main_window_quit(GNC_MAIN_WINDOW(window));
01273     return TRUE;
01274 }
01275 
01276 
01296 static void
01297 gnc_main_window_event_handler (QofInstance *entity,  QofEventId event_type,
01298                                gpointer user_data, gpointer event_data)
01299 {
01300     GncMainWindow *window;
01301     GncMainWindowPrivate *priv;
01302     GncPluginPage *page;
01303     GList *item, *next;
01304 
01305     /* hard failures */
01306     g_return_if_fail(GNC_IS_MAIN_WINDOW(user_data));
01307 
01308     /* soft failures */
01309     if (!QOF_CHECK_TYPE(entity, QOF_ID_BOOK))
01310         return;
01311     if (event_type !=  QOF_EVENT_DESTROY)
01312         return;
01313 
01314     ENTER("entity %p, event %d, window %p, event data %p",
01315           entity, event_type, user_data, event_data);
01316     window = GNC_MAIN_WINDOW(user_data);
01317     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
01318 
01319     /* This is not a typical list iteration.  We're removing while
01320      * we iterate, so we have to cache the 'next' pointer before
01321      * executing any code in the loop. */
01322     for (item = priv->installed_pages; item; item = next)
01323     {
01324         next = g_list_next(item);
01325         page = GNC_PLUGIN_PAGE(item->data);
01326         if (gnc_plugin_page_has_book (page, (QofBook *)entity))
01327             gnc_main_window_close_page (page);
01328     }
01329     LEAVE(" ");
01330 }
01331 
01332 
01348 static gchar *
01349 gnc_main_window_generate_title (GncMainWindow *window)
01350 {
01351     GncMainWindowPrivate *priv;
01352     GncPluginPage *page;
01353     QofBook *book;
01354     gchar *filename = NULL;
01355     const gchar *book_id = NULL;
01356     const gchar *dirty = "";
01357     const gchar *readonly_text = NULL;
01358     gchar *readonly;
01359     gchar *title;
01360     GtkAction* action;
01361 
01362     /* The save action is sensitive if the book is dirty */
01363     action = gnc_main_window_find_action (window, "FileSaveAction");
01364     if (action != NULL)
01365     {
01366         gtk_action_set_sensitive(action, FALSE);
01367     }
01368     if (gnc_current_session_exist())
01369     {
01370         book_id = qof_session_get_url (gnc_get_current_session ());
01371         book = gnc_get_current_book();
01372         if (qof_book_session_not_saved (book))
01373         {
01374             dirty = "*";
01375             if (action != NULL)
01376             {
01377                 gtk_action_set_sensitive(action, TRUE);
01378             }
01379         }
01380         if (qof_book_is_readonly(book))
01381         {
01382             /* Translators: This string is shown in the window title if this
01383             document is, well, read-only. */
01384             readonly_text = _("(read-only)");
01385         }
01386     }
01387     readonly = (readonly_text != NULL)
01388                ? g_strdup_printf(" %s", readonly_text)
01389                : g_strdup("");
01390 
01391     if (!book_id)
01392         filename = g_strdup(_("Unsaved Book"));
01393     else
01394     {
01395         if ( gnc_uri_is_file_uri ( book_id ) )
01396         {
01397             /* The filename is a true file.
01398              * The Gnome HIG 2.0 recommends only the file name (no path) be used. (p15) */
01399             gchar *path = gnc_uri_get_path ( book_id );
01400             filename = g_path_get_basename ( path );
01401             g_free ( path );
01402         }
01403         else
01404         {
01405             /* The filename is composed of database connection parameters.
01406              * For this we will show access_method://username@database[:port] */
01407             filename = gnc_uri_normalize_uri (book_id, FALSE);
01408         }
01409     }
01410 
01411     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
01412     page = priv->current_page;
01413     if (page)
01414     {
01415         /* The Gnome HIG 2.0 recommends the application name not be used. (p16)
01416          * but several developers prefer to use it anyway. */
01417         title = g_strdup_printf("%s%s%s - %s - GnuCash", dirty, filename, readonly,
01418                                 gnc_plugin_page_get_page_name(page));
01419     }
01420     else
01421     {
01422         title = g_strdup_printf("%s%s%s - GnuCash", dirty, filename, readonly);
01423     }
01424     g_free( filename );
01425     g_free(readonly);
01426 
01427     return title;
01428 }
01429 
01430 
01440 static void
01441 gnc_main_window_update_title (GncMainWindow *window)
01442 {
01443     gchar *title;
01444 
01445     title = gnc_main_window_generate_title(window);
01446     gtk_window_set_title(GTK_WINDOW(window), title);
01447     g_free(title);
01448 }
01449 
01450 static void
01451 gnc_main_window_update_all_titles (void)
01452 {
01453     g_list_foreach(active_windows,
01454                    (GFunc)gnc_main_window_update_title,
01455                    NULL);
01456 }
01457 
01458 static void
01459 gnc_main_window_book_dirty_cb (QofBook *book,
01460                                gboolean dirty,
01461                                gpointer user_data)
01462 {
01463     gnc_main_window_update_all_titles();
01464 
01465     /* Auto-save feature */
01466     gnc_autosave_dirty_handler(book, dirty);
01467 }
01468 
01469 static void
01470 gnc_main_window_attach_to_book (QofSession *session)
01471 {
01472     QofBook *book;
01473 
01474     g_return_if_fail(session);
01475 
01476     book = qof_session_get_book(session);
01477     qof_book_set_dirty_cb(book, gnc_main_window_book_dirty_cb, NULL);
01478     gnc_main_window_update_all_titles();
01479 #ifndef MAC_INTEGRATION
01480     gnc_main_window_update_all_menu_items();
01481 #endif
01482 }
01483 
01484 
01488 struct menu_update
01489 {
01491     gchar    *action_name;
01492 
01494     gchar    *label;
01495 
01497     gboolean  visible;
01498 };
01499 
01500 
01514 static void
01515 gnc_main_window_update_one_menu_action (GncMainWindow *window,
01516                                         struct menu_update *data)
01517 {
01518     GncMainWindowPrivate *priv;
01519     GtkAction* action;
01520 
01521     ENTER("window %p, action %s, label %s, visible %d", window,
01522           data->action_name, data->label, data->visible);
01523     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
01524     action = gtk_action_group_get_action(priv->action_group, data->action_name);
01525     if (action)
01526         g_object_set(G_OBJECT(action),
01527                      "label", data->label,
01528                      "visible", data->visible,
01529                      (char *)NULL);
01530 #ifdef MAC_INTEGRATION
01531     {
01532         GtkOSXApplication *theApp =
01533             g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
01534         gtk_osxapplication_sync_menubar(theApp);
01535     }
01536 #endif
01537     LEAVE(" ");
01538 }
01539 
01540 
01553 static void
01554 gnc_main_window_update_radio_button (GncMainWindow *window)
01555 {
01556     GncMainWindowPrivate *priv;
01557     GtkAction *action, *first_action;
01558     GSList *action_list;
01559     gchar *action_name;
01560     gint index;
01561 
01562     ENTER("window %p", window);
01563 
01564     /* Show the new entry in all windows. */
01565     index = g_list_index(active_windows, window);
01566     if (index >= n_radio_entries)
01567     {
01568         LEAVE("window %d, only %d actions", index, n_radio_entries);
01569         return;
01570     }
01571 
01572     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
01573     action_name = g_strdup_printf("Window%dAction", index);
01574     action = gtk_action_group_get_action(priv->action_group, action_name);
01575 
01576     /* Block the signal so as not to affect window ordering (top to
01577      * bottom) on the screen */
01578     action_list = gtk_radio_action_get_group(GTK_RADIO_ACTION(action));
01579     if (action_list)
01580     {
01581         first_action = g_slist_last(action_list)->data;
01582         g_signal_handlers_block_by_func(G_OBJECT(first_action),
01583                                         G_CALLBACK(gnc_main_window_cmd_window_raise),
01584                                         window);
01585         DEBUG("blocked signal on %p, set %p active, window %p", first_action,
01586               action, window);
01587         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
01588         g_signal_handlers_unblock_by_func(G_OBJECT(first_action),
01589                                           G_CALLBACK(gnc_main_window_cmd_window_raise),
01590                                           window);
01591     }
01592     g_free(action_name);
01593     LEAVE(" ");
01594 }
01595 
01596 
01610 static void
01611 gnc_main_window_update_menu_item (GncMainWindow *window)
01612 {
01613     struct menu_update data;
01614     gchar **strings, *title, *expanded;
01615     gint index;
01616 
01617     ENTER("window %p", window);
01618     index = g_list_index(active_windows, window);
01619     if (index > n_radio_entries)
01620     {
01621         LEAVE("skip window %d (only %d entries)", index, n_radio_entries);
01622         return;
01623     }
01624 
01625     /* Figure out the label name. Add the accelerator if possible. */
01626     title = gnc_main_window_generate_title(window);
01627     strings = g_strsplit(title, "_", 0);
01628     g_free(title);
01629     expanded = g_strjoinv("__", strings);
01630     if (index < 10)
01631     {
01632         data.label = g_strdup_printf("_%d %s", (index + 1) % 10, expanded);
01633         g_free(expanded);
01634     }
01635     else
01636     {
01637         data.label = expanded;
01638     }
01639     g_strfreev(strings);
01640 
01641     data.visible = TRUE;
01642     data.action_name = g_strdup_printf("Window%dAction", index);
01643     g_list_foreach(active_windows,
01644                    (GFunc)gnc_main_window_update_one_menu_action,
01645                    &data);
01646     g_free(data.action_name);
01647     g_free(data.label);
01648 
01649     LEAVE(" ");
01650 }
01651 
01659 static void
01660 gnc_main_window_update_all_menu_items (void)
01661 {
01662     struct menu_update data;
01663     gchar *label;
01664     gint i;
01665 
01666     ENTER("");
01667 #ifndef MAC_INTEGRATION
01668     /* First update the entries for all existing windows */
01669     g_list_foreach(active_windows,
01670                    (GFunc)gnc_main_window_update_menu_item,
01671                    NULL);
01672     g_list_foreach(active_windows,
01673                    (GFunc)gnc_main_window_update_radio_button,
01674                    NULL);
01675 
01676     /* Now hide any entries that aren't being used. */
01677     data.visible = FALSE;
01678     for (i = g_list_length(active_windows); i < n_radio_entries; i++)
01679     {
01680         data.action_name = g_strdup_printf("Window%dAction", i);
01681         label = g_strdup_printf("Window _%d", (i - 1) % 10);
01682         data.label = gettext(label);
01683 
01684         g_list_foreach(active_windows,
01685                        (GFunc)gnc_main_window_update_one_menu_action,
01686                        &data);
01687 
01688         g_free(data.action_name);
01689         g_free(label);
01690     }
01691 #endif
01692     LEAVE(" ");
01693 }
01694 
01695 
01707 static void
01708 gnc_main_window_update_tab_close_one_page (GncPluginPage *page,
01709         gpointer user_data)
01710 {
01711     gboolean *new_value = user_data;
01712     GtkWidget * close_button;
01713 
01714     ENTER("page %p, visible %d", page, *new_value);
01715     close_button = g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON);
01716     if (!close_button)
01717     {
01718         LEAVE("no close button");
01719         return;
01720     }
01721 
01722     if (*new_value)
01723         gtk_widget_show (close_button);
01724     else
01725         gtk_widget_hide (close_button);
01726     LEAVE(" ");
01727 }
01728 
01729 
01741 static void
01742 gnc_main_window_update_tab_close (GConfEntry *entry, gpointer user_data)
01743 {
01744     gboolean new_value;
01745 
01746     ENTER(" ");
01747     new_value = gconf_value_get_bool(entry->value);
01748     gnc_main_window_foreach_page(
01749         gnc_main_window_update_tab_close_one_page,
01750         &new_value);
01751     LEAVE(" ");
01752 }
01753 
01754 
01767 static void
01768 gnc_main_window_update_tab_width_one_page (GncPluginPage *page,
01769         gpointer user_data)
01770 {
01771     gint *new_value = user_data;
01772     GtkWidget *label;
01773 
01774     ENTER("page %p, visible %d", page, *new_value);
01775     label = g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_TAB_LABEL);
01776     if (!label)
01777     {
01778         LEAVE("no label");
01779         return;
01780     }
01781 
01782     if (*new_value != 0)
01783     {
01784         gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
01785         gtk_label_set_max_width_chars(GTK_LABEL(label), *new_value);
01786     }
01787     else
01788     {
01789         gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_NONE);
01790         gtk_label_set_max_width_chars(GTK_LABEL(label), 100);
01791     }
01792     LEAVE(" ");
01793 }
01794 
01795 
01807 static void
01808 gnc_main_window_update_tab_width (GConfEntry *entry, gpointer user_data)
01809 {
01810     gint new_value;
01811 
01812     ENTER(" ");
01813     new_value = gconf_value_get_float(entry->value);
01814     gnc_main_window_foreach_page(
01815         gnc_main_window_update_tab_width_one_page,
01816         &new_value);
01817     LEAVE(" ");
01818 }
01819 
01820 
01821 /************************************************************
01822  *                 Tab Label Implementation                 *
01823  ************************************************************/
01824 static gboolean
01825 main_window_find_tab_items (GncMainWindow *window,
01826                             GncPluginPage *page,
01827                             GtkWidget **label_p,
01828                             GtkWidget **entry_p)
01829 {
01830     GncMainWindowPrivate *priv;
01831     GtkWidget *tab_hbox, *widget, *event_box;
01832     GList *children, *tmp;
01833 
01834     ENTER("window %p, page %p, label_p %p, entry_p %p",
01835           window, page, label_p, entry_p);
01836     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
01837     *label_p = *entry_p = NULL;
01838 
01839     if (!page->notebook_page)
01840     {
01841         LEAVE("invalid notebook_page");
01842         return FALSE;
01843     }
01844 
01845     event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
01846                                            page->notebook_page);
01847 
01848     tab_hbox = gtk_bin_get_child(GTK_BIN(event_box));
01849 
01850     children = gtk_container_get_children(GTK_CONTAINER(tab_hbox));
01851     for (tmp = children; tmp; tmp = g_list_next(tmp))
01852     {
01853         widget = tmp->data;
01854         if (GTK_IS_LABEL(widget))
01855         {
01856             *label_p = widget;
01857         }
01858         else if (GTK_IS_ENTRY(widget))
01859         {
01860             *entry_p = widget;
01861         }
01862     }
01863     g_list_free(children);
01864 
01865     LEAVE("label %p, entry %p", *label_p, *entry_p);
01866     return (*label_p && *entry_p);
01867 }
01868 
01869 static gboolean
01870 main_window_find_tab_event (GncMainWindow *window,
01871                             GncPluginPage *page,
01872                             GtkWidget **event_p)
01873 {
01874     GncMainWindowPrivate *priv;
01875     GtkWidget *event_box;
01876 
01877     ENTER("window %p, page %p, event %p",
01878           window, page, event_p);
01879     *event_p = NULL;
01880 
01881     if (!page->notebook_page)
01882     {
01883         LEAVE("invalid notebook_page");
01884         return FALSE;
01885     }
01886 
01887     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
01888     event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
01889                                            page->notebook_page);
01890     if (GTK_IS_EVENT_BOX(event_box))
01891     {
01892         *event_p = event_box;
01893         LEAVE("event %p", *event_p);
01894         return (TRUE);
01895     }
01896 
01897     LEAVE("event %p", *event_p);
01898     return (FALSE);
01899 }
01900 
01901 void
01902 main_window_update_page_name (GncPluginPage *page,
01903                               const gchar *name_in)
01904 {
01905     GncMainWindow *window;
01906     GncMainWindowPrivate *priv;
01907     GtkWidget *label, *entry, *event_box;
01908     gchar *name, *old_page_name, *old_page_long_name;
01909 
01910     ENTER(" ");
01911 
01912     if ((name_in == NULL) || (*name_in == '\0'))
01913     {
01914         LEAVE("no string");
01915         return;
01916     }
01917     name = g_strstrip(g_strdup(name_in));
01918 
01919     /* Optimization, if the name hasn't changed, don't update X. */
01920     if (*name == '\0' || 0 == strcmp(name, gnc_plugin_page_get_page_name(page)))
01921     {
01922         g_free(name);
01923         LEAVE("empty string or name unchanged");
01924         return;
01925     }
01926 
01927     old_page_name = g_strdup( gnc_plugin_page_get_page_name(page));
01928     old_page_long_name = g_strdup( gnc_plugin_page_get_page_long_name(page));
01929 
01930     /* Update the plugin */
01931     gnc_plugin_page_set_page_name(page, name);
01932 
01933     /* Update the notebook tab */
01934     window = GNC_MAIN_WINDOW(page->window);
01935     if (!window)
01936     {
01937         g_free(old_page_name);
01938         g_free(old_page_long_name);
01939         g_free(name);
01940         LEAVE("no window widget available");
01941         return;
01942     }
01943 
01944     if (main_window_find_tab_items(window, page, &label, &entry))
01945         gtk_label_set_text(GTK_LABEL(label), name);
01946 
01947     /* Update Tooltip on notebook Tab */
01948     if (old_page_long_name && old_page_name
01949             && g_strrstr(old_page_long_name, old_page_name) != NULL)
01950     {
01951         gchar *new_page_long_name;
01952         gint string_position;
01953 
01954         string_position = strlen(old_page_long_name) - strlen(old_page_name);
01955         new_page_long_name = g_strconcat(g_strndup(old_page_long_name, string_position), name, NULL);
01956 
01957         gnc_plugin_page_set_page_long_name(page, new_page_long_name);
01958 
01959         if (main_window_find_tab_event(window, page, &event_box))
01960             gtk_widget_set_tooltip_text(event_box, new_page_long_name);
01961 
01962         g_free(new_page_long_name);
01963     }
01964 
01965     /* Update the notebook menu */
01966     if (page->notebook_page)
01967     {
01968         priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
01969         label = gtk_notebook_get_menu_label (GTK_NOTEBOOK(priv->notebook),
01970                                              page->notebook_page);
01971         gtk_label_set_text(GTK_LABEL(label), name);
01972     }
01973 
01974     /* Force an update of the window title */
01975     gnc_main_window_update_title(window);
01976     g_free(old_page_long_name);
01977     g_free(old_page_name);
01978     g_free(name);
01979     LEAVE("done");
01980 }
01981 
01982 
01983 void
01984 main_window_update_page_color (GncPluginPage *page,
01985                                const gchar *color_in)
01986 {
01987     GncMainWindow *window;
01988     GncMainWindowPrivate *priv;
01989     GtkWidget *event_box;
01990     GdkColor tab_color;
01991     gchar *color_string;
01992 
01993 
01994     ENTER(" ");
01995 
01996     if ((color_in == NULL) || (*color_in == '\0'))
01997     {
01998         LEAVE("no string");
01999         return;
02000     }
02001     color_string = g_strstrip(g_strdup(color_in));
02002 
02003     /* Optimization, if the color hasn't changed, don't update. */
02004     if (*color_string == '\0' || 0 == safe_strcmp(color_string, gnc_plugin_page_get_page_color(page)))
02005     {
02006         g_free(color_string);
02007         LEAVE("empty string or color unchanged");
02008         return;
02009     }
02010 
02011     /* Update the plugin */
02012     window = GNC_MAIN_WINDOW(page->window);
02013     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
02014     gnc_plugin_page_set_page_color(page, color_string);
02015 
02016     /* Update the notebook tab */
02017     main_window_find_tab_event(window, page, &event_box);
02018 
02019     if (gdk_color_parse(color_string, &tab_color))
02020     {
02021         gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, &tab_color);
02022         gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, &tab_color);
02023     }
02024     else
02025     {
02026         gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, NULL);
02027         gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, NULL);
02028     }
02029     g_free(color_string);
02030     LEAVE("done");
02031 }
02032 
02033 
02034 static void
02035 gnc_main_window_tab_entry_activate (GtkWidget *entry,
02036                                     GncPluginPage *page)
02037 {
02038     GtkWidget *label, *entry2;
02039 
02040     g_return_if_fail(GTK_IS_ENTRY(entry));
02041     g_return_if_fail(GNC_IS_PLUGIN_PAGE(page));
02042 
02043     ENTER("");
02044     if (!main_window_find_tab_items(GNC_MAIN_WINDOW(page->window),
02045                                     page, &label, &entry2))
02046     {
02047         LEAVE("can't find required widgets");
02048         return;
02049     }
02050 
02051     main_window_update_page_name(page, gtk_entry_get_text(GTK_ENTRY(entry)));
02052 
02053     gtk_widget_hide(entry);
02054     gtk_widget_show(label);
02055     LEAVE("");
02056 }
02057 
02058 
02059 static gboolean
02060 gnc_main_window_tab_entry_editing_done (GtkWidget *entry,
02061                                         GncPluginPage *page)
02062 {
02063     ENTER("");
02064     gnc_main_window_tab_entry_activate(entry, page);
02065     LEAVE("");
02066     return FALSE;
02067 }
02068 
02069 static gboolean
02070 gnc_main_window_tab_entry_focus_out_event (GtkWidget *entry,
02071         GdkEvent *event,
02072         GncPluginPage *page)
02073 {
02074     ENTER("");
02075     gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
02076     LEAVE("");
02077     return FALSE;
02078 }
02079 
02080 static gboolean
02081 gnc_main_window_tab_entry_key_press_event (GtkWidget *entry,
02082         GdkEventKey *event,
02083         GncPluginPage *page)
02084 {
02085     if (event->keyval == GDK_Escape)
02086     {
02087         GtkWidget *label, *entry2;
02088 
02089         g_return_val_if_fail(GTK_IS_ENTRY(entry), FALSE);
02090         g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
02091 
02092         ENTER("");
02093         if (!main_window_find_tab_items(GNC_MAIN_WINDOW(page->window),
02094                                         page, &label, &entry2))
02095         {
02096             LEAVE("can't find required widgets");
02097             return FALSE;
02098         }
02099 
02100         gtk_entry_set_text(GTK_ENTRY(entry), gtk_label_get_text(GTK_LABEL(label)));
02101         gtk_widget_hide(entry);
02102         gtk_widget_show(label);
02103         LEAVE("");
02104     }
02105     return FALSE;
02106 }
02107 
02108 /************************************************************
02109  *                   Widget Implementation                  *
02110  ************************************************************/
02111 
02112 /*  Get the type of a gnc main window.
02113  */
02114 GType
02115 gnc_main_window_get_type (void)
02116 {
02117     static GType gnc_main_window_type = 0;
02118 
02119     if (gnc_main_window_type == 0)
02120     {
02121         static const GTypeInfo our_info =
02122         {
02123             sizeof (GncMainWindowClass),
02124             NULL,
02125             NULL,
02126             (GClassInitFunc) gnc_main_window_class_init,
02127             NULL,
02128             NULL,
02129             sizeof (GncMainWindow),
02130             0,
02131             (GInstanceInitFunc) gnc_main_window_init
02132         };
02133 
02134         static const GInterfaceInfo plugin_info =
02135         {
02136             (GInterfaceInitFunc) gnc_window_main_window_init,
02137             NULL,
02138             NULL
02139         };
02140 
02141         gnc_main_window_type = g_type_register_static (GTK_TYPE_WINDOW,
02142                                GNC_MAIN_WINDOW_NAME,
02143                                &our_info, 0);
02144         g_type_add_interface_static (gnc_main_window_type,
02145                                      GNC_TYPE_WINDOW,
02146                                      &plugin_info);
02147     }
02148 
02149     return gnc_main_window_type;
02150 }
02151 
02152 
02160 static void
02161 gnc_main_window_class_init (GncMainWindowClass *klass)
02162 {
02163     GObjectClass *object_class = G_OBJECT_CLASS (klass);
02164     GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS(klass);
02165 
02166     parent_class = g_type_class_peek_parent (klass);
02167 
02168     window_type = g_quark_from_static_string ("gnc-main-window");
02169 
02170     object_class->finalize = gnc_main_window_finalize;
02171 
02172     /* GtkObject signals */
02173     gtkobject_class->destroy = gnc_main_window_destroy;
02174 
02175     g_type_class_add_private(klass, sizeof(GncMainWindowPrivate));
02176 
02188     main_window_signals[PAGE_ADDED] =
02189         g_signal_new ("page_added",
02190                       G_OBJECT_CLASS_TYPE (object_class),
02191                       G_SIGNAL_RUN_FIRST,
02192                       G_STRUCT_OFFSET (GncMainWindowClass, page_added),
02193                       NULL, NULL,
02194                       g_cclosure_marshal_VOID__OBJECT,
02195                       G_TYPE_NONE, 1,
02196                       G_TYPE_OBJECT);
02197 
02208     main_window_signals[PAGE_CHANGED] =
02209         g_signal_new ("page_changed",
02210                       G_OBJECT_CLASS_TYPE (object_class),
02211                       G_SIGNAL_RUN_FIRST,
02212                       G_STRUCT_OFFSET (GncMainWindowClass, page_changed),
02213                       NULL, NULL,
02214                       g_cclosure_marshal_VOID__OBJECT,
02215                       G_TYPE_NONE, 1,
02216                       G_TYPE_OBJECT);
02217 
02218     gnc_gconf_general_register_cb (KEY_SHOW_CLOSE_BUTTON,
02219                                    gnc_main_window_update_tab_close,
02220                                    NULL);
02221     gnc_gconf_general_register_cb (KEY_TAB_WIDTH,
02222                                    gnc_main_window_update_tab_width,
02223                                    NULL);
02224     gnc_hook_add_dangler(HOOK_BOOK_SAVED,
02225                          (GFunc)gnc_main_window_update_all_titles, NULL);
02226     gnc_hook_add_dangler(HOOK_BOOK_OPENED,
02227                          (GFunc)gnc_main_window_attach_to_book, NULL);
02228 
02229 }
02230 
02231 
02240 static void
02241 gnc_main_window_init (GncMainWindow *window,
02242                       GncMainWindowClass *klass)
02243 {
02244     GncMainWindowPrivate *priv;
02245 
02246     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
02247     priv->merged_actions_table =
02248         g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
02249 
02250     priv->event_handler_id =
02251         qof_event_register_handler(gnc_main_window_event_handler, window);
02252 
02253     gnc_main_window_setup_window (window);
02254     gnc_gobject_tracking_remember(G_OBJECT(window),
02255                                   G_OBJECT_CLASS(klass));
02256 }
02257 
02258 
02269 static void
02270 gnc_main_window_finalize (GObject *object)
02271 {
02272     GncMainWindow *window;
02273     GncMainWindowPrivate *priv;
02274 
02275     g_return_if_fail (object != NULL);
02276     g_return_if_fail (GNC_IS_MAIN_WINDOW (object));
02277 
02278     window = GNC_MAIN_WINDOW (object);
02279     priv = GNC_MAIN_WINDOW_GET_PRIVATE (window);
02280 
02281     if (active_windows == NULL)
02282     {
02283         /* Oops. User killed last window and we didn't catch it. */
02284         g_idle_add((GSourceFunc)gnc_shutdown, 0);
02285     }
02286 
02287     gnc_gobject_tracking_forget(object);
02288     G_OBJECT_CLASS (parent_class)->finalize (object);
02289 }
02290 
02291 
02292 static void
02293 gnc_main_window_destroy (GtkObject *object)
02294 {
02295     GncMainWindow *window;
02296     GncMainWindowPrivate *priv;
02297     GncPluginManager *manager;
02298     GList *plugins;
02299 
02300     g_return_if_fail (object != NULL);
02301     g_return_if_fail (GNC_IS_MAIN_WINDOW (object));
02302 
02303     window = GNC_MAIN_WINDOW (object);
02304 
02305     active_windows = g_list_remove (active_windows, window);
02306 
02307     /* Do these things once */
02308     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
02309     if (priv->merged_actions_table)
02310     {
02311 
02312         /* Close any pages in this window */
02313         while (priv->current_page)
02314             gnc_main_window_close_page(priv->current_page);
02315 
02316         if (gnc_window_get_progressbar_window() == GNC_WINDOW(window))
02317             gnc_window_set_progressbar_window(NULL);
02318 #ifndef MAC_INTEGRATION
02319         /* Update the "Windows" menu in all other windows */
02320         gnc_main_window_update_all_menu_items();
02321 #endif
02322         gnc_gconf_remove_notification(G_OBJECT(window), DESKTOP_GNOME_INTERFACE,
02323                                       GNC_MAIN_WINDOW_NAME);
02324         gnc_gconf_remove_notification(G_OBJECT(window), GCONF_GENERAL,
02325                                       GNC_MAIN_WINDOW_NAME);
02326 
02327         qof_event_unregister_handler(priv->event_handler_id);
02328         priv->event_handler_id = 0;
02329 
02330         g_hash_table_destroy (priv->merged_actions_table);
02331         priv->merged_actions_table = NULL;
02332 
02333         /* GncPluginManager stuff */
02334         manager = gnc_plugin_manager_get ();
02335         plugins = gnc_plugin_manager_get_plugins (manager);
02336         g_list_foreach (plugins, gnc_main_window_remove_plugin, window);
02337         g_list_free (plugins);
02338     }
02339     GTK_OBJECT_CLASS (parent_class)->destroy (object);
02340 }
02341 
02342 
02343 /*  Create a new gnc main window plugin.
02344  */
02345 GncMainWindow *
02346 gnc_main_window_new (void)
02347 {
02348     GncMainWindow *window;
02349     GtkWidget *old_window;
02350 
02351     window = g_object_new (GNC_TYPE_MAIN_WINDOW, NULL);
02352     gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
02353 
02354     old_window = gnc_ui_get_toplevel();
02355     if (old_window)
02356     {
02357         gint width, height;
02358         gtk_window_get_size (GTK_WINDOW (old_window), &width, &height);
02359         gtk_window_resize (GTK_WINDOW (window), width, height);
02360         if ((gdk_window_get_state((GTK_WIDGET(old_window))->window)
02361                 & GDK_WINDOW_STATE_MAXIMIZED) != 0)
02362         {
02363             gtk_window_maximize (GTK_WINDOW (window));
02364         }
02365     }
02366     active_windows = g_list_append (active_windows, window);
02367     gnc_main_window_update_title(window);
02368 #ifdef MAC_INTEGRATION
02369     gnc_quartz_set_menu(window);
02370 #else
02371     gnc_main_window_update_all_menu_items();
02372 #endif
02373     gnc_engine_add_commit_error_callback( gnc_main_window_engine_commit_error_callback, window );
02374 
02375     return window;
02376 }
02377 
02378 /************************************************************
02379  *                     Utility Functions                    *
02380  ************************************************************/
02381 
02382 static void
02383 gnc_main_window_engine_commit_error_callback( gpointer data,
02384         QofBackendError errcode )
02385 {
02386     GncMainWindow* window = GNC_MAIN_WINDOW(data);
02387     GtkWidget* dialog;
02388     const gchar *reason = _("Unable to save to database.");
02389     if ( errcode == ERR_BACKEND_READONLY )
02390         reason = _("Unable to save to database: Book is marked read-only.");
02391     dialog = gtk_message_dialog_new( GTK_WINDOW(window),
02392                                      GTK_DIALOG_DESTROY_WITH_PARENT,
02393                                      GTK_MESSAGE_ERROR,
02394                                      GTK_BUTTONS_CLOSE,
02395                                      "%s",
02396                                      reason );
02397     gtk_dialog_run(GTK_DIALOG (dialog));
02398     gtk_widget_destroy(dialog);
02399 
02400 }
02401 
02419 static void
02420 gnc_main_window_connect (GncMainWindow *window,
02421                          GncPluginPage *page,
02422                          GtkWidget *tab_hbox,
02423                          GtkWidget *menu_label)
02424 {
02425     GncMainWindowPrivate *priv;
02426     GtkNotebook *notebook;
02427 
02428     page->window = GTK_WIDGET(window);
02429     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
02430     notebook = GTK_NOTEBOOK (priv->notebook);
02431     priv->installed_pages = g_list_append (priv->installed_pages, page);
02432     priv->usage_order = g_list_prepend (priv->usage_order, page);
02433     gtk_notebook_append_page_menu (notebook, page->notebook_page,
02434                                    tab_hbox, menu_label);
02435     gtk_notebook_set_tab_reorderable (notebook, page->notebook_page, TRUE);
02436     gnc_plugin_page_inserted (page);
02437     gtk_notebook_set_current_page (notebook, -1);
02438     if (GNC_PLUGIN_PAGE_GET_CLASS(page)->window_changed)
02439         (GNC_PLUGIN_PAGE_GET_CLASS(page)->window_changed)(page, GTK_WIDGET(window));
02440     g_signal_emit (window, main_window_signals[PAGE_ADDED], 0, page);
02441 
02442     g_signal_connect(G_OBJECT(page->notebook_page), "popup-menu",
02443                      G_CALLBACK(gnc_main_window_popup_menu_cb), page);
02444     g_signal_connect_after(G_OBJECT(page->notebook_page), "button-press-event",
02445                            G_CALLBACK(gnc_main_window_button_press_cb), page);
02446 }
02447 
02448 
02462 static void
02463 gnc_main_window_disconnect (GncMainWindow *window,
02464                             GncPluginPage *page)
02465 {
02466     GncMainWindowPrivate *priv;
02467     GtkNotebook *notebook;
02468     GncPluginPage *new_page;
02469     gint page_num;
02470 
02471     /* Disconnect the callbacks */
02472     g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
02473                                          G_CALLBACK(gnc_main_window_popup_menu_cb), page);
02474     g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
02475                                          G_CALLBACK(gnc_main_window_button_press_cb), page);
02476 
02477     /* Disconnect the page and summarybar from the window */
02478     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
02479     if (priv->current_page == page)
02480     {
02481         gnc_plugin_page_unmerge_actions (page, window->ui_merge);
02482         gnc_plugin_page_unselected (page);
02483         priv->current_page = NULL;
02484     }
02485 
02486     /* Remove it from the list of pages in the window */
02487     priv->installed_pages = g_list_remove (priv->installed_pages, page);
02488     priv->usage_order = g_list_remove (priv->usage_order, page);
02489 
02490     /* Switch to the last recently used page */
02491     notebook = GTK_NOTEBOOK (priv->notebook);
02492     if (gnc_gconf_get_bool(GCONF_GENERAL, KEY_TAB_NEXT_RECENT, NULL))
02493     {
02494         new_page = g_list_nth_data (priv->usage_order, 0);
02495         if (new_page)
02496         {
02497             page_num =  gtk_notebook_page_num(notebook, new_page->notebook_page);
02498             gtk_notebook_set_current_page(notebook, page_num);
02499         }
02500     }
02501 
02502     /* Remove the page from the notebook */
02503     page_num =  gtk_notebook_page_num(notebook, page->notebook_page);
02504     gtk_notebook_remove_page (notebook, page_num);
02505 
02506     if ( gtk_notebook_get_current_page(notebook) == -1)
02507     {
02508         /* Need to synthesize a page changed signal when the last
02509          * page is removed.  The notebook doesn't generate a signal
02510          * for this, therefore the switch_page code in this file
02511          * never gets called to generate this signal. */
02512         gnc_main_window_switch_page(notebook, NULL, -1, window);
02513         //g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, NULL);
02514     }
02515 
02516     gnc_plugin_page_removed (page);
02517 
02518     gtk_ui_manager_ensure_update (window->ui_merge);
02519     gnc_window_set_status (GNC_WINDOW(window), page, NULL);
02520 }
02521 
02522 
02523 /************************************************************
02524  *                                                          *
02525  ************************************************************/
02526 
02527 
02528 void
02529 gnc_main_window_display_page (GncPluginPage *page)
02530 {
02531     GncMainWindow *window;
02532     GncMainWindowPrivate *priv;
02533     GtkNotebook *notebook;
02534     gint page_num;
02535 
02536     window = GNC_MAIN_WINDOW (page->window);
02537     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
02538     notebook = GTK_NOTEBOOK (priv->notebook);
02539     page_num = gtk_notebook_page_num(notebook, page->notebook_page);
02540     gtk_notebook_set_current_page (notebook, page_num);
02541     gtk_window_present(GTK_WINDOW(window));
02542 }
02543 
02544 
02545 /*  Display a data plugin page in a window.  If the page already
02546  *  exists in any window, then that window will be brought to the
02547  *  front and the notebook switch to display the specified page.  If
02548  *  the page is new then it will be added to the specified window.  If
02549  *  the window is NULL, the new page will be added to the first
02550  *  window.
02551  */
02552 void
02553 gnc_main_window_open_page (GncMainWindow *window,
02554                            GncPluginPage *page)
02555 {
02556     GncMainWindowPrivate *priv;
02557     GtkWidget *tab_hbox;
02558     GtkWidget *label, *entry, *event_box;
02559     const gchar *icon, *text, *color_string;
02560     GtkWidget *image;
02561     GList *tmp;
02562     gint width;
02563     GdkColor tab_color;
02564 
02565     ENTER("window %p, page %p", window, page);
02566 
02567     if (window)
02568         g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
02569     g_return_if_fail (GNC_IS_PLUGIN_PAGE (page));
02570     g_return_if_fail (gnc_plugin_page_has_books(page));
02571 
02572     if (gnc_main_window_page_exists(page))
02573     {
02574         gnc_main_window_display_page(page);
02575         return;
02576     }
02577 
02578     /* Does the page want to be in a new window? */
02579     if (gnc_plugin_page_get_use_new_window(page))
02580     {
02581         /* See if there's a blank window. If so, use that. */
02582         for (tmp = active_windows; tmp; tmp = g_list_next(tmp))
02583         {
02584             window = GNC_MAIN_WINDOW(tmp->data);
02585             priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
02586             if (priv->installed_pages == NULL)
02587             {
02588                 break;
02589             }
02590         }
02591         if (tmp == NULL)
02592             window = gnc_main_window_new ();
02593         gtk_widget_show(GTK_WIDGET(window));
02594     }
02595     else if ((window == NULL) && active_windows)
02596     {
02597         window = active_windows->data;
02598     }
02599 
02600     page->window = GTK_WIDGET(window);
02601     page->notebook_page = gnc_plugin_page_create_widget (page);
02602     g_object_set_data (G_OBJECT (page->notebook_page),
02603                        PLUGIN_PAGE_LABEL, page);
02604 
02605     /*
02606      * The page tab.
02607      */
02608     width = gnc_gconf_get_float(GCONF_GENERAL, KEY_TAB_WIDTH, NULL);
02609     icon = GNC_PLUGIN_PAGE_GET_CLASS(page)->tab_icon;
02610     label = gtk_label_new (gnc_plugin_page_get_page_name(page));
02611     if (width != 0)
02612     {
02613         gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
02614         gtk_label_set_max_width_chars(GTK_LABEL(label), width);
02615     }
02616     gtk_widget_show (label);
02617 
02618     tab_hbox = gtk_hbox_new (FALSE, 6);
02619     gtk_widget_show (tab_hbox);
02620 
02621     if (icon != NULL)
02622     {
02623         image = gtk_image_new_from_stock (icon, GTK_ICON_SIZE_MENU);
02624         gtk_widget_show (image);
02625         gtk_box_pack_start (GTK_BOX (tab_hbox), image, FALSE, FALSE, 0);
02626         gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0);
02627     }
02628     else
02629         gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0);
02630 
02631     event_box = gtk_event_box_new();
02632     /* Note: this doesn't work properly on Windows with gtk+2.18.x (last
02633      * with 2.18.7). Setting the eventbox visible with that version results
02634      * in the tab's text being invisible. See bug #610675 for more on this.
02635      */
02636     gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box), TRUE);
02637     gtk_widget_show(event_box);
02638     gtk_container_add(GTK_CONTAINER(event_box), tab_hbox);
02639     color_string = gnc_plugin_page_get_page_color(page);
02640     if (color_string == NULL) color_string = "";
02641     if (gdk_color_parse(color_string, &tab_color))
02642     {
02643         gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, &tab_color);
02644         gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, &tab_color);
02645     }
02646     else
02647     {
02648         gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, NULL);
02649         gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, NULL);
02650     }
02651 
02652     text = gnc_plugin_page_get_page_long_name(page);
02653     if (text)
02654     {
02655         gtk_widget_set_tooltip_text(event_box, text);
02656     }
02657 
02658     entry = gtk_entry_new();
02659     gtk_widget_hide (entry);
02660     gtk_box_pack_start (GTK_BOX (tab_hbox), entry, TRUE, TRUE, 0);
02661     g_signal_connect(G_OBJECT(entry), "activate",
02662                      G_CALLBACK(gnc_main_window_tab_entry_activate), page);
02663     g_signal_connect(G_OBJECT(entry), "focus-out-event",
02664                      G_CALLBACK(gnc_main_window_tab_entry_focus_out_event),
02665                      page);
02666     g_signal_connect(G_OBJECT(entry), "key-press-event",
02667                      G_CALLBACK(gnc_main_window_tab_entry_key_press_event),
02668                      page);
02669     g_signal_connect(G_OBJECT(entry), "editing-done",
02670                      G_CALLBACK(gnc_main_window_tab_entry_editing_done),
02671                      page);
02672 
02673     /* Add close button - Not for immutable pages */
02674     if (!g_object_get_data (G_OBJECT (page), PLUGIN_PAGE_IMMUTABLE))
02675     {
02676         GtkWidget *close_image, *close_button;
02677         GtkRequisition requisition;
02678 
02679         close_button = gtk_button_new();
02680         gtk_button_set_relief(GTK_BUTTON(close_button), GTK_RELIEF_NONE);
02681         close_image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
02682         gtk_widget_show(close_image);
02683         gtk_widget_size_request(close_image, &requisition);
02684         gtk_widget_set_size_request(close_button, requisition.width + 4,
02685                                     requisition.height + 2);
02686         gtk_button_set_alignment(GTK_BUTTON(close_button), 0.5, 0.5);
02687         gtk_container_add(GTK_CONTAINER(close_button), close_image);
02688         if (gnc_gconf_get_bool(GCONF_GENERAL, KEY_SHOW_CLOSE_BUTTON, NULL))
02689             gtk_widget_show (close_button);
02690         else
02691             gtk_widget_hide (close_button);
02692 
02693         g_signal_connect_swapped (G_OBJECT (close_button), "clicked",
02694                                   G_CALLBACK(gnc_main_window_close_page), page);
02695 
02696         gtk_box_pack_start (GTK_BOX (tab_hbox), close_button, FALSE, FALSE, 0);
02697 
02698         g_object_set_data (G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON, close_button);
02699     }
02700 
02701     /*
02702      * The popup menu
02703      */
02704     label = gtk_label_new (gnc_plugin_page_get_page_name(page));
02705 
02706     /*
02707      * Now install it all in the window.
02708      */
02709     gnc_main_window_connect(window, page, event_box, label);
02710     LEAVE("");
02711 }
02712 
02713 
02714 /*  Remove a data plugin page from a window and display the previous
02715  *  page.  If the page removed was the last page in the window, and
02716  *  there is more than one window open, then the entire window will be
02717  *  destroyed.
02718  */
02719 void
02720 gnc_main_window_close_page (GncPluginPage *page)
02721 {
02722     GncMainWindow *window;
02723     GncMainWindowPrivate *priv;
02724 
02725     if (!page || !page->notebook_page)
02726         return;
02727 
02728     if (!gnc_plugin_page_finish_pending(page))
02729         return;
02730 
02731     if (!GNC_IS_MAIN_WINDOW (page->window))
02732         return;
02733 
02734     window = GNC_MAIN_WINDOW (page->window);
02735     if (!window)
02736     {
02737         g_warning("Page is not in a window.");
02738         return;
02739     }
02740 
02741     gnc_main_window_disconnect(window, page);
02742     gnc_plugin_page_destroy_widget (page);
02743     g_object_unref(page);
02744 
02745     /* If this isn't the last window, go ahead and destroy the window. */
02746     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
02747     if (priv->installed_pages == NULL)
02748     {
02749         if (g_list_length(active_windows) > 1)
02750         {
02751             gtk_widget_destroy(GTK_WIDGET(window));
02752         }
02753     }
02754 }
02755 
02756 
02757 /*  Retrieve a pointer to the page that is currently at the front of
02758  *  the specified window.  Any plugin that needs to manipulate its
02759  *  menus based upon the currently selected menu page should connect
02760  *  to the "page_changed" signal on a window.  The callback function
02761  *  from that signal can then call this function to obtain a pointer
02762  *  to the current page.
02763  */
02764 GncPluginPage *
02765 gnc_main_window_get_current_page (GncMainWindow *window)
02766 {
02767     GncMainWindowPrivate *priv;
02768 
02769     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
02770     return priv->current_page;
02771 }
02772 
02773 
02774 /*  Manually add a set of actions to the specified window.  Plugins
02775  *  whose user interface is not hard coded (e.g. the menu-additions
02776  *  plugin) must create their actions at run time, then use this
02777  *  function to install them into the window.
02778  */
02779 void
02780 gnc_main_window_manual_merge_actions (GncMainWindow *window,
02781                                       const gchar *group_name,
02782                                       GtkActionGroup *group,
02783                                       guint merge_id)
02784 {
02785     GncMainWindowPrivate *priv;
02786     MergedActionEntry *entry;
02787 
02788     g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
02789     g_return_if_fail (group_name != NULL);
02790     g_return_if_fail (GTK_IS_ACTION_GROUP(group));
02791     g_return_if_fail (merge_id > 0);
02792 
02793     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
02794     entry = g_new0 (MergedActionEntry, 1);
02795     entry->action_group = group;
02796     entry->merge_id = merge_id;
02797     gtk_ui_manager_ensure_update (window->ui_merge);
02798     g_hash_table_insert (priv->merged_actions_table, g_strdup (group_name), entry);
02799 }
02800 
02801 
02802 /*  Add a set of actions to the specified window.  This function
02803  *  should not need to be called directly by plugin implementors.
02804  *  Correctly assigning values to the GncPluginClass fields during
02805  *  plugin initialization will cause this routine to be automatically
02806  *  called.
02807  */
02808 void
02809 gnc_main_window_merge_actions (GncMainWindow *window,
02810                                const gchar *group_name,
02811                                GtkActionEntry *actions,
02812                                guint n_actions,
02813                                GtkToggleActionEntry *toggle_actions,
02814                                guint n_toggle_actions,
02815                                const gchar *filename,
02816                                gpointer user_data)
02817 {
02818     GncMainWindowPrivate *priv;
02819     GncMainWindowActionData *data;
02820     MergedActionEntry *entry;
02821     GError *error = NULL;
02822     gchar *pathname;
02823 
02824     g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
02825     g_return_if_fail (group_name != NULL);
02826     g_return_if_fail (actions != NULL);
02827     g_return_if_fail (n_actions > 0);
02828     g_return_if_fail (filename != NULL);
02829 
02830     pathname = gnc_gnome_locate_ui_file (filename);
02831     if (pathname == NULL)
02832         return;
02833 
02834     data = g_new0 (GncMainWindowActionData, 1);
02835     data->window = window;
02836     data->data = user_data;
02837 
02838     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
02839     entry = g_new0 (MergedActionEntry, 1);
02840     entry->action_group = gtk_action_group_new (group_name);
02841     gnc_gtk_action_group_set_translation_domain (entry->action_group, GETTEXT_PACKAGE);
02842     gtk_action_group_add_actions (entry->action_group, actions, n_actions, data);
02843     if (toggle_actions != NULL && n_toggle_actions > 0)
02844     {
02845         gtk_action_group_add_toggle_actions (entry->action_group,
02846                                              toggle_actions, n_toggle_actions,
02847                                              data);
02848     }
02849     gtk_ui_manager_insert_action_group (window->ui_merge, entry->action_group, 0);
02850     entry->merge_id = gtk_ui_manager_add_ui_from_file (window->ui_merge, pathname, &error);
02851     g_assert(entry->merge_id || error);
02852     if (entry->merge_id)
02853     {
02854         gtk_ui_manager_ensure_update (window->ui_merge);
02855         g_hash_table_insert (priv->merged_actions_table, g_strdup (group_name), entry);
02856     }
02857     else
02858     {
02859         g_critical("Failed to load ui file.\n  Filename %s\n  Error %s",
02860                    filename, error->message);
02861         g_error_free(error);
02862         g_free(entry);
02863     }
02864     g_free(pathname);
02865 }
02866 
02867 
02868 /*  Remove a set of actions from the specified window.  This function
02869  *  should not need to be called directly by plugin implementors.  It
02870  *  will automatically be called when a plugin is removed from a
02871  *  window.
02872  */
02873 void
02874 gnc_main_window_unmerge_actions (GncMainWindow *window,
02875                                  const gchar *group_name)
02876 {
02877     GncMainWindowPrivate *priv;
02878     MergedActionEntry *entry;
02879 
02880     g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
02881     g_return_if_fail (group_name != NULL);
02882 
02883     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
02884     if (priv->merged_actions_table == NULL)
02885         return;
02886     entry = g_hash_table_lookup (priv->merged_actions_table, group_name);
02887 
02888     if (entry == NULL)
02889         return;
02890 
02891     gtk_ui_manager_remove_action_group (window->ui_merge, entry->action_group);
02892     gtk_ui_manager_remove_ui (window->ui_merge, entry->merge_id);
02893     gtk_ui_manager_ensure_update (window->ui_merge);
02894 
02895     g_hash_table_remove (priv->merged_actions_table, group_name);
02896 }
02897 
02898 
02899 /*  Force a full update of the user interface for the specified
02900  *  window.  This can be an expensive function, but is needed because
02901  *  the gtk ui manager doesn't always seem to update properly when
02902  *  actions are changed.
02903  */
02904 void
02905 gnc_main_window_actions_updated (GncMainWindow *window)
02906 {
02907     GtkActionGroup *force;
02908 
02909     g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
02910 
02911     /* Unfortunately gtk_ui_manager_ensure_update doesn't work
02912      * here.  Force a full update by adding and removing an empty
02913      * action group.
02914      */
02915     force = gtk_action_group_new("force_update");
02916     gtk_ui_manager_insert_action_group (window->ui_merge, force, 0);
02917     gtk_ui_manager_ensure_update (window->ui_merge);
02918     gtk_ui_manager_remove_action_group (window->ui_merge, force);
02919     g_object_unref(force);
02920 }
02921 
02922 
02923 GtkAction *
02924 gnc_main_window_find_action (GncMainWindow *window, const gchar *name)
02925 {
02926     GtkAction *action = NULL;
02927     const GList *groups, *tmp;
02928 
02929     groups = gtk_ui_manager_get_action_groups(window->ui_merge);
02930     for (tmp = groups; tmp; tmp = g_list_next(tmp))
02931     {
02932         action = gtk_action_group_get_action(GTK_ACTION_GROUP(tmp->data), name);
02933         if (action)
02934             break;
02935     }
02936     return action;
02937 }
02938 
02939 
02940 /*  Retrieve a specific set of user interface actions from a window.
02941  *  This function can be used to get an group of action to be
02942  *  manipulated when the front page of a window has changed.
02943  */
02944 GtkActionGroup *
02945 gnc_main_window_get_action_group (GncMainWindow *window,
02946                                   const gchar *group_name)
02947 {
02948     GncMainWindowPrivate *priv;
02949     MergedActionEntry *entry;
02950 
02951     g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), NULL);
02952     g_return_val_if_fail (group_name != NULL, NULL);
02953 
02954     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
02955     if (priv->merged_actions_table == NULL)
02956         return NULL;
02957     entry = g_hash_table_lookup (priv->merged_actions_table, group_name);
02958 
02959     if (entry == NULL)
02960         return NULL;
02961 
02962     return entry->action_group;
02963 }
02964 
02965 
02966 static void
02967 gnc_main_window_update_toolbar (GncMainWindow *window)
02968 {
02969     GtkToolbarStyle style;
02970     GSList *list;
02971 
02972     ENTER("window %p", window);
02973 
02974     style = gnc_get_toolbar_style();
02975     list = gtk_ui_manager_get_toplevels(window->ui_merge, GTK_UI_MANAGER_TOOLBAR);
02976     g_slist_foreach(list, (GFunc)gtk_toolbar_set_style, GINT_TO_POINTER(style));
02977     g_slist_free(list);
02978     LEAVE("");
02979 }
02980 
02981 static void
02982 gnc_main_window_update_tab_position (GncMainWindow *window)
02983 {
02984     GtkPositionType position = GTK_POS_TOP;
02985     gchar *conf_string;
02986     GncMainWindowPrivate *priv;
02987 
02988     ENTER ("window %p", window);
02989     conf_string = gnc_gconf_get_string (GCONF_GENERAL,
02990                                         KEY_TAB_POSITION, NULL);
02991     if (conf_string)
02992     {
02993         position = gnc_enum_from_nick (GTK_TYPE_POSITION_TYPE,
02994                                        conf_string, GTK_POS_TOP);
02995         g_free (conf_string);
02996     }
02997 
02998     priv = GNC_MAIN_WINDOW_GET_PRIVATE (window);
02999     gtk_notebook_set_tab_pos (GTK_NOTEBOOK (priv->notebook), position);
03000 
03001     LEAVE ("");
03002 }
03003 
03004 /*
03005  * Based on code from Epiphany (src/ephy-window.c)
03006  */
03007 static void
03008 gnc_main_window_update_edit_actions_sensitivity (GncMainWindow *window, gboolean hide)
03009 {
03010     GncMainWindowPrivate *priv;
03011     GncPluginPage *page;
03012     GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
03013     GtkAction *action;
03014     gboolean can_copy = FALSE, can_cut = FALSE, can_paste = FALSE;
03015 
03016     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
03017     page = priv->current_page;
03018     if (page && GNC_PLUGIN_PAGE_GET_CLASS(page)->update_edit_menu_actions)
03019     {
03020         (GNC_PLUGIN_PAGE_GET_CLASS(page)->update_edit_menu_actions)(page, hide);
03021         return;
03022     }
03023 
03024     if (GTK_IS_EDITABLE (widget))
03025     {
03026         gboolean has_selection;
03027 
03028         has_selection = gtk_editable_get_selection_bounds
03029                         (GTK_EDITABLE (widget), NULL, NULL);
03030 
03031         can_copy = has_selection;
03032         can_cut = has_selection;
03033         can_paste = TRUE;
03034     }
03035     else if (GTK_IS_TEXT_VIEW (widget))
03036     {
03037         gboolean has_selection;
03038         GtkTextBuffer *text_buffer;
03039 
03040         text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
03041         has_selection = gtk_text_buffer_get_selection_bounds
03042                         (text_buffer, NULL, NULL);
03043 
03044         can_copy = has_selection;
03045         can_cut = has_selection;
03046         can_paste = TRUE;
03047     }
03048     else
03049     {
03050 #ifdef ORIGINAL_EPIPHANY_CODE
03051         /* For now we assume all actions are possible */
03052         can_copy = can_cut = can_paste = TRUE;
03053 #else
03054         /* If its not a GtkEditable, we don't know what to do
03055          * with it. */
03056         can_copy = can_cut = can_paste = FALSE;
03057 #endif
03058     }
03059 
03060     action = gnc_main_window_find_action (window, "EditCopyAction");
03061     gtk_action_set_sensitive (action, can_copy);
03062     gtk_action_set_visible (action, !hide || can_copy);
03063     action = gnc_main_window_find_action (window, "EditCutAction");
03064     gtk_action_set_sensitive (action, can_cut);
03065     gtk_action_set_visible (action, !hide || can_cut);
03066     action = gnc_main_window_find_action (window, "EditPasteAction");
03067     gtk_action_set_sensitive (action, can_paste);
03068     gtk_action_set_visible (action,  !hide || can_paste);
03069 }
03070 
03071 static void
03072 gnc_main_window_enable_edit_actions_sensitivity (GncMainWindow *window)
03073 {
03074     GtkAction *action;
03075 
03076     action = gnc_main_window_find_action (window, "EditCopyAction");
03077     gtk_action_set_sensitive (action, TRUE);
03078     gtk_action_set_visible (action, TRUE);
03079     action = gnc_main_window_find_action (window, "EditCutAction");
03080     gtk_action_set_sensitive (action, TRUE);
03081     gtk_action_set_visible (action, TRUE);
03082     action = gnc_main_window_find_action (window, "EditPasteAction");
03083     gtk_action_set_sensitive (action, TRUE);
03084     gtk_action_set_visible (action, TRUE);
03085 }
03086 
03087 static void
03088 gnc_main_window_edit_menu_show_cb (GtkWidget *menu,
03089                                    GncMainWindow *window)
03090 {
03091     gnc_main_window_update_edit_actions_sensitivity (window, FALSE);
03092 }
03093 
03094 static void
03095 gnc_main_window_edit_menu_hide_cb (GtkWidget *menu,
03096                                    GncMainWindow *window)
03097 {
03098     gnc_main_window_enable_edit_actions_sensitivity (window);
03099 }
03100 
03101 static void
03102 gnc_main_window_init_menu_updaters (GncMainWindow *window)
03103 {
03104     GtkWidget *edit_menu_item, *edit_menu;
03105 
03106     edit_menu_item = gtk_ui_manager_get_widget
03107                      (window->ui_merge, "/menubar/Edit");
03108     edit_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (edit_menu_item));
03109 
03110     g_signal_connect (edit_menu, "show",
03111                       G_CALLBACK (gnc_main_window_edit_menu_show_cb), window);
03112     g_signal_connect (edit_menu, "hide",
03113                       G_CALLBACK (gnc_main_window_edit_menu_hide_cb), window);
03114 }
03115 
03116 static void
03117 gnc_main_window_gconf_changed (GConfClient *client,
03118                                guint cnxn_id,
03119                                GConfEntry *entry,
03120                                gpointer user_data)
03121 {
03122     GncMainWindow *window;
03123     GConfValue *value;
03124     const gchar *key, *key_tail;
03125 
03126     window = GNC_MAIN_WINDOW(user_data);
03127 
03128     key = gconf_entry_get_key(entry);
03129     value = gconf_entry_get_value(entry);
03130     if (!key || !value)
03131         return;
03132 
03133     key_tail = strrchr(key, '/');
03134     if (key_tail != NULL)
03135         key_tail++;
03136     if (strcmp(key_tail, KEY_TOOLBAR_STYLE) == 0)
03137     {
03138         gnc_main_window_update_toolbar(window);
03139     }
03140     else if (strcmp(key_tail, KEY_TAB_POSITION) == 0)
03141     {
03142         gnc_main_window_update_tab_position(window);
03143     }
03144 }
03145 
03146 /* CS: This callback functions will set the statusbar text to the
03147  * "tooltip" property of the currently selected GtkAction.
03148  *
03149  * This code is directly copied from gtk+/test/testmerge.c.
03150  * Thanks to (L)GPL! */
03151 typedef struct _ActionStatus ActionStatus;
03152 struct _ActionStatus
03153 {
03154     GtkAction *action;
03155     GtkWidget *statusbar;
03156 };
03157 
03158 static void
03159 action_status_destroy (gpointer data)
03160 {
03161     ActionStatus *action_status = data;
03162 
03163     g_object_unref (action_status->action);
03164     g_object_unref (action_status->statusbar);
03165 
03166     g_free (action_status);
03167 }
03168 
03169 static void
03170 set_tip (GtkWidget *widget)
03171 {
03172     ActionStatus *data;
03173     gchar *tooltip;
03174 
03175     data = g_object_get_data (G_OBJECT (widget), "action-status");
03176 
03177     if (data)
03178     {
03179         g_object_get (data->action, "tooltip", &tooltip, NULL);
03180 
03181         gtk_statusbar_push (GTK_STATUSBAR (data->statusbar), 0,
03182                             tooltip ? tooltip : "");
03183 
03184         g_free (tooltip);
03185     }
03186 }
03187 
03188 static void
03189 unset_tip (GtkWidget *widget)
03190 {
03191     ActionStatus *data;
03192 
03193     data = g_object_get_data (G_OBJECT (widget), "action-status");
03194 
03195     if (data)
03196         gtk_statusbar_pop (GTK_STATUSBAR (data->statusbar), 0);
03197 }
03198 
03199 static void
03200 connect_proxy (GtkUIManager *merge,
03201                GtkAction    *action,
03202                GtkWidget    *proxy,
03203                GtkWidget    *statusbar)
03204 {
03205     if (GTK_IS_MENU_ITEM (proxy))
03206     {
03207         ActionStatus *data;
03208 
03209         data = g_object_get_data (G_OBJECT (proxy), "action-status");
03210         if (data)
03211         {
03212             g_object_unref (data->action);
03213             g_object_unref (data->statusbar);
03214 
03215             data->action = g_object_ref (action);
03216             data->statusbar = g_object_ref (statusbar);
03217         }
03218         else
03219         {
03220             data = g_new0 (ActionStatus, 1);
03221 
03222             data->action = g_object_ref (action);
03223             data->statusbar = g_object_ref (statusbar);
03224 
03225             g_object_set_data_full (G_OBJECT (proxy), "action-status",
03226                                     data, action_status_destroy);
03227 
03228             g_signal_connect (proxy, "select",  G_CALLBACK (set_tip), NULL);
03229             g_signal_connect (proxy, "deselect", G_CALLBACK (unset_tip), NULL);
03230         }
03231     }
03232 }
03233 /* CS: end copied code from gtk+/test/testmerge.c */
03234 
03235 static void
03236 gnc_main_window_window_menu (GncMainWindow *window)
03237 {
03238     GncMainWindowPrivate *priv;
03239     guint merge_id;
03240 #ifdef MAC_INTEGRATION
03241     gchar *filename = gnc_gnome_locate_ui_file("gnc-windows-menu-ui-quartz.xml");
03242 #else
03243     gchar *filename = gnc_gnome_locate_ui_file("gnc-windows-menu-ui.xml");
03244 #endif
03245     GError *error = NULL;
03246     g_assert(filename);
03247     merge_id = gtk_ui_manager_add_ui_from_file(window->ui_merge, filename,
03248                &error);
03249     g_free(filename);
03250     g_assert(merge_id);
03251 #ifndef MAC_INTEGRATION
03252     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
03253     gtk_action_group_add_radio_actions (priv->action_group,
03254                                         radio_entries, n_radio_entries,
03255                                         0,
03256                                         G_CALLBACK(gnc_main_window_cmd_window_raise),
03257                                         window);
03258 #endif
03259 };
03260 
03261 static void
03262 gnc_main_window_setup_window (GncMainWindow *window)
03263 {
03264     GncMainWindowPrivate *priv;
03265     GtkWidget *main_vbox;
03266     guint merge_id;
03267     GncPluginManager *manager;
03268     GList *plugins;
03269     GError *error = NULL;
03270     gchar *filename;
03271 
03272     ENTER(" ");
03273 
03274     /* Catch window manager delete signal */
03275     g_signal_connect (G_OBJECT (window), "delete-event",
03276                       G_CALLBACK (gnc_main_window_delete_event), window);
03277 
03278     /* Create widgets and add them to the window */
03279     main_vbox = gtk_vbox_new (FALSE, 0);
03280     gtk_widget_show (main_vbox);
03281     gtk_container_add (GTK_CONTAINER (window), main_vbox);
03282 
03283     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
03284     priv->menu_dock = gtk_vbox_new (FALSE, 0);
03285     gtk_widget_show (priv->menu_dock);
03286     gtk_box_pack_start (GTK_BOX (main_vbox), priv->menu_dock,
03287                         FALSE, TRUE, 0);
03288 
03289     priv->notebook = gtk_notebook_new ();
03290     g_object_set(G_OBJECT(priv->notebook),
03291                  "scrollable", TRUE,
03292                  "enable-popup", TRUE,
03293                  (char *)NULL);
03294     gtk_widget_show (priv->notebook);
03295     g_signal_connect (G_OBJECT (priv->notebook), "switch-page",
03296                       G_CALLBACK (gnc_main_window_switch_page), window);
03297     g_signal_connect (G_OBJECT (priv->notebook), "page-reordered",
03298                       G_CALLBACK (gnc_main_window_page_reordered), window);
03299     gtk_box_pack_start (GTK_BOX (main_vbox), priv->notebook,
03300                         TRUE, TRUE, 0);
03301 
03302     priv->statusbar = gtk_statusbar_new ();
03303     gtk_widget_show (priv->statusbar);
03304     gtk_box_pack_start (GTK_BOX (main_vbox), priv->statusbar,
03305                         FALSE, TRUE, 0);
03306     gtk_statusbar_set_has_resize_grip( GTK_STATUSBAR(priv->statusbar), TRUE );
03307 
03308     priv->progressbar = gtk_progress_bar_new ();
03309     gtk_progress_bar_set_text(GTK_PROGRESS_BAR(priv->progressbar), " ");
03310     gtk_widget_show (priv->progressbar);
03311     gtk_box_pack_start (GTK_BOX (priv->statusbar), priv->progressbar,
03312                         FALSE, TRUE, 0);
03313     gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(priv->progressbar),
03314                                     0.01);
03315 
03316     window->ui_merge = gtk_ui_manager_new ();
03317 
03318     /* Create menu and toolbar information */
03319     priv->action_group = gtk_action_group_new ("MainWindowActions");
03320     gnc_gtk_action_group_set_translation_domain (priv->action_group, GETTEXT_PACKAGE);
03321     gtk_action_group_add_actions (priv->action_group, gnc_menu_actions,
03322                                   gnc_menu_n_actions, window);
03323     gtk_action_group_add_toggle_actions (priv->action_group,
03324                                          toggle_actions, n_toggle_actions,
03325                                          window);
03326     gnc_plugin_update_actions(priv->action_group,
03327                               initially_insensitive_actions,
03328                               "sensitive", FALSE);
03329     gnc_plugin_update_actions(priv->action_group,
03330                               always_insensitive_actions,
03331                               "sensitive", FALSE);
03332     gnc_plugin_update_actions(priv->action_group,
03333                               always_hidden_actions,
03334                               "visible", FALSE);
03335     gnc_plugin_set_important_actions (priv->action_group,
03336                                       gnc_menu_important_actions);
03337     gtk_ui_manager_insert_action_group (window->ui_merge, priv->action_group, 0);
03338 
03339     g_signal_connect (G_OBJECT (window->ui_merge), "add_widget",
03340                       G_CALLBACK (gnc_main_window_add_widget), window);
03341     /* Use the "connect-proxy" signal for tooltip display in the
03342        status bar */
03343     g_signal_connect (G_OBJECT (window->ui_merge), "connect-proxy",
03344                       G_CALLBACK (connect_proxy), priv->statusbar);
03345 
03346     filename = gnc_gnome_locate_ui_file("gnc-main-window-ui.xml");
03347 
03348     /* Can't do much without a ui. */
03349     g_assert (filename);
03350 
03351     merge_id = gtk_ui_manager_add_ui_from_file (window->ui_merge,
03352                filename, &error);
03353     g_assert(merge_id || error);
03354     if (merge_id)
03355     {
03356         gtk_window_add_accel_group (GTK_WINDOW (window),
03357                                     gtk_ui_manager_get_accel_group(window->ui_merge));
03358         gtk_ui_manager_ensure_update (window->ui_merge);
03359     }
03360     else
03361     {
03362         g_critical("Failed to load ui file.\n  Filename %s\n  Error %s",
03363                    filename, error->message);
03364         g_error_free(error);
03365         g_assert(merge_id != 0);
03366     }
03367     g_free(filename);
03368     gnc_main_window_window_menu(window);
03369     gnc_gconf_add_notification(G_OBJECT(window), GCONF_GENERAL,
03370                                gnc_main_window_gconf_changed,
03371                                GNC_MAIN_WINDOW_NAME);
03372     gnc_gconf_add_notification(G_OBJECT(window), DESKTOP_GNOME_INTERFACE,
03373                                gnc_main_window_gconf_changed,
03374                                GNC_MAIN_WINDOW_NAME);
03375     gnc_main_window_update_toolbar(window);
03376     gnc_main_window_update_tab_position(window);
03377 
03378     gnc_main_window_init_menu_updaters(window);
03379 
03380     /* Testing */
03381     /* Now update the "eXtensions" menu */
03382     if (!gnc_is_extra_enabled())
03383     {
03384         GtkAction*  action;
03385 
03386         action = gtk_action_group_get_action(priv->action_group,
03387                                              "ExtensionsAction");
03388         gtk_action_set_visible(action, FALSE);
03389     }
03390 
03391     /* GncPluginManager stuff */
03392     manager = gnc_plugin_manager_get ();
03393     plugins = gnc_plugin_manager_get_plugins (manager);
03394     g_list_foreach (plugins, gnc_main_window_add_plugin, window);
03395     g_list_free (plugins);
03396 
03397     g_signal_connect (G_OBJECT (manager), "plugin-added",
03398                       G_CALLBACK (gnc_main_window_plugin_added), window);
03399     g_signal_connect (G_OBJECT (manager), "plugin-removed",
03400                       G_CALLBACK (gnc_main_window_plugin_removed), window);
03401 
03402     LEAVE(" ");
03403 }
03404 
03405 #ifdef MAC_INTEGRATION
03406 /* Event handlers for the shutdown process.  Gnc_quartz_shutdown is
03407  * connected to NSApplicationWillTerminate, the last chance to do
03408  * anything before quitting. The problem is that it's launched from a
03409  * CFRunLoop, not a g_main_loop, and if we call anything that would
03410  * affect the main_loop we get an assert that we're in a subidiary
03411  * loop.
03412  */
03413 static void
03414 gnc_quartz_shutdown (GtkOSXApplication *theApp, gpointer data)
03415 {
03416     /* Do Nothing. It's too late. */
03417 }
03418 /* Should quit responds to NSApplicationBlockTermination; returning
03419  * TRUE means "don't terminate", FALSE means "do terminate". If we
03420  * decide that it's OK to terminate, then we queue a gnc_shutdown for
03421  * the next idle time (because we're not running in the main loop) and
03422  * then tell the OS not to terminate. That gives the gnc_shutdown an
03423  * opportunity to shut down.
03424  */
03425 static gboolean
03426 gnc_quartz_should_quit (GtkOSXApplication *theApp, GncMainWindow *window)
03427 {
03428     QofSession *session;
03429     gboolean needs_save;
03430 
03431     if (!gnc_main_window_all_finish_pending() ||
03432             gnc_file_save_in_progress())
03433     {
03434         return TRUE;
03435     }
03436     session = gnc_get_current_session();
03437     needs_save = qof_book_session_not_saved(qof_session_get_book(session)) &&
03438                  !gnc_file_save_in_progress();
03439     if (needs_save && gnc_main_window_prompt_for_save(GTK_WIDGET(window)))
03440         return TRUE;
03441 
03442     g_timeout_add(250, gnc_main_window_timed_quit, NULL);
03443     return TRUE;
03444 }
03445 
03446 static void
03447 gnc_quartz_set_menu(GncMainWindow* window)
03448 {
03449     GtkOSXApplicationMenuGroup *group;
03450     GtkOSXApplication *theApp = g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
03451     GtkWidget       *menu;
03452     GtkWidget       *item;
03453 
03454     menu = gtk_ui_manager_get_widget (window->ui_merge, "/menubar");
03455     if (GTK_IS_MENU_ITEM (menu))
03456         menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
03457     gtk_widget_hide(menu);
03458     gtk_osxapplication_set_menu_bar (theApp, GTK_MENU_SHELL (menu));
03459 
03460     item = gtk_ui_manager_get_widget (window->ui_merge,
03461                                       "/menubar/File/FileQuit");
03462     if (GTK_IS_MENU_ITEM (item))
03463         gtk_widget_hide (GTK_WIDGET (item));
03464 
03465     item = gtk_ui_manager_get_widget (window->ui_merge,
03466                                       "/menubar/Edit/EditPreferences");
03467     if (GTK_IS_MENU_ITEM (item))
03468         gtk_osxapplication_insert_app_menu_item (theApp, GTK_WIDGET (item), 0);
03469 
03470     item = gtk_ui_manager_get_widget (window->ui_merge,
03471                                       "/menubar/Help/HelpAbout");
03472     if (GTK_IS_MENU_ITEM (item))
03473     {
03474         gtk_osxapplication_insert_app_menu_item (theApp,
03475                 gtk_separator_menu_item_new (),
03476                 0);
03477         gtk_osxapplication_insert_app_menu_item (theApp, GTK_WIDGET (item), 0);
03478     }
03479 
03480     item = gtk_ui_manager_get_widget (window->ui_merge,
03481                                       "/menubar/Help");
03482     gtk_osxapplication_set_help_menu(theApp, GTK_MENU_ITEM(item));
03483     item = gtk_ui_manager_get_widget (window->ui_merge,
03484                                       "/menubar/Windows");
03485     gtk_osxapplication_set_window_menu(theApp, GTK_MENU_ITEM(item));
03486     g_signal_connect(theApp, "NSApplicationBlockTermination",
03487                      G_CALLBACK(gnc_quartz_should_quit), window);
03488 
03489 }
03490 #endif //MAC_INTEGRATION
03491 
03492 /* Callbacks */
03493 static void
03494 gnc_main_window_add_widget (GtkUIManager *merge,
03495                             GtkWidget *widget,
03496                             GncMainWindow *window)
03497 {
03498     GncMainWindowPrivate *priv;
03499 
03500     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
03501     if (GTK_IS_TOOLBAR (widget))
03502     {
03503         priv->toolbar = widget;
03504     }
03505 
03506     gtk_box_pack_start (GTK_BOX (priv->menu_dock), widget, FALSE, FALSE, 0);
03507     gtk_widget_show (widget);
03508 }
03509 
03522 static gboolean
03523 gnc_main_window_show_summarybar (GncMainWindow *window, GtkAction *action)
03524 {
03525     GncMainWindowPrivate *priv;
03526 
03527     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
03528     if (action == NULL)
03529         action = gtk_action_group_get_action(priv->action_group,
03530                                              "ViewSummaryAction");
03531     if (action == NULL)
03532         return TRUE;
03533     return gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
03534 }
03535 
03545 static void
03546 gnc_main_window_switch_page (GtkNotebook *notebook,
03547                              GtkNotebookPage *notebook_page,
03548                              gint pos,
03549                              GncMainWindow *window)
03550 {
03551     GncMainWindowPrivate *priv;
03552     GtkWidget *child;
03553     GncPluginPage *page;
03554     gboolean immutable, visible;
03555 
03556     ENTER("Notebook %p, page, %p, index %d, window %p",
03557           notebook, notebook_page, pos, window);
03558     g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
03559 
03560     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
03561     if (priv->current_page != NULL)
03562     {
03563         page = priv->current_page;
03564         gnc_plugin_page_unmerge_actions (page, window->ui_merge);
03565         gnc_plugin_page_unselected (page);
03566     }
03567 
03568     child = gtk_notebook_get_nth_page (notebook, pos);
03569     if (child)
03570     {
03571         page = g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL);
03572     }
03573     else
03574     {
03575         page = NULL;
03576     }
03577 
03578     priv->current_page = page;
03579 
03580     if (page != NULL)
03581     {
03582         /* Update the user interface (e.g. menus and toolbars */
03583         gnc_plugin_page_merge_actions (page, window->ui_merge);
03584         visible = gnc_main_window_show_summarybar(window, NULL);
03585         gnc_plugin_page_show_summarybar (page, visible);
03586 
03587         /* Allow page specific actions */
03588         gnc_plugin_page_selected (page);
03589         gnc_window_update_status (GNC_WINDOW(window), page);
03590 
03591         /* Update the page reference info */
03592         priv->usage_order = g_list_remove (priv->usage_order, page);
03593         priv->usage_order = g_list_prepend (priv->usage_order, page);
03594     }
03595 
03596     /* Update the menus based upon whether this is an "immutable" page. */
03597     immutable = page &&
03598                 g_object_get_data (G_OBJECT (page), PLUGIN_PAGE_IMMUTABLE);
03599     gnc_plugin_update_actions(priv->action_group,
03600                               immutable_page_actions,
03601                               "sensitive", !immutable);
03602     gnc_plugin_update_actions(priv->action_group,
03603                               multiple_page_actions,
03604                               "sensitive",
03605                               g_list_length(priv->installed_pages) > 1);
03606 
03607     gnc_main_window_update_title(window);
03608 #ifndef MAC_INTEGRATION
03609     gnc_main_window_update_menu_item(window);
03610 #endif
03611     g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, page);
03612     LEAVE(" ");
03613 }
03614 
03621 static void
03622 gnc_main_window_page_reordered (GtkNotebook *notebook,
03623                                 GtkWidget *child,
03624                                 guint pos,
03625                                 GncMainWindow *window)
03626 {
03627     GncMainWindowPrivate *priv;
03628     GncPluginPage *page;
03629     GList *old_link;
03630 
03631     ENTER("Notebook %p, child %p, index %d, window %p",
03632           notebook, child, pos, window);
03633     g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
03634 
03635     if (!child) return;
03636 
03637     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
03638 
03639     page = g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL);
03640     if (!page) return;
03641 
03642     old_link = g_list_find (priv->installed_pages, page);
03643     if (!old_link) return;
03644 
03645     priv->installed_pages = g_list_delete_link (priv->installed_pages,
03646                             old_link);
03647     priv->installed_pages = g_list_insert (priv->installed_pages,
03648                                            page, pos);
03649 
03650     LEAVE(" ");
03651 }
03652 
03653 static void
03654 gnc_main_window_plugin_added (GncPlugin *manager,
03655                               GncPlugin *plugin,
03656                               GncMainWindow *window)
03657 {
03658     g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
03659     g_return_if_fail (GNC_IS_PLUGIN (plugin));
03660 
03661     gnc_plugin_add_to_window (plugin, window, window_type);
03662 }
03663 
03664 static void
03665 gnc_main_window_plugin_removed (GncPlugin *manager,
03666                                 GncPlugin *plugin,
03667                                 GncMainWindow *window)
03668 {
03669     g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
03670     g_return_if_fail (GNC_IS_PLUGIN (plugin));
03671 
03672     gnc_plugin_remove_from_window (plugin, window, window_type);
03673 }
03674 
03675 
03676 /* Command callbacks */
03677 static void
03678 gnc_main_window_cmd_page_setup (GtkAction *action,
03679                                 GncMainWindow *window)
03680 {
03681     GtkWindow *gtk_window;
03682 
03683     g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
03684 
03685     gtk_window = gnc_window_get_gtk_window(GNC_WINDOW(window));
03686     gnc_ui_page_setup(gtk_window);
03687 }
03688 
03689 static void
03690 gnc_main_window_cmd_file_properties (GtkAction *action, GncMainWindow *window)
03691 {
03692     SCM func = scm_c_eval_string("gnc:main-window-properties-cb");
03693     if (!scm_is_procedure (func))
03694     {
03695         PERR ("not a procedure\n");
03696         return;
03697     }
03698     scm_call_0(func);
03699 }
03700 
03701 static void
03702 gnc_main_window_cmd_file_close (GtkAction *action, GncMainWindow *window)
03703 {
03704     GncMainWindowPrivate *priv;
03705     GncPluginPage *page;
03706 
03707     g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
03708 
03709     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
03710     page = priv->current_page;
03711     gnc_main_window_close_page(page);
03712 }
03713 
03714 static void
03715 gnc_main_window_cmd_file_quit (GtkAction *action, GncMainWindow *window)
03716 {
03717     if (!gnc_main_window_all_finish_pending())
03718         return;
03719 
03720     gnc_main_window_quit(window);
03721 }
03722 
03723 static void
03724 gnc_main_window_cmd_edit_cut (GtkAction *action, GncMainWindow *window)
03725 {
03726     GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
03727     GtkTextBuffer *text_buffer;
03728     GtkClipboard *clipboard;
03729     gboolean editable;
03730 
03731     if (GTK_IS_EDITABLE (widget))
03732     {
03733         gtk_editable_cut_clipboard (GTK_EDITABLE (widget));
03734     }
03735     else if (GTK_IS_TEXT_VIEW (widget))
03736     {
03737         text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
03738         clipboard = gtk_widget_get_clipboard (GTK_WIDGET(text_buffer),
03739                                               GDK_SELECTION_CLIPBOARD);
03740         editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (widget));
03741         gtk_text_buffer_cut_clipboard (text_buffer, clipboard, editable);
03742     }
03743 }
03744 
03745 static void
03746 gnc_main_window_cmd_edit_copy (GtkAction *action, GncMainWindow *window)
03747 {
03748     GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
03749     GtkTextBuffer *text_buffer;
03750     GtkClipboard *clipboard;
03751 
03752     if (GTK_IS_EDITABLE (widget))
03753     {
03754         gtk_editable_copy_clipboard (GTK_EDITABLE (widget));
03755     }
03756     else if (GTK_IS_TEXT_VIEW (widget))
03757     {
03758         text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
03759         clipboard = gtk_widget_get_clipboard (GTK_WIDGET(text_buffer),
03760                                               GDK_SELECTION_CLIPBOARD);
03761         gtk_text_buffer_copy_clipboard (text_buffer, clipboard);
03762     }
03763 }
03764 
03765 static void
03766 gnc_main_window_cmd_edit_paste (GtkAction *action, GncMainWindow *window)
03767 {
03768     GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
03769     GtkTextBuffer *text_buffer;
03770     GtkClipboard *clipboard;
03771     gboolean editable;
03772 
03773     if (GTK_IS_EDITABLE (widget))
03774     {
03775         gtk_editable_paste_clipboard (GTK_EDITABLE (widget));
03776     }
03777     else if (GTK_IS_TEXT_VIEW (widget))
03778     {
03779         text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
03780         clipboard = gtk_widget_get_clipboard (GTK_WIDGET(text_buffer),
03781                                               GDK_SELECTION_CLIPBOARD);
03782         editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (widget));
03783         gtk_text_buffer_paste_clipboard (text_buffer, clipboard, NULL, FALSE);
03784     }
03785 }
03786 
03787 static void
03788 gnc_main_window_cmd_edit_preferences (GtkAction *action, GncMainWindow *window)
03789 {
03790     gnc_preferences_dialog ();
03791 }
03792 
03793 static void
03794 gnc_main_window_cmd_view_refresh (GtkAction *action, GncMainWindow *window)
03795 {
03796 }
03797 
03798 static void
03799 gnc_main_window_cmd_actions_reset_warnings (GtkAction *action, GncMainWindow *window)
03800 {
03801     gnc_reset_warnings_dialog(GTK_WINDOW(window));
03802 }
03803 
03804 static void
03805 gnc_main_window_cmd_actions_rename_page (GtkAction *action, GncMainWindow *window)
03806 {
03807     GncMainWindowPrivate *priv;
03808     GncPluginPage *page;
03809     GtkWidget *label, *entry;
03810 
03811     ENTER(" ");
03812     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
03813     page = priv->current_page;
03814     if (!page)
03815     {
03816         LEAVE("No current page");
03817         return;
03818     }
03819 
03820     if (!main_window_find_tab_items(window, page, &label, &entry))
03821     {
03822         LEAVE("can't find required widgets");
03823         return;
03824     }
03825 
03826     gtk_entry_set_text(GTK_ENTRY(entry), gtk_label_get_text(GTK_LABEL(label)));
03827     gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
03828     gtk_widget_hide(label);
03829     gtk_widget_show(entry);
03830     gtk_widget_grab_focus(entry);
03831     LEAVE("opened for editing");
03832 }
03833 
03834 static void
03835 gnc_main_window_cmd_view_toolbar (GtkAction *action, GncMainWindow *window)
03836 {
03837     GncMainWindowPrivate *priv;
03838 
03839     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
03840     if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)))
03841     {
03842         gtk_widget_show (priv->toolbar);
03843     }
03844     else
03845     {
03846         gtk_widget_hide (priv->toolbar);
03847     }
03848 }
03849 
03850 static void
03851 gnc_main_window_cmd_view_summary (GtkAction *action, GncMainWindow *window)
03852 {
03853     GncMainWindowPrivate *priv;
03854     GList *item;
03855     gboolean visible;
03856 
03857     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
03858     visible = gnc_main_window_show_summarybar(window, action);
03859     for (item = priv->installed_pages; item; item = g_list_next(item))
03860     {
03861         gnc_plugin_page_show_summarybar(item->data, visible);
03862     }
03863 }
03864 
03865 static void
03866 gnc_main_window_cmd_view_statusbar (GtkAction *action, GncMainWindow *window)
03867 {
03868     GncMainWindowPrivate *priv;
03869 
03870     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
03871     if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)))
03872     {
03873         gtk_widget_show (priv->statusbar);
03874     }
03875     else
03876     {
03877         gtk_widget_hide (priv->statusbar);
03878     }
03879 }
03880 
03881 static void
03882 gnc_main_window_cmd_window_new (GtkAction *action, GncMainWindow *window)
03883 {
03884     GncMainWindow *new_window;
03885 
03886     /* Create the new window */
03887     ENTER(" ");
03888     new_window = gnc_main_window_new ();
03889     gtk_widget_show(GTK_WIDGET(new_window));
03890     LEAVE(" ");
03891 }
03892 
03893 static void
03894 gnc_main_window_cmd_window_move_page (GtkAction *action, GncMainWindow *window)
03895 {
03896     GncMainWindowPrivate *priv, *new_priv;
03897     GncMainWindow *new_window;
03898     GncPluginPage *page;
03899     GtkNotebook *notebook;
03900     GtkWidget *tab_widget, *menu_widget;
03901 
03902     ENTER("action %p,window %p", action, window);
03903 
03904     /* Setup */
03905     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
03906     page = priv->current_page;
03907     if (!page)
03908     {
03909         LEAVE("invalid page");
03910         return;
03911     }
03912     if (!page->notebook_page)
03913     {
03914         LEAVE("invalid notebook_page");
03915         return;
03916     }
03917 
03918     notebook = GTK_NOTEBOOK (priv->notebook);
03919     tab_widget = gtk_notebook_get_tab_label (notebook, page->notebook_page);
03920     menu_widget = gtk_notebook_get_menu_label (notebook, page->notebook_page);
03921 
03922     /* Ref the page components, then remove it from its old window */
03923     g_object_ref(page);
03924     g_object_ref(tab_widget);
03925     g_object_ref(menu_widget);
03926     g_object_ref(page->notebook_page);
03927     gnc_main_window_disconnect(window, page);
03928 
03929     /* Create the new window */
03930     new_window = gnc_main_window_new ();
03931     gtk_widget_show(GTK_WIDGET(new_window));
03932 
03933     /* Now add the page to the new window */
03934     gnc_main_window_connect (new_window, page, tab_widget, menu_widget);
03935 
03936     /* Unref the page components now that we're done */
03937     g_object_unref(page->notebook_page);
03938     g_object_unref(menu_widget);
03939     g_object_unref(tab_widget);
03940     g_object_unref(page);
03941 
03942     /* just a little debugging. :-) */
03943     new_priv = GNC_MAIN_WINDOW_GET_PRIVATE(new_window);
03944     DEBUG("Moved page %p from window %p to new window %p",
03945           page, window, new_window);
03946     DEBUG("Old window current is %p, new window current is %p",
03947           priv->current_page, priv->current_page);
03948 
03949     LEAVE("page moved");
03950 }
03951 
03952 static void
03953 gnc_main_window_cmd_window_raise (GtkAction *action,
03954                                   GtkRadioAction *current,
03955                                   GncMainWindow *old_window)
03956 {
03957     GncMainWindow *new_window;
03958     gint value;
03959 
03960     g_return_if_fail(GTK_IS_ACTION(action));
03961     g_return_if_fail(GTK_IS_RADIO_ACTION(current));
03962     g_return_if_fail(GNC_IS_MAIN_WINDOW(old_window));
03963 
03964     ENTER("action %p, current %p, window %p", action, current, old_window);
03965     value = gtk_radio_action_get_current_value(current);
03966     new_window = g_list_nth_data(active_windows, value);
03967     gtk_window_present(GTK_WINDOW(new_window));
03968 #ifndef MAC_INTEGRATION
03969     /* revert the change in the radio group
03970      * impossible while handling "changed" (G_SIGNAL_NO_RECURSE) */
03971     g_idle_add((GSourceFunc)gnc_main_window_update_radio_button, old_window);
03972 #endif
03973     LEAVE(" ");
03974 }
03975 
03976 static void
03977 gnc_main_window_cmd_help_tutorial (GtkAction *action, GncMainWindow *window)
03978 {
03979     gnc_gnome_help (HF_GUIDE, NULL);
03980 }
03981 
03982 static void
03983 gnc_main_window_cmd_help_contents (GtkAction *action, GncMainWindow *window)
03984 {
03985     gnc_gnome_help (HF_HELP, NULL);
03986 }
03987 
03997 static gchar *
03998 get_file (const gchar *partial)
03999 {
04000     gchar *filename, *text = NULL;
04001 
04002     filename = gnc_gnome_locate_data_file(partial);
04003     g_file_get_contents(filename, &text, NULL, NULL);
04004     g_free(filename);
04005 
04006     /* Anything there? */
04007     if (text && *text)
04008         return text;
04009 
04010     /* Just a empty string or no string at all. */
04011     if (text)
04012         g_free(text);
04013     return NULL;
04014 }
04015 
04016 
04026 static gchar **
04027 get_file_strsplit (const gchar *partial)
04028 {
04029     gchar *text, **lines;
04030 
04031     text = get_file(partial);
04032     if (!text)
04033         return NULL;
04034 
04035     lines = g_strsplit_set(text, "\r\n", -1);
04036     g_free(text);
04037     return lines;
04038 }
04039 
04040 
04047 static void
04048 gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window)
04049 {
04050     const gchar *fixed_message = _("The GnuCash personal finance manager. "
04051                                    "The GNU way to manage your money!");
04052     const gchar *copyright = "© 1997-2010 Contributors";
04053     gchar **authors, **documenters, *license, *message;
04054     GdkPixbuf *logo;
04055 
04056     logo = gnc_gnome_get_gdkpixbuf ("gnucash-icon-48x48.png");
04057 
04058     authors = get_file_strsplit("doc/AUTHORS");
04059     documenters = get_file_strsplit("doc/DOCUMENTERS");
04060     license = get_file("doc/LICENSE");
04061 #ifdef GNUCASH_SVN
04062     /* Development version */
04063     message = g_strdup_printf(_("%s  This copy was built from svn r%s on %s."),
04064                               fixed_message, GNUCASH_SVN_REV, GNUCASH_BUILD_DATE);
04065 #else
04066     message = g_strdup_printf(_("%s  This copy was built from r%s on %s."),
04067                               fixed_message, GNUCASH_SVN_REV, GNUCASH_BUILD_DATE);
04068 #endif
04069     gtk_show_about_dialog
04070     (GTK_WINDOW (window),
04071      "authors", authors,
04072      "documenters", documenters,
04073      "comments", message,
04074      "copyright", copyright,
04075      "license", license,
04076      "logo", logo,
04077      "name", "GnuCash",
04078      "translator-credits", _("translator_credits"),
04079      "version", VERSION,
04080      "website", "http://www.gnucash.org",
04081      (gchar *)NULL);
04082 
04083     g_free(message);
04084     if (license)     g_free(license);
04085     if (documenters) g_strfreev(documenters);
04086     if (authors)     g_strfreev(authors);
04087     g_object_unref (logo);
04088 }
04089 
04090 
04091 /************************************************************
04092  *                                                          *
04093  ************************************************************/
04094 
04095 void
04096 gnc_main_window_show_all_windows(void)
04097 {
04098     GList *window_iter;
04099 #ifdef MAC_INTEGRATION
04100     GtkOSXApplication *theApp = g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
04101 #endif
04102     for (window_iter = active_windows; window_iter != NULL; window_iter = window_iter->next)
04103     {
04104         gtk_widget_show(GTK_WIDGET(window_iter->data));
04105 #ifdef MAC_INTEGRATION
04106         gnc_quartz_set_menu(window_iter->data);
04107 #endif
04108     }
04109 #ifdef MAC_INTEGRATION
04110     g_signal_connect(theApp, "NSApplicationWillTerminate",
04111                      G_CALLBACK(gnc_quartz_shutdown), NULL);
04112     gtk_osxapplication_ready(theApp);
04113 #endif
04114 }
04115 
04120 GtkWidget *
04121 gnc_ui_get_toplevel (void)
04122 {
04123     GList *window;
04124 
04125     for (window = active_windows; window; window = window->next)
04126         if (gtk_window_is_active (GTK_WINDOW (window->data)))
04127             return window->data;
04128 
04129     return NULL;
04130 }
04131 
04132 
04138 static GtkWindow *
04139 gnc_main_window_get_gtk_window (GncWindow *window)
04140 {
04141     g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), NULL);
04142     return GTK_WINDOW(window);
04143 }
04144 
04145 
04151 static GtkWidget *
04152 gnc_main_window_get_statusbar (GncWindow *window_in)
04153 {
04154     GncMainWindowPrivate *priv;
04155     GncMainWindow *window;
04156 
04157     g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), NULL);
04158 
04159     window = GNC_MAIN_WINDOW(window_in);
04160     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
04161     return priv->statusbar;
04162 }
04163 
04164 
04170 static GtkWidget *
04171 gnc_main_window_get_progressbar (GncWindow *window_in)
04172 {
04173     GncMainWindowPrivate *priv;
04174     GncMainWindow *window;
04175 
04176     g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), NULL);
04177 
04178     window = GNC_MAIN_WINDOW(window_in);
04179     priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
04180     return priv->progressbar;
04181 }
04182 
04183 
04184 static void
04185 gnc_main_window_all_ui_set_sensitive (GncWindow *unused, gboolean sensitive)
04186 {
04187     GncMainWindow *window;
04188     GncMainWindowPrivate *priv;
04189     GList *groupp, *groups, *winp, *tmp;
04190     GtkWidget *close_button;
04191 
04192     for (winp = active_windows; winp; winp = g_list_next(winp))
04193     {
04194         window = winp->data;
04195         priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
04196 
04197         groups = gtk_ui_manager_get_action_groups(window->ui_merge);
04198         for (groupp = groups; groupp; groupp = g_list_next(groupp))
04199         {
04200             gtk_action_group_set_sensitive(GTK_ACTION_GROUP(groupp->data), sensitive);
04201         }
04202 
04203         for (tmp = priv->installed_pages; tmp; tmp = g_list_next(tmp))
04204         {
04205             close_button = g_object_get_data(tmp->data, PLUGIN_PAGE_CLOSE_BUTTON);
04206             if (!close_button)
04207                 continue;
04208             gtk_widget_set_sensitive (close_button, sensitive);
04209         }
04210     }
04211 }
04212 
04213 
04218 static void
04219 gnc_window_main_window_init (GncWindowIface *iface)
04220 {
04221     iface->get_gtk_window  = gnc_main_window_get_gtk_window;
04222     iface->get_statusbar   = gnc_main_window_get_statusbar;
04223     iface->get_progressbar = gnc_main_window_get_progressbar;
04224     iface->ui_set_sensitive = gnc_main_window_all_ui_set_sensitive;
04225 }
04226 
04227 
04228 /*  Set the window where all progressbar updates should occur.  This
04229  *  is a wrapper around the gnc_window_set_progressbar_window()
04230  *  function.
04231  */
04232 void
04233 gnc_main_window_set_progressbar_window (GncMainWindow *window)
04234 {
04235     GncWindow *gncwin;
04236     gncwin = GNC_WINDOW(window);
04237     gnc_window_set_progressbar_window(gncwin);
04238 }
04239 
04240 
04253 static void
04254 do_popup_menu(GncPluginPage *page, GdkEventButton *event)
04255 {
04256     GtkUIManager *ui_merge;
04257     GtkWidget *menu;
04258     int button, event_time;
04259 
04260     g_return_if_fail(GNC_IS_PLUGIN_PAGE(page));
04261 
04262     ENTER("page %p, event %p", page, event);
04263     ui_merge = gnc_plugin_page_get_ui_merge(page);
04264     if (ui_merge == NULL)
04265     {
04266         LEAVE("no ui merge");
04267         return;
04268     }
04269 
04270     menu = gtk_ui_manager_get_widget(ui_merge, "/MainPopup");
04271     if (!menu)
04272     {
04273         LEAVE("no menu");
04274         return;
04275     }
04276 
04277     if (event)
04278     {
04279         button = event->button;
04280         event_time = event->time;
04281     }
04282     else
04283     {
04284         button = 0;
04285         event_time = gtk_get_current_event_time ();
04286     }
04287 
04288     gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button, event_time);
04289     LEAVE(" ");
04290 }
04291 
04292 
04306 static gboolean
04307 gnc_main_window_popup_menu_cb (GtkWidget *widget,
04308                                GncPluginPage *page)
04309 {
04310     ENTER("widget %p, page %p", widget, page);
04311     do_popup_menu(page, NULL);
04312     LEAVE(" ");
04313     return TRUE;
04314 }
04315 
04316 
04317 /*  Callback function invoked when the user clicks in the content of
04318  *  any Gnucash window.  If this was a "right-click" then Gnucash will
04319  *  popup the contextual menu.
04320  */
04321 gboolean
04322 gnc_main_window_button_press_cb (GtkWidget *whatever,
04323                                  GdkEventButton *event,
04324                                  GncPluginPage *page)
04325 {
04326     g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
04327 
04328     ENTER("widget %p, event %p, page %p", whatever, event, page);
04329     /* Ignore double-clicks and triple-clicks */
04330     if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
04331     {
04332         do_popup_menu(page, event);
04333         LEAVE("menu shown");
04334         return TRUE;
04335     }
04336 
04337     LEAVE("other click");
04338     return FALSE;
04339 }
04340 
04341 
04342 /* CS: Code copied from gtk/gtkactiongroup.c */
04343 static gchar *
04344 dgettext_swapped (const gchar *msgid,
04345                   const gchar *domainname)
04346 {
04347     /* CS: Pass this through dgettext if and only if msgid is
04348        nonempty. */
04349     return (msgid && *msgid) ? dgettext (domainname, msgid) : (gchar*) msgid;
04350 }
04351 
04352 /*
04353  * This is copied into GnuCash from Gtk in order to fix problems when
04354  * empty msgids were passed through gettext().
04355  *
04356  * See http://bugzilla.gnome.org/show_bug.cgi?id=326200 . If that bug
04357  * is fixed in the gtk that we can rely open, then
04358  * gnc_gtk_action_group_set_translation_domain can be replaced by
04359  * gtk_action_group_set_translation_domain again.
04360  */
04361 void
04362 gnc_gtk_action_group_set_translation_domain (GtkActionGroup *action_group,
04363         const gchar    *domain)
04364 {
04365     g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
04366 
04367     gtk_action_group_set_translate_func (action_group,
04368                                          (GtkTranslateFunc)dgettext_swapped,
04369                                          g_strdup (domain),
04370                                          g_free);
04371 }
04372 /* CS: End of code copied from gtk/gtkactiongroup.c */
04373 
04374 void
04375 gnc_main_window_all_action_set_sensitive (const gchar *action_name,
04376         gboolean sensitive)
04377 {
04378     GList *tmp;
04379     GtkAction *action;
04380 
04381     for (tmp = active_windows; tmp; tmp = g_list_next(tmp))
04382     {
04383         action = gnc_main_window_find_action (tmp->data, action_name);
04384         gtk_action_set_sensitive (action, sensitive);
04385     }
04386 }
04387 
04388 GtkUIManager *gnc_main_window_get_uimanager (GncMainWindow *window)
04389 {
04390     g_assert(window);
04391     return window->ui_merge;
04392 }
04393 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines