GnuCash  4.11-11-ge9df8d41d2+
gnc-main-window.c
Go to the documentation of this file.
1 /*
2  * gnc-main-window.c -- GtkWindow which represents the
3  * GnuCash main window.
4  *
5  * Copyright (C) 2003 Jan Arne Petersen <jpetersen@uni-bonn.de>
6  * Copyright (C) 2003,2005,2006 David Hampton <hampton@employees.org>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, contact:
20  *
21  * Free Software Foundation Voice: +1-617-542-5942
22  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
23  * Boston, MA 02110-1301, USA gnu@gnu.org
24  */
25 
35 #include <config.h>
36 
37 #include <glib/gi18n.h>
38 #include <gtk/gtk.h>
39 #include <gdk/gdk.h>
40 #include <gdk/gdkkeysyms.h>
41 
42 #include "gnc-plugin.h"
43 #include "gnc-plugin-manager.h"
44 #include "gnc-main-window.h"
45 
46 #include "dialog-options.h"
47 #include "dialog-preferences.h"
48 #include "dialog-reset-warnings.h"
49 #include "dialog-transfer.h"
50 #include "dialog-utils.h"
51 #include "engine-helpers.h"
52 #include "file-utils.h"
53 #include "gnc-component-manager.h"
54 #include "gnc-engine.h"
55 #include "gnc-features.h"
56 #include "gnc-file.h"
57 #include "gnc-filepath-utils.h"
58 #include "gnc-gkeyfile-utils.h"
59 #include "gnc-gnome-utils.h"
60 #include "gnc-gobject-utils.h"
61 #include "gnc-gui-query.h"
62 #include "gnc-hooks.h"
63 #include "gnc-icons.h"
64 #include "gnc-session.h"
65 #include "gnc-state.h"
66 #include "gnc-ui.h"
67 #include "gnc-ui-util.h"
68 #include <gnc-glib-utils.h>
69 #include "gnc-uri-utils.h"
70 #include "gnc-version.h"
71 #include "gnc-warnings.h"
72 #include "gnc-window.h"
73 #include "gnc-prefs.h"
74 #include "option-util.h"
75 // +JSLED
76 //#include "gnc-html.h"
77 #include "gnc-autosave.h"
78 #include "print-session.h"
79 #ifdef MAC_INTEGRATION
80 #include <gtkmacintegration/gtkosxapplication.h>
81 #endif
82 #ifdef HAVE_SYS_STAT_H
83 # define __need_system_sys_stat_h //To block Guile-2.0's evil substitute
84 # include <sys/types.h>
85 # include <sys/stat.h> // for stat(2)
86 #endif
87 
89 enum
90 {
91  PAGE_ADDED,
92  PAGE_CHANGED,
93  LAST_SIGNAL
94 };
95 
98 #define PLUGIN_PAGE_LABEL "plugin-page"
99 
100 #define PLUGIN_PAGE_CLOSE_BUTTON "close-button"
101 #define PLUGIN_PAGE_TAB_LABEL "label"
102 
103 #define GNC_PREF_SHOW_CLOSE_BUTTON "tab-close-buttons"
104 #define GNC_PREF_TAB_NEXT_RECENT "tab-next-recent"
105 #define GNC_PREF_TAB_POSITION_TOP "tab-position-top"
106 #define GNC_PREF_TAB_POSITION_BOTTOM "tab-position-bottom"
107 #define GNC_PREF_TAB_POSITION_LEFT "tab-position-left"
108 #define GNC_PREF_TAB_POSITION_RIGHT "tab-position-right"
109 #define GNC_PREF_TAB_WIDTH "tab-width"
110 #define GNC_PREF_TAB_COLOR "show-account-color-tabs"
111 #define GNC_PREF_SAVE_CLOSE_EXPIRES "save-on-close-expires"
112 #define GNC_PREF_SAVE_CLOSE_WAIT_TIME "save-on-close-wait-time"
113 #define GNC_PREF_TAB_OPEN_ADJACENT "tab-open-adjacent"
114 
115 #define GNC_MAIN_WINDOW_NAME "GncMainWindow"
116 
117 #define DIALOG_BOOK_OPTIONS_CM_CLASS "dialog-book-options"
118 
119 /* Static Globals *******************************************************/
120 
122 static QofLogModule log_module = GNC_MOD_GUI;
124 static GObjectClass *parent_class = NULL;
126 static GQuark window_type = 0;
129 static GList *active_windows = NULL;
132 static guint secs_to_save = 0;
133 #define MSG_AUTO_SAVE _("Changes will be saved automatically in %u seconds")
134 
135 /* Declarations *********************************************************/
136 static void gnc_main_window_class_init (GncMainWindowClass *klass);
137 static void gnc_main_window_init (GncMainWindow *window,
138  void *data);
139 static void gnc_main_window_finalize (GObject *object);
140 static void gnc_main_window_destroy (GtkWidget *widget);
141 
142 static void gnc_main_window_setup_window (GncMainWindow *window);
143 static void gnc_window_main_window_init (GncWindowIface *iface);
144 #ifndef MAC_INTEGRATION
145 static void gnc_main_window_update_all_menu_items (void);
146 #endif
147 
148 /* Callbacks */
149 static void gnc_main_window_add_widget (GtkUIManager *merge, GtkWidget *widget, GncMainWindow *window);
150 static void gnc_main_window_switch_page (GtkNotebook *notebook, gpointer *notebook_page, gint pos, GncMainWindow *window);
151 static void gnc_main_window_page_reordered (GtkNotebook *notebook, GtkWidget *child, guint pos, GncMainWindow *window);
152 static void gnc_main_window_plugin_added (GncPlugin *manager, GncPlugin *plugin, GncMainWindow *window);
153 static void gnc_main_window_plugin_removed (GncPlugin *manager, GncPlugin *plugin, GncMainWindow *window);
154 static void gnc_main_window_engine_commit_error_callback( gpointer data, QofBackendError errcode );
155 
156 /* Command callbacks */
157 static void gnc_main_window_cmd_page_setup (GtkAction *action, GncMainWindow *window);
158 static void gnc_main_window_cmd_file_properties (GtkAction *action, GncMainWindow *window);
159 static void gnc_main_window_cmd_file_close (GtkAction *action, GncMainWindow *window);
160 static void gnc_main_window_cmd_file_quit (GtkAction *action, GncMainWindow *window);
161 static void gnc_main_window_cmd_edit_cut (GtkAction *action, GncMainWindow *window);
162 static void gnc_main_window_cmd_edit_copy (GtkAction *action, GncMainWindow *window);
163 static void gnc_main_window_cmd_edit_paste (GtkAction *action, GncMainWindow *window);
164 static void gnc_main_window_cmd_edit_preferences (GtkAction *action, GncMainWindow *window);
165 static void gnc_main_window_cmd_view_refresh (GtkAction *action, GncMainWindow *window);
166 static void gnc_main_window_cmd_view_toolbar (GtkAction *action, GncMainWindow *window);
167 static void gnc_main_window_cmd_view_summary (GtkAction *action, GncMainWindow *window);
168 static void gnc_main_window_cmd_view_statusbar (GtkAction *action, GncMainWindow *window);
169 static void gnc_main_window_cmd_actions_reset_warnings (GtkAction *action, GncMainWindow *window);
170 static void gnc_main_window_cmd_actions_rename_page (GtkAction *action, GncMainWindow *window);
171 static void gnc_main_window_cmd_window_new (GtkAction *action, GncMainWindow *window);
172 static void gnc_main_window_cmd_window_move_page (GtkAction *action, GncMainWindow *window);
173 #ifndef MAC_INTEGRATION
174 static void gnc_main_window_cmd_window_raise (GtkAction *action, GtkRadioAction *current, GncMainWindow *window);
175 #endif
176 static void gnc_main_window_cmd_help_tutorial (GtkAction *action, GncMainWindow *window);
177 static void gnc_main_window_cmd_help_contents (GtkAction *action, GncMainWindow *window);
178 static void gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window);
179 
180 static void do_popup_menu(GncPluginPage *page, GdkEventButton *event);
181 static GtkWidget *gnc_main_window_get_statusbar (GncWindow *window_in);
182 static void statusbar_notification_lastmodified(void);
183 static void gnc_main_window_update_tab_position (gpointer prefs, gchar *pref, gpointer user_data);
184 static void gnc_main_window_remove_prefs (GncMainWindow *window);
185 
186 #ifdef MAC_INTEGRATION
187 static void gnc_quartz_shutdown(GtkosxApplication *theApp, gpointer data);
188 static gboolean gnc_quartz_should_quit(GtkosxApplication *theApp, GncMainWindow *window);
189 static void gnc_quartz_set_menu(GncMainWindow* window);
190 #endif
191 
194 typedef struct GncMainWindowPrivate
195 {
200  GtkWidget *menu_dock;
203  GtkWidget *toolbar;
205  GtkWidget *notebook;
207  gboolean show_color_tabs;
211  GtkWidget *statusbar;
215  GtkWidget *progressbar;
216 
220  GtkActionGroup *action_group;
221 
225  GList *usage_order;
231  gint pos[2];
236  GHashTable *merged_actions_table;
238  gboolean restoring_pages;
240 
241 GNC_DEFINE_TYPE_WITH_CODE(GncMainWindow, gnc_main_window, GTK_TYPE_WINDOW,
242  G_ADD_PRIVATE (GncMainWindow)
243  G_IMPLEMENT_INTERFACE (GNC_TYPE_WINDOW,
244  gnc_window_main_window_init))
245 
246 #define GNC_MAIN_WINDOW_GET_PRIVATE(o) \
247  ((GncMainWindowPrivate*)g_type_instance_get_private((GTypeInstance*)o, GNC_TYPE_MAIN_WINDOW))
248 
251 typedef struct
252 {
255  guint merge_id;
258  GtkActionGroup *action_group;
259 } MergedActionEntry;
260 
263 static guint main_window_signals[LAST_SIGNAL] = { 0 };
264 
265 
270 static GtkActionEntry gnc_menu_actions [] =
271 {
272  /* Toplevel */
273 
274  { "FileAction", NULL, N_("_File"), NULL, NULL, NULL, },
275  { "EditAction", NULL, N_("_Edit"), NULL, NULL, NULL },
276  { "ViewAction", NULL, N_("_View"), NULL, NULL, NULL },
277  { "ActionsAction", NULL, N_("_Actions"), NULL, NULL, NULL },
278  { "TransactionAction", NULL, N_("Tra_nsaction"), NULL, NULL, NULL },
279  { "ReportsAction", NULL, N_("_Reports"), NULL, NULL, NULL },
280  { "ToolsAction", NULL, N_("_Tools"), NULL, NULL, NULL },
281  { "ExtensionsAction", NULL, N_("E_xtensions"), NULL, NULL, NULL },
282  { "WindowsAction", NULL, N_("_Windows"), NULL, NULL, NULL },
283  { "HelpAction", NULL, N_("_Help"), NULL, NULL, NULL },
284 
285  /* File menu */
286 
287  { "FileImportAction", NULL, N_("_Import"), NULL, NULL, NULL },
288  { "FileExportAction", NULL, N_("_Export"), NULL, NULL, NULL },
289  {
290  "FilePrintAction", "document-print", N_("_Print..."), "<primary>p",
291  N_("Print the currently active page"), NULL
292  },
293 #ifndef GTK_STOCK_PAGE_SETUP
294 # define GTK_STOCK_PAGE_SETUP NULL
295 #endif
296  {
297  "FilePageSetupAction", "document-page-setup", N_("Pa_ge Setup..."), "<primary><shift>p",
298  N_("Specify the page size and orientation for printing"),
299  G_CALLBACK (gnc_main_window_cmd_page_setup)
300  },
301  {
302  "FilePropertiesAction", "document-properties", N_("Proper_ties"), "<Alt>Return",
303  N_("Edit the properties of the current file"),
304  G_CALLBACK (gnc_main_window_cmd_file_properties)
305  },
306  {
307  "FileCloseAction", "window-close", N_("_Close"), "<primary>W",
308  N_("Close the currently active page"),
309  G_CALLBACK (gnc_main_window_cmd_file_close)
310  },
311  {
312  "FileQuitAction", "application-exit", N_("_Quit"), "<primary>Q",
313  N_("Quit this application"),
314  G_CALLBACK (gnc_main_window_cmd_file_quit)
315  },
316 
317  /* Edit menu */
318 
319  {
320  "EditCutAction", "edit-cut", N_("Cu_t"), "<primary>X",
321  N_("Cut the current selection and copy it to clipboard"),
322  G_CALLBACK (gnc_main_window_cmd_edit_cut)
323  },
324  {
325  "EditCopyAction", "edit-copy", N_("_Copy"), "<primary>C",
326  N_("Copy the current selection to clipboard"),
327  G_CALLBACK (gnc_main_window_cmd_edit_copy)
328  },
329  {
330  "EditPasteAction", "edit-paste", N_("_Paste"), "<primary>V",
331  N_("Paste the clipboard content at the cursor position"),
332  G_CALLBACK (gnc_main_window_cmd_edit_paste)
333  },
334  {
335  "EditPreferencesAction", "preferences-system", N_("Pr_eferences"), NULL,
336  N_("Edit the global preferences of GnuCash"),
337  G_CALLBACK (gnc_main_window_cmd_edit_preferences)
338  },
339 
340  /* View menu */
341 
342  {
343  "ViewSortByAction", NULL, N_("_Sort By..."), NULL,
344  N_("Select sorting criteria for this page view"), NULL
345  },
346  {
347  "ViewFilterByAction", NULL, N_("_Filter By..."), NULL,
348  N_("Select the account types that should be displayed."), NULL
349  },
350  {
351  "ViewRefreshAction", "view-refresh", N_("_Refresh"), "<primary>r",
352  N_("Refresh this window"),
353  G_CALLBACK (gnc_main_window_cmd_view_refresh)
354  },
355 
356  /* Actions menu */
357 
358  { "ScrubMenuAction", NULL, N_("_Check & Repair"), NULL, NULL, NULL },
359  {
360  "ActionsForgetWarningsAction", NULL, N_("Reset _Warnings..."), NULL,
361  N_("Reset the state of all warning messages so they will be shown again."),
362  G_CALLBACK (gnc_main_window_cmd_actions_reset_warnings)
363  },
364  {
365  "ActionsRenamePageAction", NULL, N_("Re_name Page"), NULL,
366  N_("Rename this page."),
367  G_CALLBACK (gnc_main_window_cmd_actions_rename_page)
368  },
369 
370  /* Windows menu */
371 
372  {
373  "WindowNewAction", NULL, N_("_New Window"), NULL,
374  N_("Open a new top-level GnuCash window."),
375  G_CALLBACK (gnc_main_window_cmd_window_new)
376  },
377  {
378  "WindowMovePageAction", NULL, N_("New Window with _Page"), NULL,
379  N_("Move the current page to a new top-level GnuCash window."),
380  G_CALLBACK (gnc_main_window_cmd_window_move_page)
381  },
382 
383  /* Help menu */
384 
385  {
386  "HelpTutorialAction", "help-browser", N_("Tutorial and Concepts _Guide"), "<primary>H",
387  N_("Open the GnuCash Tutorial"),
388  G_CALLBACK (gnc_main_window_cmd_help_tutorial)
389  },
390  {
391  "HelpContentsAction", "help-browser", N_("_Contents"), "F1",
392  N_("Open the GnuCash Help"),
393  G_CALLBACK (gnc_main_window_cmd_help_contents)
394  },
395  {
396  "HelpAboutAction", "help-about", N_("_About"), NULL,
397  N_("About GnuCash"),
398  G_CALLBACK (gnc_main_window_cmd_help_about)
399  },
400 };
402 static guint gnc_menu_n_actions = G_N_ELEMENTS (gnc_menu_actions);
403 
406 static GtkToggleActionEntry toggle_actions [] =
407 {
408  {
409  "ViewToolbarAction", NULL, N_("_Toolbar"), NULL,
410  N_("Show/hide the toolbar on this window"),
411  G_CALLBACK (gnc_main_window_cmd_view_toolbar), TRUE
412  },
413  {
414  "ViewSummaryAction", NULL, N_("Su_mmary Bar"), NULL,
415  N_("Show/hide the summary bar on this window"),
416  G_CALLBACK (gnc_main_window_cmd_view_summary), TRUE
417  },
418  {
419  "ViewStatusbarAction", NULL, N_("Stat_us Bar"), NULL,
420  N_("Show/hide the status bar on this window"),
421  G_CALLBACK (gnc_main_window_cmd_view_statusbar), TRUE
422  },
423 };
425 static guint n_toggle_actions = G_N_ELEMENTS (toggle_actions);
426 
427 #ifndef MAC_INTEGRATION
428 
430 static GtkRadioActionEntry radio_entries [] =
431 {
432  { "Window0Action", NULL, N_("Window _1"), NULL, NULL, 0 },
433  { "Window1Action", NULL, N_("Window _2"), NULL, NULL, 1 },
434  { "Window2Action", NULL, N_("Window _3"), NULL, NULL, 2 },
435  { "Window3Action", NULL, N_("Window _4"), NULL, NULL, 3 },
436  { "Window4Action", NULL, N_("Window _5"), NULL, NULL, 4 },
437  { "Window5Action", NULL, N_("Window _6"), NULL, NULL, 5 },
438  { "Window6Action", NULL, N_("Window _7"), NULL, NULL, 6 },
439  { "Window7Action", NULL, N_("Window _8"), NULL, NULL, 7 },
440  { "Window8Action", NULL, N_("Window _9"), NULL, NULL, 8 },
441  { "Window9Action", NULL, N_("Window _0"), NULL, NULL, 9 },
442 };
443 
445 static guint n_radio_entries = G_N_ELEMENTS (radio_entries);
446 #endif
447 
451 static const gchar *gnc_menu_important_actions[] =
452 {
453  "FileCloseAction",
454  NULL,
455 };
456 
457 
462 static const gchar *always_insensitive_actions[] =
463 {
464  "FilePrintAction",
465  NULL
466 };
467 
468 
472 static const gchar *initially_insensitive_actions[] =
473 {
474  "FileCloseAction",
475  NULL
476 };
477 
478 
483 static const gchar *always_hidden_actions[] =
484 {
485  "ViewSortByAction",
486  "ViewFilterByAction",
487  NULL
488 };
489 
490 
493 static const gchar *immutable_page_actions[] =
494 {
495  "FileCloseAction",
496  NULL
497 };
498 
499 
502 static const gchar *multiple_page_actions[] =
503 {
504  "WindowMovePageAction",
505  NULL
506 };
507 
508 
509 /************************************************************
510  * *
511  ************************************************************/
512 #define WINDOW_COUNT "WindowCount"
513 #define WINDOW_STRING "Window %d"
514 #define WINDOW_GEOMETRY "WindowGeometry"
515 #define WINDOW_POSITION "WindowPosition"
516 #define WINDOW_MAXIMIZED "WindowMaximized"
517 #define TOOLBAR_VISIBLE "ToolbarVisible"
518 #define STATUSBAR_VISIBLE "StatusbarVisible"
519 #define SUMMARYBAR_VISIBLE "SummarybarVisible"
520 #define WINDOW_FIRSTPAGE "FirstPage"
521 #define WINDOW_PAGECOUNT "PageCount"
522 #define WINDOW_PAGEORDER "PageOrder"
523 #define PAGE_TYPE "PageType"
524 #define PAGE_NAME "PageName"
525 #define PAGE_STRING "Page %d"
526 
527 typedef struct
528 {
529  GKeyFile *key_file;
530  const gchar *group_name;
531  gint window_num;
532  gint page_num;
533  gint page_offset;
535 
536 
537 gboolean
539 {
540  GncMainWindowPrivate *priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
541  return priv->restoring_pages;
542 }
543 
544 
545 /* Iterator function to walk all pages in all windows, calling the
546  * specified function for each page. */
547 void
548 gnc_main_window_foreach_page (GncMainWindowPageFunc fn, gpointer user_data)
549 {
550  GncMainWindowPrivate *priv;
551  GncMainWindow *window;
552  GncPluginPage *page;
553  GList *w, *p;
554 
555  ENTER(" ");
556  for (w = active_windows; w; w = g_list_next(w))
557  {
558  window = w->data;
559  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
560  for (p = priv->installed_pages; p; p = g_list_next(p))
561  {
562  page = p->data;
563  fn(page, user_data);
564  }
565  }
566  LEAVE(" ");
567 }
568 
569 
581 static void
582 gnc_main_window_restore_page (GncMainWindow *window,
583  GncMainWindowSaveData *data)
584 {
585  GncMainWindowPrivate *priv;
586  GncPluginPage *page;
587  gchar *page_group, *page_type = NULL, *name = NULL;
588  const gchar *class_type;
589  GError *error = NULL;
590 
591  ENTER("window %p, data %p (key file %p, window %d, page start %d, page num %d)",
592  window, data, data->key_file, data->window_num, data->page_offset,
593  data->page_num);
594 
595  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
596  page_group = g_strdup_printf(PAGE_STRING,
597  data->page_offset + data->page_num);
598  page_type = g_key_file_get_string(data->key_file, page_group,
599  PAGE_TYPE, &error);
600  if (error)
601  {
602  g_warning("error reading group %s key %s: %s",
603  page_group, PAGE_TYPE, error->message);
604  goto cleanup;
605  }
606 
607  /* See if the page already exists. */
608  page = g_list_nth_data(priv->installed_pages, data->page_num);
609  if (page)
610  {
611  class_type = GNC_PLUGIN_PAGE_GET_CLASS(page)->plugin_name;
612  if (strcmp(page_type, class_type) != 0)
613  {
614  g_warning("error: page types don't match: state %s, existing page %s",
615  page_type, class_type);
616  goto cleanup;
617  }
618  }
619  else
620  {
621  /* create and install the page */
622  page = gnc_plugin_page_recreate_page(GTK_WIDGET(window), page_type,
623  data->key_file, page_group);
624  if (page)
625  {
626  /* Does the page still need to be installed into the window? */
627  if (page->window == NULL)
628  {
630  gnc_main_window_open_page(window, page);
631  }
632 
633  /* Restore the page name */
634  name = g_key_file_get_string(data->key_file, page_group,
635  PAGE_NAME, &error);
636  if (error)
637  {
638  g_warning("error reading group %s key %s: %s",
639  page_group, PAGE_NAME, error->message);
640  /* Fall through and still show the page. */
641  }
642  else
643  {
644  DEBUG("updating page name for %p to %s.", page, name);
645  main_window_update_page_name(page, name);
646  g_free(name);
647  }
648  }
649  }
650 
651  LEAVE("ok");
652 cleanup:
653  if (error)
654  g_error_free(error);
655  if (page_type)
656  g_free(page_type);
657  g_free(page_group);
658 }
659 
660 
669 static void
670 gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *data)
671 {
672  GncMainWindowPrivate *priv;
673  GtkAction *action;
674  gint *pos, *geom, *order;
675  gsize length;
676  gboolean max, visible, desired_visibility;
677  gchar *window_group;
678  gint page_start, page_count, i;
679  GError *error = NULL;
680 
681  /* Setup */
682  ENTER("window %p, data %p (key file %p, window %d)",
683  window, data, data->key_file, data->window_num);
684  window_group = g_strdup_printf(WINDOW_STRING, data->window_num + 1);
685 
686  /* Deal with the uncommon case that the state file defines a window
687  * but no pages. An example to get in such a situation can be found
688  * here: https://bugs.gnucash.org/show_bug.cgi?id=436479#c3
689  * If this happens on the first window, we will open an account hierarchy
690  * to avoid confusing the user by presenting a completely empty window.
691  * If it happens on a later window, we'll just skip restoring that window.
692  */
693  if (!g_key_file_has_group (data->key_file, window_group) ||
694  !g_key_file_has_key (data->key_file, window_group, WINDOW_PAGECOUNT, &error))
695  {
696  if (window)
697  {
699  PINFO ("saved state had an empty first main window\n"
700  "an account hierarchy page was added automatically to avoid confusion");
701  }
702  else
703  PINFO ("saved state had an empty main window, skipping restore");
704 
705  goto cleanup;
706  }
707 
708 
709  /* Get this window's notebook info */
710  page_count = g_key_file_get_integer(data->key_file,
711  window_group, WINDOW_PAGECOUNT, &error);
712  if (error)
713  {
714  g_warning("error reading group %s key %s: %s",
715  window_group, WINDOW_PAGECOUNT, error->message);
716  goto cleanup;
717  }
718  if (page_count == 0)
719  {
720  /* Should never happen, but has during alpha testing. Having this
721  * check doesn't hurt anything. */
722  goto cleanup;
723  }
724  page_start = g_key_file_get_integer(data->key_file,
725  window_group, WINDOW_FIRSTPAGE, &error);
726  if (error)
727  {
728  g_warning("error reading group %s key %s: %s",
729  window_group, WINDOW_FIRSTPAGE, error->message);
730  goto cleanup;
731  }
732 
733  /* Build a window if we don't already have one */
734  if (window == NULL)
735  {
736  DEBUG("Window %d doesn't exist. Creating new window.", data->window_num);
737  DEBUG("active_windows %p.", active_windows);
738  if (active_windows)
739  DEBUG("first window %p.", active_windows->data);
740  window = gnc_main_window_new();
741  }
742 
743  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
744 
745  /* Get the window coordinates, etc. */
746  geom = g_key_file_get_integer_list(data->key_file, window_group,
747  WINDOW_GEOMETRY, &length, &error);
748  if (error)
749  {
750  g_warning("error reading group %s key %s: %s",
751  window_group, WINDOW_GEOMETRY, error->message);
752  g_error_free(error);
753  error = NULL;
754  }
755  else if (length != 2)
756  {
757  g_warning("invalid number of values for group %s key %s",
758  window_group, WINDOW_GEOMETRY);
759  }
760  else
761  {
762  gtk_window_resize(GTK_WINDOW(window), geom[0], geom[1]);
763  DEBUG("window (%p) size %dx%d", window, geom[0], geom[1]);
764  }
765  /* keep the geometry for a test whether the windows position
766  is offscreen */
767 
768  pos = g_key_file_get_integer_list(data->key_file, window_group,
769  WINDOW_POSITION, &length, &error);
770  if (error)
771  {
772  g_warning("error reading group %s key %s: %s",
773  window_group, WINDOW_POSITION, error->message);
774  g_error_free(error);
775  error = NULL;
776  }
777  else if (length != 2)
778  {
779  g_warning("invalid number of values for group %s key %s",
780  window_group, WINDOW_POSITION);
781  }
782  /* Prevent restoring coordinates if this would move the window off-screen */
783  else if ((pos[0] + (geom ? geom[0] : 0) < 0) ||
784  (pos[0] > gdk_screen_width()) ||
785  (pos[1] + (geom ? geom[1] : 0) < 0) ||
786  (pos[1] > gdk_screen_height()))
787  {
788  DEBUG("position %dx%d, size%dx%d is offscreen; will not move",
789  pos[0], pos[1], geom ? geom[0] : 0, geom ? geom[1] : 0);
790  }
791  else
792  {
793  gtk_window_move(GTK_WINDOW(window), pos[0], pos[1]);
794  priv->pos[0] = pos[0];
795  priv->pos[1] = pos[1];
796  DEBUG("window (%p) position %dx%d", window, pos[0], pos[1]);
797  }
798  if (geom)
799  {
800  g_free(geom);
801  }
802  if (pos)
803  {
804  g_free(pos);
805  }
806 
807  max = g_key_file_get_boolean(data->key_file, window_group,
808  WINDOW_MAXIMIZED, &error);
809  if (error)
810  {
811  g_warning("error reading group %s key %s: %s",
812  window_group, WINDOW_MAXIMIZED, error->message);
813  g_error_free(error);
814  error = NULL;
815  }
816  else if (max)
817  {
818  gtk_window_maximize(GTK_WINDOW(window));
819  }
820 
821  /* Common view menu items */
822  action = gnc_main_window_find_action(window, "ViewToolbarAction");
823  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
824  desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
825  TOOLBAR_VISIBLE, &error);
826  if (error)
827  {
828  g_warning("error reading group %s key %s: %s",
829  window_group, TOOLBAR_VISIBLE, error->message);
830  g_error_free(error);
831  error = NULL;
832  }
833  else if (visible != desired_visibility)
834  {
835  gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
836  }
837 
838  action = gnc_main_window_find_action(window, "ViewSummaryAction");
839  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
840  desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
841  SUMMARYBAR_VISIBLE, &error);
842  if (error)
843  {
844  g_warning("error reading group %s key %s: %s",
845  window_group, TOOLBAR_VISIBLE, error->message);
846  g_error_free(error);
847  error = NULL;
848  }
849  else if (visible != desired_visibility)
850  {
851  gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
852  }
853 
854  action = gnc_main_window_find_action(window, "ViewStatusbarAction");
855  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
856  desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
857  STATUSBAR_VISIBLE, &error);
858  if (error)
859  {
860  g_warning("error reading group %s key %s: %s",
861  window_group, TOOLBAR_VISIBLE, error->message);
862  g_error_free(error);
863  error = NULL;
864  }
865  else if (visible != desired_visibility)
866  {
867  gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
868  }
869  priv->restoring_pages = TRUE;
870  /* Now populate the window with pages. */
871  for (i = 0; i < page_count; i++)
872  {
873  data->page_offset = page_start;
874  data->page_num = i;
875  gnc_main_window_restore_page(window, data);
876 
877  /* give the page a chance to display */
878  while (gtk_events_pending ())
879  gtk_main_iteration ();
880  }
881  priv->restoring_pages = FALSE;
882  /* Restore page ordering within the notebook. Use +1 notation so the
883  * numbers in the page order match the page sections, at least for
884  * the one window case. */
885  order = g_key_file_get_integer_list(data->key_file, window_group,
886  WINDOW_PAGEORDER, &length, &error);
887  if (error)
888  {
889  g_warning("error reading group %s key %s: %s",
890  window_group, WINDOW_PAGEORDER, error->message);
891  g_error_free(error);
892  error = NULL;
893  }
894  else if (length != page_count)
895  {
896  g_warning("%s key %s length %" G_GSIZE_FORMAT " differs from window page count %d",
897  window_group, WINDOW_PAGEORDER, length, page_count);
898  }
899  else
900  {
901  /* Dump any list that might exist */
902  g_list_free(priv->usage_order);
903  priv->usage_order = NULL;
904  /* Now rebuild the list from the key file. */
905  for (i = 0; i < length; i++)
906  {
907  gpointer page = g_list_nth_data(priv->installed_pages, order[i] - 1);
908  if (page)
909  {
910  priv->usage_order = g_list_append(priv->usage_order, page);
911  }
912  }
913  gtk_notebook_set_current_page (GTK_NOTEBOOK(priv->notebook),
914  order[0] - 1);
915 
916  g_signal_emit_by_name (window, "page_changed",
917  g_list_nth_data (priv->usage_order, 0));
918  }
919  if (order)
920  {
921  g_free(order);
922  }
923 
924  LEAVE("window %p", window);
925 cleanup:
926  if (error)
927  g_error_free(error);
928  g_free(window_group);
929  if (window)
930  gtk_widget_show (GTK_WIDGET(window));
931 }
932 
933 void
934 gnc_main_window_restore_all_windows(const GKeyFile *keyfile)
935 {
936  gint i, window_count;
937  GError *error = NULL;
939  GncMainWindow *window;
940 
941  /* We use the same struct for reading and for writing, so we cast
942  away the const. */
943  data.key_file = (GKeyFile *) keyfile;
944  window_count = g_key_file_get_integer(data.key_file, STATE_FILE_TOP,
945  WINDOW_COUNT, &error);
946  if (error)
947  {
948  g_warning("error reading group %s key %s: %s",
949  STATE_FILE_TOP, WINDOW_COUNT, error->message);
950  g_error_free(error);
951  LEAVE("can't read count");
952  return;
953  }
954 
955  /* Restore all state information on the open windows. Window
956  numbers in state file are 1-based. GList indices are 0-based. */
957  gnc_set_busy_cursor (NULL, TRUE);
958  for (i = 0; i < window_count; i++)
959  {
960  data.window_num = i;
961  window = g_list_nth_data(active_windows, i);
962  gnc_main_window_restore_window(window, &data);
963  }
964  gnc_unset_busy_cursor (NULL);
965 
966  statusbar_notification_lastmodified();
967 }
968 
969 void
971 {
972  GtkAction *action;
973 
974  /* The default state should be to have an Account Tree page open
975  * in the window. */
976  DEBUG("no saved state file");
977  if (!window)
978  window = g_list_nth_data(active_windows, 0);
979  gtk_widget_show (GTK_WIDGET(window));
980  action = gnc_main_window_find_action(window, "ViewAccountTreeAction");
981  gtk_action_activate(action);
982 }
983 
993 static void
994 gnc_main_window_save_page (GncPluginPage *page, GncMainWindowSaveData *data)
995 {
996  gchar *page_group;
997  const gchar *plugin_name, *page_name;
998 
999  ENTER("page %p, data %p (key file %p, window %d, page %d)",
1000  page, data, data->key_file, data->window_num, data->page_num);
1001  plugin_name = gnc_plugin_page_get_plugin_name(page);
1002  page_name = gnc_plugin_page_get_page_name(page);
1003  if (!plugin_name || !page_name)
1004  {
1005  LEAVE("not saving invalid page");
1006  return;
1007  }
1008  page_group = g_strdup_printf(PAGE_STRING, data->page_num++);
1009  g_key_file_set_string(data->key_file, page_group, PAGE_TYPE, plugin_name);
1010  g_key_file_set_string(data->key_file, page_group, PAGE_NAME, page_name);
1011 
1012  gnc_plugin_page_save_page(page, data->key_file, page_group);
1013  g_free(page_group);
1014  LEAVE(" ");
1015 }
1016 
1017 
1026 static void
1027 gnc_main_window_save_window (GncMainWindow *window, GncMainWindowSaveData *data)
1028 {
1029  GncMainWindowPrivate *priv;
1030  GtkAction *action;
1031  gint i, num_pages, coords[4], *order;
1032  gboolean maximized, minimized, visible;
1033  gchar *window_group;
1034 
1035  /* Setup */
1036  ENTER("window %p, data %p (key file %p, window %d)",
1037  window, data, data->key_file, data->window_num);
1038  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1039 
1040  /* Check for bogus window structures. */
1041  num_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook));
1042  if (0 == num_pages)
1043  {
1044  LEAVE("empty window %p", window);
1045  return;
1046  }
1047 
1048  /* Save this window's notebook info */
1049  window_group = g_strdup_printf(WINDOW_STRING, data->window_num++);
1050  g_key_file_set_integer(data->key_file, window_group,
1051  WINDOW_PAGECOUNT, num_pages);
1052  g_key_file_set_integer(data->key_file, window_group,
1053  WINDOW_FIRSTPAGE, data->page_num);
1054 
1055  /* Save page ordering within the notebook. Use +1 notation so the
1056  * numbers in the page order match the page sections, at least for
1057  * the one window case. */
1058  order = g_malloc(sizeof(gint) * num_pages);
1059  for (i = 0; i < num_pages; i++)
1060  {
1061  gpointer page = g_list_nth_data(priv->usage_order, i);
1062  order[i] = g_list_index(priv->installed_pages, page) + 1;
1063  }
1064  g_key_file_set_integer_list(data->key_file, window_group,
1065  WINDOW_PAGEORDER, order, num_pages);
1066  g_free(order);
1067 
1068  /* Save the window coordinates, etc. */
1069  gtk_window_get_position(GTK_WINDOW(window), &coords[0], &coords[1]);
1070  gtk_window_get_size(GTK_WINDOW(window), &coords[2], &coords[3]);
1071  maximized = (gdk_window_get_state(gtk_widget_get_window ((GTK_WIDGET(window))))
1072  & GDK_WINDOW_STATE_MAXIMIZED) != 0;
1073  minimized = (gdk_window_get_state(gtk_widget_get_window ((GTK_WIDGET(window))))
1074  & GDK_WINDOW_STATE_ICONIFIED) != 0;
1075 
1076  if (minimized)
1077  {
1078  gint *pos = priv->pos;
1079  g_key_file_set_integer_list(data->key_file, window_group,
1080  WINDOW_POSITION, &pos[0], 2);
1081  DEBUG("window minimized (%p) position %dx%d", window, pos[0], pos[1]);
1082  }
1083  else
1084  g_key_file_set_integer_list(data->key_file, window_group,
1085  WINDOW_POSITION, &coords[0], 2);
1086  g_key_file_set_integer_list(data->key_file, window_group,
1087  WINDOW_GEOMETRY, &coords[2], 2);
1088  g_key_file_set_boolean(data->key_file, window_group,
1089  WINDOW_MAXIMIZED, maximized);
1090  DEBUG("window (%p) position %dx%d, size %dx%d, %s", window, coords[0], coords[1],
1091  coords[2], coords[3],
1092  maximized ? "maximized" : "not maximized");
1093 
1094  /* Common view menu items */
1095  action = gnc_main_window_find_action(window, "ViewToolbarAction");
1096  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
1097  g_key_file_set_boolean(data->key_file, window_group,
1098  TOOLBAR_VISIBLE, visible);
1099  action = gnc_main_window_find_action(window, "ViewSummaryAction");
1100  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
1101  g_key_file_set_boolean(data->key_file, window_group,
1102  SUMMARYBAR_VISIBLE, visible);
1103  action = gnc_main_window_find_action(window, "ViewStatusbarAction");
1104  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
1105  g_key_file_set_boolean(data->key_file, window_group,
1106  STATUSBAR_VISIBLE, visible);
1107 
1108  /* Save individual pages in this window */
1109  g_list_foreach(priv->installed_pages, (GFunc)gnc_main_window_save_page, data);
1110 
1111  g_free(window_group);
1112  LEAVE("window %p", window);
1113 }
1114 
1115 void
1117 {
1118  GncMainWindowSaveData data;
1119 
1120  /* Set up the iterator data structures */
1121  data.key_file = keyfile;
1122  data.window_num = 1;
1123  data.page_num = 1;
1124 
1125  g_key_file_set_integer(data.key_file,
1126  STATE_FILE_TOP, WINDOW_COUNT,
1127  g_list_length(active_windows));
1128  /* Dump all state information on the open windows */
1129  g_list_foreach(active_windows, (GFunc)gnc_main_window_save_window, &data);
1130 }
1131 
1132 
1133 gboolean
1135 {
1136  GncMainWindowPrivate *priv;
1137  GList *item;
1138 
1139  g_return_val_if_fail(GNC_IS_MAIN_WINDOW(window), TRUE);
1140 
1141  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1142  for (item = priv->installed_pages; item; item = g_list_next(item))
1143  {
1144  if (!gnc_plugin_page_finish_pending(item->data))
1145  {
1146  return FALSE;
1147  }
1148  }
1149  return TRUE;
1150 }
1151 
1152 
1153 gboolean
1155 {
1156  const GList *windows, *item;
1157 
1158  windows = gnc_gobject_tracking_get_list(GNC_MAIN_WINDOW_NAME);
1159  for (item = windows; item; item = g_list_next(item))
1160  {
1161  if (!gnc_main_window_finish_pending(item->data))
1162  {
1163  return FALSE;
1164  }
1165  }
1166  if (gnc_gui_refresh_suspended ())
1167  {
1168  gnc_warning_dialog (NULL, "%s", "An operation is still running, wait for it to complete before quitting.");
1169  return FALSE;
1170  }
1171  return TRUE;
1172 }
1173 
1174 
1185 static gboolean
1186 gnc_main_window_page_exists (GncPluginPage *page)
1187 {
1188  GncMainWindow *window;
1189  GncMainWindowPrivate *priv;
1190  GList *walker;
1191 
1192  for (walker = active_windows; walker; walker = g_list_next(walker))
1193  {
1194  window = walker->data;
1195  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1196  if (g_list_find(priv->installed_pages, page))
1197  {
1198  return TRUE;
1199  }
1200  }
1201  return FALSE;
1202 }
1203 
1204 static gboolean auto_save_countdown (GtkWidget *dialog)
1205 {
1206  GtkWidget *label;
1207  gchar *timeoutstr = NULL;
1208 
1209  /* Stop count down if user closed the dialog since the last time we were called */
1210  if (!GTK_IS_DIALOG (dialog))
1211  return FALSE; /* remove timer */
1212 
1213  /* Stop count down if count down text can't be updated */
1214  label = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), "count-down-label"));
1215  if (!GTK_IS_LABEL (label))
1216  return FALSE; /* remove timer */
1217 
1218  /* Protect against rolling over to MAXUINT */
1219  if (secs_to_save)
1220  --secs_to_save;
1221  DEBUG ("Counting down: %d seconds", secs_to_save);
1222 
1223  timeoutstr = g_strdup_printf (MSG_AUTO_SAVE, secs_to_save);
1224  gtk_label_set_text (GTK_LABEL (label), timeoutstr);
1225  g_free (timeoutstr);
1226 
1227  /* Count down reached 0. Save and close dialog */
1228  if (!secs_to_save)
1229  {
1230  gtk_dialog_response (GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);
1231  return FALSE; /* remove timer */
1232  }
1233 
1234  /* Run another cycle */
1235  return TRUE;
1236 }
1237 
1238 
1248 static gboolean
1249 gnc_main_window_prompt_for_save (GtkWidget *window)
1250 {
1251  QofSession *session;
1252  QofBook *book;
1253  GtkWidget *dialog, *msg_area, *label;
1254  gint response;
1255  const gchar *filename, *tmp;
1256  const gchar *title = _("Save changes to file %s before closing?");
1257  /* This should be the same message as in gnc-file.c */
1258  const gchar *message_hours =
1259  _("If you don't save, changes from the past %d hours and %d minutes will be discarded.");
1260  const gchar *message_days =
1261  _("If you don't save, changes from the past %d days and %d hours will be discarded.");
1262  time64 oldest_change;
1263  gint minutes, hours, days;
1264  guint timer_source = 0;
1265  if (!gnc_current_session_exist())
1266  return FALSE;
1267  session = gnc_get_current_session();
1268  book = qof_session_get_book(session);
1269  if (!qof_book_session_not_saved(book))
1270  return FALSE;
1271  filename = qof_session_get_url(session);
1272  if (!strlen (filename))
1273  filename = _("<unknown>");
1274  if ((tmp = strrchr(filename, '/')) != NULL)
1275  filename = tmp + 1;
1276 
1277  /* Remove any pending auto-save timeouts */
1278  gnc_autosave_remove_timer(book);
1279 
1280  dialog = gtk_message_dialog_new(GTK_WINDOW(window),
1281  GTK_DIALOG_MODAL,
1282  GTK_MESSAGE_WARNING,
1283  GTK_BUTTONS_NONE,
1284  title,
1285  filename);
1286  oldest_change = qof_book_get_session_dirty_time(book);
1287  minutes = (gnc_time (NULL) - oldest_change) / 60 + 1;
1288  hours = minutes / 60;
1289  minutes = minutes % 60;
1290  days = hours / 24;
1291  hours = hours % 24;
1292  if (days > 0)
1293  {
1294  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1295  message_days, days, hours);
1296  }
1297  else if (hours > 0)
1298  {
1299  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1300  message_hours, hours, minutes);
1301  }
1302  else
1303  {
1304  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1305  ngettext("If you don't save, changes from the past %d minute will be discarded.",
1306  "If you don't save, changes from the past %d minutes will be discarded.",
1307  minutes), minutes);
1308  }
1309  gtk_dialog_add_buttons(GTK_DIALOG(dialog),
1310  _("Close _Without Saving"), GTK_RESPONSE_CLOSE,
1311  _("_Cancel"), GTK_RESPONSE_CANCEL,
1312  _("_Save"), GTK_RESPONSE_APPLY,
1313  NULL);
1314  gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);
1315 
1316  /* If requested by the user, add a timeout to the question to save automatically
1317  * if the user doesn't answer after a chosen number of seconds.
1318  */
1319  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_CLOSE_EXPIRES))
1320  {
1321  gchar *timeoutstr = NULL;
1322 
1323  secs_to_save = gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_CLOSE_WAIT_TIME);
1324  timeoutstr = g_strdup_printf (MSG_AUTO_SAVE, secs_to_save);
1325  label = GTK_WIDGET(gtk_label_new (timeoutstr));
1326  g_free (timeoutstr);
1327  gtk_widget_show (label);
1328 
1329  msg_area = gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG(dialog));
1330  gtk_box_pack_end (GTK_BOX(msg_area), label, TRUE, TRUE, 0);
1331  g_object_set (G_OBJECT (label), "xalign", 0.0, NULL);
1332 
1333  g_object_set_data (G_OBJECT (dialog), "count-down-label", label);
1334  timer_source = g_timeout_add_seconds (1, (GSourceFunc)auto_save_countdown, dialog);
1335  }
1336 
1337  response = gtk_dialog_run (GTK_DIALOG (dialog));
1338  if (timer_source)
1339  g_source_remove (timer_source);
1340  gtk_widget_destroy(dialog);
1341 
1342  switch (response)
1343  {
1344  case GTK_RESPONSE_APPLY:
1345  gnc_file_save (GTK_WINDOW (window));
1346  return FALSE;
1347 
1348  case GTK_RESPONSE_CLOSE:
1350  return FALSE;
1351 
1352  default:
1353  return TRUE;
1354  }
1355 }
1356 
1357 
1358 static void
1359 gnc_main_window_add_plugin (gpointer plugin,
1360  gpointer window)
1361 {
1362  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
1363  g_return_if_fail (GNC_IS_PLUGIN (plugin));
1364 
1365  ENTER(" ");
1366  gnc_plugin_add_to_window (GNC_PLUGIN (plugin),
1367  GNC_MAIN_WINDOW (window),
1368  window_type);
1369  LEAVE(" ");
1370 }
1371 
1372 static void
1373 gnc_main_window_remove_plugin (gpointer plugin,
1374  gpointer window)
1375 {
1376  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
1377  g_return_if_fail (GNC_IS_PLUGIN (plugin));
1378 
1379  ENTER(" ");
1380  gnc_plugin_remove_from_window (GNC_PLUGIN (plugin),
1381  GNC_MAIN_WINDOW (window),
1382  window_type);
1383  LEAVE(" ");
1384 }
1385 
1386 
1387 static gboolean
1388 gnc_main_window_timed_quit (gpointer dummy)
1389 {
1390  if (gnc_file_save_in_progress())
1391  return TRUE;
1392 
1393  gnc_shutdown (0);
1394  return FALSE;
1395 }
1396 
1397 static gboolean
1398 gnc_main_window_quit(GncMainWindow *window)
1399 {
1400  QofSession *session;
1401  gboolean needs_save, do_shutdown = TRUE;
1402  if (gnc_current_session_exist())
1403  {
1404  session = gnc_get_current_session();
1405  needs_save =
1407  !gnc_file_save_in_progress();
1408  do_shutdown = !needs_save ||
1409  (needs_save &&
1410  !gnc_main_window_prompt_for_save(GTK_WIDGET(window)));
1411  }
1412  if (do_shutdown)
1413  {
1414  GList *w, *next;
1415 
1416  /* This is not a typical list iteration. There is a possibility
1417  * that the window maybe removed from the active_windows list so
1418  * we have to cache the 'next' pointer before executing any code
1419  * in the loop. */
1420  for (w = active_windows; w; w = next)
1421  {
1422  GncMainWindowPrivate *priv;
1423  GncMainWindow *wind = w->data;
1424 
1425  next = g_list_next (w);
1426 
1427  wind->window_quitting = TRUE; // set window_quitting on all windows
1428 
1429  priv = GNC_MAIN_WINDOW_GET_PRIVATE(wind);
1430 
1431  // if there are no pages destroy window
1432  if (priv->installed_pages == NULL)
1433  gtk_widget_destroy (GTK_WIDGET(wind));
1434  }
1435  /* remove the preference callbacks from the main window */
1436  gnc_main_window_remove_prefs (window);
1437  g_timeout_add(250, gnc_main_window_timed_quit, NULL);
1438  return TRUE;
1439  }
1440  return FALSE;
1441 }
1442 
1443 static gboolean
1444 gnc_main_window_delete_event (GtkWidget *window,
1445  GdkEvent *event,
1446  gpointer user_data)
1447 {
1448  static gboolean already_dead = FALSE;
1449 
1450  if (already_dead)
1451  return TRUE;
1452 
1453  if (gnc_list_length_cmp (active_windows, 1) > 0)
1454  {
1455  gint response;
1456  GtkWidget *dialog;
1457  gchar *message = _("This window is closing and will not be restored.");
1458 
1459  dialog = gtk_message_dialog_new (GTK_WINDOW (window),
1460  GTK_DIALOG_DESTROY_WITH_PARENT,
1461  GTK_MESSAGE_QUESTION,
1462  GTK_BUTTONS_NONE,
1463  "%s", _("Close Window?"));
1464  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
1465  "%s", message);
1466 
1467  gtk_dialog_add_buttons (GTK_DIALOG(dialog),
1468  _("_Cancel"), GTK_RESPONSE_CANCEL,
1469  _("_OK"), GTK_RESPONSE_YES,
1470  (gchar *)NULL);
1471  gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_YES);
1472  response = gnc_dialog_run (GTK_DIALOG(dialog), GNC_PREF_WARN_CLOSING_WINDOW_QUESTION);
1473  gtk_widget_destroy (dialog);
1474 
1475  if (response == GTK_RESPONSE_CANCEL)
1476  return TRUE;
1477  }
1478 
1479  if (!gnc_main_window_finish_pending(GNC_MAIN_WINDOW(window)))
1480  {
1481  /* Don't close the window. */
1482  return TRUE;
1483  }
1484 
1485  if (gnc_list_length_cmp (active_windows, 1) > 0)
1486  return FALSE;
1487 
1488  already_dead = gnc_main_window_quit(GNC_MAIN_WINDOW(window));
1489  return TRUE;
1490 }
1491 
1492 
1512 static void
1513 gnc_main_window_event_handler (QofInstance *entity, QofEventId event_type,
1514  gpointer user_data, gpointer event_data)
1515 {
1516  GncMainWindow *window;
1517  GncMainWindowPrivate *priv;
1518  GncPluginPage *page;
1519  GList *item, *next;
1520 
1521  /* hard failures */
1522  g_return_if_fail(GNC_IS_MAIN_WINDOW(user_data));
1523 
1524  /* soft failures */
1525  if (!QOF_CHECK_TYPE(entity, QOF_ID_BOOK))
1526  return;
1527  if (event_type != QOF_EVENT_DESTROY)
1528  return;
1529 
1530  ENTER("entity %p, event %d, window %p, event data %p",
1531  entity, event_type, user_data, event_data);
1532  window = GNC_MAIN_WINDOW(user_data);
1533  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1534 
1535  /* This is not a typical list iteration. We're removing while
1536  * we iterate, so we have to cache the 'next' pointer before
1537  * executing any code in the loop. */
1538  for (item = priv->installed_pages; item; item = next)
1539  {
1540  next = g_list_next(item);
1541  page = GNC_PLUGIN_PAGE(item->data);
1542  if (gnc_plugin_page_has_book (page, (QofBook *)entity))
1544  }
1545 
1546  if (GTK_IS_WIDGET(window) && window->window_quitting)
1547  gtk_widget_destroy (GTK_WIDGET(window));
1548 
1549  LEAVE(" ");
1550 }
1551 
1552 
1569 static gchar *
1570 gnc_main_window_generate_title (GncMainWindow *window)
1571 {
1572  GncMainWindowPrivate *priv;
1573  GncPluginPage *page;
1574  QofBook *book;
1575  gboolean immutable;
1576  gchar *filename = NULL;
1577  const gchar *uri = NULL;
1578  const gchar *dirty = "";
1579  const gchar *readonly_text = NULL;
1580  gchar *readonly;
1581  gchar *title;
1582 
1583  if (gnc_current_session_exist())
1584  {
1585  uri = qof_session_get_url (gnc_get_current_session ());
1586  book = gnc_get_current_book();
1587  if (qof_book_session_not_saved (book))
1588  dirty = "*";
1589  if (qof_book_is_readonly(book))
1590  {
1591  /* Translators: This string is shown in the window title if this
1592  document is, well, read-only. */
1593  readonly_text = _("(read-only)");
1594  }
1595  }
1596  readonly = (readonly_text != NULL)
1597  ? g_strdup_printf(" %s", readonly_text)
1598  : g_strdup("");
1599 
1600  if (!uri || g_strcmp0 (uri, "") == 0)
1601  filename = g_strdup(_("Unsaved Book"));
1602  else
1603  {
1604  if (gnc_uri_targets_local_fs (uri))
1605  {
1606  /* The filename is a true file.
1607  The Gnome HIG 2.0 recommends only the file name (no path) be used. (p15) */
1608  gchar *path = gnc_uri_get_path ( uri );
1609  filename = g_path_get_basename ( path );
1610  g_free ( path );
1611  }
1612  else
1613  {
1614  /* The filename is composed of database connection parameters.
1615  For this we will show access_method://username@database[:port] */
1616  filename = gnc_uri_normalize_uri (uri, FALSE);
1617  }
1618  }
1619 
1620  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1621  page = priv->current_page;
1622  if (page)
1623  {
1624  /* The Gnome HIG 2.0 recommends the application name not be used. (p16)
1625  but several developers prefer to use it anyway. */
1626  title = g_strdup_printf("%s%s%s - %s - GnuCash", dirty, filename, readonly,
1628  }
1629  else
1630  {
1631  title = g_strdup_printf("%s%s%s - GnuCash", dirty, filename, readonly);
1632  }
1633  /* Update the menus based upon whether this is an "immutable" page. */
1634  immutable = page &&
1635  g_object_get_data (G_OBJECT (page), PLUGIN_PAGE_IMMUTABLE);
1637  immutable_page_actions,
1638  "sensitive", !immutable);
1639  /* Trigger sensitivity updtates of other actions such as Save/Revert */
1640  g_signal_emit_by_name (window, "page_changed", page);
1641  g_free( filename );
1642  g_free(readonly);
1643 
1644  return title;
1645 }
1646 
1647 
1657 static void
1658 gnc_main_window_update_title (GncMainWindow *window)
1659 {
1660  gchar *title;
1661 
1662  title = gnc_main_window_generate_title(window);
1663  gtk_window_set_title(GTK_WINDOW(window), title);
1664  g_free(title);
1665 }
1666 
1667 static void
1668 gnc_main_window_update_all_titles (void)
1669 {
1670  g_list_foreach(active_windows,
1671  (GFunc)gnc_main_window_update_title,
1672  NULL);
1673 }
1674 
1675 static void
1676 gnc_main_window_book_dirty_cb (QofBook *book,
1677  gboolean dirty,
1678  gpointer user_data)
1679 {
1680  gnc_main_window_update_all_titles();
1681 
1682  /* Auto-save feature */
1683  gnc_autosave_dirty_handler(book, dirty);
1684 }
1685 
1686 static void
1687 gnc_main_window_attach_to_book (QofSession *session)
1688 {
1689  QofBook *book;
1690 
1691  g_return_if_fail(session);
1692 
1693  book = qof_session_get_book(session);
1694  qof_book_set_dirty_cb(book, gnc_main_window_book_dirty_cb, NULL);
1695  gnc_main_window_update_all_titles();
1696 #ifndef MAC_INTEGRATION
1697  gnc_main_window_update_all_menu_items();
1698 #endif
1699 }
1700 
1701 static guint gnc_statusbar_notification_messageid = 0;
1702 //#define STATUSBAR_NOTIFICATION_AUTOREMOVAL
1703 #ifdef STATUSBAR_NOTIFICATION_AUTOREMOVAL
1704 /* Removes the statusbar notification again that has been pushed to the
1705  * statusbar by generate_statusbar_lastmodified_message. */
1706 static gboolean statusbar_notification_off(gpointer user_data_unused)
1707 {
1708  GncMainWindow *mainwindow = GNC_MAIN_WINDOW (gnc_ui_get_main_window (NULL));
1709  //g_warning("statusbar_notification_off\n");
1710  if (gnc_statusbar_notification_messageid == 0)
1711  return FALSE;
1712 
1713  if (mainwindow)
1714  {
1715  GtkWidget *statusbar = gnc_main_window_get_statusbar(GNC_WINDOW(mainwindow));
1716  gtk_statusbar_remove(GTK_STATUSBAR(statusbar), 0, gnc_statusbar_notification_messageid);
1717  gnc_statusbar_notification_messageid = 0;
1718  }
1719  else
1720  {
1721  g_warning("oops, no GncMainWindow obtained\n");
1722  }
1723  return FALSE; // should not be called again
1724 }
1725 #endif // STATUSBAR_NOTIFICATION_AUTOREMOVAL
1726 
1727 /* Creates a statusbar message stating the last modification time of the opened
1728  * data file. */
1729 static gchar *generate_statusbar_lastmodified_message()
1730 {
1731  gchar *message = NULL;
1732  const gchar *uri = NULL;
1733 
1734  if (gnc_current_session_exist())
1735  {
1736  uri = qof_session_get_url (gnc_get_current_session ());
1737  }
1738 
1739  if (!(uri && strlen (uri)))
1740  return NULL;
1741  else
1742  {
1743  if (gnc_uri_targets_local_fs (uri))
1744  {
1745  /* The filename is a true file. */
1746  gchar *filepath = gnc_uri_get_path ( uri );
1747  gchar *filename = g_path_get_basename ( filepath );
1748  GFile *file = g_file_new_for_uri (uri);
1749  GFileInfo *info = g_file_query_info (file,
1750  G_FILE_ATTRIBUTE_TIME_MODIFIED,
1751  G_FILE_QUERY_INFO_NONE,
1752  NULL, NULL);
1753 
1754  if (info && g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED))
1755  {
1756  guint64 modtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
1757 
1758  /* Translators: This is the date and time that is shown in
1759  the status bar after opening a file: The date and time of
1760  last modification. The string is a format string using
1761  boost::date_time's format flags, see the boost docs for an
1762  explanation of the modifiers. */
1763  char *time_string = gnc_print_time64 (modtime,
1764  _("Last modified on %a, %b %d, %Y at %I:%M %p"));
1765  //g_warning("got time %ld, str=%s\n", mtime, time_string);
1766  /* Translators: This message appears in the status bar after opening the file. */
1767  message = g_strdup_printf(_("File %s opened. %s"),
1768  filename, time_string);
1769  free(time_string);
1770  }
1771  else
1772  {
1773  g_warning("Unable to read mtime for file %s\n", filepath);
1774  // message is still NULL
1775  }
1776  g_free(filename);
1777  g_free(filepath);
1778  g_object_unref (info);
1779  g_object_unref (file);
1780  }
1781  // If the URI is not a file but a database, we can maybe also show
1782  // something useful, but I have no idea how to obtain this information.
1783  }
1784  return message;
1785 }
1786 
1787 static void
1788 statusbar_notification_lastmodified()
1789 {
1790  // First look up the first GncMainWindow to set the statusbar there
1791  GList *iter;
1792  GtkWidget *widget = NULL;
1793  for (iter = active_windows; iter && !(widget && GNC_IS_MAIN_WINDOW(widget));
1794  iter = g_list_next(iter))
1795  {
1796  widget = iter->data;
1797  }
1798  if (widget && GNC_IS_MAIN_WINDOW(widget))
1799  {
1800  // Ok, we found a mainwindow where we can set a statusbar message
1801  GncMainWindow *mainwindow = GNC_MAIN_WINDOW(widget);
1802  GtkWidget *statusbar = gnc_main_window_get_statusbar(GNC_WINDOW(mainwindow));
1803 
1804  gchar *msg = generate_statusbar_lastmodified_message();
1805  if (msg)
1806  {
1807  gnc_statusbar_notification_messageid = gtk_statusbar_push(GTK_STATUSBAR(statusbar), 0, msg);
1808  }
1809  g_free(msg);
1810 
1811 #ifdef STATUSBAR_NOTIFICATION_AUTOREMOVAL
1812  // Also register a timeout callback to remove that statusbar
1813  // notification again after 10 seconds
1814  g_timeout_add(10 * 1000, statusbar_notification_off, NULL); // maybe not needed anyway?
1815 #endif
1816  }
1817  else
1818  {
1819  g_warning("uh oh, no GNC_IS_MAIN_WINDOW\n");
1820  }
1821 }
1822 
1823 
1828 {
1830  gchar *action_name;
1831 
1833  gchar *label;
1834 
1836  gboolean visible;
1837 };
1838 
1839 #ifndef MAC_INTEGRATION
1840 
1853 static void
1854 gnc_main_window_update_one_menu_action (GncMainWindow *window,
1855  struct menu_update *data)
1856 {
1857  GncMainWindowPrivate *priv;
1858  GtkAction* action;
1859 
1860  ENTER("window %p, action %s, label %s, visible %d", window,
1861  data->action_name, data->label, data->visible);
1862  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1863  action = gtk_action_group_get_action(priv->action_group, data->action_name);
1864  if (action)
1865  g_object_set(G_OBJECT(action),
1866  "label", data->label,
1867  "visible", data->visible,
1868  (char *)NULL);
1869  LEAVE(" ");
1870 }
1871 
1884 static void
1885 gnc_main_window_update_radio_button (GncMainWindow *window)
1886 {
1887  GncMainWindowPrivate *priv;
1888  GtkAction *action, *first_action;
1889  GSList *action_list;
1890  gchar *action_name;
1891  gint index;
1892 
1893  ENTER("window %p", window);
1894 
1895  /* Show the new entry in all windows. */
1896  index = g_list_index(active_windows, window);
1897  if (index >= n_radio_entries)
1898  {
1899  LEAVE("window %d, only %d actions", index, n_radio_entries);
1900  return;
1901  }
1902 
1903  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1904  action_name = g_strdup_printf("Window%dAction", index);
1905  action = gtk_action_group_get_action(priv->action_group, action_name);
1906 
1907  /* Block the signal so as not to affect window ordering (top to
1908  * bottom) on the screen */
1909  action_list = gtk_radio_action_get_group(GTK_RADIO_ACTION(action));
1910  if (action_list)
1911  {
1912  first_action = g_slist_last(action_list)->data;
1913  g_signal_handlers_block_by_func(G_OBJECT(first_action),
1914  G_CALLBACK(gnc_main_window_cmd_window_raise),
1915  window);
1916  DEBUG("blocked signal on %p, set %p active, window %p", first_action,
1917  action, window);
1918  gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
1919  g_signal_handlers_unblock_by_func(G_OBJECT(first_action),
1920  G_CALLBACK(gnc_main_window_cmd_window_raise),
1921  window);
1922  }
1923  g_free(action_name);
1924  LEAVE(" ");
1925 }
1926 
1940 static void
1941 gnc_main_window_update_menu_item (GncMainWindow *window)
1942 {
1943  struct menu_update data;
1944  gchar **strings, *title, *expanded;
1945  gint index;
1946 
1947  ENTER("window %p", window);
1948  index = g_list_index(active_windows, window);
1949  if (index > n_radio_entries)
1950  {
1951  LEAVE("skip window %d (only %d entries)", index, n_radio_entries);
1952  return;
1953  }
1954 
1955  /* Figure out the label name. Add the accelerator if possible. */
1956  title = gnc_main_window_generate_title(window);
1957  strings = g_strsplit(title, "_", 0);
1958  g_free(title);
1959  expanded = g_strjoinv("__", strings);
1960  if (index < 10)
1961  {
1962  data.label = g_strdup_printf("_%d %s", (index + 1) % 10, expanded);
1963  g_free(expanded);
1964  }
1965  else
1966  {
1967  data.label = expanded;
1968  }
1969  g_strfreev(strings);
1970 
1971  data.visible = TRUE;
1972  data.action_name = g_strdup_printf("Window%dAction", index);
1973  g_list_foreach(active_windows,
1974  (GFunc)gnc_main_window_update_one_menu_action,
1975  &data);
1976  g_free(data.action_name);
1977  g_free(data.label);
1978 
1979  LEAVE(" ");
1980 }
1981 #endif /* !MAC_INTEGRATION */
1982 
1991 #ifndef MAC_INTEGRATION
1992 static void
1993 gnc_main_window_update_all_menu_items (void)
1994 {
1995  struct menu_update data;
1996  gchar *label;
1997  gint i;
1998 
1999  ENTER("");
2000  /* First update the entries for all existing windows */
2001  g_list_foreach(active_windows,
2002  (GFunc)gnc_main_window_update_menu_item,
2003  NULL);
2004  g_list_foreach(active_windows,
2005  (GFunc)gnc_main_window_update_radio_button,
2006  NULL);
2007 
2008  /* Now hide any entries that aren't being used. */
2009  data.visible = FALSE;
2010  for (i = g_list_length(active_windows); i < n_radio_entries; i++)
2011  {
2012  data.action_name = g_strdup_printf("Window%dAction", i);
2013  label = g_strdup_printf("Window _%d", (i - 1) % 10);
2014  data.label = gettext(label);
2015 
2016  g_list_foreach(active_windows,
2017  (GFunc)gnc_main_window_update_one_menu_action,
2018  &data);
2019 
2020  g_free(data.action_name);
2021  g_free(label);
2022  }
2023  LEAVE(" ");
2024 }
2025 #endif /* !MAC_INTEGRATION */
2026 
2038 static void
2039 gnc_main_window_update_tab_close_one_page (GncPluginPage *page,
2040  gpointer user_data)
2041 {
2042  gboolean *new_value = user_data;
2043  GtkWidget * close_button;
2044 
2045  ENTER("page %p, visible %d", page, *new_value);
2046  close_button = g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON);
2047  if (!close_button)
2048  {
2049  LEAVE("no close button");
2050  return;
2051  }
2052 
2053  if (*new_value)
2054  gtk_widget_show (close_button);
2055  else
2056  gtk_widget_hide (close_button);
2057  LEAVE(" ");
2058 }
2059 
2060 
2073 static void
2074 gnc_main_window_update_tab_close (gpointer prefs, gchar *pref, gpointer user_data)
2075 {
2076  gboolean new_value;
2077 
2078  ENTER(" ");
2079  new_value = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SHOW_CLOSE_BUTTON);
2080  gnc_main_window_foreach_page(
2081  gnc_main_window_update_tab_close_one_page,
2082  &new_value);
2083  LEAVE(" ");
2084 }
2085 
2086 
2095 static void
2096 gnc_main_window_update_tab_color_one_page (GncPluginPage *page,
2097  gpointer user_data)
2098 {
2099  const gchar *color_string;
2100 
2101  ENTER("page %p", page);
2102  color_string = gnc_plugin_page_get_page_color(page);
2103  main_window_update_page_color (page, color_string);
2104  LEAVE(" ");
2105 }
2106 
2107 
2118 static void
2119 gnc_main_window_update_tab_color (gpointer gsettings, gchar *pref, gpointer user_data)
2120 {
2121  GncMainWindowPrivate *priv;
2122  GncMainWindow *window;
2123 
2124  ENTER(" ");
2125  g_return_if_fail(GNC_IS_MAIN_WINDOW(user_data));
2126  window = user_data;
2127  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2128  if (g_strcmp0 (GNC_PREF_TAB_COLOR, pref) == 0)
2129  priv->show_color_tabs = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_COLOR);
2130  gnc_main_window_foreach_page (gnc_main_window_update_tab_color_one_page, window);
2131  LEAVE(" ");
2132 }
2133 
2134 
2146 static void
2147 gnc_main_window_set_tab_ellipsize (GtkWidget *label, gint tab_width)
2148 {
2149  const gchar *lab_text = gtk_label_get_text (GTK_LABEL(label));
2150 
2151  if (tab_width != 0)
2152  {
2153  gint text_length = g_utf8_strlen (lab_text, -1);
2154  if (text_length < tab_width)
2155  {
2156  gtk_label_set_width_chars (GTK_LABEL(label), text_length);
2157  gtk_label_set_ellipsize (GTK_LABEL(label), PANGO_ELLIPSIZE_NONE);
2158  }
2159  else
2160  {
2161  gtk_label_set_width_chars (GTK_LABEL(label), tab_width);
2162  gtk_label_set_ellipsize (GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
2163  }
2164  }
2165  else
2166  {
2167  gtk_label_set_width_chars (GTK_LABEL(label), 15);
2168  gtk_label_set_ellipsize (GTK_LABEL(label), PANGO_ELLIPSIZE_NONE);
2169  }
2170 }
2171 
2172 
2183 static void
2184 gnc_main_window_update_tab_width_one_page (GncPluginPage *page,
2185  gpointer user_data)
2186 {
2187  gint *new_value = user_data;
2188  GtkWidget *label;
2189 
2190  ENTER("page %p, visible %d", page, *new_value);
2191  label = g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_TAB_LABEL);
2192  if (!label)
2193  {
2194  LEAVE("no label");
2195  return;
2196  }
2197  gnc_main_window_set_tab_ellipsize (label, *new_value);
2198  LEAVE(" ");
2199 }
2200 
2201 
2214 static void
2215 gnc_main_window_update_tab_width (gpointer prefs, gchar *pref, gpointer user_data)
2216 {
2217  gint new_value;
2218 
2219  ENTER(" ");
2220  new_value = gnc_prefs_get_float (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_WIDTH);
2221  gnc_main_window_foreach_page(
2222  gnc_main_window_update_tab_width_one_page,
2223  &new_value);
2224  LEAVE(" ");
2225 }
2226 
2227 
2228 /************************************************************
2229  * Tab Label Implementation *
2230  ************************************************************/
2231 static gboolean
2232 main_window_find_tab_items (GncMainWindow *window,
2233  GncPluginPage *page,
2234  GtkWidget **label_p,
2235  GtkWidget **entry_p)
2236 {
2237  GncMainWindowPrivate *priv;
2238  GtkWidget *tab_hbox, *widget, *tab_widget;
2239  GList *children, *tmp;
2240 
2241  ENTER("window %p, page %p, label_p %p, entry_p %p",
2242  window, page, label_p, entry_p);
2243  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2244  *label_p = *entry_p = NULL;
2245 
2246  if (!page->notebook_page)
2247  {
2248  LEAVE("invalid notebook_page");
2249  return FALSE;
2250  }
2251 
2252  tab_widget = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
2253  page->notebook_page);
2254  if (GTK_IS_EVENT_BOX (tab_widget))
2255  tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
2256  else if (GTK_IS_BOX (tab_widget))
2257  tab_hbox = tab_widget;
2258  else
2259  {
2260  PWARN ("Unknown widget for tab label %p", tab_widget);
2261  return FALSE;
2262  }
2263 
2264  children = gtk_container_get_children(GTK_CONTAINER(tab_hbox));
2265  for (tmp = children; tmp; tmp = g_list_next(tmp))
2266  {
2267  widget = tmp->data;
2268  if (GTK_IS_LABEL(widget))
2269  {
2270  *label_p = widget;
2271  }
2272  else if (GTK_IS_ENTRY(widget))
2273  {
2274  *entry_p = widget;
2275  }
2276  }
2277  g_list_free(children);
2278 
2279  LEAVE("label %p, entry %p", *label_p, *entry_p);
2280  return (*label_p && *entry_p);
2281 }
2282 
2283 static gboolean
2284 main_window_find_tab_widget (GncMainWindow *window,
2285  GncPluginPage *page,
2286  GtkWidget **widget_p)
2287 {
2288  GncMainWindowPrivate *priv;
2289 
2290  ENTER("window %p, page %p, widget %p",
2291  window, page, widget_p);
2292  *widget_p = NULL;
2293 
2294  if (!page->notebook_page)
2295  {
2296  LEAVE("invalid notebook_page");
2297  return FALSE;
2298  }
2299 
2300  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2301  *widget_p = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
2302  page->notebook_page);
2303 
2304  LEAVE("widget %p", *widget_p);
2305  return TRUE;
2306 }
2307 
2308 void
2310  const gchar *name_in)
2311 {
2312  GncMainWindow *window;
2313  GncMainWindowPrivate *priv;
2314  GtkWidget *label, *entry;
2315  gchar *name, *old_page_name, *old_page_long_name;
2316  gint lab_width;
2317 
2318  ENTER(" ");
2319 
2320  if ((name_in == NULL) || (*name_in == '\0'))
2321  {
2322  LEAVE("no string");
2323  return;
2324  }
2325  name = g_strstrip(g_strdup(name_in));
2326 
2327  /* Optimization, if the name hasn't changed, don't update X. */
2328  if (*name == '\0' || 0 == strcmp(name, gnc_plugin_page_get_page_name(page)))
2329  {
2330  g_free(name);
2331  LEAVE("empty string or name unchanged");
2332  return;
2333  }
2334 
2335  old_page_name = g_strdup( gnc_plugin_page_get_page_name(page));
2336  old_page_long_name = g_strdup( gnc_plugin_page_get_page_long_name(page));
2337 
2338  /* Update the plugin */
2339  gnc_plugin_page_set_page_name(page, name);
2340 
2341  /* Update the notebook tab */
2342  window = GNC_MAIN_WINDOW(page->window);
2343  if (!window)
2344  {
2345  g_free(old_page_name);
2346  g_free(old_page_long_name);
2347  g_free(name);
2348  LEAVE("no window widget available");
2349  return;
2350  }
2351 
2352  if (main_window_find_tab_items(window, page, &label, &entry))
2353  gtk_label_set_text(GTK_LABEL(label), name);
2354 
2355  /* Adjust the label width for new text */
2356  lab_width = gnc_prefs_get_float (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_WIDTH);
2357  gnc_main_window_update_tab_width_one_page (page, &lab_width);
2358 
2359  /* Update Tooltip on notebook Tab */
2360  if (old_page_long_name && old_page_name
2361  && g_strrstr(old_page_long_name, old_page_name) != NULL)
2362  {
2363  gchar *new_page_long_name;
2364  gint string_position;
2365  GtkWidget *tab_widget;
2366 
2367  string_position = strlen(old_page_long_name) - strlen(old_page_name);
2368  new_page_long_name = g_strconcat(g_strndup(old_page_long_name, string_position), name, NULL);
2369 
2370  gnc_plugin_page_set_page_long_name(page, new_page_long_name);
2371 
2372  if (main_window_find_tab_widget(window, page, &tab_widget))
2373  gtk_widget_set_tooltip_text(tab_widget, new_page_long_name);
2374 
2375  g_free(new_page_long_name);
2376  }
2377 
2378  /* Update the notebook menu */
2379  if (page->notebook_page)
2380  {
2381  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2382  label = gtk_notebook_get_menu_label (GTK_NOTEBOOK(priv->notebook),
2383  page->notebook_page);
2384  gtk_label_set_text(GTK_LABEL(label), name);
2385  }
2386 
2387  /* Force an update of the window title */
2388  gnc_main_window_update_title(window);
2389  g_free(old_page_long_name);
2390  g_free(old_page_name);
2391  g_free(name);
2392  LEAVE("done");
2393 }
2394 
2395 
2396 void
2398  const gchar *color_in)
2399 {
2400  GncMainWindow *window;
2401  GncMainWindowPrivate *priv;
2402  GtkWidget *tab_widget;
2403  GdkRGBA tab_color;
2404  gchar *color_string = NULL;
2405  gboolean want_color = FALSE;
2406 
2407  ENTER(" ");
2408  if (color_in)
2409  color_string = g_strstrip(g_strdup(color_in));
2410 
2411  if (color_string && *color_string != '\0')
2412  want_color = TRUE;
2413 
2414  /* Update the plugin */
2415  window = GNC_MAIN_WINDOW(page->window);
2416  if (want_color)
2417  gnc_plugin_page_set_page_color(page, color_string);
2418  else
2419  gnc_plugin_page_set_page_color(page, NULL);
2420 
2421  /* Update the notebook tab */
2422  main_window_find_tab_widget (window, page, &tab_widget);
2423  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2424 
2425  if (want_color && gdk_rgba_parse(&tab_color, color_string) && priv->show_color_tabs)
2426  {
2427  GtkCssProvider *provider = gtk_css_provider_new();
2428  GtkStyleContext *stylectxt;
2429  gchar *col_str, *widget_css;
2430 
2431  if (!GTK_IS_EVENT_BOX (tab_widget))
2432  {
2433  GtkWidget *event_box = gtk_event_box_new ();
2434  g_object_ref (tab_widget);
2435  gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
2436  page->notebook_page, event_box);
2437  gtk_container_add (GTK_CONTAINER(event_box), tab_widget);
2438  g_object_unref (tab_widget);
2439  tab_widget = event_box;
2440  }
2441 
2442  stylectxt = gtk_widget_get_style_context (GTK_WIDGET (tab_widget));
2443  col_str = gdk_rgba_to_string (&tab_color);
2444  widget_css = g_strconcat ("*{\n background-color:", col_str, ";\n}\n", NULL);
2445 
2446  gtk_css_provider_load_from_data (provider, widget_css, -1, NULL);
2447  gtk_style_context_add_provider (stylectxt, GTK_STYLE_PROVIDER (provider),
2448  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
2449  g_object_unref (provider);
2450  g_free (col_str);
2451  g_free (widget_css);
2452  }
2453  else
2454  {
2455  if (GTK_IS_EVENT_BOX (tab_widget))
2456  {
2457  GtkWidget *tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
2458  g_object_ref (tab_hbox);
2459  gtk_container_remove (GTK_CONTAINER(tab_widget), tab_hbox);
2460  gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
2461  page->notebook_page, tab_hbox);
2462  g_object_unref (tab_hbox);
2463  }
2464  }
2465  g_free(color_string);
2466  LEAVE("done");
2467 }
2468 
2469 
2470 void
2472  gboolean read_only)
2473 {
2474  GncMainWindow *window;
2475  GncMainWindowPrivate *priv;
2476  GtkWidget *tab_widget;
2477  GtkWidget *image = NULL;
2478  GList *children;
2479  gchar *image_name = NULL;
2480  const gchar *icon_name;
2481 
2482  ENTER(" ");
2483 
2484  g_return_if_fail (page && page->window);
2485 
2486  if (!GNC_IS_MAIN_WINDOW (page->window))
2487  return;
2488 
2489  window = GNC_MAIN_WINDOW(page->window);
2490 
2491  /* Get the notebook tab widget */
2492  main_window_find_tab_widget (window, page, &tab_widget);
2493  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2494 
2495  if (!tab_widget)
2496  {
2497  LEAVE("no tab widget");
2498  return;
2499  }
2500 
2501  if (GTK_IS_EVENT_BOX(tab_widget))
2502  tab_widget = gtk_bin_get_child (GTK_BIN(tab_widget));
2503 
2504  children = gtk_container_get_children (GTK_CONTAINER(tab_widget));
2505  /* For each, walk the list of container children to get image widget */
2506  for (GList *child = children; child; child = g_list_next (child))
2507  {
2508  GtkWidget *widget = child->data;
2509  if (GTK_IS_IMAGE(widget))
2510  image = widget;
2511  }
2512  g_list_free (children);
2513 
2514  if (!image)
2515  {
2516  LEAVE("no image to replace");
2517  return;
2518  }
2519 
2520  g_object_get (image, "icon-name", &image_name, NULL);
2521 
2522  if (read_only)
2523  icon_name = "changes-prevent-symbolic";
2524  else
2525  icon_name = GNC_PLUGIN_PAGE_GET_CLASS(page)->tab_icon;
2526 
2527  if (g_strcmp0 (icon_name, image_name) == 0)
2528  {
2529  LEAVE("page icon the same, no need to replace");
2530  g_free (image_name);
2531  return;
2532  }
2533  gtk_container_remove (GTK_CONTAINER(tab_widget), image);
2534  image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
2535  gtk_widget_show (image);
2536 
2537  gtk_container_add (GTK_CONTAINER(tab_widget), image);
2538  gtk_widget_set_margin_start (GTK_WIDGET(image), 5);
2539  gtk_box_reorder_child (GTK_BOX(tab_widget), image, 0);
2540 
2541  g_free (image_name);
2542  LEAVE("done");
2543 }
2544 
2545 
2546 static void
2547 gnc_main_window_tab_entry_activate (GtkWidget *entry,
2548  GncPluginPage *page)
2549 {
2550  GtkWidget *label, *entry2;
2551 
2552  g_return_if_fail(GTK_IS_ENTRY(entry));
2553  g_return_if_fail(GNC_IS_PLUGIN_PAGE(page));
2554 
2555  ENTER("");
2556  if (!main_window_find_tab_items(GNC_MAIN_WINDOW(page->window),
2557  page, &label, &entry2))
2558  {
2559  LEAVE("can't find required widgets");
2560  return;
2561  }
2562 
2563  main_window_update_page_name(page, gtk_entry_get_text(GTK_ENTRY(entry)));
2564 
2565  gtk_widget_hide(entry);
2566  gtk_widget_show(label);
2567  LEAVE("");
2568 }
2569 
2570 
2571 static gboolean
2572 gnc_main_window_tab_entry_editing_done (GtkWidget *entry,
2573  GncPluginPage *page)
2574 {
2575  ENTER("");
2576  gnc_main_window_tab_entry_activate(entry, page);
2577  LEAVE("");
2578  return FALSE;
2579 }
2580 
2581 static gboolean
2582 gnc_main_window_tab_entry_focus_out_event (GtkWidget *entry,
2583  GdkEvent *event,
2584  GncPluginPage *page)
2585 {
2586  ENTER("");
2587  gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
2588  LEAVE("");
2589  return FALSE;
2590 }
2591 
2592 static gboolean
2593 gnc_main_window_tab_entry_key_press_event (GtkWidget *entry,
2594  GdkEventKey *event,
2595  GncPluginPage *page)
2596 {
2597  if (event->keyval == GDK_KEY_Escape)
2598  {
2599  GtkWidget *label, *entry2;
2600 
2601  g_return_val_if_fail(GTK_IS_ENTRY(entry), FALSE);
2602  g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
2603 
2604  ENTER("");
2605  if (!main_window_find_tab_items(GNC_MAIN_WINDOW(page->window),
2606  page, &label, &entry2))
2607  {
2608  LEAVE("can't find required widgets");
2609  return FALSE;
2610  }
2611 
2612  gtk_entry_set_text(GTK_ENTRY(entry), gtk_label_get_text(GTK_LABEL(label)));
2613  gtk_widget_hide(entry);
2614  gtk_widget_show(label);
2615  LEAVE("");
2616  }
2617  return FALSE;
2618 }
2619 
2620 /************************************************************
2621  * Widget Implementation *
2622  ************************************************************/
2623 
2624 
2625 
2633 static void
2634 gnc_main_window_class_init (GncMainWindowClass *klass)
2635 {
2636  GObjectClass *object_class = G_OBJECT_CLASS (klass);
2637  GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS(klass);
2638 
2639  parent_class = g_type_class_peek_parent (klass);
2640 
2641  window_type = g_quark_from_static_string ("gnc-main-window");
2642 
2643  object_class->finalize = gnc_main_window_finalize;
2644 
2645  /* GtkWidget signals */
2646  gtkwidget_class->destroy = gnc_main_window_destroy;
2647 
2659  main_window_signals[PAGE_ADDED] =
2660  g_signal_new ("page_added",
2661  G_OBJECT_CLASS_TYPE (object_class),
2662  G_SIGNAL_RUN_FIRST,
2663  G_STRUCT_OFFSET (GncMainWindowClass, page_added),
2664  NULL, NULL,
2665  g_cclosure_marshal_VOID__OBJECT,
2666  G_TYPE_NONE, 1,
2667  G_TYPE_OBJECT);
2668 
2679  main_window_signals[PAGE_CHANGED] =
2680  g_signal_new ("page_changed",
2681  G_OBJECT_CLASS_TYPE (object_class),
2682  G_SIGNAL_RUN_FIRST,
2683  G_STRUCT_OFFSET (GncMainWindowClass, page_changed),
2684  NULL, NULL,
2685  g_cclosure_marshal_VOID__OBJECT,
2686  G_TYPE_NONE, 1,
2687  G_TYPE_OBJECT);
2688 
2689  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
2690  GNC_PREF_SHOW_CLOSE_BUTTON,
2691  gnc_main_window_update_tab_close,
2692  NULL);
2693  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
2694  GNC_PREF_TAB_WIDTH,
2695  gnc_main_window_update_tab_width,
2696  NULL);
2697 
2698  gnc_hook_add_dangler(HOOK_BOOK_SAVED,
2699  (GFunc)gnc_main_window_update_all_titles, NULL, NULL);
2700  gnc_hook_add_dangler(HOOK_BOOK_OPENED,
2701  (GFunc)gnc_main_window_attach_to_book, NULL, NULL);
2702 
2703 }
2704 
2705 
2714 static void
2715 gnc_main_window_init (GncMainWindow *window, void *data)
2716 {
2717  GncMainWindowPrivate *priv;
2718 
2719  GncMainWindowClass *klass = (GncMainWindowClass*)data;
2720 
2721  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2722  priv->merged_actions_table =
2723  g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
2724 
2725  // Set the name for this dialog so it can be easily manipulated with css
2726  gtk_widget_set_name (GTK_WIDGET(window), "gnc-id-main-window");
2727 
2728  priv->event_handler_id =
2729  qof_event_register_handler(gnc_main_window_event_handler, window);
2730 
2731  priv->restoring_pages = FALSE;
2732 
2733  /* Get the show_color_tabs value preference */
2734  priv->show_color_tabs = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_COLOR);
2735 
2736  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
2737  GNC_PREF_TAB_COLOR,
2738  gnc_main_window_update_tab_color,
2739  window);
2740 
2741  gnc_main_window_setup_window (window);
2742  gnc_gobject_tracking_remember(G_OBJECT(window),
2743  G_OBJECT_CLASS(klass));
2744 }
2745 
2746 
2757 static void
2758 gnc_main_window_finalize (GObject *object)
2759 {
2760  g_return_if_fail (object != NULL);
2761  g_return_if_fail (GNC_IS_MAIN_WINDOW (object));
2762 
2763  if (active_windows == NULL)
2764  {
2765  /* Oops. User killed last window and we didn't catch it. */
2766  g_idle_add((GSourceFunc)gnc_shutdown, 0);
2767  }
2768 
2770  G_OBJECT_CLASS (parent_class)->finalize (object);
2771 }
2772 
2773 
2774 static void
2775 gnc_main_window_remove_prefs (GncMainWindow *window)
2776 {
2777  // remove the registered preference callbacks setup in this file.
2778  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2779  GNC_PREF_TAB_COLOR,
2780  gnc_main_window_update_tab_color,
2781  window);
2782 
2783  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2784  GNC_PREF_SHOW_CLOSE_BUTTON,
2785  gnc_main_window_update_tab_close,
2786  NULL);
2787 
2788  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2789  GNC_PREF_TAB_WIDTH,
2790  gnc_main_window_update_tab_width,
2791  NULL);
2792 
2793  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2794  GNC_PREF_TAB_POSITION_TOP,
2795  gnc_main_window_update_tab_position,
2796  window);
2797 
2798  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2799  GNC_PREF_TAB_POSITION_BOTTOM,
2800  gnc_main_window_update_tab_position,
2801  window);
2802 
2803  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2804  GNC_PREF_TAB_POSITION_LEFT,
2805  gnc_main_window_update_tab_position,
2806  window);
2807 
2808  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2809  GNC_PREF_TAB_POSITION_RIGHT,
2810  gnc_main_window_update_tab_position,
2811  window);
2812 
2813  // remove the registered negative color preference callback.
2814  if (gnc_prefs_get_reg_negative_color_pref_id() > 0 && window->window_quitting)
2815  {
2816  gnc_prefs_remove_cb_by_id (GNC_PREFS_GROUP_GENERAL,
2818  gnc_prefs_set_reg_negative_color_pref_id (0);
2819  }
2820 
2821  // remove the registered auto_raise_lists preference callback.
2822  if (gnc_prefs_get_reg_auto_raise_lists_id() > 0 && window->window_quitting)
2823  {
2824  gnc_prefs_remove_cb_by_id (GNC_PREFS_GROUP_GENERAL_REGISTER,
2826  gnc_prefs_set_reg_auto_raise_lists_id (0);
2827  }
2828 }
2829 
2830 
2831 static void
2832 gnc_main_window_destroy (GtkWidget *widget)
2833 {
2834  GncMainWindow *window;
2835  GncMainWindowPrivate *priv;
2836  GncPluginManager *manager;
2837  GList *plugins;
2838 
2839  g_return_if_fail (widget != NULL);
2840  g_return_if_fail (GNC_IS_MAIN_WINDOW (widget));
2841 
2842  window = GNC_MAIN_WINDOW (widget);
2843 
2844  active_windows = g_list_remove (active_windows, window);
2845 
2846  /* Do these things once */
2847  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2848  if (priv->merged_actions_table)
2849  {
2850 
2851  /* Close any pages in this window */
2852  while (priv->current_page)
2854 
2855  if (gnc_window_get_progressbar_window() == GNC_WINDOW(window))
2856  gnc_window_set_progressbar_window(NULL);
2857 #ifndef MAC_INTEGRATION
2858  /* Update the "Windows" menu in all other windows */
2859  gnc_main_window_update_all_menu_items();
2860 #endif
2861  /* remove the preference callbacks from the main window */
2862  gnc_main_window_remove_prefs (window);
2863 
2865  priv->event_handler_id = 0;
2866 
2867  g_hash_table_destroy (priv->merged_actions_table);
2868  priv->merged_actions_table = NULL;
2869 
2870  /* GncPluginManager stuff */
2871  manager = gnc_plugin_manager_get ();
2872  plugins = gnc_plugin_manager_get_plugins (manager);
2873  g_list_foreach (plugins, gnc_main_window_remove_plugin, window);
2874  g_list_free (plugins);
2875  }
2876  GTK_WIDGET_CLASS (parent_class)->destroy (widget);
2877 }
2878 
2879 
2880 static gboolean
2881 gnc_main_window_key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
2882 {
2883  GncMainWindowPrivate *priv;
2884  GdkModifierType modifiers;
2885 
2886  g_return_val_if_fail (GNC_IS_MAIN_WINDOW(widget), FALSE);
2887 
2888  priv = GNC_MAIN_WINDOW_GET_PRIVATE(widget);
2889 
2890  modifiers = gtk_accelerator_get_default_mod_mask ();
2891 
2892  if ((event->state & modifiers) == (GDK_CONTROL_MASK | GDK_MOD1_MASK)) // Ctrl+Alt+
2893  {
2894  const gchar *account_key = C_ ("lower case key for short cut to 'Accounts'", "a");
2895  guint account_keyval = gdk_keyval_from_name (account_key);
2896 
2897  if ((account_keyval == event->keyval) || (account_keyval == gdk_keyval_to_lower (event->keyval)))
2898  {
2899  gint page = 0;
2900 
2901  for (GList *item = priv->installed_pages; item; item = g_list_next (item))
2902  {
2903  const gchar *pname = gnc_plugin_page_get_plugin_name (GNC_PLUGIN_PAGE(item->data));
2904 
2905  if (g_strcmp0 (pname, "GncPluginPageAccountTree") == 0)
2906  {
2907  gtk_notebook_set_current_page (GTK_NOTEBOOK(priv->notebook), page);
2908  return TRUE;
2909  }
2910  page++;
2911  }
2912  }
2913  else if ((GDK_KEY_Menu == event->keyval) || (GDK_KEY_space == event->keyval))
2914  {
2915  GList *menu = gtk_menu_get_for_attach_widget (GTK_WIDGET(priv->notebook));
2916 
2917  if (menu)
2918  {
2919  gtk_menu_popup_at_widget (GTK_MENU(menu->data),
2920  GTK_WIDGET(priv->notebook),
2921  GDK_GRAVITY_SOUTH,
2922  GDK_GRAVITY_SOUTH,
2923  NULL);
2924  return TRUE;
2925  }
2926  }
2927  }
2928  return FALSE;
2929 }
2930 
2931 
2932 /* Create a new gnc main window plugin.
2933  */
2934 GncMainWindow *
2936 {
2937  GncMainWindow *window;
2938  GtkWindow *old_window;
2939 
2940  window = g_object_new (GNC_TYPE_MAIN_WINDOW, NULL);
2941  gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
2942 
2943  old_window = gnc_ui_get_main_window (NULL);
2944  if (old_window)
2945  {
2946  gint width, height;
2947  gtk_window_get_size (old_window, &width, &height);
2948  gtk_window_resize (GTK_WINDOW (window), width, height);
2949  if ((gdk_window_get_state((gtk_widget_get_window (GTK_WIDGET(old_window))))
2950  & GDK_WINDOW_STATE_MAXIMIZED) != 0)
2951  {
2952  gtk_window_maximize (GTK_WINDOW (window));
2953  }
2954  }
2955  active_windows = g_list_append (active_windows, window);
2956  gnc_main_window_update_title(window);
2957  window->window_quitting = FALSE;
2958  window->just_plugin_prefs = FALSE;
2959 #ifdef MAC_INTEGRATION
2960  gnc_quartz_set_menu(window);
2961 #else
2962  gnc_main_window_update_all_menu_items();
2963 #endif
2964  gnc_engine_add_commit_error_callback( gnc_main_window_engine_commit_error_callback, window );
2965 
2966  // set up a callback for noteboook navigation
2967  g_signal_connect (G_OBJECT(window), "key-press-event",
2968  G_CALLBACK(gnc_main_window_key_press_event),
2969  NULL);
2970 
2971  return window;
2972 }
2973 
2974 /************************************************************
2975  * Utility Functions *
2976  ************************************************************/
2977 
2978 static void
2979 gnc_main_window_engine_commit_error_callback( gpointer data,
2980  QofBackendError errcode )
2981 {
2982  GncMainWindow* window = GNC_MAIN_WINDOW(data);
2983  GtkWidget* dialog;
2984  const gchar *reason = _("Unable to save to database.");
2985  if ( errcode == ERR_BACKEND_READONLY )
2986  reason = _("Unable to save to database: Book is marked read-only.");
2987  dialog = gtk_message_dialog_new( GTK_WINDOW(window),
2988  GTK_DIALOG_DESTROY_WITH_PARENT,
2989  GTK_MESSAGE_ERROR,
2990  GTK_BUTTONS_CLOSE,
2991  "%s",
2992  reason );
2993  gtk_dialog_run(GTK_DIALOG (dialog));
2994  gtk_widget_destroy(dialog);
2995 
2996 }
2997 
3015 static void
3016 gnc_main_window_connect (GncMainWindow *window,
3017  GncPluginPage *page,
3018  GtkWidget *tab_hbox,
3019  GtkWidget *menu_label)
3020 {
3021  GncMainWindowPrivate *priv;
3022  GtkNotebook *notebook;
3023  gint current_position = -1;
3024 
3025  page->window = GTK_WIDGET(window);
3026  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3027  notebook = GTK_NOTEBOOK (priv->notebook);
3028 
3029  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_OPEN_ADJACENT))
3030  current_position = g_list_index (priv->installed_pages, priv->current_page) + 1;
3031 
3032  priv->installed_pages = g_list_insert (priv->installed_pages, page, current_position);
3033  priv->usage_order = g_list_prepend (priv->usage_order, page);
3034  gtk_notebook_insert_page_menu (notebook, page->notebook_page,
3035  tab_hbox, menu_label, current_position);
3036  gtk_notebook_set_tab_reorderable (notebook, page->notebook_page, TRUE);
3037  gnc_plugin_page_inserted (page);
3038  gtk_notebook_set_current_page (notebook, current_position);
3039 
3040  if (GNC_PLUGIN_PAGE_GET_CLASS(page)->window_changed)
3041  (GNC_PLUGIN_PAGE_GET_CLASS(page)->window_changed)(page, GTK_WIDGET(window));
3042  g_signal_emit (window, main_window_signals[PAGE_ADDED], 0, page);
3043 
3044  g_signal_connect(G_OBJECT(page->notebook_page), "popup-menu",
3045  G_CALLBACK(gnc_main_window_popup_menu_cb), page);
3046  g_signal_connect_after(G_OBJECT(page->notebook_page), "button-press-event",
3047  G_CALLBACK(gnc_main_window_button_press_cb), page);
3048 }
3049 
3050 
3064 static void
3065 gnc_main_window_disconnect (GncMainWindow *window,
3066  GncPluginPage *page)
3067 {
3068  GncMainWindowPrivate *priv;
3069  GtkNotebook *notebook;
3070  GncPluginPage *new_page;
3071  gint page_num;
3072 
3073  /* Disconnect the callbacks */
3074  g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
3075  G_CALLBACK(gnc_main_window_popup_menu_cb), page);
3076  g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
3077  G_CALLBACK(gnc_main_window_button_press_cb), page);
3078 
3079  // Remove the page_changed signal callback
3080  gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(page));
3081 
3082  /* Disconnect the page and summarybar from the window */
3083  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3084  if (priv->current_page == page)
3085  {
3086  gnc_plugin_page_unmerge_actions (page, window->ui_merge);
3087  gnc_plugin_page_unselected (page);
3088  priv->current_page = NULL;
3089  }
3090 
3091  /* Remove it from the list of pages in the window */
3092  priv->installed_pages = g_list_remove (priv->installed_pages, page);
3093  priv->usage_order = g_list_remove (priv->usage_order, page);
3094 
3095  /* Switch to the last recently used page */
3096  notebook = GTK_NOTEBOOK (priv->notebook);
3097  if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_NEXT_RECENT))
3098  {
3099  new_page = g_list_nth_data (priv->usage_order, 0);
3100  if (new_page)
3101  {
3102  page_num = gtk_notebook_page_num(notebook, new_page->notebook_page);
3103  gtk_notebook_set_current_page(notebook, page_num);
3104  /* This may have caused WebKit to schedule a timer interrupt which it
3105  sometimes forgets to cancel before deleting the object. See
3106  <https://bugs.webkit.org/show_bug.cgi?id=119003>. Get around this
3107  by flushing all events to get rid of the timer interrupt. */
3108  while (gtk_events_pending())
3109  gtk_main_iteration();
3110  }
3111  }
3112 
3113  /* Remove the page from the notebook */
3114  page_num = gtk_notebook_page_num(notebook, page->notebook_page);
3115  gtk_notebook_remove_page (notebook, page_num);
3116 
3117  if ( gtk_notebook_get_current_page(notebook) == -1)
3118  {
3119  /* Need to synthesize a page changed signal when the last
3120  * page is removed. The notebook doesn't generate a signal
3121  * for this, therefore the switch_page code in this file
3122  * never gets called to generate this signal. */
3123  gnc_main_window_switch_page(notebook, NULL, -1, window);
3124  //g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, NULL);
3125  }
3126 
3127  gnc_plugin_page_removed (page);
3128 
3129  gtk_ui_manager_ensure_update (window->ui_merge);
3130  gnc_window_set_status (GNC_WINDOW(window), page, NULL);
3131 }
3132 
3133 
3134 /************************************************************
3135  * *
3136  ************************************************************/
3137 
3138 
3139 void
3141 {
3142  GncMainWindow *window;
3143  GncMainWindowPrivate *priv;
3144  GtkNotebook *notebook;
3145  gint page_num;
3146 
3147  window = GNC_MAIN_WINDOW (page->window);
3148  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3149  notebook = GTK_NOTEBOOK (priv->notebook);
3150  page_num = gtk_notebook_page_num(notebook, page->notebook_page);
3151  gtk_notebook_set_current_page (notebook, page_num);
3152  gtk_window_present(GTK_WINDOW(window));
3153 }
3154 
3155 
3156 /* Display a data plugin page in a window. If the page already
3157  * exists in any window, then that window will be brought to the
3158  * front and the notebook switch to display the specified page. If
3159  * the page is new then it will be added to the specified window. If
3160  * the window is NULL, the new page will be added to the first
3161  * window.
3162  */
3163 void
3165  GncPluginPage *page)
3166 {
3167  GncMainWindowPrivate *priv;
3168  GtkWidget *tab_hbox;
3169  GtkWidget *label, *entry;
3170  const gchar *icon, *text, *color_string, *lab_text;
3171  GtkWidget *image;
3172  GList *tmp;
3173  gint width;
3174 
3175  ENTER("window %p, page %p", window, page);
3176  if (window)
3177  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3178  g_return_if_fail (GNC_IS_PLUGIN_PAGE (page));
3179  g_return_if_fail (gnc_plugin_page_has_books(page));
3180 
3181  if (gnc_main_window_page_exists(page))
3182  {
3184  return;
3185  }
3186 
3187  /* Does the page want to be in a new window? */
3189  {
3190  /* See if there's a blank window. If so, use that. */
3191  for (tmp = active_windows; tmp; tmp = g_list_next(tmp))
3192  {
3193  window = GNC_MAIN_WINDOW(tmp->data);
3194  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3195  if (priv->installed_pages == NULL)
3196  {
3197  break;
3198  }
3199  }
3200  if (tmp == NULL)
3201  window = gnc_main_window_new ();
3202  gtk_widget_show(GTK_WIDGET(window));
3203  }
3204  else if ((window == NULL) && active_windows)
3205  {
3206  window = active_windows->data;
3207  }
3208 
3209  page->window = GTK_WIDGET(window);
3211  g_object_set_data (G_OBJECT (page->notebook_page),
3212  PLUGIN_PAGE_LABEL, page);
3213 
3214  /*
3215  * The page tab.
3216  */
3217  width = gnc_prefs_get_float(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_WIDTH);
3218  icon = GNC_PLUGIN_PAGE_GET_CLASS(page)->tab_icon;
3219  lab_text = gnc_plugin_page_get_page_name(page);
3220  label = gtk_label_new (lab_text);
3221  g_object_set_data (G_OBJECT (page), PLUGIN_PAGE_TAB_LABEL, label);
3222 
3223  gnc_main_window_set_tab_ellipsize (label, width);
3224 
3225  gtk_widget_show (label);
3226 
3227  tab_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
3228 
3229  if (g_strcmp0 (gnc_plugin_page_get_plugin_name (page), "GncPluginPageAccountTree") == 0)
3230  gtk_widget_set_name (GTK_WIDGET(tab_hbox), "gnc-id-account-page-tab-box");
3231 
3232  gtk_box_set_homogeneous (GTK_BOX (tab_hbox), FALSE);
3233  gtk_widget_show (tab_hbox);
3234 
3235  if (icon != NULL)
3236  {
3237  image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU);
3238  gtk_widget_show (image);
3239  gtk_box_pack_start (GTK_BOX (tab_hbox), image, FALSE, FALSE, 0);
3240  gtk_widget_set_margin_start (GTK_WIDGET(image), 5);
3241  gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0);
3242  }
3243  else
3244  gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0);
3245 
3247  if (text)
3248  {
3249  gtk_widget_set_tooltip_text(tab_hbox, text);
3250  }
3251 
3252  entry = gtk_entry_new();
3253  gtk_widget_hide (entry);
3254  gtk_box_pack_start (GTK_BOX (tab_hbox), entry, TRUE, TRUE, 0);
3255  g_signal_connect(G_OBJECT(entry), "activate",
3256  G_CALLBACK(gnc_main_window_tab_entry_activate), page);
3257  g_signal_connect(G_OBJECT(entry), "focus-out-event",
3258  G_CALLBACK(gnc_main_window_tab_entry_focus_out_event),
3259  page);
3260  g_signal_connect(G_OBJECT(entry), "key-press-event",
3261  G_CALLBACK(gnc_main_window_tab_entry_key_press_event),
3262  page);
3263  g_signal_connect(G_OBJECT(entry), "editing-done",
3264  G_CALLBACK(gnc_main_window_tab_entry_editing_done),
3265  page);
3266 
3267  /* Add close button - Not for immutable pages */
3268  if (!g_object_get_data (G_OBJECT (page), PLUGIN_PAGE_IMMUTABLE))
3269  {
3270  GtkWidget *close_image, *close_button;
3271  GtkRequisition requisition;
3272 
3273  close_button = gtk_button_new();
3274  gtk_button_set_relief(GTK_BUTTON(close_button), GTK_RELIEF_NONE);
3275  close_image = gtk_image_new_from_icon_name ("window-close", GTK_ICON_SIZE_MENU);
3276  gtk_widget_show(close_image);
3277  gtk_widget_get_preferred_size (close_image, &requisition, NULL);
3278  gtk_widget_set_size_request(close_button, requisition.width + 4,
3279  requisition.height + 2);
3280  gtk_container_add(GTK_CONTAINER(close_button), close_image);
3281  if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_SHOW_CLOSE_BUTTON))
3282  gtk_widget_show (close_button);
3283  else
3284  gtk_widget_hide (close_button);
3285 
3286  g_signal_connect_swapped (G_OBJECT (close_button), "clicked",
3287  G_CALLBACK(gnc_main_window_close_page), page);
3288 
3289  gtk_box_pack_start (GTK_BOX (tab_hbox), close_button, FALSE, FALSE, 0);
3290  gtk_widget_set_margin_end (GTK_WIDGET(close_button), 5);
3291  g_object_set_data (G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON, close_button);
3292  }
3293 
3294  /*
3295  * The popup menu
3296  */
3297  label = gtk_label_new (gnc_plugin_page_get_page_name(page));
3298 
3299  /*
3300  * Now install it all in the window.
3301  */
3302  gnc_main_window_connect(window, page, tab_hbox, label);
3303 
3304  color_string = gnc_plugin_page_get_page_color(page);
3305  main_window_update_page_color (page, color_string);
3306  LEAVE("");
3307 }
3308 
3309 
3310 /* Remove a data plugin page from a window and display the previous
3311  * page. If the page removed was the last page in the window, and
3312  * there is more than one window open, then the entire window will be
3313  * destroyed.
3314  */
3315 void
3317 {
3318  GncMainWindow *window;
3319  GncMainWindowPrivate *priv;
3320 
3321  if (!page || !page->notebook_page)
3322  return;
3323 
3324  if (!gnc_plugin_page_finish_pending(page))
3325  return;
3326 
3327  if (!GNC_IS_MAIN_WINDOW (page->window))
3328  return;
3329 
3330  window = GNC_MAIN_WINDOW (page->window);
3331  if (!window)
3332  {
3333  g_warning("Page is not in a window.");
3334  return;
3335  }
3336 
3337  gnc_main_window_disconnect(window, page);
3339  g_object_unref(page);
3340 
3341  /* If this isn't the last window, go ahead and destroy the window. */
3342  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3343  if (priv->installed_pages == NULL)
3344  {
3345  if (window->window_quitting)
3346  {
3348  GList *plugins = gnc_plugin_manager_get_plugins (manager);
3349 
3350  /* remove only the preference callbacks from the window plugins */
3351  window->just_plugin_prefs = TRUE;
3352  g_list_foreach (plugins, gnc_main_window_remove_plugin, window);
3353  window->just_plugin_prefs = FALSE;
3354  g_list_free (plugins);
3355 
3356  /* remove the preference callbacks from the main window */
3357  gnc_main_window_remove_prefs (window);
3358  }
3359  if (window && (gnc_list_length_cmp (active_windows, 1) > 0))
3360  gtk_widget_destroy (GTK_WIDGET(window));
3361  }
3362 }
3363 
3364 
3365 /* Retrieve a pointer to the page that is currently at the front of
3366  * the specified window. Any plugin that needs to manipulate its
3367  * menus based upon the currently selected menu page should connect
3368  * to the "page_changed" signal on a window. The callback function
3369  * from that signal can then call this function to obtain a pointer
3370  * to the current page.
3371  */
3372 GncPluginPage *
3374 {
3375  GncMainWindowPrivate *priv;
3376 
3377  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3378  return priv->current_page;
3379 }
3380 
3381 
3382 /* Manually add a set of actions to the specified window. Plugins
3383  * whose user interface is not hard coded (e.g. the menu-additions
3384  * plugin) must create their actions at run time, then use this
3385  * function to install them into the window.
3386  */
3387 void
3389  const gchar *group_name,
3390  GtkActionGroup *group,
3391  guint merge_id)
3392 {
3393  GncMainWindowPrivate *priv;
3394  MergedActionEntry *entry;
3395 
3396  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3397  g_return_if_fail (group_name != NULL);
3398  g_return_if_fail (GTK_IS_ACTION_GROUP(group));
3399  g_return_if_fail (merge_id > 0);
3400 
3401  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3402  entry = g_new0 (MergedActionEntry, 1);
3403  entry->action_group = group;
3404  entry->merge_id = merge_id;
3405  gtk_ui_manager_ensure_update (window->ui_merge);
3406  g_hash_table_insert (priv->merged_actions_table, g_strdup (group_name), entry);
3407 }
3408 
3409 
3410 /* Add a set of actions to the specified window. This function
3411  * should not need to be called directly by plugin implementors.
3412  * Correctly assigning values to the GncPluginClass fields during
3413  * plugin initialization will cause this routine to be automatically
3414  * called.
3415  */
3416 void
3418  const gchar *group_name,
3419  GtkActionEntry *actions,
3420  guint n_actions,
3421  GtkToggleActionEntry *toggle_actions,
3422  guint n_toggle_actions,
3423  const gchar *filename,
3424  gpointer user_data)
3425 {
3426  GncMainWindowPrivate *priv;
3428  MergedActionEntry *entry;
3429  GError *error = NULL;
3430  gchar *pathname;
3431 
3432  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3433  g_return_if_fail (group_name != NULL);
3434  g_return_if_fail (actions != NULL);
3435  g_return_if_fail (n_actions > 0);
3436  g_return_if_fail (filename != NULL);
3437 
3438  pathname = gnc_filepath_locate_ui_file (filename);
3439  if (pathname == NULL)
3440  return;
3441 
3442  data = g_new0 (GncMainWindowActionData, 1);
3443  data->window = window;
3444  data->data = user_data;
3445 
3446  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3447  entry = g_new0 (MergedActionEntry, 1);
3448  entry->action_group = gtk_action_group_new (group_name);
3449  gtk_action_group_set_translation_domain (entry->action_group, PROJECT_NAME);
3450  gtk_action_group_add_actions (entry->action_group, actions, n_actions, data);
3451  if (toggle_actions != NULL && n_toggle_actions > 0)
3452  {
3453  gtk_action_group_add_toggle_actions (entry->action_group,
3454  toggle_actions, n_toggle_actions,
3455  data);
3456  }
3457  gtk_ui_manager_insert_action_group (window->ui_merge, entry->action_group, 0);
3458  entry->merge_id = gtk_ui_manager_add_ui_from_file (window->ui_merge, pathname, &error);
3459  g_assert(entry->merge_id || error);
3460  if (entry->merge_id)
3461  {
3462  gtk_ui_manager_ensure_update (window->ui_merge);
3463  g_hash_table_insert (priv->merged_actions_table, g_strdup (group_name), entry);
3464  }
3465  else
3466  {
3467  g_critical("Failed to load ui file.\n Filename %s\n Error %s",
3468  filename, error->message);
3469  g_error_free(error);
3470  g_free(entry);
3471  }
3472  g_free(pathname);
3473 }
3474 
3475 
3476 /* Remove a set of actions from the specified window. This function
3477  * should not need to be called directly by plugin implementors. It
3478  * will automatically be called when a plugin is removed from a
3479  * window.
3480  */
3481 void
3483  const gchar *group_name)
3484 {
3485  GncMainWindowPrivate *priv;
3486  MergedActionEntry *entry;
3487 
3488  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3489  g_return_if_fail (group_name != NULL);
3490 
3491  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3492  if (priv->merged_actions_table == NULL)
3493  return;
3494  entry = g_hash_table_lookup (priv->merged_actions_table, group_name);
3495 
3496  if (entry == NULL)
3497  return;
3498 
3499  gtk_ui_manager_remove_action_group (window->ui_merge, entry->action_group);
3500  gtk_ui_manager_remove_ui (window->ui_merge, entry->merge_id);
3501  gtk_ui_manager_ensure_update (window->ui_merge);
3502 
3503  g_hash_table_remove (priv->merged_actions_table, group_name);
3504 }
3505 
3506 
3507 /* Force a full update of the user interface for the specified
3508  * window. This can be an expensive function, but is needed because
3509  * the gtk ui manager doesn't always seem to update properly when
3510  * actions are changed.
3511  */
3512 void
3514 {
3515  GtkActionGroup *force;
3516 
3517  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3518 
3519  /* Unfortunately gtk_ui_manager_ensure_update doesn't work
3520  * here. Force a full update by adding and removing an empty
3521  * action group.
3522  */
3523  force = gtk_action_group_new("force_update");
3524  gtk_ui_manager_insert_action_group (window->ui_merge, force, 0);
3525  gtk_ui_manager_ensure_update (window->ui_merge);
3526  gtk_ui_manager_remove_action_group (window->ui_merge, force);
3527  g_object_unref(force);
3528 }
3529 
3530 
3531 GtkAction *
3532 gnc_main_window_find_action (GncMainWindow *window, const gchar *name)
3533 {
3534  GtkAction *action = NULL;
3535  const GList *groups, *tmp;
3536 
3537  groups = gtk_ui_manager_get_action_groups(window->ui_merge);
3538  for (tmp = groups; tmp; tmp = g_list_next(tmp))
3539  {
3540  action = gtk_action_group_get_action(GTK_ACTION_GROUP(tmp->data), name);
3541  if (action)
3542  break;
3543  }
3544  return action;
3545 }
3546 
3547 
3548 /* Retrieve a specific set of user interface actions from a window.
3549  * This function can be used to get an group of action to be
3550  * manipulated when the front page of a window has changed.
3551  */
3552 GtkActionGroup *
3554  const gchar *group_name)
3555 {
3556  GncMainWindowPrivate *priv;
3557  MergedActionEntry *entry;
3558 
3559  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), NULL);
3560  g_return_val_if_fail (group_name != NULL, NULL);
3561 
3562  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3563  if (priv->merged_actions_table == NULL)
3564  return NULL;
3565  entry = g_hash_table_lookup (priv->merged_actions_table, group_name);
3566 
3567  if (entry == NULL)
3568  return NULL;
3569 
3570  return entry->action_group;
3571 }
3572 
3573 static void
3574 gnc_main_window_update_tab_position (gpointer prefs, gchar *pref, gpointer user_data)
3575 {
3576  GncMainWindow *window;
3577  GtkPositionType position = GTK_POS_TOP;
3578  GncMainWindowPrivate *priv;
3579 
3580  g_return_if_fail (GNC_IS_MAIN_WINDOW(user_data));
3581 
3582  window = GNC_MAIN_WINDOW(user_data);
3583 
3584  ENTER ("window %p", window);
3585  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_BOTTOM))
3586  position = GTK_POS_BOTTOM;
3587  else if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT))
3588  position = GTK_POS_LEFT;
3589  else if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT))
3590  position = GTK_POS_RIGHT;
3591 
3592  priv = GNC_MAIN_WINDOW_GET_PRIVATE (window);
3593  gtk_notebook_set_tab_pos (GTK_NOTEBOOK (priv->notebook), position);
3594 
3595  LEAVE ("");
3596 }
3597 
3598 /*
3599  * Based on code from Epiphany (src/ephy-window.c)
3600  */
3601 static void
3602 gnc_main_window_update_edit_actions_sensitivity (GncMainWindow *window, gboolean hide)
3603 {
3604  GncMainWindowPrivate *priv;
3605  GncPluginPage *page;
3606  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
3607  GtkAction *action;
3608  gboolean can_copy = FALSE, can_cut = FALSE, can_paste = FALSE;
3609 
3610  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3611  page = priv->current_page;
3612  if (page && GNC_PLUGIN_PAGE_GET_CLASS(page)->update_edit_menu_actions)
3613  {
3614  (GNC_PLUGIN_PAGE_GET_CLASS(page)->update_edit_menu_actions)(page, hide);
3615  return;
3616  }
3617 
3618  if (GTK_IS_EDITABLE (widget))
3619  {
3620  gboolean has_selection;
3621 
3622  has_selection = gtk_editable_get_selection_bounds
3623  (GTK_EDITABLE (widget), NULL, NULL);
3624 
3625  can_copy = has_selection;
3626  can_cut = has_selection;
3627  can_paste = TRUE;
3628  }
3629  else if (GTK_IS_TEXT_VIEW (widget))
3630  {
3631  gboolean has_selection;
3632  GtkTextBuffer *text_buffer;
3633 
3634  text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
3635  has_selection = gtk_text_buffer_get_selection_bounds
3636  (text_buffer, NULL, NULL);
3637 
3638  can_copy = has_selection;
3639  can_cut = has_selection;
3640  can_paste = TRUE;
3641  }
3642  else
3643  {
3644 #ifdef ORIGINAL_EPIPHANY_CODE
3645  /* For now we assume all actions are possible */
3646  can_copy = can_cut = can_paste = TRUE;
3647 #else
3648  /* If its not a GtkEditable, we don't know what to do
3649  * with it. */
3650  can_copy = can_cut = can_paste = FALSE;
3651 #endif
3652  }
3653 
3654  action = gnc_main_window_find_action (window, "EditCopyAction");
3655  gtk_action_set_sensitive (action, can_copy);
3656  gtk_action_set_visible (action, !hide || can_copy);
3657  action = gnc_main_window_find_action (window, "EditCutAction");
3658  gtk_action_set_sensitive (action, can_cut);
3659  gtk_action_set_visible (action, !hide || can_cut);
3660  action = gnc_main_window_find_action (window, "EditPasteAction");
3661  gtk_action_set_sensitive (action, can_paste);
3662  gtk_action_set_visible (action, !hide || can_paste);
3663 }
3664 
3665 static void
3666 gnc_main_window_enable_edit_actions_sensitivity (GncMainWindow *window)
3667 {
3668  GtkAction *action;
3669 
3670  action = gnc_main_window_find_action (window, "EditCopyAction");
3671  gtk_action_set_sensitive (action, TRUE);
3672  gtk_action_set_visible (action, TRUE);
3673  action = gnc_main_window_find_action (window, "EditCutAction");
3674  gtk_action_set_sensitive (action, TRUE);
3675  gtk_action_set_visible (action, TRUE);
3676  action = gnc_main_window_find_action (window, "EditPasteAction");
3677  gtk_action_set_sensitive (action, TRUE);
3678  gtk_action_set_visible (action, TRUE);
3679 }
3680 
3681 static void
3682 gnc_main_window_edit_menu_show_cb (GtkWidget *menu,
3683  GncMainWindow *window)
3684 {
3685  gnc_main_window_update_edit_actions_sensitivity (window, FALSE);
3686 }
3687 
3688 static void
3689 gnc_main_window_edit_menu_hide_cb (GtkWidget *menu,
3690  GncMainWindow *window)
3691 {
3692  gnc_main_window_enable_edit_actions_sensitivity (window);
3693 }
3694 
3695 static void
3696 gnc_main_window_init_menu_updaters (GncMainWindow *window)
3697 {
3698  GtkWidget *edit_menu_item, *edit_menu;
3699 
3700  edit_menu_item = gtk_ui_manager_get_widget
3701  (window->ui_merge, "/menubar/Edit");
3702  edit_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (edit_menu_item));
3703 
3704  g_signal_connect (edit_menu, "show",
3705  G_CALLBACK (gnc_main_window_edit_menu_show_cb), window);
3706  g_signal_connect (edit_menu, "hide",
3707  G_CALLBACK (gnc_main_window_edit_menu_hide_cb), window);
3708 }
3709 
3710 static void
3711 gnc_main_window_window_menu (GncMainWindow *window)
3712 {
3713  guint merge_id;
3714 #ifdef MAC_INTEGRATION
3715  gchar *filename = gnc_filepath_locate_ui_file("gnc-windows-menu-ui-quartz.xml");
3716 #else
3717  gchar *filename = gnc_filepath_locate_ui_file("gnc-windows-menu-ui.xml");
3718  GncMainWindowPrivate *priv;
3719 #endif
3720  GError *error = NULL;
3721  g_assert(filename);
3722  merge_id = gtk_ui_manager_add_ui_from_file(window->ui_merge, filename,
3723  &error);
3724  g_free(filename);
3725  g_assert(merge_id);
3726 #ifndef MAC_INTEGRATION
3727  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3728  gtk_action_group_add_radio_actions (priv->action_group,
3729  radio_entries, n_radio_entries,
3730  0,
3731  G_CALLBACK(gnc_main_window_cmd_window_raise),
3732  window);
3733 #endif
3734 };
3735 
3736 /* This is used to prevent the tab having focus */
3737 static gboolean
3738 gnc_main_window_page_focus_in (GtkWidget *widget, GdkEvent *event,
3739  gpointer user_data)
3740 {
3741  GncMainWindow *window = user_data;
3743 
3744  g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, page);
3745  return FALSE;
3746 }
3747 
3748 static void
3749 gnc_main_window_setup_window (GncMainWindow *window)
3750 {
3751  GncMainWindowPrivate *priv;
3752  GtkWidget *main_vbox;
3753  guint merge_id;
3754  GncPluginManager *manager;
3755  GList *plugins;
3756  GError *error = NULL;
3757  gchar *filename;
3758 
3759  ENTER(" ");
3760 
3761  /* Catch window manager delete signal */
3762  g_signal_connect (G_OBJECT (window), "delete-event",
3763  G_CALLBACK (gnc_main_window_delete_event), window);
3764 
3765  /* Create widgets and add them to the window */
3766  main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3767  gtk_box_set_homogeneous (GTK_BOX (main_vbox), FALSE);
3768  gtk_widget_show (main_vbox);
3769  gtk_container_add (GTK_CONTAINER (window), main_vbox);
3770 
3771  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3772  priv->menu_dock = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3773  gtk_box_set_homogeneous (GTK_BOX (priv->menu_dock), FALSE);
3774  gtk_widget_show (priv->menu_dock);
3775  gtk_box_pack_start (GTK_BOX (main_vbox), priv->menu_dock,
3776  FALSE, TRUE, 0);
3777 
3778  priv->notebook = gtk_notebook_new ();
3779  g_object_set(G_OBJECT(priv->notebook),
3780  "scrollable", TRUE,
3781  "enable-popup", TRUE,
3782  (char *)NULL);
3783  gtk_widget_show (priv->notebook);
3784  g_signal_connect (G_OBJECT (priv->notebook), "switch-page",
3785  G_CALLBACK (gnc_main_window_switch_page), window);
3786  g_signal_connect (G_OBJECT (priv->notebook), "page-reordered",
3787  G_CALLBACK (gnc_main_window_page_reordered), window);
3788  g_signal_connect (G_OBJECT (priv->notebook), "focus-in-event",
3789  G_CALLBACK (gnc_main_window_page_focus_in), window);
3790  gtk_box_pack_start (GTK_BOX (main_vbox), priv->notebook,
3791  TRUE, TRUE, 0);
3792 
3793  priv->statusbar = gtk_statusbar_new ();
3794  gtk_widget_show (priv->statusbar);
3795  gtk_box_pack_start (GTK_BOX (main_vbox), priv->statusbar,
3796  FALSE, TRUE, 0);
3797 
3798  priv->progressbar = gtk_progress_bar_new ();
3799  gtk_progress_bar_set_show_text (GTK_PROGRESS_BAR(priv->progressbar), TRUE);
3800  gtk_progress_bar_set_text(GTK_PROGRESS_BAR(priv->progressbar), " ");
3801  gtk_widget_show (priv->progressbar);
3802  gtk_box_pack_start (GTK_BOX (priv->statusbar), priv->progressbar,
3803  FALSE, TRUE, 0);
3804  gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(priv->progressbar),
3805  0.01);
3806 
3807  window->ui_merge = gtk_ui_manager_new ();
3808 
3809  /* Create menu and toolbar information */
3810  priv->action_group = gtk_action_group_new ("MainWindowActions");
3811  gtk_action_group_set_translation_domain (priv->action_group, PROJECT_NAME);
3812  gtk_action_group_add_actions (priv->action_group, gnc_menu_actions,
3813  gnc_menu_n_actions, window);
3814  gtk_action_group_add_toggle_actions (priv->action_group,
3815  toggle_actions, n_toggle_actions,
3816  window);
3818  initially_insensitive_actions,
3819  "sensitive", FALSE);
3821  always_insensitive_actions,
3822  "sensitive", FALSE);
3824  always_hidden_actions,
3825  "visible", FALSE);
3827  gnc_menu_important_actions);
3828  gtk_ui_manager_insert_action_group (window->ui_merge, priv->action_group, 0);
3829 
3830  g_signal_connect (G_OBJECT (window->ui_merge), "add_widget",
3831  G_CALLBACK (gnc_main_window_add_widget), window);
3832 
3833  /* Use the "connect-proxy" signal for tooltip display in the status bar */
3834  g_signal_connect (G_OBJECT (window->ui_merge), "connect-proxy",
3835  G_CALLBACK (gnc_window_connect_proxy), priv->statusbar);
3836 
3837  filename = gnc_filepath_locate_ui_file("gnc-main-window-ui.xml");
3838 
3839  /* Can't do much without a ui. */
3840  g_assert (filename);
3841 
3842  merge_id = gtk_ui_manager_add_ui_from_file (window->ui_merge,
3843  filename, &error);
3844  g_assert(merge_id || error);
3845  if (merge_id)
3846  {
3847  gtk_window_add_accel_group (GTK_WINDOW (window),
3848  gtk_ui_manager_get_accel_group(window->ui_merge));
3849  gtk_ui_manager_ensure_update (window->ui_merge);
3850  }
3851  else
3852  {
3853  g_critical("Failed to load ui file.\n Filename %s\n Error %s",
3854  filename, error->message);
3855  g_error_free(error);
3856  g_assert(merge_id != 0);
3857  }
3858  g_free(filename);
3859  gnc_main_window_window_menu(window);
3860  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
3861  GNC_PREF_TAB_POSITION_TOP,
3862  gnc_main_window_update_tab_position,
3863  window);
3864  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
3865  GNC_PREF_TAB_POSITION_BOTTOM,
3866  gnc_main_window_update_tab_position,
3867  window);
3868  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
3869  GNC_PREF_TAB_POSITION_LEFT,
3870  gnc_main_window_update_tab_position,
3871  window);
3872  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
3873  GNC_PREF_TAB_POSITION_RIGHT,
3874  gnc_main_window_update_tab_position,
3875  window);
3876  gnc_main_window_update_tab_position(NULL, NULL, window);
3877 
3878  gnc_main_window_init_menu_updaters(window);
3879 
3880  /* Testing */
3881  /* Now update the "eXtensions" menu */
3882  if (!gnc_prefs_is_extra_enabled())
3883  {
3884  GtkAction* action;
3885 
3886  action = gtk_action_group_get_action(priv->action_group,
3887  "ExtensionsAction");
3888  gtk_action_set_visible(action, FALSE);
3889  }
3890 
3891  /* GncPluginManager stuff */
3892  manager = gnc_plugin_manager_get ();
3893  plugins = gnc_plugin_manager_get_plugins (manager);
3894  g_list_foreach (plugins, gnc_main_window_add_plugin, window);
3895  g_list_free (plugins);
3896 
3897  g_signal_connect (G_OBJECT (manager), "plugin-added",
3898  G_CALLBACK (gnc_main_window_plugin_added), window);
3899  g_signal_connect (G_OBJECT (manager), "plugin-removed",
3900  G_CALLBACK (gnc_main_window_plugin_removed), window);
3901 
3902  LEAVE(" ");
3903 }
3904 
3905 #ifdef MAC_INTEGRATION
3906 /* Event handlers for the shutdown process. Gnc_quartz_shutdown is
3907  * connected to NSApplicationWillTerminate, the last chance to do
3908  * anything before quitting. The problem is that it's launched from a
3909  * CFRunLoop, not a g_main_loop, and if we call anything that would
3910  * affect the main_loop we get an assert that we're in a subidiary
3911  * loop.
3912  */
3913 static void
3914 gnc_quartz_shutdown (GtkosxApplication *theApp, gpointer data)
3915 {
3916  /* Do Nothing. It's too late. */
3917 }
3918 /* Should quit responds to NSApplicationBlockTermination; returning TRUE means
3919  * "don't terminate", FALSE means "do terminate". gnc_main_window_quit() queues
3920  * a timer that starts an orderly shutdown in 250ms and if we tell macOS it's OK
3921  * to quit GnuCash gets terminated instead of doing its orderly shutdown,
3922  * leaving the book locked.
3923  */
3924 static gboolean
3925 gnc_quartz_should_quit (GtkosxApplication *theApp, GncMainWindow *window)
3926 {
3928  gnc_main_window_quit (window);
3929  return TRUE;
3930 }
3931 
3932 static void
3933 gnc_quartz_set_menu(GncMainWindow* window)
3934 {
3935  GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
3936  GtkWidget *menu;
3937  GtkWidget *item;
3938 
3939  menu = gtk_ui_manager_get_widget (window->ui_merge, "/menubar");
3940  if (GTK_IS_MENU_ITEM (menu))
3941  menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
3942  gtk_widget_hide(menu);
3943  gtkosx_application_set_menu_bar (theApp, GTK_MENU_SHELL (menu));
3944 
3945  item = gtk_ui_manager_get_widget (window->ui_merge,
3946  "/menubar/File/FileQuit");
3947  if (GTK_IS_MENU_ITEM (item))
3948  gtk_widget_hide (GTK_WIDGET (item));
3949 
3950  item = gtk_ui_manager_get_widget (window->ui_merge,
3951  "/menubar/Help/HelpAbout");
3952  if (GTK_IS_MENU_ITEM (item))
3953  {
3954  gtkosx_application_insert_app_menu_item (theApp, GTK_WIDGET (item), 0);
3955  }
3956 
3957  item = gtk_ui_manager_get_widget (window->ui_merge,
3958  "/menubar/Edit/EditPreferences");
3959  if (GTK_IS_MENU_ITEM (item))
3960  {
3961  gtkosx_application_insert_app_menu_item (theApp,
3962  gtk_separator_menu_item_new (), 1);
3963  gtkosx_application_insert_app_menu_item (theApp, GTK_WIDGET (item), 2);
3964  }
3965 
3966  item = gtk_ui_manager_get_widget (window->ui_merge,
3967  "/menubar/Help");
3968  gtkosx_application_set_help_menu(theApp, GTK_MENU_ITEM(item));
3969  item = gtk_ui_manager_get_widget (window->ui_merge,
3970  "/menubar/Windows");
3971  gtkosx_application_set_window_menu(theApp, GTK_MENU_ITEM(item));
3972  g_signal_connect(theApp, "NSApplicationBlockTermination",
3973  G_CALLBACK(gnc_quartz_should_quit), window);
3974  gtkosx_application_set_use_quartz_accelerators (theApp, FALSE);
3975  g_object_unref (theApp);
3976 
3977 }
3978 #endif //MAC_INTEGRATION
3979 
3980 /* Callbacks */
3981 static void
3982 gnc_main_window_add_widget (GtkUIManager *merge,
3983  GtkWidget *widget,
3984  GncMainWindow *window)
3985 {
3986  GncMainWindowPrivate *priv;
3987 
3988  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3989  if (GTK_IS_TOOLBAR (widget))
3990  {
3991  priv->toolbar = widget;
3992  gtk_toolbar_set_style (GTK_TOOLBAR(priv->toolbar),
3993  GTK_TOOLBAR_BOTH);
3994  gtk_toolbar_set_icon_size (GTK_TOOLBAR(priv->toolbar),
3995  GTK_ICON_SIZE_SMALL_TOOLBAR);
3996  }
3997 
3998  gtk_box_pack_start (GTK_BOX (priv->menu_dock), widget, FALSE, FALSE, 0);
3999  gtk_widget_show (widget);
4000 }
4001 
4014 static gboolean
4015 gnc_main_window_show_summarybar (GncMainWindow *window, GtkAction *action)
4016 {
4017  GncMainWindowPrivate *priv;
4018 
4019  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4020  if (action == NULL)
4021  action = gtk_action_group_get_action(priv->action_group,
4022  "ViewSummaryAction");
4023  if (action == NULL)
4024  return TRUE;
4025  return gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
4026 }
4027 
4037 static void
4038 gnc_main_window_switch_page (GtkNotebook *notebook,
4039  gpointer *notebook_page,
4040  gint pos,
4041  GncMainWindow *window)
4042 {
4043  GncMainWindowPrivate *priv;
4044  GtkWidget *child;
4045  GncPluginPage *page;
4046  gboolean visible;
4047 
4048  ENTER("Notebook %p, page, %p, index %d, window %p",
4049  notebook, notebook_page, pos, window);
4050  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
4051 
4052  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4053  if (priv->current_page != NULL)
4054  {
4055  page = priv->current_page;
4056  gnc_plugin_page_unmerge_actions (page, window->ui_merge);
4057  gnc_plugin_page_unselected (page);
4058  }
4059 
4060  child = gtk_notebook_get_nth_page (notebook, pos);
4061  if (child)
4062  {
4063  page = g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL);
4064  }
4065  else
4066  {
4067  page = NULL;
4068  }
4069 
4070  priv->current_page = page;
4071 
4072  if (page != NULL)
4073  {
4074  /* Update the user interface (e.g. menus and toolbars */
4075  gnc_plugin_page_merge_actions (page, window->ui_merge);
4076  visible = gnc_main_window_show_summarybar(window, NULL);
4078 
4079  /* Allow page specific actions */
4080  gnc_plugin_page_selected (page);
4081  gnc_window_update_status (GNC_WINDOW(window), page);
4082 
4083  /* Update the page reference info */
4084  priv->usage_order = g_list_remove (priv->usage_order, page);
4085  priv->usage_order = g_list_prepend (priv->usage_order, page);
4086  }
4087 
4089  multiple_page_actions,
4090  "sensitive",
4091  g_list_length(priv->installed_pages) > 1);
4092 
4093  gnc_main_window_update_title(window);
4094 #ifndef MAC_INTEGRATION
4095  gnc_main_window_update_menu_item(window);
4096 #endif
4097  g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, page);
4098  LEAVE(" ");
4099 }
4100 
4107 static void
4108 gnc_main_window_page_reordered (GtkNotebook *notebook,
4109  GtkWidget *child,
4110  guint pos,
4111  GncMainWindow *window)
4112 {
4113  GncMainWindowPrivate *priv;
4114  GncPluginPage *page;
4115  GList *old_link;
4116 
4117  ENTER("Notebook %p, child %p, index %d, window %p",
4118  notebook, child, pos, window);
4119  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
4120 
4121  if (!child) return;
4122 
4123  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4124 
4125  page = g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL);
4126  if (!page) return;
4127 
4128  old_link = g_list_find (priv->installed_pages, page);
4129  if (!old_link) return;
4130 
4131  priv->installed_pages = g_list_delete_link (priv->installed_pages,
4132  old_link);
4133  priv->installed_pages = g_list_insert (priv->installed_pages,
4134  page, pos);
4135 
4136  LEAVE(" ");
4137 }
4138 
4139 static void
4140 gnc_main_window_plugin_added (GncPlugin *manager,
4141  GncPlugin *plugin,
4142  GncMainWindow *window)
4143 {
4144  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
4145  g_return_if_fail (GNC_IS_PLUGIN (plugin));
4146 
4147  gnc_plugin_add_to_window (plugin, window, window_type);
4148 }
4149 
4150 static void
4151 gnc_main_window_plugin_removed (GncPlugin *manager,
4152  GncPlugin *plugin,
4153  GncMainWindow *window)
4154 {
4155  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
4156  g_return_if_fail (GNC_IS_PLUGIN (plugin));
4157 
4158  gnc_plugin_remove_from_window (plugin, window, window_type);
4159 }
4160 
4161 
4162 /* Command callbacks */
4163 static void
4164 gnc_main_window_cmd_page_setup (GtkAction *action,
4165  GncMainWindow *window)
4166 {
4167  GtkWindow *gtk_window;
4168 
4169  g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
4170 
4171  gtk_window = gnc_window_get_gtk_window(GNC_WINDOW(window));
4172  gnc_ui_page_setup(gtk_window);
4173 }
4174 
4175 gboolean
4177 {
4178  QofBook *book = gnc_get_current_book ();
4179  gboolean use_split_action_for_num_before =
4181  gboolean use_book_currency_before =
4183  gint use_read_only_threshold_before =
4185  gboolean use_split_action_for_num_after;
4186  gboolean use_book_currency_after;
4187  gint use_read_only_threshold_after;
4188  gboolean return_val = FALSE;
4189  GList *results = NULL, *iter;
4190 
4191  if (!options) return return_val;
4192 
4193  results = gnc_option_db_commit (options);
4194  for (iter = results; iter; iter = iter->next)
4195  {
4196  GtkWidget *dialog = gtk_message_dialog_new(gnc_ui_get_main_window (NULL),
4197  0,
4198  GTK_MESSAGE_ERROR,
4199  GTK_BUTTONS_OK,
4200  "%s",
4201  (char*)iter->data);
4202  gtk_dialog_run(GTK_DIALOG(dialog));
4203  gtk_widget_destroy(dialog);
4204  g_free (iter->data);
4205  }
4206  g_list_free (results);
4207  qof_book_begin_edit (book);
4208  qof_book_save_options (book, gnc_option_db_save, options, TRUE);
4209  use_split_action_for_num_after =
4211  use_book_currency_after = gnc_book_use_book_currency (book);
4212 
4213  // mark cached value as invalid so we get new value
4214  book->cached_num_days_autoreadonly_isvalid = FALSE;
4215  use_read_only_threshold_after = qof_book_get_num_days_autoreadonly (book);
4216 
4217  if (use_split_action_for_num_before != use_split_action_for_num_after)
4218  {
4220  use_split_action_for_num_after);
4221  return_val = TRUE;
4222  }
4223  if (use_book_currency_before != use_book_currency_after)
4224  {
4225  gnc_book_option_book_currency_selected_cb (use_book_currency_after);
4226  return_val = TRUE;
4227  }
4228  if (use_read_only_threshold_before != use_read_only_threshold_after)
4229  return_val = TRUE;
4230 
4231  qof_book_commit_edit (book);
4232  return return_val;
4233 }
4234 
4235 static void
4236 gnc_book_options_dialog_apply_cb(GNCOptionWin * optionwin,
4237  gpointer user_data)
4238 {
4239  GNCOptionDB * options = user_data;
4240 
4241  if (!options) return;
4242 
4244  gnc_gui_refresh_all ();
4245 }
4246 
4247 static void
4248 gnc_book_options_dialog_close_cb(GNCOptionWin * optionwin,
4249  gpointer user_data)
4250 {
4251  GNCOptionDB * options = user_data;
4252 
4253  gnc_options_dialog_destroy(optionwin);
4254  gnc_option_db_destroy(options);
4255 }
4256 
4260 void
4262 {
4263  gnc_suspend_gui_refresh ();
4264  if (num_action)
4265  {
4266  /* Set a feature flag in the book for use of the split action field as number.
4267  * This will prevent older GnuCash versions that don't support this feature
4268  * from opening this file. */
4269  gnc_features_set_used (gnc_get_current_book(),
4270  GNC_FEATURE_NUM_FIELD_SOURCE);
4271  }
4272  gnc_book_option_num_field_source_change (num_action);
4273  gnc_resume_gui_refresh ();
4274 }
4275 
4279 void
4280 gnc_book_option_book_currency_selected_cb (gboolean use_book_currency)
4281 {
4282  gnc_suspend_gui_refresh ();
4283  if (use_book_currency)
4284  {
4285  /* Set a feature flag in the book for use of book currency. This will
4286  * prevent older GnuCash versions that don't support this feature from
4287  * opening this file. */
4288  gnc_features_set_used (gnc_get_current_book(),
4289  GNC_FEATURE_BOOK_CURRENCY);
4290  }
4291  gnc_book_option_book_currency_selected (use_book_currency);
4292  gnc_resume_gui_refresh ();
4293 }
4294 
4295 static gboolean
4296 show_handler (const char *class_name, gint component_id,
4297  gpointer user_data, gpointer iter_data)
4298 {
4299  GNCOptionWin *optwin = user_data;
4300  GtkWidget *widget;
4301 
4302  if (!optwin)
4303  return(FALSE);
4304 
4305  widget = gnc_options_dialog_widget(optwin);
4306 
4307  gtk_window_present(GTK_WINDOW(widget));
4308  return(TRUE);
4309 }
4310 
4311 GtkWidget *
4312 gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent)
4313 {
4314  QofBook *book = gnc_get_current_book ();
4315  GNCOptionDB *options;
4316  GNCOptionWin *optionwin;
4317 
4318  options = gnc_option_db_new_for_type (QOF_ID_BOOK);
4319  qof_book_load_options (book, gnc_option_db_load, options);
4320  gnc_option_db_clean (options);
4321 
4322  /* Only allow one Book Options dialog if called from file->properties
4323  menu */
4324  if (gnc_forall_gui_components(DIALOG_BOOK_OPTIONS_CM_CLASS,
4325  show_handler, NULL))
4326  {
4327  return NULL;
4328  }
4329  optionwin = gnc_options_dialog_new_modal (
4330  modal,
4331  (title ? title : _( "Book Options")),
4332  DIALOG_BOOK_OPTIONS_CM_CLASS, parent);
4333  gnc_options_dialog_build_contents (optionwin, options);
4334 
4336 
4337  gnc_options_dialog_set_apply_cb (optionwin,
4338  gnc_book_options_dialog_apply_cb,
4339  (gpointer)options);
4340  gnc_options_dialog_set_close_cb (optionwin,
4341  gnc_book_options_dialog_close_cb,
4342  (gpointer)options);
4343  if (modal)
4345  return gnc_options_dialog_widget (optionwin);
4346 }
4347 
4348 static void
4349 gnc_main_window_cmd_file_properties (GtkAction *action, GncMainWindow *window)
4350 {
4351  gnc_book_options_dialog_cb (FALSE, NULL, GTK_WINDOW (window));
4352 }
4353 
4354 static void
4355 gnc_main_window_cmd_file_close (GtkAction *action, GncMainWindow *window)
4356 {
4357  GncMainWindowPrivate *priv;
4358  GncPluginPage *page;
4359 
4360  g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
4361 
4362  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4363  page = priv->current_page;
4365 }
4366 
4367 static void
4368 gnc_main_window_cmd_file_quit (GtkAction *action, GncMainWindow *window)
4369 {
4371  return;
4372 
4373  gnc_main_window_quit(window);
4374 }
4375 
4376 static void
4377 gnc_main_window_cmd_edit_cut (GtkAction *action, GncMainWindow *window)
4378 {
4379  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW(window));
4380 
4381  if (GTK_IS_EDITABLE(widget))
4382  {
4383  gtk_editable_cut_clipboard (GTK_EDITABLE(widget));
4384  }
4385  else if (GTK_IS_TEXT_VIEW(widget))
4386  {
4387  GtkTextBuffer *text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
4388  GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(widget),
4389  GDK_SELECTION_CLIPBOARD);
4390  gboolean editable = gtk_text_view_get_editable (GTK_TEXT_VIEW(widget));
4391 
4392  if (clipboard)
4393  gtk_text_buffer_cut_clipboard (text_buffer, clipboard, editable);
4394  }
4395 }
4396 
4397 static void
4398 gnc_main_window_cmd_edit_copy (GtkAction *action, GncMainWindow *window)
4399 {
4400  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW(window));
4401 
4402  if (GTK_IS_EDITABLE(widget))
4403  {
4404  gtk_editable_copy_clipboard (GTK_EDITABLE(widget));
4405  }
4406  else if (GTK_IS_TEXT_VIEW(widget))
4407  {
4408  GtkTextBuffer *text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
4409  GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(widget),
4410  GDK_SELECTION_CLIPBOARD);
4411  if (clipboard)
4412  gtk_text_buffer_copy_clipboard (text_buffer, clipboard);
4413  }
4414 }
4415 
4416 static void
4417 gnc_main_window_cmd_edit_paste (GtkAction *action, GncMainWindow *window)
4418 {
4419  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW(window));
4420 
4421  if (GTK_IS_EDITABLE(widget))
4422  {
4423  gtk_editable_paste_clipboard (GTK_EDITABLE(widget));
4424  }
4425  else if (GTK_IS_TEXT_VIEW(widget))
4426  {
4427  GtkTextBuffer *text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
4428  GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(widget),
4429  GDK_SELECTION_CLIPBOARD);
4430  gboolean editable = gtk_text_view_get_editable (GTK_TEXT_VIEW(widget));
4431 
4432  if (clipboard)
4433  gtk_text_buffer_paste_clipboard (text_buffer, clipboard, NULL, editable);
4434  }
4435 }
4436 
4437 static void
4438 gnc_main_window_cmd_edit_preferences (GtkAction *action, GncMainWindow *window)
4439 {
4440  gnc_preferences_dialog (GTK_WINDOW(window));
4441 }
4442 
4443 static void
4444 gnc_main_window_cmd_view_refresh (GtkAction *action, GncMainWindow *window)
4445 {
4446 }
4447 
4448 static void
4449 gnc_main_window_cmd_actions_reset_warnings (GtkAction *action, GncMainWindow *window)
4450 {
4451  gnc_reset_warnings_dialog(GTK_WINDOW(window));
4452 }
4453 
4454 static void
4455 gnc_main_window_cmd_actions_rename_page (GtkAction *action, GncMainWindow *window)
4456 {
4457  GncMainWindowPrivate *priv;
4458  GncPluginPage *page;
4459  GtkWidget *label, *entry;
4460 
4461  ENTER(" ");
4462  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4463  page = priv->current_page;
4464  if (!page)
4465  {
4466  LEAVE("No current page");
4467  return;
4468  }
4469 
4470  if (!main_window_find_tab_items(window, page, &label, &entry))
4471  {
4472  LEAVE("can't find required widgets");
4473  return;
4474  }
4475 
4476  gtk_entry_set_text(GTK_ENTRY(entry), gtk_label_get_text(GTK_LABEL(label)));
4477  gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
4478  gtk_widget_hide(label);
4479  gtk_widget_show(entry);
4480  gtk_widget_grab_focus(entry);
4481  LEAVE("opened for editing");
4482 }
4483 
4484 static void
4485 gnc_main_window_cmd_view_toolbar (GtkAction *action, GncMainWindow *window)
4486 {
4487  GncMainWindowPrivate *priv;
4488 
4489  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4490  if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)))
4491  {
4492  gtk_widget_show (priv->toolbar);
4493  }
4494  else
4495  {
4496  gtk_widget_hide (priv->toolbar);
4497  }
4498 }
4499 
4500 static void
4501 gnc_main_window_cmd_view_summary (GtkAction *action, GncMainWindow *window)
4502 {
4503  GncMainWindowPrivate *priv;
4504  GList *item;
4505  gboolean visible;
4506 
4507  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4508  visible = gnc_main_window_show_summarybar(window, action);
4509  for (item = priv->installed_pages; item; item = g_list_next(item))
4510  {
4512  }
4513 }
4514 
4515 static void
4516 gnc_main_window_cmd_view_statusbar (GtkAction *action, GncMainWindow *window)
4517 {
4518  GncMainWindowPrivate *priv;
4519 
4520  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4521  if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)))
4522  {
4523  gtk_widget_show (priv->statusbar);
4524  }
4525  else
4526  {
4527  gtk_widget_hide (priv->statusbar);
4528  }
4529 }
4530 
4531 static void
4532 gnc_main_window_cmd_window_new (GtkAction *action, GncMainWindow *window)
4533 {
4534  GncMainWindow *new_window;
4535 
4536  /* Create the new window */
4537  ENTER(" ");
4538  new_window = gnc_main_window_new ();
4539  gtk_widget_show(GTK_WIDGET(new_window));
4540  LEAVE(" ");
4541 }
4542 
4543 static void
4544 gnc_main_window_cmd_window_move_page (GtkAction *action, GncMainWindow *window)
4545 {
4546  GncMainWindowPrivate *priv;
4547  GncMainWindow *new_window;
4548  GncPluginPage *page;
4549  GtkNotebook *notebook;
4550  GtkWidget *tab_widget, *menu_widget;
4551 
4552  ENTER("action %p,window %p", action, window);
4553 
4554  /* Setup */
4555  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4556  page = priv->current_page;
4557  if (!page)
4558  {
4559  LEAVE("invalid page");
4560  return;
4561  }
4562  if (!page->notebook_page)
4563  {
4564  LEAVE("invalid notebook_page");
4565  return;
4566  }
4567 
4568  notebook = GTK_NOTEBOOK (priv->notebook);
4569  tab_widget = gtk_notebook_get_tab_label (notebook, page->notebook_page);
4570  menu_widget = gtk_notebook_get_menu_label (notebook, page->notebook_page);
4571 
4572  // Remove the page_changed signal callback
4573  gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(page));
4574 
4575  /* Ref the page components, then remove it from its old window */
4576  g_object_ref(page);
4577  g_object_ref(tab_widget);
4578  g_object_ref(menu_widget);
4579  g_object_ref(page->notebook_page);
4580  gnc_main_window_disconnect(window, page);
4581 
4582  /* Create the new window */
4583  new_window = gnc_main_window_new ();
4584  gtk_widget_show(GTK_WIDGET(new_window));
4585 
4586  /* Now add the page to the new window */
4587  gnc_main_window_connect (new_window, page, tab_widget, menu_widget);
4588 
4589  /* Unref the page components now that we're done */
4590  g_object_unref(page->notebook_page);
4591  g_object_unref(menu_widget);
4592  g_object_unref(tab_widget);
4593  g_object_unref(page);
4594 
4595  /* just a little debugging. :-) */
4596  DEBUG("Moved page %p from window %p to new window %p",
4597  page, window, new_window);
4598  DEBUG("Old window current is %p, new window current is %p",
4599  priv->current_page, priv->current_page);
4600 
4601  LEAVE("page moved");
4602 }
4603 
4604 #ifndef MAC_INTEGRATION
4605 static void
4606 gnc_main_window_cmd_window_raise (GtkAction *action,
4607  GtkRadioAction *current,
4608  GncMainWindow *old_window)
4609 {
4610  GncMainWindow *new_window;
4611  gint value;
4612 
4613  g_return_if_fail(GTK_IS_ACTION(action));
4614  g_return_if_fail(GTK_IS_RADIO_ACTION(current));
4615  g_return_if_fail(GNC_IS_MAIN_WINDOW(old_window));
4616 
4617  ENTER("action %p, current %p, window %p", action, current, old_window);
4618  value = gtk_radio_action_get_current_value(current);
4619  new_window = g_list_nth_data(active_windows, value);
4620  gtk_window_present(GTK_WINDOW(new_window));
4621  /* revert the change in the radio group
4622  * impossible while handling "changed" (G_SIGNAL_NO_RECURSE) */
4623  g_idle_add((GSourceFunc)gnc_main_window_update_radio_button, old_window);
4624  LEAVE(" ");
4625 }
4626 #endif /* !MAC_INTEGRATION */
4627 
4628 static void
4629 gnc_main_window_cmd_help_tutorial (GtkAction *action, GncMainWindow *window)
4630 {
4631  gnc_gnome_help (GTK_WINDOW(window), HF_GUIDE, NULL);
4632 }
4633 
4634 static void
4635 gnc_main_window_cmd_help_contents (GtkAction *action, GncMainWindow *window)
4636 {
4637  gnc_gnome_help (GTK_WINDOW(window), HF_HELP, NULL);
4638 }
4639 
4649 static gchar *
4650 get_file (const gchar *partial)
4651 {
4652  gchar *filename, *text = NULL;
4653  gsize length;
4654 
4655  filename = gnc_filepath_locate_doc_file(partial);
4656  if (filename && g_file_get_contents(filename, &text, &length, NULL))
4657  {
4658  if (length)
4659  {
4660  g_free(filename);
4661  return text;
4662  }
4663  g_free(text);
4664  }
4665  g_free (filename);
4666  return NULL;
4667 }
4668 
4669 
4679 static gchar **
4680 get_file_strsplit (const gchar *partial)
4681 {
4682  gchar *text, **lines;
4683 
4684  text = get_file(partial);
4685  if (!text)
4686  return NULL;
4687 
4688  lines = g_strsplit_set(text, "\r\n", -1);
4689  g_free(text);
4690  return lines;
4691 }
4698 static gboolean
4699 url_signal_cb (GtkAboutDialog *dialog, gchar *uri, gpointer data)
4700 {
4701  gnc_launch_doclink (GTK_WINDOW(dialog), uri);
4702  return TRUE;
4703 }
4704 
4711 static void
4712 gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window)
4713 {
4714  /* Translators: %s will be replaced with the current year */
4715  gchar *copyright = g_strdup_printf(_("Copyright © 1997-%s The GnuCash contributors."),
4716  GNC_VCS_REV_YEAR);
4717  gchar **authors = get_file_strsplit("AUTHORS");
4718  gchar **documenters = get_file_strsplit("DOCUMENTERS");
4719  gchar *license = get_file("LICENSE");
4720  GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
4721  GdkPixbuf *logo = gtk_icon_theme_load_icon (icon_theme,
4722  GNC_ICON_APP,
4723  128,
4724  GTK_ICON_LOOKUP_USE_BUILTIN,
4725  NULL);
4726  gchar *version = g_strdup_printf ("%s: %s\n%s: %s\nFinance::Quote: %s",
4727  _("Version"), gnc_version(),
4728  _("Build ID"), gnc_build_id(),
4731  : "-");
4732  GtkDialog *dialog = GTK_DIALOG (gtk_about_dialog_new ());
4733  g_object_set (G_OBJECT (dialog),
4734  "authors", authors,
4735  "documenters", documenters,
4736  "comments", _("Accounting for personal and small business finance."),
4737  "copyright", copyright,
4738  "license", license,
4739  "logo", logo,
4740  "name", "GnuCash",
4741  /* Translators: the following string will be shown in Help->About->Credits
4742  Enter your name or that of your team and an email contact for feedback.
4743  The string can have multiple rows, so you can also add a list of
4744  contributors. */
4745  "translator-credits", _("translator-credits"),
4746  "version", version,
4747  "website", PACKAGE_URL,
4748  "website-label", _("Visit the GnuCash website."),
4749  NULL);
4750 
4751  g_free(version);
4752  g_free(copyright);
4753  if (license)
4754  g_free(license);
4755  if (documenters)
4756  g_strfreev(documenters);
4757  if (authors)
4758  g_strfreev(authors);
4759  g_object_unref (logo);
4760  g_signal_connect (dialog, "activate-link",
4761  G_CALLBACK (url_signal_cb), NULL);
4762  /* Set dialog to resize. */
4763  gtk_window_set_resizable(GTK_WINDOW (dialog), TRUE);
4764 
4765  gtk_window_set_transient_for (GTK_WINDOW (dialog),
4766  GTK_WINDOW (window));
4767  gtk_dialog_run (dialog);
4768  gtk_widget_destroy (GTK_WIDGET (dialog));
4769 }
4770 
4771 
4772 /************************************************************
4773  * *
4774  ************************************************************/
4775 
4776 void
4778 {
4779  GList *window_iter;
4780 #ifdef MAC_INTEGRATION
4781  GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
4782 #endif
4783  for (window_iter = active_windows; window_iter != NULL; window_iter = window_iter->next)
4784  {
4785  gtk_widget_show(GTK_WIDGET(window_iter->data));
4786  }
4787 #ifdef MAC_INTEGRATION
4788  g_signal_connect(theApp, "NSApplicationWillTerminate",
4789  G_CALLBACK(gnc_quartz_shutdown), NULL);
4790  gtkosx_application_ready(theApp);
4791  g_object_unref (theApp);
4792 #endif
4793 }
4794 
4795 GtkWindow *
4796 gnc_ui_get_gtk_window (GtkWidget *widget)
4797 {
4798  GtkWidget *toplevel;
4799 
4800  if (!widget)
4801  return NULL;
4802 
4803  toplevel = gtk_widget_get_toplevel (widget);
4804  if (toplevel && GTK_IS_WINDOW (toplevel))
4805  return GTK_WINDOW (toplevel);
4806  else
4807  return NULL;
4808 }
4809 
4810 GtkWindow *
4811 gnc_ui_get_main_window (GtkWidget *widget)
4812 {
4813  GList *window;
4814 
4815  GtkWindow *toplevel = gnc_ui_get_gtk_window (widget);
4816  while (toplevel && !GNC_IS_MAIN_WINDOW (toplevel))
4817  toplevel = gtk_window_get_transient_for(toplevel);
4818 
4819  if (toplevel)
4820  return toplevel;
4821 
4822  for (window = active_windows; window; window = window->next)
4823  if (gtk_window_is_active (GTK_WINDOW (window->data)))
4824  return window->data;
4825 
4826  for (window = active_windows; window; window = window->next)
4827  if (gtk_widget_get_mapped (GTK_WIDGET(window->data)))
4828  return window->data;
4829 
4830  return NULL;
4831 }
4832 
4833 
4839 static GtkWindow *
4840 gnc_main_window_get_gtk_window (GncWindow *window)
4841 {
4842  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), NULL);
4843  return GTK_WINDOW(window);
4844 }
4845 
4846 
4852 static GtkWidget *
4853 gnc_main_window_get_statusbar (GncWindow *window_in)
4854 {
4855  GncMainWindowPrivate *priv;
4856  GncMainWindow *window;
4857 
4858  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), NULL);
4859 
4860  window = GNC_MAIN_WINDOW(window_in);
4861  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4862  return priv->statusbar;
4863 }
4864 
4865 
4871 static GtkWidget *
4872 gnc_main_window_get_progressbar (GncWindow *window_in)
4873 {
4874  GncMainWindowPrivate *priv;
4875  GncMainWindow *window;
4876 
4877  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), NULL);
4878 
4879  window = GNC_MAIN_WINDOW(window_in);
4880  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4881  return priv->progressbar;
4882 }
4883 
4884 
4885 static void
4886 gnc_main_window_all_ui_set_sensitive (GncWindow *unused, gboolean sensitive)
4887 {
4888  GncMainWindow *window;
4889  GncMainWindowPrivate *priv;
4890  GList *groupp, *groups, *winp, *tmp;
4891  GtkWidget *close_button;
4892 
4893  for (winp = active_windows; winp; winp = g_list_next(winp))
4894  {
4895  window = winp->data;
4896  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4897 
4898  groups = gtk_ui_manager_get_action_groups(window->ui_merge);
4899  for (groupp = groups; groupp; groupp = g_list_next(groupp))
4900  {
4901  gtk_action_group_set_sensitive(GTK_ACTION_GROUP(groupp->data), sensitive);
4902  }
4903 
4904  for (tmp = priv->installed_pages; tmp; tmp = g_list_next(tmp))
4905  {
4906  close_button = g_object_get_data(tmp->data, PLUGIN_PAGE_CLOSE_BUTTON);
4907  if (!close_button)
4908  continue;
4909  gtk_widget_set_sensitive (close_button, sensitive);
4910  }
4911  }
4912 }
4913 
4914 
4919 static void
4920 gnc_window_main_window_init (GncWindowIface *iface)
4921 {
4922  iface->get_gtk_window = gnc_main_window_get_gtk_window;
4923  iface->get_statusbar = gnc_main_window_get_statusbar;
4924  iface->get_progressbar = gnc_main_window_get_progressbar;
4925  iface->ui_set_sensitive = gnc_main_window_all_ui_set_sensitive;
4926 }
4927 
4928 
4929 /* Set the window where all progressbar updates should occur. This
4930  * is a wrapper around the gnc_window_set_progressbar_window()
4931  * function.
4932  */
4933 void
4935 {
4936  GncWindow *gncwin;
4937  gncwin = GNC_WINDOW(window);
4938  gnc_window_set_progressbar_window(gncwin);
4939 }
4940 
4941 
4954 static void
4955 do_popup_menu(GncPluginPage *page, GdkEventButton *event)
4956 {
4957  GtkUIManager *ui_merge;
4958  GtkWidget *menu;
4959 
4960  g_return_if_fail(GNC_IS_PLUGIN_PAGE(page));
4961 
4962  ENTER("page %p, event %p", page, event);
4963  ui_merge = gnc_plugin_page_get_ui_merge(page);
4964  if (ui_merge == NULL)
4965  {
4966  LEAVE("no ui merge");
4967  return;
4968  }
4969 
4970  menu = gtk_ui_manager_get_widget(ui_merge, "/MainPopup");
4971  if (!menu)
4972  {
4973  LEAVE("no menu");
4974  return;
4975  }
4976  gtk_menu_popup_at_pointer (GTK_MENU(menu), (GdkEvent *) event);
4977 
4978  LEAVE(" ");
4979 }
4980 
4981 
4995 gboolean
4997  GncPluginPage *page)
4998 {
4999  ENTER("widget %p, page %p", widget, page);
5000  do_popup_menu(page, NULL);
5001  LEAVE(" ");
5002  return TRUE;
5003 }
5004 
5005 
5006 /* Callback function invoked when the user clicks in the content of
5007  * any Gnucash window. If this was a "right-click" then Gnucash will
5008  * popup the contextual menu.
5009  */
5010 gboolean
5011 gnc_main_window_button_press_cb (GtkWidget *whatever,
5012  GdkEventButton *event,
5013  GncPluginPage *page)
5014 {
5015  g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
5016 
5017  ENTER("widget %p, event %p, page %p", whatever, event, page);
5018  /* Ignore double-clicks and triple-clicks */
5019  if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
5020  {
5021  do_popup_menu(page, event);
5022  LEAVE("menu shown");
5023  return TRUE;
5024  }
5025 
5026  LEAVE("other click");
5027  return FALSE;
5028 }
5029 
5030 void
5032  gboolean sensitive)
5033 {
5034  GList *tmp;
5035  GtkAction *action;
5036 
5037  for (tmp = active_windows; tmp; tmp = g_list_next(tmp))
5038  {
5039  action = gnc_main_window_find_action (tmp->data, action_name);
5040  gtk_action_set_sensitive (action, sensitive);
5041  }
5042 }
5043 
5045 {
5046  g_assert(window);
5047  return window->ui_merge;
5048 }
5049 
void gnc_preferences_dialog(GtkWindow *parent)
This function creates the preferences dialog and presents it to the user.
GtkActionGroup * action_group
The group of all actions provided by the main window itself.
GncPluginPage * gnc_plugin_page_recreate_page(GtkWidget *window, const gchar *page_type, GKeyFile *key_file, const gchar *page_group)
This function looks up a specific plugin type by name, and then calls a plugin specific function to c...
GtkWidget * statusbar
A pointer to the status bar at the bottom edge of the window.
Functions to load, save and get gui state.
gboolean gnc_plugin_page_has_books(GncPluginPage *page)
Query a page to see if it has a reference to any book.
gboolean gnc_plugin_page_get_use_new_window(GncPluginPage *page)
Retrieve the "use new window" setting associated with this page.
gboolean gnc_plugin_page_finish_pending(GncPluginPage *page)
Tell a page to finish any outstanding activities.
void gnc_main_window_restore_all_windows(const GKeyFile *keyfile)
Restore the persistent state of all windows.
void gnc_plugin_page_destroy_widget(GncPluginPage *plugin_page)
Destroy the display widget that corresponds to this plugin.
The instance data structure for a content plugin.
void qof_book_set_dirty_cb(QofBook *book, QofBookDirtyCB cb, gpointer user_data)
Set the function to call when a book transitions from clean to dirty, or vice versa.
Definition: qofbook.cpp:510
const GList * gnc_gobject_tracking_get_list(const gchar *name)
Get a list of all known objects of a specified type.
gboolean gnc_main_window_button_press_cb(GtkWidget *whatever, GdkEventButton *event, GncPluginPage *page)
Callback function invoked when the user clicks in the content of any Gnucash window.
gulong gnc_prefs_register_cb(const char *group, const gchar *pref_name, gpointer func, gpointer user_data)
Register a callback that gets triggered when the given preference changes.
Definition: gnc-prefs.c:128
gboolean gnc_book_use_book_currency(QofBook *book)
Returns TRUE if both book-currency and default gain/loss policy KVPs exist and are valid and trading ...
Definition: gnc-ui-util.c:446
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
utility functions for the GnuCash UI
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
void gnc_gobject_tracking_remember(GObject *object, GObjectClass *klass)
Tell gnucash to remember this object in the database.
functions to query various version related strings that were set at build time.
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition: qofbackend.h:57
void gnc_main_window_merge_actions(GncMainWindow *window, const gchar *group_name, GtkActionEntry *actions, guint n_actions, GtkToggleActionEntry *toggle_actions, guint n_toggle_actions, const gchar *filename, gpointer user_data)
Add a set of actions to the specified window.
const gchar * gnc_plugin_page_get_page_long_name(GncPluginPage *page)
Retrieve the long name of this page.
gulong gnc_prefs_get_reg_negative_color_pref_id(void)
Get and Set registered preference id for register negative_color_pref.
Definition: gnc-prefs.c:401
void gnc_gobject_tracking_forget(GObject *object)
Tell gnucash to remember this object in the database.
time64 qof_book_get_session_dirty_time(const QofBook *book)
Retrieve the earliest modification time on the book.
Definition: qofbook.cpp:504
gboolean gnc_book_options_dialog_apply_helper(GNCOptionDB *options)
Processes selected options in the Book Options dialog: checks book_currency and use_split_action_for_...
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
const gchar * gnc_plugin_page_get_page_name(GncPluginPage *page)
Retrieve the name of this page.
gint qof_book_get_num_days_autoreadonly(const QofBook *book)
Returns the number of days for auto-read-only transactions.
Definition: qofbook.cpp:1092
Functions that are supported by all types of windows.
void gnc_features_set_used(QofBook *book, const gchar *feature)
Indicate that the current book uses the given feature.
Definition: gnc-features.c:135
gboolean qof_book_use_split_action_for_num_field(const QofBook *book)
Returns TRUE if this book uses split action field as the &#39;Num&#39; field, FALSE if it uses transaction nu...
GtkWidget * toolbar
The toolbar created by the UI manager.
GKeyFile helper routines.
gboolean restoring_pages
Set when restoring plugin pages.
gint pos[2]
Array for window position.
Plugin management functions for the GnuCash UI.
void gnc_plugin_add_to_window(GncPlugin *plugin, GncMainWindow *window, GQuark type)
Add the specified plugin from the specified window.
Definition: gnc-plugin.c:128
GtkWidget * window
The window that contains the display widget for this plugin.
gint event_handler_id
The identifier for this window&#39;s engine event handler.
gchar * gnc_uri_get_path(const gchar *uri)
Extracts the path part from a uri.
const gchar * gnc_plugin_page_get_page_color(GncPluginPage *page)
Retrieve the color of this page.
gboolean gnc_main_window_is_restoring_pages(GncMainWindow *window)
Check if the main window is restoring the plugin pages.
GtkWidget * gnc_book_options_dialog_cb(gboolean modal, gchar *title, GtkWindow *parent)
Opens the Book Options dialog.
void gnc_plugin_page_unmerge_actions(GncPluginPage *page, GtkUIManager *ui_merge)
Remove the actions for a content page from the specified window.
void gnc_plugin_page_set_page_long_name(GncPluginPage *page, const char *name)
Set the long name of this page.
gchar * gnc_filepath_locate_ui_file(const gchar *name)
Given a ui file name, find the file in the ui directory associated with this application.
void gnc_engine_add_commit_error_callback(EngineCommitErrorCallback cb, gpointer data)
Set a callback function to be called in case an engine commit fails.
Definition: gnc-engine.c:166
void gnc_shutdown(int exit_status)
Shutdown gnucash.
void gnc_ui_page_setup(GtkWindow *parent)
Run a page setup dialog and save the resulting GtkPageSetup in a static variable. ...
Definition: print-session.c:79
void gnc_main_window_unmerge_actions(GncMainWindow *window, const gchar *group_name)
Remove a set of actions from the specified window.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
GtkWindow * gnc_ui_get_gtk_window(GtkWidget *widget)
Get a pointer to the widget&#39;s immediate top level GtkWindow.
void gnc_main_window_show_all_windows(void)
Shows all main windows.
void gnc_book_option_num_field_source_change_cb(gboolean num_action)
Calls gnc_book_option_num_field_source_change to initiate registered callbacks when num_field_source ...
void gnc_main_window_display_page(GncPluginPage *page)
Bring the window containing the specified page to the top of the window stack, then switch the notebo...
void gnc_main_window_save_all_windows(GKeyFile *keyfile)
Save the persistent state of all windows.
GncPluginPage * gnc_main_window_get_current_page(GncMainWindow *window)
Retrieve a pointer to the page that is currently at the front of the specified window.
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
Display a data plugin page in a window.
gint gnc_prefs_get_int(const gchar *group, const gchar *pref_name)
Get an integer value from the preferences backend.
Functions for adding content to a window.
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
Definition: qofevent.cpp:73
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
void gnc_prefs_remove_cb_by_id(const gchar *group, guint id)
Remove a function that was registered for a callback when a specific preference in the settings group...
Definition: gnc-prefs.c:153
gboolean visible
Whether or not the GtkRadioAction should be visible.
void gnc_main_window_all_action_set_sensitive(const gchar *action_name, gboolean sensitive)
Change the sensitivity of a command in all windows.
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
Definition: qofsession.cpp:578
#define QOF_CHECK_TYPE(obj, type)
return TRUE if object is of the given type
Definition: qofid.h:102
void main_window_update_page_color(GncPluginPage *page, const gchar *color_in)
Update the color on the page tabs in the main window.
gchar * gnc_uri_normalize_uri(const gchar *uri, gboolean allow_password)
Composes a normalized uri starting from any uri (filename, db spec,...).
gchar * gnc_filepath_locate_doc_file(const gchar *name)
Given a documentation file name, find the file in the doc directory associated with this application...
gint QofEventId
Define the type of events allowed.
Definition: qofevent.h:45
Gobject helper routines.
void qof_book_mark_session_saved(QofBook *book)
The qof_book_mark_saved() routine marks the book as having been saved (to a file, to a database)...
Definition: qofbook.cpp:467
void gnc_plugin_page_set_use_new_window(GncPluginPage *page, gboolean use_new)
Set the "use new window" setting associated with this page.
void gnc_gnome_help(GtkWindow *parent, const char *file_name, const char *anchor)
Launch the systems default help browser, gnome&#39;s yelp for linux, and open to a given link within a gi...
GtkWidget * notebook
The notebook containing all the pages in this window.
void gnc_plugin_page_disconnect_page_changed(GncPluginPage *page)
Disconnect the page_changed_id signal callback.
gchar * action_name
The name of the GtkRadioAction to be updated.
void gnc_book_option_book_currency_selected_cb(gboolean use_book_currency)
Calls gnc_book_option_book_currency_selected to initiate registered callbacks when currency accountin...
gboolean gnc_plugin_page_has_book(GncPluginPage *page, QofBook *book)
Query a page to see if it has a reference to a given book.
char * gnc_print_time64(time64 time, const char *format)
print a time64 as a date string per format
Definition: gnc-date.cpp:379
gboolean gnc_main_window_popup_menu_cb(GtkWidget *widget, GncPluginPage *page)
Callback function invoked when the user requests that Gnucash popup the contextual menu via the keybo...
The instance private data structure for an embedded window object.
void gnc_options_dialog_set_book_options_help_cb(GNCOptionWin *win)
Set the help callback to &#39;gnc_book_options_help_cb&#39; to open a help browser and point it to the Book O...
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
Definition: qofevent.cpp:103
gboolean window_quitting
Set to TRUE when quitting from this window.
GncPluginManager * gnc_plugin_manager_get(void)
Retrieve a pointer to the plugin manager.
GList * usage_order
A list of pages in order of use (most recent -> least recent)
Gnome specific utility functions.
void gnc_plugin_remove_from_window(GncPlugin *plugin, GncMainWindow *window, GQuark type)
Remove the specified plugin from the specified window.
Definition: gnc-plugin.c:180
void gnc_plugin_page_save_page(GncPluginPage *page, GKeyFile *key_file, const gchar *group_name)
Call the plugin specific function that will save the state of a content page to a disk...
#define PLUGIN_PAGE_LABEL
This label is used to provide a mapping from a visible page widget back to the corresponding GncPlugi...
gboolean show_color_tabs
Show account color as background on tabs.
Dialog for handling user preferences.
gboolean qof_book_session_not_saved(const QofBook *book)
qof_book_not_saved() returns the value of the session_dirty flag, set when changes to any object in t...
Definition: qofbook.cpp:459
All type declarations for the whole Gnucash engine.
const char * gnc_quote_source_fq_version(void)
This function returns the version of the Finance::Quote module installed on a user&#39;s computer...
gboolean gnc_main_window_finish_pending(GncMainWindow *window)
Tell a window to finish any outstanding activities.
GLib helper routines.
Generic api to store and retrieve preferences.
Utility functions for file access.
gboolean gnc_uri_targets_local_fs(const gchar *uri)
Checks if the given uri is either a valid file uri or a local filesystem path.
gchar * label
The new label for this GtkRadioAction.
This data structure is used to describe the requested state of a GtkRadioAction, and us used to pass ...
GtkActionGroup * gnc_main_window_get_action_group(GncMainWindow *window, const gchar *group_name)
Retrieve a specific set of user interface actions from a window.
void main_window_update_page_set_read_only_icon(GncPluginPage *page, gboolean read_only)
Update the icon on the page tabs in the main window.
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition: qofbook.cpp:580
GncMainWindow * gnc_main_window_new(void)
Create a new gnc main window plugin.
void qof_book_load_options(QofBook *book, GNCOptionLoad load_cb, GNCOptionDB *odb)
Load a GNCOptionsDB from KVP data.
Definition: qofbook.cpp:1259
void gnc_plugin_page_set_page_color(GncPluginPage *page, const char *color)
Set the color of this page.
void gnc_plugin_update_actions(GtkActionGroup *action_group, const gchar **action_names, const gchar *property_name, gboolean value)
Update a property on a set of existing GtkActions.
Definition: gnc-plugin.c:280
void gnc_plugin_page_merge_actions(GncPluginPage *page, GtkUIManager *ui_merge)
Add the actions for a content page to the specified window.
void gnc_main_window_manual_merge_actions(GncMainWindow *window, const gchar *group_name, GtkActionGroup *group, guint merge_id)
Manually add a set of actions to the specified window.
void gnc_options_dialog_set_new_book_option_values(GNCOptionDB *odb)
Set the initial values of new book options to values specified in user preferences.
The instance data structure for a menu-only plugin.
Definition: gnc-plugin.h:100
cannot write to file/directory
Definition: qofbackend.h:68
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
GHashTable * merged_actions_table
A hash table of all action groups that have been installed into this window.
const char * gnc_version(void)
Parse <prefix>/etc/gnucash/environment and set environment variables based on the contents of that fi...
Definition: gnc-version.c:35
void gnc_main_window_close_page(GncPluginPage *page)
Remove a data plugin page from a window and display the previous page.
Functions for adding plugins to a GnuCash window.
void qof_book_save_options(QofBook *book, GNCOptionSave save_cb, GNCOptionDB *odb, gboolean clear)
Save a GNCOptionsDB back to the book&#39;s KVP.
Definition: qofbook.cpp:1265
GNC_DEFINE_TYPE_WITH_CODE(GncMainWindow, gnc_main_window, GTK_TYPE_WINDOW, G_IMPLEMENT_INTERFACE(GNC_TYPE_WINDOW, gnc_window_main_window_init))
This data structure maintains information about one action groups that has been installed in this win...
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
GList * gnc_plugin_manager_get_plugins(GncPluginManager *manager)
Get a list of all plugins being held by the plugin manager.
void gnc_plugin_page_set_page_name(GncPluginPage *page, const char *name)
Set the name of this page.
GList * installed_pages
A list of all pages that are installed in this window.
GtkWidget * progressbar
A pointer to the progress bar at the bottom right of the window that is contained in the status bar...
time64 gnc_time(time64 *tbuf)
get the current local time
Definition: gnc-date.cpp:273
Utility functions for convert uri in separate components and back.
GncPluginPage * current_page
The currently selected page.
gint64 time64
Many systems, including Microsoft Windows and BSD-derived Unixes like Darwin, are retaining the int-3...
Definition: gnc-date.h:93
gulong gnc_prefs_get_reg_auto_raise_lists_id(void)
Get and Set registered preference id for register auto_raise_lists.
Definition: gnc-prefs.c:391
void gnc_launch_doclink(GtkWindow *parent, const char *uri)
Launch the default browser and open the provided URI.
GtkUIManager * gnc_plugin_page_get_ui_merge(GncPluginPage *page)
Retrieve the GtkUIManager object associated with this page.
The instance data structure for a main window object.
void gnc_main_window_restore_default_state(GncMainWindow *window)
Restore the persistent state of one window to a sane default.
The class data structure for a main window object.
File path resolution utility functions.
gboolean gnc_main_window_all_finish_pending(void)
Tell all pages in all windows to finish any outstanding activities.
gint gnc_list_length_cmp(const GList *list, size_t len)
Scans the GList elements the minimum number of iterations required to test it against a specified siz...
void gnc_plugin_set_important_actions(GtkActionGroup *action_group, const gchar **name)
Mark certain actions as "important".
Definition: gnc-plugin.c:256
void gnc_main_window_actions_updated(GncMainWindow *window)
Force a full update of the user interface for the specified window.
void main_window_update_page_name(GncPluginPage *page, const gchar *name_in)
Update the name of the page in the main window.
GtkWidget * notebook_page
The display widget for this plugin.
GtkWidget * menu_dock
The dock (vbox) at the top of the window containing the menubar and toolbar.
void gnc_plugin_page_show_summarybar(GncPluginPage *page, gboolean visible)
Show/hide the summarybar associated with this page.
GtkUIManager * gnc_main_window_get_uimanager(GncMainWindow *window)
Returns the pointer to the GtkUIManager which is used for the menu item merging.
GtkAction * gnc_main_window_find_action(GncMainWindow *window, const gchar *name)
Find action in main window.
void gnc_window_connect_proxy(GtkUIManager *merge, GtkAction *action, GtkWidget *proxy, GtkWidget *statusbar)
This callback functions will set the statusbar text to the "tooltip" property of the currently select...
Definition: gnc-window.c:278
GtkWidget * gnc_plugin_page_create_widget(GncPluginPage *plugin_page)
Create the display widget that corresponds to this plugin.
void gnc_main_window_set_progressbar_window(GncMainWindow *window)
Set the window where all progressbar updates should occur.
void gnc_prefs_remove_cb_by_func(const gchar *group, const gchar *pref_name, gpointer func, gpointer user_data)
Remove a function that was registered for a callback when the given preference changed.
Definition: gnc-prefs.c:143
gdouble gnc_prefs_get_float(const gchar *group, const gchar *pref_name)
Get an float value from the preferences backend.
const gchar * gnc_plugin_page_get_plugin_name(GncPluginPage *plugin_page)
Retrieve the textual name of a plugin.
Utility functions for file access.