00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00036 #include "config.h"
00037
00038 #include <gnome.h>
00039 #include <glib/gi18n.h>
00040 #include <libguile.h>
00041 #include "guile-mappings.h"
00042
00043 #include "gnc-plugin.h"
00044 #include "gnc-plugin-manager.h"
00045 #include "gnc-main-window.h"
00046
00047 #include "dialog-preferences.h"
00048 #include "dialog-reset-warnings.h"
00049 #include "dialog-transfer.h"
00050 #include "dialog-utils.h"
00051 #include "file-utils.h"
00052 #include "gnc-component-manager.h"
00053 #include "gnc-engine.h"
00054 #include "gnc-file.h"
00055 #include "gnc-gkeyfile-utils.h"
00056 #include "gnc-gnome-utils.h"
00057 #include "gnc-gobject-utils.h"
00058 #include "gnc-gui-query.h"
00059 #include "gnc-hooks.h"
00060 #include "gnc-session.h"
00061 #include "gnc-ui.h"
00062 #include "gnc-ui-util.h"
00063 #include "gnc-version.h"
00064 #include "gnc-window.h"
00065 #include "gnc-main.h"
00066 #include "gnc-gconf-utils.h"
00067
00068 #include "gnc-html.h"
00069 #include "gnc-autosave.h"
00070
00072 enum {
00073 PAGE_ADDED,
00074 PAGE_CHANGED,
00075 LAST_SIGNAL
00076 };
00077
00080 #define PLUGIN_PAGE_LABEL "plugin-page"
00081
00082 #define PLUGIN_PAGE_CLOSE_BUTTON "close-button"
00083 #define PLUGIN_PAGE_TAB_LABEL "label"
00084
00085 #define KEY_SHOW_CLOSE_BUTTON "tab_close_buttons"
00086 #define KEY_TAB_NEXT_RECENT "tab_next_recent"
00087 #define KEY_TAB_POSITION "tab_position"
00088 #define KEY_TAB_WIDTH "tab_width"
00089
00090 #define GNC_MAIN_WINDOW_NAME "GncMainWindow"
00091
00092
00093
00095 static QofLogModule log_module = GNC_MOD_GUI;
00097 static GObjectClass *parent_class = NULL;
00099 static GQuark window_type = 0;
00102 static GList *active_windows = NULL;
00103
00104
00105 static void gnc_main_window_class_init (GncMainWindowClass *klass);
00106 static void gnc_main_window_init (GncMainWindow *window, GncMainWindowClass *klass);
00107 static void gnc_main_window_finalize (GObject *object);
00108 static void gnc_main_window_destroy (GtkObject *object);
00109
00110 static void gnc_main_window_setup_window (GncMainWindow *window);
00111 static void gnc_window_main_window_init (GncWindowIface *iface);
00112 static void gnc_main_window_update_all_menu_items (void);
00113
00114
00115 static void gnc_main_window_add_widget (GtkUIManager *merge, GtkWidget *widget, GncMainWindow *window);
00116 static void gnc_main_window_switch_page (GtkNotebook *notebook, GtkNotebookPage *notebook_page, gint pos, GncMainWindow *window);
00117 #ifdef HAVE_GTK_2_10
00118 static void gnc_main_window_page_reordered (GtkNotebook *notebook, GtkWidget *child, guint pos, GncMainWindow *window);
00119 #endif
00120 static void gnc_main_window_plugin_added (GncPlugin *manager, GncPlugin *plugin, GncMainWindow *window);
00121 static void gnc_main_window_plugin_removed (GncPlugin *manager, GncPlugin *plugin, GncMainWindow *window);
00122
00123
00124 static void gnc_main_window_cmd_file_properties (GtkAction *action, GncMainWindow *window);
00125 static void gnc_main_window_cmd_file_close (GtkAction *action, GncMainWindow *window);
00126 static void gnc_main_window_cmd_file_quit (GtkAction *action, GncMainWindow *window);
00127 static void gnc_main_window_cmd_edit_cut (GtkAction *action, GncMainWindow *window);
00128 static void gnc_main_window_cmd_edit_copy (GtkAction *action, GncMainWindow *window);
00129 static void gnc_main_window_cmd_edit_paste (GtkAction *action, GncMainWindow *window);
00130 static void gnc_main_window_cmd_edit_preferences (GtkAction *action, GncMainWindow *window);
00131 static void gnc_main_window_cmd_view_refresh (GtkAction *action, GncMainWindow *window);
00132 static void gnc_main_window_cmd_view_toolbar (GtkAction *action, GncMainWindow *window);
00133 static void gnc_main_window_cmd_view_summary (GtkAction *action, GncMainWindow *window);
00134 static void gnc_main_window_cmd_view_statusbar (GtkAction *action, GncMainWindow *window);
00135 static void gnc_main_window_cmd_actions_reset_warnings (GtkAction *action, GncMainWindow *window);
00136 static void gnc_main_window_cmd_actions_rename_page (GtkAction *action, GncMainWindow *window);
00137 static void gnc_main_window_cmd_window_new (GtkAction *action, GncMainWindow *window);
00138 static void gnc_main_window_cmd_window_move_page (GtkAction *action, GncMainWindow *window);
00139 static void gnc_main_window_cmd_window_raise (GtkAction *action, GtkRadioAction *current, GncMainWindow *window);
00140 static void gnc_main_window_cmd_help_tutorial (GtkAction *action, GncMainWindow *window);
00141 static void gnc_main_window_cmd_help_contents (GtkAction *action, GncMainWindow *window);
00142 static void gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window);
00143
00144 static void do_popup_menu(GncPluginPage *page, GdkEventButton *event);
00145 static gboolean gnc_main_window_popup_menu_cb (GtkWidget *widget, GncPluginPage *page);
00146
00147 static GtkAction *gnc_main_window_find_action (GncMainWindow *window, const gchar *name);
00148
00151 typedef struct GncMainWindowPrivate
00152 {
00157 GtkWidget *menu_dock;
00158
00159
00160 GtkWidget *toolbar;
00162 GtkWidget *notebook;
00166 GtkWidget *statusbar;
00170 GtkWidget *progressbar;
00171
00175 GtkActionGroup *action_group;
00176
00178 GList *installed_pages;
00180 GList *usage_order;
00182 GncPluginPage *current_page;
00184 gint event_handler_id;
00185
00190 GHashTable *merged_actions_table;
00191 } GncMainWindowPrivate;
00192
00193 #define GNC_MAIN_WINDOW_GET_PRIVATE(o) \
00194 (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_MAIN_WINDOW, GncMainWindowPrivate))
00195
00198 typedef struct {
00201 guint merge_id;
00204 GtkActionGroup *action_group;
00205 } MergedActionEntry;
00206
00209 static guint main_window_signals[LAST_SIGNAL] = { 0 };
00210
00211
00216 static GtkActionEntry gnc_menu_actions [] =
00217 {
00218
00219
00220 { "FileAction", NULL, N_("_File"), NULL, NULL, NULL, },
00221 { "EditAction", NULL, N_("_Edit"), NULL, NULL, NULL },
00222 { "ViewAction", NULL, N_("_View"), NULL, NULL, NULL },
00223 { "ActionsAction", NULL, N_("_Actions"), NULL, NULL, NULL },
00224 { "TransactionAction", NULL, N_("Tra_nsaction"), NULL, NULL, NULL },
00225 { "ReportsAction", NULL, N_("_Reports"), NULL, NULL, NULL },
00226 { "ToolsAction", NULL, N_("_Tools"), NULL, NULL, NULL },
00227 { "ExtensionsAction", NULL, N_("E_xtensions"), NULL, NULL, NULL },
00228 { "WindowsAction", NULL, N_("_Windows"), NULL, NULL, NULL },
00229 { "HelpAction", NULL, N_("_Help"), NULL, NULL, NULL },
00230
00231
00232
00233 { "FileNewMenuAction", GTK_STOCK_NEW, N_("_New"), "", NULL, NULL },
00234 { "FileOpenMenuAction", GTK_STOCK_OPEN, N_("_Open"), "", NULL, NULL },
00235 { "FileImportAction", NULL, N_("_Import"), NULL, NULL, NULL },
00236 { "FileExportAction", NULL, N_("_Export"), NULL, NULL, NULL },
00237 { "FilePrintAction", GTK_STOCK_PRINT, N_("_Print..."), "<control>p",
00238 N_("Print the currently active page"), NULL },
00239 { "FilePropertiesAction", GTK_STOCK_PROPERTIES, N_("Proper_ties"), "<Alt>Return",
00240 N_("Edit the properties of the current file"),
00241 G_CALLBACK (gnc_main_window_cmd_file_properties) },
00242 { "FileCloseAction", GTK_STOCK_CLOSE, N_("_Close"), NULL,
00243 N_("Close the currently active page"),
00244 G_CALLBACK (gnc_main_window_cmd_file_close) },
00245 { "FileQuitAction", GTK_STOCK_QUIT, N_("_Quit"), NULL,
00246 N_("Quit this application"),
00247 G_CALLBACK (gnc_main_window_cmd_file_quit) },
00248
00249
00250
00251 { "EditCutAction", GTK_STOCK_CUT, N_("Cu_t"), NULL,
00252 N_("Cut the current selection and copy it to clipboard"),
00253 G_CALLBACK (gnc_main_window_cmd_edit_cut) },
00254 { "EditCopyAction", GTK_STOCK_COPY, N_("_Copy"), NULL,
00255 N_("Copy the current selection to clipboard"),
00256 G_CALLBACK (gnc_main_window_cmd_edit_copy) },
00257 { "EditPasteAction", GTK_STOCK_PASTE, N_("_Paste"), NULL,
00258 N_("Paste the clipboard content at the cursor position"),
00259 G_CALLBACK (gnc_main_window_cmd_edit_paste) },
00260 { "EditPreferencesAction", GTK_STOCK_PREFERENCES, N_("Pr_eferences"), NULL,
00261 N_("Edit the global preferences of GnuCash"),
00262 G_CALLBACK (gnc_main_window_cmd_edit_preferences) },
00263
00264
00265
00266 { "ViewSortByAction", NULL, N_("_Sort By..."), NULL,
00267 N_("Select sorting criteria for this page view"), NULL },
00268 { "ViewFilterByAction", NULL, N_("_Filter By..."), NULL,
00269 N_("Select the account types that should be displayed."), NULL },
00270 { "ViewRefreshAction", GTK_STOCK_REFRESH, N_("_Refresh"), "<control>r",
00271 N_("Refresh this window"),
00272 G_CALLBACK (gnc_main_window_cmd_view_refresh) },
00273
00274
00275
00276 { "ScrubMenuAction", NULL, N_("_Check & Repair"), NULL, NULL, NULL },
00277 { "ActionsForgetWarningsAction", NULL, N_("Reset _Warnings..."), NULL,
00278 N_("Reset the state of all warning messages so they will be shown again."),
00279 G_CALLBACK (gnc_main_window_cmd_actions_reset_warnings) },
00280 { "ActionsRenamePageAction", NULL, N_("Re_name Page"), NULL,
00281 N_("Rename this page."),
00282 G_CALLBACK (gnc_main_window_cmd_actions_rename_page) },
00283
00284
00285
00286 { "WindowNewAction", NULL, N_("_New Window"), NULL,
00287 N_("Open a new top-level GnuCash window."),
00288 G_CALLBACK (gnc_main_window_cmd_window_new) },
00289 { "WindowMovePageAction", NULL, N_("New Window with _Page"), NULL,
00290 N_("Move the current page to a new top-level GnuCash window."),
00291 G_CALLBACK (gnc_main_window_cmd_window_move_page) },
00292
00293
00294
00295 { "HelpTutorialAction", GNOME_STOCK_BOOK_BLUE, N_("Tutorial and Concepts _Guide"), NULL,
00296 N_("Open the GnuCash Tutorial"),
00297 G_CALLBACK (gnc_main_window_cmd_help_tutorial) },
00298 { "HelpContentsAction", GTK_STOCK_HELP, N_("_Contents"), "F1",
00299 N_("Open the GnuCash Help"),
00300 G_CALLBACK (gnc_main_window_cmd_help_contents) },
00301 { "HelpAboutAction", GNOME_STOCK_ABOUT, N_("_About"), NULL,
00302 N_("About GnuCash"),
00303 G_CALLBACK (gnc_main_window_cmd_help_about) },
00304 };
00306 static guint gnc_menu_n_actions = G_N_ELEMENTS (gnc_menu_actions);
00307
00310 static GtkToggleActionEntry toggle_actions [] =
00311 {
00312 { "ViewToolbarAction", NULL, N_("_Toolbar"), NULL,
00313 N_("Show/hide the toolbar on this window"),
00314 G_CALLBACK (gnc_main_window_cmd_view_toolbar), TRUE },
00315 { "ViewSummaryAction", NULL, N_("Su_mmary Bar"), NULL,
00316 N_("Show/hide the summary bar on this window"),
00317 G_CALLBACK (gnc_main_window_cmd_view_summary), TRUE },
00318 { "ViewStatusbarAction", NULL, N_("Stat_us Bar"), NULL,
00319 N_("Show/hide the status bar on this window"),
00320 G_CALLBACK (gnc_main_window_cmd_view_statusbar), TRUE },
00321 };
00323 static guint n_toggle_actions = G_N_ELEMENTS (toggle_actions);
00324
00327 static GtkRadioActionEntry radio_entries [] =
00328 {
00329 { "Window0Action", NULL, N_("Window _1"), NULL, NULL, 0 },
00330 { "Window1Action", NULL, N_("Window _2"), NULL, NULL, 1 },
00331 { "Window2Action", NULL, N_("Window _3"), NULL, NULL, 2 },
00332 { "Window3Action", NULL, N_("Window _4"), NULL, NULL, 3 },
00333 { "Window4Action", NULL, N_("Window _5"), NULL, NULL, 4 },
00334 { "Window5Action", NULL, N_("Window _6"), NULL, NULL, 5 },
00335 { "Window6Action", NULL, N_("Window _7"), NULL, NULL, 6 },
00336 { "Window7Action", NULL, N_("Window _8"), NULL, NULL, 7 },
00337 { "Window8Action", NULL, N_("Window _9"), NULL, NULL, 8 },
00338 { "Window9Action", NULL, N_("Window _0"), NULL, NULL, 9 },
00339 };
00341 static guint n_radio_entries = G_N_ELEMENTS (radio_entries);
00342
00343
00347 static const gchar *gnc_menu_important_actions[] = {
00348 "FileCloseAction",
00349 NULL,
00350 };
00351
00352
00357 static const gchar *always_insensitive_actions[] = {
00358 "FilePrintAction",
00359 NULL
00360 };
00361
00362
00366 static const gchar *initially_insensitive_actions[] = {
00367 "FileCloseAction",
00368 NULL
00369 };
00370
00371
00376 static const gchar *always_hidden_actions[] = {
00377 "ViewSortByAction",
00378 "ViewFilterByAction",
00379 NULL
00380 };
00381
00382
00385 static const gchar *immutable_page_actions[] = {
00386 "FileCloseAction",
00387 NULL
00388 };
00389
00390
00393 static const gchar *multiple_page_actions[] = {
00394 "WindowMovePageAction",
00395 NULL
00396 };
00397
00398
00399
00400
00401
00402 static GtkTooltips *tips = NULL;
00403
00404
00405
00406
00407 #define WINDOW_COUNT "WindowCount"
00408 #define WINDOW_STRING "Window %d"
00409 #define WINDOW_GEOMETRY "WindowGeometry"
00410 #define WINDOW_POSITION "WindowPosition"
00411 #define WINDOW_MAXIMIZED "WindowMaximized"
00412 #define TOOLBAR_VISIBLE "ToolbarVisible"
00413 #define STATUSBAR_VISIBLE "StatusbarVisible"
00414 #define SUMMARYBAR_VISIBLE "SummarybarVisible"
00415 #define WINDOW_FIRSTPAGE "FirstPage"
00416 #define WINDOW_PAGECOUNT "PageCount"
00417 #define WINDOW_PAGEORDER "PageOrder"
00418 #define PAGE_TYPE "PageType"
00419 #define PAGE_NAME "PageName"
00420 #define PAGE_STRING "Page %d"
00421
00422 typedef struct {
00423 GKeyFile *key_file;
00424 const gchar *group_name;
00425 gint window_num;
00426 gint page_num;
00427 gint page_offset;
00428 } GncMainWindowSaveData;
00429
00430
00431
00432
00433 void
00434 gnc_main_window_foreach_page (GncMainWindowPageFunc fn, gpointer user_data)
00435 {
00436 GncMainWindowPrivate *priv;
00437 GncMainWindow *window;
00438 GncPluginPage *page;
00439 GList *w, *p;
00440
00441 ENTER(" ");
00442 for (w = active_windows; w; w = g_list_next(w)) {
00443 window = w->data;
00444 priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
00445 for (p = priv->installed_pages; p; p = g_list_next(p)) {
00446 page = p->data;
00447 fn(page, user_data);
00448 }
00449 }
00450 LEAVE(" ");
00451 }
00452
00453
00465 static void
00466 gnc_main_window_restore_page (GncMainWindow *window,
00467 GncMainWindowSaveData *data)
00468 {
00469 GncMainWindowPrivate *priv;
00470 GncPluginPage *page;
00471 gchar *page_group, *page_type = NULL, *name = NULL;
00472 const gchar *class_type;
00473 GError *error = NULL;
00474
00475 ENTER("window %p, data %p (key file %p, window %d, page start %d, page num %d)",
00476 window, data, data->key_file, data->window_num, data->page_offset,
00477 data->page_num);
00478
00479 priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
00480 page_group = g_strdup_printf(PAGE_STRING,
00481 data->page_offset + data->page_num);
00482 page_type = g_key_file_get_string(data->key_file, page_group,
00483 PAGE_TYPE, &error);
00484 if (error) {
00485 g_warning("error reading group %s key %s: %s",
00486 page_group, PAGE_TYPE, error->message);
00487 goto cleanup;
00488 }
00489
00490
00491 page = g_list_nth_data(priv->installed_pages, data->page_num);
00492 if (page) {
00493 class_type = GNC_PLUGIN_PAGE_GET_CLASS(page)->plugin_name;
00494 if (strcmp(page_type, class_type) != 0) {
00495 g_warning("error: page types don't match: state %s, existing page %s",
00496 page_type, class_type);
00497 goto cleanup;
00498 }
00499 } else {
00500
00501 page = gnc_plugin_page_recreate_page(GTK_WIDGET(window), page_type,
00502 data->key_file, page_group);
00503 if (page) {
00504
00505 if (page->window == NULL) {
00506 gnc_plugin_page_set_use_new_window(page, FALSE);
00507 gnc_main_window_open_page(window, page);
00508 }
00509
00510
00511 name = g_key_file_get_string(data->key_file, page_group,
00512 PAGE_NAME, &error);
00513 if (error) {
00514 g_warning("error reading group %s key %s: %s",
00515 page_group, PAGE_NAME, error->message);
00516
00517 } else {
00518 DEBUG("updating page name for %p to %s.", page, name);
00519 main_window_update_page_name(page, name);
00520 g_free(name);
00521 }
00522 }
00523 }
00524
00525 LEAVE("ok");
00526 cleanup:
00527 if (error)
00528 g_error_free(error);
00529 if (page_type)
00530 g_free(page_type);
00531 g_free(page_group);
00532 }
00533
00534
00543 static void
00544 gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *data)
00545 {
00546 GncMainWindowPrivate *priv;
00547 GtkAction *action;
00548 gint *pos, *geom, *order;
00549 gsize length;
00550 gboolean max, visible, desired_visibility;
00551 gchar *window_group;
00552 gint page_start, page_count, i;
00553 GError *error = NULL;
00554
00555
00556 ENTER("window %p, data %p (key file %p, window %d)",
00557 window, data, data->key_file, data->window_num);
00558 window_group = g_strdup_printf(WINDOW_STRING, data->window_num + 1);
00559
00560
00561 page_count = g_key_file_get_integer(data->key_file,
00562 window_group, WINDOW_PAGECOUNT, &error);
00563 if (error) {
00564 g_warning("error reading group %s key %s: %s",
00565 window_group, WINDOW_PAGECOUNT, error->message);
00566 goto cleanup;
00567 }
00568 if (page_count == 0) {
00569
00570
00571 goto cleanup;
00572 }
00573 page_start = g_key_file_get_integer(data->key_file,
00574 window_group, WINDOW_FIRSTPAGE, &error);
00575 if (error) {
00576 g_warning("error reading group %s key %s: %s",
00577 window_group, WINDOW_FIRSTPAGE, error->message);
00578 goto cleanup;
00579 }
00580
00581
00582 if (window == NULL) {
00583 DEBUG("Window %d doesn't exist. Creating new window.", data->window_num);
00584 DEBUG("active_windows %p.", active_windows);
00585 if (active_windows)
00586 DEBUG("first window %p.", active_windows->data);
00587 window = gnc_main_window_new();
00588 gtk_widget_show(GTK_WIDGET(window));
00589 }
00590
00591 priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
00592
00593
00594 geom = g_key_file_get_integer_list(data->key_file, window_group,
00595 WINDOW_GEOMETRY, &length, &error);
00596 if (error) {
00597 g_warning("error reading group %s key %s: %s",
00598 window_group, WINDOW_GEOMETRY, error->message);
00599 g_error_free(error);
00600 error = NULL;
00601 } else if (length != 2) {
00602 g_warning("invalid number of values for group %s key %s",
00603 window_group, WINDOW_GEOMETRY);
00604 } else {
00605 gtk_window_resize(GTK_WINDOW(window), geom[0], geom[1]);
00606 DEBUG("window (%p) size %dx%d", window, geom[0], geom[1]);
00607 }
00608
00609
00610
00611 pos = g_key_file_get_integer_list(data->key_file, window_group,
00612 WINDOW_POSITION, &length, &error);
00613 if (error) {
00614 g_warning("error reading group %s key %s: %s",
00615 window_group, WINDOW_POSITION, error->message);
00616 g_error_free(error);
00617 error = NULL;
00618 } else if (length != 2) {
00619 g_warning("invalid number of values for group %s key %s",
00620 window_group, WINDOW_POSITION);
00621 } else if ((pos[0] + (geom ? geom[0] : 0) < 0) ||
00622 (pos[0] > gdk_screen_width()) ||
00623 (pos[1] + (geom ? geom[1] : 0) < 0) ||
00624 (pos[1] > gdk_screen_height())) {
00625
00626
00627 } else {
00628 gtk_window_move(GTK_WINDOW(window), pos[0], pos[1]);
00629 DEBUG("window (%p) position %dx%d", window, pos[0], pos[1]);
00630 }
00631 if (geom) {
00632 g_free(geom);
00633 }
00634 if (pos) {
00635 g_free(pos);
00636 }
00637
00638 max = g_key_file_get_boolean(data->key_file, window_group,
00639 WINDOW_MAXIMIZED, &error);
00640 if (error) {
00641 g_warning("error reading group %s key %s: %s",
00642 window_group, WINDOW_MAXIMIZED, error->message);
00643 g_error_free(error);
00644 error = NULL;
00645 } else if (max) {
00646 gtk_window_maximize(GTK_WINDOW(window));
00647 }
00648
00649
00650 action = gnc_main_window_find_action(window, "ViewToolbarAction");
00651 visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
00652 desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
00653 TOOLBAR_VISIBLE, &error);
00654 if (error) {
00655 g_warning("error reading group %s key %s: %s",
00656 window_group, TOOLBAR_VISIBLE, error->message);
00657 g_error_free(error);
00658 error = NULL;
00659 } else if (visible != desired_visibility) {
00660 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),desired_visibility);
00661 }
00662
00663 action = gnc_main_window_find_action(window, "ViewSummaryAction");
00664 visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
00665 desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
00666 SUMMARYBAR_VISIBLE, &error);
00667 if (error) {
00668 g_warning("error reading group %s key %s: %s",
00669 window_group, TOOLBAR_VISIBLE, error->message);
00670 g_error_free(error);
00671 error = NULL;
00672 } else if (visible != desired_visibility) {
00673 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),desired_visibility);
00674 }
00675
00676 action = gnc_main_window_find_action(window, "ViewStatusbarAction");
00677 visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
00678 desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
00679 STATUSBAR_VISIBLE, &error);
00680 if (error) {
00681 g_warning("error reading group %s key %s: %s",
00682 window_group, TOOLBAR_VISIBLE, error->message);
00683 g_error_free(error);
00684 error = NULL;
00685 } else if (visible != desired_visibility) {
00686 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),desired_visibility);
00687 }
00688
00689
00690 for (i = 0; i < page_count; i++) {
00691 data->page_offset = page_start;
00692 data->page_num = i;
00693 gnc_main_window_restore_page(window, data);
00694
00695
00696 while (gtk_events_pending ())
00697 gtk_main_iteration ();
00698 }
00699
00700
00701
00702
00703 order = g_key_file_get_integer_list(data->key_file, window_group,
00704 WINDOW_PAGEORDER, &length, &error);
00705 if (error) {
00706 g_warning("error reading group %s key %s: %s",
00707 window_group, WINDOW_PAGEORDER, error->message);
00708 g_error_free(error);
00709 error = NULL;
00710 } else if (length != page_count) {
00711 g_warning("%s key %s length %" G_GSIZE_FORMAT " differs from window page count %d",
00712 window_group, WINDOW_PAGEORDER, length, page_count);
00713 } else {
00714
00715 g_list_free(priv->usage_order);
00716 priv->usage_order = NULL;
00717
00718 for (i = 0; i < length; i++) {
00719 gpointer page = g_list_nth_data(priv->installed_pages, order[i] - 1);
00720 if (page) {
00721 priv->usage_order = g_list_append(priv->usage_order, page);
00722 }
00723 }
00724 gtk_notebook_set_current_page (GTK_NOTEBOOK(priv->notebook),
00725 order[0] - 1);
00726 }
00727 if (order) {
00728 g_free(order);
00729 }
00730
00731 LEAVE("window %p", window);
00732 cleanup:
00733 if (error)
00734 g_error_free(error);
00735 g_free(window_group);
00736 }
00737
00738 void
00739 gnc_main_window_restore_all_windows(const GKeyFile *keyfile)
00740 {
00741 gint i, window_count;
00742 GError *error = NULL;
00743 GncMainWindowSaveData data;
00744 GncMainWindow *window;
00745
00746
00747
00748 data.key_file = (GKeyFile *) keyfile;
00749 window_count = g_key_file_get_integer(data.key_file, STATE_FILE_TOP,
00750 WINDOW_COUNT, &error);
00751 if (error) {
00752 g_warning("error reading group %s key %s: %s",
00753 STATE_FILE_TOP, WINDOW_COUNT, error->message);
00754 g_error_free(error);
00755 LEAVE("can't read count");
00756 return;
00757 }
00758
00759
00760
00761 gnc_set_busy_cursor (NULL, TRUE);
00762 for (i = 0; i < window_count; i++) {
00763 data.window_num = i;
00764 window = g_list_nth_data(active_windows, i);
00765 gnc_main_window_restore_window(window, &data);
00766 }
00767 gnc_unset_busy_cursor (NULL);
00768 }
00769
00770 void
00771 gnc_main_window_restore_default_state(void)
00772 {
00773 GtkAction *action;
00774 GncMainWindow *window;
00775
00776
00777
00778 DEBUG("no saved state file");
00779 window = g_list_nth_data(active_windows, 0);
00780 action = gnc_main_window_find_action(window, "FileNewAccountTreeAction");
00781 gtk_action_activate(action);
00782 }
00783
00793 static void
00794 gnc_main_window_save_page (GncPluginPage *page, GncMainWindowSaveData *data)
00795 {
00796 gchar *page_group;
00797 const gchar *plugin_name, *page_name;
00798
00799 ENTER("page %p, data %p (key file %p, window %d, page %d)",
00800 page, data, data->key_file, data->window_num, data->page_num);
00801 plugin_name = gnc_plugin_page_get_plugin_name(page);
00802 page_name = gnc_plugin_page_get_page_name(page);
00803 if (!plugin_name || !page_name) {
00804 LEAVE("not saving invalid page");
00805 return;
00806 }
00807 page_group = g_strdup_printf(PAGE_STRING, data->page_num++);
00808 g_key_file_set_string(data->key_file, page_group, PAGE_TYPE, plugin_name);
00809 g_key_file_set_string(data->key_file, page_group, PAGE_NAME, page_name);
00810
00811 gnc_plugin_page_save_page(page, data->key_file, page_group);
00812 g_free(page_group);
00813 LEAVE(" ");
00814 }
00815
00816
00825 static void
00826 gnc_main_window_save_window (GncMainWindow *window, GncMainWindowSaveData *data)
00827 {
00828 GncMainWindowPrivate *priv;
00829 GtkAction *action;
00830 gint i, num_pages, coords[4], *order;
00831 gboolean maximized, visible;
00832 gchar *window_group;
00833
00834
00835 ENTER("window %p, data %p (key file %p, window %d)",
00836 window, data, data->key_file, data->window_num);
00837 priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
00838
00839
00840 num_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook));
00841 if (0 == num_pages) {
00842 LEAVE("empty window %p", window);
00843 return;
00844 }
00845
00846
00847 window_group = g_strdup_printf(WINDOW_STRING, data->window_num++);
00848 g_key_file_set_integer(data->key_file, window_group,
00849 WINDOW_PAGECOUNT, num_pages);
00850 g_key_file_set_integer(data->key_file, window_group,
00851 WINDOW_FIRSTPAGE, data->page_num);
00852
00853
00854
00855
00856 order = g_malloc(sizeof(gint) * num_pages);
00857 for (i = 0; i < num_pages; i++) {
00858 gpointer page = g_list_nth_data(priv->usage_order, i);
00859 order[i] = g_list_index(priv->installed_pages, page) + 1;
00860 }
00861 g_key_file_set_integer_list(data->key_file, window_group,
00862 WINDOW_PAGEORDER, order, num_pages);
00863 g_free(order);
00864
00865
00866 gtk_window_get_position(GTK_WINDOW(window), &coords[0], &coords[1]);
00867 gtk_window_get_size(GTK_WINDOW(window), &coords[2], &coords[3]);
00868 maximized = (gdk_window_get_state((GTK_WIDGET(window))->window)
00869 & GDK_WINDOW_STATE_MAXIMIZED) != 0;
00870 g_key_file_set_integer_list(data->key_file, window_group,
00871 WINDOW_POSITION, &coords[0], 2);
00872 g_key_file_set_integer_list(data->key_file, window_group,
00873 WINDOW_GEOMETRY, &coords[2], 2);
00874 g_key_file_set_boolean(data->key_file, window_group,
00875 WINDOW_MAXIMIZED, maximized);
00876 DEBUG("window (%p) position %dx%d, size %dx%d, %s", window, coords[0], coords[1],
00877 coords[2], coords[3],
00878 maximized ? "maximized" : "not maximized");
00879
00880
00881 action = gnc_main_window_find_action(window, "ViewToolbarAction");
00882 visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
00883 g_key_file_set_boolean(data->key_file, window_group,
00884 TOOLBAR_VISIBLE, visible);
00885 action = gnc_main_window_find_action(window, "ViewSummaryAction");
00886 visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
00887 g_key_file_set_boolean(data->key_file, window_group,
00888 SUMMARYBAR_VISIBLE, visible);
00889 action = gnc_main_window_find_action(window, "ViewStatusbarAction");
00890 visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
00891 g_key_file_set_boolean(data->key_file, window_group,
00892 STATUSBAR_VISIBLE, visible);
00893
00894
00895 g_list_foreach(priv->installed_pages, (GFunc)gnc_main_window_save_page, data);
00896
00897 g_free(window_group);
00898 LEAVE("window %p", window);
00899 }
00900
00901 void
00902 gnc_main_window_save_all_windows(GKeyFile *keyfile)
00903 {
00904 GncMainWindowSaveData data;
00905
00906
00907 data.key_file = keyfile;
00908 data.window_num = 1;
00909 data.page_num = 1;
00910
00911 g_key_file_set_integer(data.key_file,
00912 STATE_FILE_TOP, WINDOW_COUNT,
00913 g_list_length(active_windows));
00914
00915 g_list_foreach(active_windows, (GFunc)gnc_main_window_save_window, &data);
00916 }
00917
00918
00919 gboolean
00920 gnc_main_window_finish_pending (GncMainWindow *window)
00921 {
00922 GncMainWindowPrivate *priv;
00923 GList *item;
00924
00925 g_return_val_if_fail(GNC_IS_MAIN_WINDOW(window), TRUE);
00926
00927 priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
00928 for (item = priv->installed_pages; item; item = g_list_next(item)) {
00929 if (!gnc_plugin_page_finish_pending(item->data)) {
00930 return FALSE;
00931 }
00932 }
00933 return TRUE;
00934 }
00935
00936
00937 gboolean
00938 gnc_main_window_all_finish_pending (void)
00939 {
00940 const GList *windows, *item;
00941
00942 windows = gnc_gobject_tracking_get_list(GNC_MAIN_WINDOW_NAME);
00943 for (item = windows; item; item = g_list_next(item)) {
00944 if (!gnc_main_window_finish_pending(item->data)) {
00945 return FALSE;
00946 }
00947 }
00948 return TRUE;
00949 }
00950
00951
00962 static gboolean
00963 gnc_main_window_page_exists (GncPluginPage *page)
00964 {
00965 GncMainWindow *window;
00966 GncMainWindowPrivate *priv;
00967 GList *walker;
00968
00969 for (walker = active_windows; walker; walker = g_list_next(walker)) {
00970 window = walker->data;
00971 priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
00972 if (g_list_find(priv->installed_pages, page)) {
00973 return TRUE;
00974 }
00975 }
00976 return FALSE;
00977 }
00978
00979
00989 static gboolean
00990 gnc_main_window_prompt_for_save (GtkWidget *window)
00991 {
00992 QofSession *session;
00993 QofBook *book;
00994 GtkWidget *dialog;
00995 gint response;
00996 const gchar *filename, *tmp;
00997 const gchar *title = _("Save changes to file %s before closing?");
00998
00999 const gchar *message_mins =
01000 _("If you don't save, changes from the past %d minutes will be discarded.");
01001 const gchar *message_hours =
01002 _("If you don't save, changes from the past %d hours and %d minutes will be discarded.");
01003 const gchar *message_days =
01004 _("If you don't save, changes from the past %d days and %d hours will be discarded.");
01005 time_t oldest_change;
01006 gint minutes, hours, days;
01007
01008 session = gnc_get_current_session();
01009 book = qof_session_get_book(session);
01010 filename = qof_session_get_url(session);
01011 if (filename == NULL)
01012 filename = _("<unknown>");
01013 if ((tmp = strrchr(filename, '/')) != NULL)
01014 filename = tmp + 1;
01015
01016
01017 gnc_autosave_remove_timer(book);
01018
01019 dialog = gtk_message_dialog_new(GTK_WINDOW(window),
01020 GTK_DIALOG_MODAL,
01021 GTK_MESSAGE_WARNING,
01022 GTK_BUTTONS_NONE,
01023 title,
01024 filename);
01025 oldest_change = qof_book_get_dirty_time(book);
01026 minutes = (time(NULL) - oldest_change) / 60 + 1;
01027 hours = minutes / 60;
01028 minutes = minutes % 60;
01029 days = hours / 24;
01030 hours = hours % 24;
01031 if (days > 0) {
01032 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
01033 message_days, days, hours);
01034 } else if (hours > 0) {
01035 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
01036 message_hours, hours, minutes);
01037 } else {
01038 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
01039 message_mins, minutes);
01040 }
01041 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
01042 _("Close _Without Saving"), GTK_RESPONSE_CLOSE,
01043 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
01044 GTK_STOCK_SAVE, GTK_RESPONSE_APPLY,
01045 NULL);
01046 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);
01047 response = gtk_dialog_run (GTK_DIALOG (dialog));
01048 gtk_widget_destroy(dialog);
01049
01050 switch (response) {
01051 case GTK_RESPONSE_APPLY:
01052 gnc_file_save();
01053 return FALSE;
01054
01055 case GTK_RESPONSE_CLOSE:
01056 qof_book_mark_saved(book);
01057 return FALSE;
01058
01059 default:
01060 return TRUE;
01061 }
01062 }
01063
01064
01065 static void
01066 gnc_main_window_add_plugin (gpointer plugin,
01067 gpointer window)
01068 {
01069 g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
01070 g_return_if_fail (GNC_IS_PLUGIN (plugin));
01071
01072 ENTER(" ");
01073 gnc_plugin_add_to_window (GNC_PLUGIN (plugin),
01074 GNC_MAIN_WINDOW (window),
01075 window_type);
01076 LEAVE(" ");
01077 }
01078
01079 static void
01080 gnc_main_window_remove_plugin (gpointer plugin,
01081 gpointer window)
01082 {
01083 g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
01084 g_return_if_fail (GNC_IS_PLUGIN (plugin));
01085
01086 ENTER(" ");
01087 gnc_plugin_remove_from_window (GNC_PLUGIN (plugin),
01088 GNC_MAIN_WINDOW (window),
01089 window_type);
01090 LEAVE(" ");
01091 }
01092
01093
01094 static gboolean
01095 gnc_main_window_timed_quit (gpointer dummy)
01096 {
01097 if (gnc_file_save_in_progress())
01098 return TRUE;
01099
01100 gnc_shutdown (0);
01101 return FALSE;
01102 }
01103
01104 static gboolean
01105 gnc_main_window_quit(GncMainWindow *window)
01106 {
01107 QofSession *session;
01108 gboolean needs_save, do_shutdown;
01109
01110 session = gnc_get_current_session();
01111 needs_save = qof_book_not_saved(qof_session_get_book(session)) &&
01112 !gnc_file_save_in_progress();
01113 do_shutdown = !needs_save ||
01114 (needs_save && !gnc_main_window_prompt_for_save(GTK_WIDGET(window)));
01115
01116 if (do_shutdown) {
01117 g_timeout_add(250, gnc_main_window_timed_quit, NULL);
01118 return TRUE;
01119 }
01120 return FALSE;
01121 }
01122
01123 static gboolean
01124 gnc_main_window_delete_event (GtkWidget *window,
01125 GdkEvent *event,
01126 gpointer user_data)
01127 {
01128 static gboolean already_dead = FALSE;
01129
01130 if (already_dead)
01131 return TRUE;
01132
01133 if (!gnc_main_window_finish_pending(GNC_MAIN_WINDOW(window))) {
01134
01135 return TRUE;
01136 }
01137
01138 if (g_list_length(active_windows) > 1)
01139 return FALSE;
01140
01141 already_dead = gnc_main_window_quit(GNC_MAIN_WINDOW(window));
01142 return TRUE;
01143 }
01144
01145
01165 static void
01166 gnc_main_window_event_handler (QofInstance *entity, QofEventId event_type,
01167 gpointer user_data, gpointer event_data)
01168 {
01169 GncMainWindow *window;
01170 GncMainWindowPrivate *priv;
01171 GncPluginPage *page;
01172 GList *item, *next;
01173
01174
01175 g_return_if_fail(GNC_IS_MAIN_WINDOW(user_data));
01176
01177
01178 if (!QOF_CHECK_TYPE(entity, QOF_ID_BOOK))
01179 return;
01180 if (event_type != QOF_EVENT_DESTROY)
01181 return;
01182
01183 ENTER("entity %p, event %d, window %p, event data %p",
01184 entity, event_type, user_data, event_data);
01185 window = GNC_MAIN_WINDOW(user_data);
01186 priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
01187
01188
01189
01190
01191 for (item = priv->installed_pages; item; item = next) {
01192 next = g_list_next(item);
01193 page = GNC_PLUGIN_PAGE(item->data);
01194 if (gnc_plugin_page_has_book (page, (QofBook *)entity))
01195 gnc_main_window_close_page (page);
01196 }
01197 LEAVE(" ");
01198 }
01199
01200
01216 static gchar *
01217 gnc_main_window_generate_title (GncMainWindow *window)
01218 {
01219 GncMainWindowPrivate *priv;
01220 GncPluginPage *page;
01221 QofBook *book;
01222 const gchar *filename = NULL, *dirty = "";
01223 gchar *title, *ptr;
01224 GtkAction* action;
01225
01226
01227 action = gnc_main_window_find_action (window, "FileSaveAction");
01228 if (action != NULL) {
01229 gtk_action_set_sensitive(action, FALSE);
01230 }
01231 if (gnc_current_session_exist()) {
01232 filename = gnc_session_get_url (gnc_get_current_session ());
01233 book = gnc_get_current_book();
01234 if (qof_instance_is_dirty(QOF_INSTANCE(book))) {
01235 dirty = "*";
01236 if (action != NULL) {
01237 gtk_action_set_sensitive(action, TRUE);
01238 }
01239 }
01240 }
01241
01242 if (!filename)
01243 filename = _("<no file>");
01244 else {
01245
01246 ptr = g_utf8_strrchr(filename, -1, G_DIR_SEPARATOR);
01247 if (ptr != NULL)
01248 filename = g_utf8_next_char(ptr);
01249 }
01250
01251 priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
01252 page = priv->current_page;
01253 if (page) {
01254
01255 title = g_strdup_printf("%s%s - %s", dirty, filename,
01256 gnc_plugin_page_get_page_name(page));
01257 } else {
01258 title = g_strdup_printf("%s%s", dirty, filename);
01259 }
01260
01261 return title;
01262 }
01263
01264
01274 static void
01275 gnc_main_window_update_title (GncMainWindow *window)
01276 {
01277 gchar *title;
01278
01279 title = gnc_main_window_generate_title(window);
01280 gtk_window_set_title(GTK_WINDOW(window), title);
01281 g_free(title);
01282 }
01283
01284 static void
01285 gnc_main_window_update_all_titles (void)
01286 {
01287 g_list_foreach(active_windows,
01288 (GFunc)gnc_main_window_update_title,
01289 NULL);
01290 }
01291
01292 static void
01293 gnc_main_window_book_dirty_cb (QofBook *book,
01294 gboolean dirty,
01295 gpointer user_data)
01296 {
01297 gnc_main_window_update_all_titles();
01298
01299
01300 gnc_autosave_dirty_handler(book, dirty);
01301 }
01302
01303 static void
01304 gnc_main_window_attach_to_book (QofSession *session)
01305 {
01306 QofBook *book;
01307
01308 g_return_if_fail(session);
01309
01310 book = qof_session_get_book(session);
01311 qof_book_set_dirty_cb(book, gnc_main_window_book_dirty_cb, NULL);
01312 gnc_main_window_update_all_titles();
01313 gnc_main_window_update_all_menu_items();
01314 }
01315
01316
01320 struct menu_update {
01322 gchar *action_name;
01323
01325 gchar *label;
01326
01328 gboolean visible;
01329 };
01330
01331
01345 static void
01346 gnc_main_window_update_one_menu_action (GncMainWindow *window,
01347 struct menu_update *data)
01348 {
01349 GncMainWindowPrivate *priv;
01350 GtkAction* action;
01351
01352 ENTER("window %p, action %s, label %s, visible %d", window,
01353 data->action_name, data->label, data->visible);
01354 priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
01355 action = gtk_action_group_get_action(priv->action_group, data->action_name);
01356 if (action)
01357 g_object_set(G_OBJECT(action),
01358 "label", data->label,
01359 "visible", data->visible,
01360 (char *)NULL);
01361 LEAVE(" ");
01362 }
01363
01364
01377 static void
01378 gnc_main_window_update_radio_button (GncMainWindow *window)
01379 {
01380 GncMainWindowPrivate *priv;
01381 GtkAction *action, *first_action;
01382 GSList *action_list;
01383 gchar *action_name;
01384 gint index;
01385
01386 ENTER("window %p", window);
01387
01388
01389 index = g_list_index(active_windows, window);
01390 if (index >= n_radio_entries) {
01391 LEAVE("window %d, only %d actions", index, n_radio_entries);
01392 return;
01393 }
01394
01395 priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
01396 action_name = g_strdup_printf("Window%dAction", index);
01397 action = gtk_action_group_get_action(priv->action_group, action_name);
01398
01399
01400
01401 action_list = gtk_radio_action_get_group(GTK_RADIO_ACTION(action));
01402 first_action = g_slist_last(action_list)->data;
01403 g_signal_handlers_block_by_func(G_OBJECT(first_action),
01404 G_CALLBACK(gnc_main_window_cmd_window_raise), window);
01405 DEBUG("blocked signal on %p, set %p active, window %p", first_action, action, window);
01406 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
01407 g_signal_handlers_unblock_by_func(G_OBJECT(first_action),
01408 G_CALLBACK(gnc_main_window_cmd_window_raise), window);
01409 g_free(action_name);
01410 LEAVE(" ");
01411 }
01412
01413
01427 static void
01428 gnc_main_window_update_menu_item (GncMainWindow *window)
01429 {
01430 struct menu_update data;
01431 gchar **strings, *title, *expanded;
01432 gint index;
01433
01434