GnuCash  4.13-177-g21dd8aa057+
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 "dialog-doclink-utils.h"
55 #include "gnc-engine.h"
56 #include "gnc-features.h"
57 #include "gnc-file.h"
58 #include "gnc-filepath-utils.h"
59 #include "gnc-gkeyfile-utils.h"
60 #include "gnc-gnome-utils.h"
61 #include "gnc-gobject-utils.h"
62 #include "gnc-gui-query.h"
63 #include "gnc-gtk-utils.h"
64 #include "gnc-hooks.h"
65 #include "gnc-icons.h"
66 #include "gnc-session.h"
67 #include "gnc-state.h"
68 #include "gnc-ui.h"
69 #include "gnc-ui-util.h"
70 #include <gnc-glib-utils.h>
71 #include "gnc-uri-utils.h"
72 #include "gnc-version.h"
73 #include "gnc-warnings.h"
74 #include "gnc-window.h"
75 #include "gnc-prefs.h"
76 #include "option-util.h"
77 // +JSLED
78 //#include "gnc-html.h"
79 #include "gnc-autosave.h"
80 #include "print-session.h"
81 #ifdef MAC_INTEGRATION
82 #include <gtkmacintegration/gtkosxapplication.h>
83 #endif
84 #ifdef HAVE_SYS_STAT_H
85 # define __need_system_sys_stat_h //To block Guile-2.0's evil substitute
86 # include <sys/types.h>
87 # include <sys/stat.h> // for stat(2)
88 #endif
89 
91 enum
92 {
93  PAGE_ADDED,
94  PAGE_CHANGED,
95  LAST_SIGNAL
96 };
97 
100 #define PLUGIN_PAGE_LABEL "plugin-page"
101 
102 #define PLUGIN_PAGE_CLOSE_BUTTON "close-button"
103 #define PLUGIN_PAGE_TAB_LABEL "label"
104 
105 #define GNC_PREF_SHOW_CLOSE_BUTTON "tab-close-buttons"
106 #define GNC_PREF_TAB_NEXT_RECENT "tab-next-recent"
107 #define GNC_PREF_TAB_POSITION_TOP "tab-position-top"
108 #define GNC_PREF_TAB_POSITION_BOTTOM "tab-position-bottom"
109 #define GNC_PREF_TAB_POSITION_LEFT "tab-position-left"
110 #define GNC_PREF_TAB_POSITION_RIGHT "tab-position-right"
111 #define GNC_PREF_TAB_WIDTH "tab-width"
112 #define GNC_PREF_TAB_COLOR "show-account-color-tabs"
113 #define GNC_PREF_SAVE_CLOSE_EXPIRES "save-on-close-expires"
114 #define GNC_PREF_SAVE_CLOSE_WAIT_TIME "save-on-close-wait-time"
115 #define GNC_PREF_TAB_OPEN_ADJACENT "tab-open-adjacent"
116 
117 #define GNC_MAIN_WINDOW_NAME "GncMainWindow"
118 
119 #define DIALOG_BOOK_OPTIONS_CM_CLASS "dialog-book-options"
120 
121 /* Static Globals *******************************************************/
122 
124 static QofLogModule log_module = GNC_MOD_GUI;
126 static GObjectClass *parent_class = NULL;
128 static GQuark window_type = 0;
131 static GList *active_windows = NULL;
134 static guint secs_to_save = 0;
135 #define MSG_AUTO_SAVE _("Changes will be saved automatically in %u seconds")
136 
137 /* Declarations *********************************************************/
138 static void gnc_main_window_class_init (GncMainWindowClass *klass);
139 static void gnc_main_window_init (GncMainWindow *window,
140  void *data);
141 static void gnc_main_window_finalize (GObject *object);
142 static void gnc_main_window_destroy (GtkWidget *widget);
143 
144 static void gnc_main_window_setup_window (GncMainWindow *window);
145 static void gnc_window_main_window_init (GncWindowIface *iface);
146 #ifndef MAC_INTEGRATION
147 static void gnc_main_window_update_all_menu_items (void);
148 #endif
149 
150 /* Callbacks */
151 static void gnc_main_window_add_widget (GtkUIManager *merge, GtkWidget *widget, GncMainWindow *window);
152 static void gnc_main_window_switch_page (GtkNotebook *notebook, gpointer *notebook_page, gint pos, GncMainWindow *window);
153 static void gnc_main_window_page_reordered (GtkNotebook *notebook, GtkWidget *child, guint pos, GncMainWindow *window);
154 static void gnc_main_window_plugin_added (GncPlugin *manager, GncPlugin *plugin, GncMainWindow *window);
155 static void gnc_main_window_plugin_removed (GncPlugin *manager, GncPlugin *plugin, GncMainWindow *window);
156 static void gnc_main_window_engine_commit_error_callback( gpointer data, QofBackendError errcode );
157 
158 /* Command callbacks */
159 static void gnc_main_window_cmd_page_setup (GtkAction *action, GncMainWindow *window);
160 static void gnc_main_window_cmd_file_properties (GtkAction *action, GncMainWindow *window);
161 static void gnc_main_window_cmd_file_close (GtkAction *action, GncMainWindow *window);
162 static void gnc_main_window_cmd_file_quit (GtkAction *action, GncMainWindow *window);
163 static void gnc_main_window_cmd_edit_cut (GtkAction *action, GncMainWindow *window);
164 static void gnc_main_window_cmd_edit_copy (GtkAction *action, GncMainWindow *window);
165 static void gnc_main_window_cmd_edit_paste (GtkAction *action, GncMainWindow *window);
166 static void gnc_main_window_cmd_edit_preferences (GtkAction *action, GncMainWindow *window);
167 static void gnc_main_window_cmd_view_refresh (GtkAction *action, GncMainWindow *window);
168 static void gnc_main_window_cmd_view_toolbar (GtkAction *action, GncMainWindow *window);
169 static void gnc_main_window_cmd_view_summary (GtkAction *action, GncMainWindow *window);
170 static void gnc_main_window_cmd_view_statusbar (GtkAction *action, GncMainWindow *window);
171 static void gnc_main_window_cmd_view_tab_position (GtkAction *action, GtkRadioAction *current, GncMainWindow *window);
172 static void gnc_main_window_cmd_actions_reset_warnings (GtkAction *action, GncMainWindow *window);
173 static void gnc_main_window_cmd_actions_rename_page (GtkAction *action, GncMainWindow *window);
174 static void gnc_main_window_cmd_window_new (GtkAction *action, GncMainWindow *window);
175 static void gnc_main_window_cmd_window_move_page (GtkAction *action, GncMainWindow *window);
176 #ifndef MAC_INTEGRATION
177 static void gnc_main_window_cmd_window_raise (GtkAction *action, GtkRadioAction *current, GncMainWindow *window);
178 #endif
179 static void gnc_main_window_cmd_help_tutorial (GtkAction *action, GncMainWindow *window);
180 static void gnc_main_window_cmd_help_contents (GtkAction *action, GncMainWindow *window);
181 static void gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window);
182 
183 static void do_popup_menu(GncPluginPage *page, GdkEventButton *event);
184 static GtkWidget *gnc_main_window_get_statusbar (GncWindow *window_in);
185 static void statusbar_notification_lastmodified(void);
186 static void gnc_main_window_update_tab_position (gpointer prefs, gchar *pref, gpointer user_data);
187 static void gnc_main_window_remove_prefs (GncMainWindow *window);
188 
189 #ifdef MAC_INTEGRATION
190 static void gnc_quartz_shutdown(GtkosxApplication *theApp, gpointer data);
191 static gboolean gnc_quartz_should_quit(GtkosxApplication *theApp, GncMainWindow *window);
192 static void gnc_quartz_set_menu(GncMainWindow* window);
193 #endif
194 
197 typedef struct GncMainWindowPrivate
198 {
203  GtkWidget *menu_dock;
206  GtkWidget *toolbar;
208  GtkWidget *notebook;
210  gboolean show_color_tabs;
214  GtkWidget *statusbar;
218  GtkWidget *progressbar;
219 
223  GtkActionGroup *action_group;
224 
228  GList *usage_order;
234  gint pos[2];
239  GHashTable *merged_actions_table;
241  gboolean restoring_pages;
243 
244 GNC_DEFINE_TYPE_WITH_CODE(GncMainWindow, gnc_main_window, GTK_TYPE_WINDOW,
245  G_ADD_PRIVATE (GncMainWindow)
246  G_IMPLEMENT_INTERFACE (GNC_TYPE_WINDOW,
247  gnc_window_main_window_init))
248 
249 #define GNC_MAIN_WINDOW_GET_PRIVATE(o) \
250  ((GncMainWindowPrivate*)gnc_main_window_get_instance_private((GncMainWindow*)o))
251 
254 typedef struct
255 {
258  guint merge_id;
261  GtkActionGroup *action_group;
262 } MergedActionEntry;
263 
266 static guint main_window_signals[LAST_SIGNAL] = { 0 };
267 
268 
273 static GtkActionEntry gnc_menu_actions [] =
274 {
275  /* Toplevel */
276 
277  { "FileAction", NULL, N_("_File"), NULL, NULL, NULL, },
278  { "EditAction", NULL, N_("_Edit"), NULL, NULL, NULL },
279  { "ViewAction", NULL, N_("_View"), NULL, NULL, NULL },
280  { "ActionsAction", NULL, N_("_Actions"), NULL, NULL, NULL },
281  { "TransactionAction", NULL, N_("Tra_nsaction"), NULL, NULL, NULL },
282  { "ReportsAction", NULL, N_("_Reports"), NULL, NULL, NULL },
283  { "ToolsAction", NULL, N_("_Tools"), NULL, NULL, NULL },
284  { "ExtensionsAction", NULL, N_("E_xtensions"), NULL, NULL, NULL },
285  { "WindowsAction", NULL, N_("_Windows"), NULL, NULL, NULL },
286  { "HelpAction", NULL, N_("_Help"), NULL, NULL, NULL },
287 
288  /* File menu */
289 
290  { "FileImportAction", NULL, N_("_Import"), NULL, NULL, NULL },
291  { "FileExportAction", NULL, N_("_Export"), NULL, NULL, NULL },
292  {
293  "FilePrintAction", "document-print", N_("_Print..."), "<primary>p",
294  N_("Print the currently active page"), NULL
295  },
296 #ifndef GTK_STOCK_PAGE_SETUP
297 # define GTK_STOCK_PAGE_SETUP NULL
298 #endif
299  {
300  "FilePageSetupAction", "document-page-setup", N_("Pa_ge Setup"), "<primary><shift>p",
301  N_("Specify the page size and orientation for printing"),
302  G_CALLBACK (gnc_main_window_cmd_page_setup)
303  },
304  {
305  "FilePropertiesAction", "document-properties", N_("Proper_ties"), "<Alt>Return",
306  N_("Edit the properties of the current file"),
307  G_CALLBACK (gnc_main_window_cmd_file_properties)
308  },
309  {
310  "FileCloseAction", "window-close", N_("_Close"), "<primary>W",
311  N_("Close the currently active page"),
312  G_CALLBACK (gnc_main_window_cmd_file_close)
313  },
314  {
315  "FileQuitAction", "application-exit", N_("_Quit"), "<primary>Q",
316  N_("Quit this application"),
317  G_CALLBACK (gnc_main_window_cmd_file_quit)
318  },
319 
320  /* Edit menu */
321 
322  {
323  "EditCutAction", "edit-cut", N_("Cu_t"), "<primary>X",
324  N_("Cut the current selection and copy it to clipboard"),
325  G_CALLBACK (gnc_main_window_cmd_edit_cut)
326  },
327  {
328  "EditCopyAction", "edit-copy", N_("_Copy"), "<primary>C",
329  N_("Copy the current selection to clipboard"),
330  G_CALLBACK (gnc_main_window_cmd_edit_copy)
331  },
332  {
333  "EditPasteAction", "edit-paste", N_("_Paste"), "<primary>V",
334  N_("Paste the clipboard content at the cursor position"),
335  G_CALLBACK (gnc_main_window_cmd_edit_paste)
336  },
337  {
338  "EditPreferencesAction", "preferences-system", N_("Pr_eferences"), NULL,
339  N_("Edit the global preferences of GnuCash"),
340  G_CALLBACK (gnc_main_window_cmd_edit_preferences)
341  },
342 
343  /* View menu */
344 
345  { "ViewTabPositionAction", NULL, N_("Tab P_osition"), NULL, NULL, NULL },
346  {
347  "ViewSortByAction", NULL, N_("_Sort By..."), NULL,
348  N_("Select sorting criteria for this page view"), NULL
349  },
350  {
351  "ViewFilterByAction", NULL, N_("_Filter By..."), NULL,
352  N_("Select the account types that should be displayed."), NULL
353  },
354  {
355  "ViewRefreshAction", "view-refresh", N_("_Refresh"), "<primary>r",
356  N_("Refresh this window"),
357  G_CALLBACK (gnc_main_window_cmd_view_refresh)
358  },
359 
360  /* Actions menu */
361 
362  { "ScrubMenuAction", NULL, N_("_Check & Repair"), NULL, NULL, NULL },
363  {
364  "ActionsForgetWarningsAction", NULL, N_("Reset _Warnings..."), NULL,
365  N_("Reset the state of all warning messages so they will be shown again."),
366  G_CALLBACK (gnc_main_window_cmd_actions_reset_warnings)
367  },
368  {
369  "ActionsRenamePageAction", NULL, N_("Re_name Page"), NULL,
370  N_("Rename this page."),
371  G_CALLBACK (gnc_main_window_cmd_actions_rename_page)
372  },
373 
374  /* Windows menu */
375 
376  {
377  "WindowNewAction", NULL, N_("_New Window"), NULL,
378  N_("Open a new top-level GnuCash window."),
379  G_CALLBACK (gnc_main_window_cmd_window_new)
380  },
381  {
382  "WindowMovePageAction", NULL, N_("New Window with _Page"), NULL,
383  N_("Move the current page to a new top-level GnuCash window."),
384  G_CALLBACK (gnc_main_window_cmd_window_move_page)
385  },
386 
387  /* Help menu */
388 
389  {
390  "HelpTutorialAction", "help-browser", N_("Tutorial and Concepts _Guide"), "<primary>H",
391  N_("Open the GnuCash Tutorial"),
392  G_CALLBACK (gnc_main_window_cmd_help_tutorial)
393  },
394  {
395  "HelpContentsAction", "help-browser", N_("_Contents"), "F1",
396  N_("Open the GnuCash Help"),
397  G_CALLBACK (gnc_main_window_cmd_help_contents)
398  },
399  {
400  "HelpAboutAction", "help-about", N_("_About"), NULL,
401  N_("About GnuCash"),
402  G_CALLBACK (gnc_main_window_cmd_help_about)
403  },
404 };
406 static guint gnc_menu_n_actions = G_N_ELEMENTS (gnc_menu_actions);
407 
410 static GtkToggleActionEntry toggle_actions [] =
411 {
412  {
413  "ViewToolbarAction", NULL, N_("_Toolbar"), NULL,
414  N_("Show/hide the toolbar on this window"),
415  G_CALLBACK (gnc_main_window_cmd_view_toolbar), TRUE
416  },
417  {
418  "ViewSummaryAction", NULL, N_("Su_mmary Bar"), NULL,
419  N_("Show/hide the summary bar on this window"),
420  G_CALLBACK (gnc_main_window_cmd_view_summary), TRUE
421  },
422  {
423  "ViewStatusbarAction", NULL, N_("Stat_us Bar"), NULL,
424  N_("Show/hide the status bar on this window"),
425  G_CALLBACK (gnc_main_window_cmd_view_statusbar), TRUE
426  },
427 };
429 static guint n_toggle_actions = G_N_ELEMENTS (toggle_actions);
430 
433 static GtkRadioActionEntry tab_pos_radio_entries [] =
434 {
435  {
436  "ViewTabPositionTopAction", NULL, N_("To_p"), NULL,
437  N_("Display the notebook tabs at the top of the window."), GTK_POS_TOP
438  },
439  {
440  "ViewTabPositionBottomAction", NULL, N_("B_ottom"), NULL,
441  N_("Display the notebook tabs at the bottom of the window."), GTK_POS_BOTTOM
442  },
443  {
444  "ViewTabPositionLeftAction", NULL, N_("_Left"), NULL,
445  N_("Display the notebook tabs at the left of the window."), GTK_POS_LEFT
446  },
447  {
448  "ViewTabPositionRightAction", NULL, N_("_Right"), NULL,
449  N_("Display the notebook tabs at the right of the window."), GTK_POS_RIGHT
450  },
451 };
452 
455 static guint n_tab_pos_radio_entries = G_N_ELEMENTS (tab_pos_radio_entries);
456 
457 #ifndef MAC_INTEGRATION
458 
460 static GtkRadioActionEntry radio_entries [] =
461 {
462  { "Window0Action", NULL, N_("Window _1"), NULL, NULL, 0 },
463  { "Window1Action", NULL, N_("Window _2"), NULL, NULL, 1 },
464  { "Window2Action", NULL, N_("Window _3"), NULL, NULL, 2 },
465  { "Window3Action", NULL, N_("Window _4"), NULL, NULL, 3 },
466  { "Window4Action", NULL, N_("Window _5"), NULL, NULL, 4 },
467  { "Window5Action", NULL, N_("Window _6"), NULL, NULL, 5 },
468  { "Window6Action", NULL, N_("Window _7"), NULL, NULL, 6 },
469  { "Window7Action", NULL, N_("Window _8"), NULL, NULL, 7 },
470  { "Window8Action", NULL, N_("Window _9"), NULL, NULL, 8 },
471  { "Window9Action", NULL, N_("Window _0"), NULL, NULL, 9 },
472 };
473 
475 static guint n_radio_entries = G_N_ELEMENTS (radio_entries);
476 #endif
477 
481 static const gchar *gnc_menu_important_actions[] =
482 {
483  "FileCloseAction",
484  NULL,
485 };
486 
487 
492 static const gchar *always_insensitive_actions[] =
493 {
494  "FilePrintAction",
495  NULL
496 };
497 
498 
502 static const gchar *initially_insensitive_actions[] =
503 {
504  "FileCloseAction",
505  NULL
506 };
507 
508 
513 static const gchar *always_hidden_actions[] =
514 {
515  "ViewSortByAction",
516  "ViewFilterByAction",
517  NULL
518 };
519 
520 
523 static const gchar *immutable_page_actions[] =
524 {
525  "FileCloseAction",
526  NULL
527 };
528 
529 
532 static const gchar *multiple_page_actions[] =
533 {
534  "WindowMovePageAction",
535  NULL
536 };
537 
538 
539 /************************************************************
540  * *
541  ************************************************************/
542 #define WINDOW_COUNT "WindowCount"
543 #define WINDOW_STRING "Window %d"
544 #define WINDOW_GEOMETRY "WindowGeometry"
545 #define WINDOW_POSITION "WindowPosition"
546 #define WINDOW_MAXIMIZED "WindowMaximized"
547 #define TOOLBAR_VISIBLE "ToolbarVisible"
548 #define STATUSBAR_VISIBLE "StatusbarVisible"
549 #define SUMMARYBAR_VISIBLE "SummarybarVisible"
550 #define WINDOW_FIRSTPAGE "FirstPage"
551 #define WINDOW_PAGECOUNT "PageCount"
552 #define WINDOW_PAGEORDER "PageOrder"
553 #define PAGE_TYPE "PageType"
554 #define PAGE_NAME "PageName"
555 #define PAGE_STRING "Page %d"
556 
557 typedef struct
558 {
559  GKeyFile *key_file;
560  const gchar *group_name;
561  gint window_num;
562  gint page_num;
563  gint page_offset;
565 
566 
567 gboolean
569 {
570  GncMainWindowPrivate *priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
571  return priv->restoring_pages;
572 }
573 
574 
575 /* Iterator function to walk all pages in all windows, calling the
576  * specified function for each page. */
577 void
578 gnc_main_window_foreach_page (GncMainWindowPageFunc fn, gpointer user_data)
579 {
580  GncMainWindowPrivate *priv;
581  GncMainWindow *window;
582  GncPluginPage *page;
583  GList *w, *p;
584 
585  ENTER(" ");
586  for (w = active_windows; w; w = g_list_next(w))
587  {
588  window = w->data;
589  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
590  for (p = priv->installed_pages; p; p = g_list_next(p))
591  {
592  page = p->data;
593  fn(page, user_data);
594  }
595  }
596  LEAVE(" ");
597 }
598 
599 
611 static void
612 gnc_main_window_restore_page (GncMainWindow *window,
613  GncMainWindowSaveData *data)
614 {
615  GncMainWindowPrivate *priv;
616  GncPluginPage *page;
617  gchar *page_group, *page_type = NULL, *name = NULL;
618  const gchar *class_type;
619  GError *error = NULL;
620 
621  ENTER("window %p, data %p (key file %p, window %d, page start %d, page num %d)",
622  window, data, data->key_file, data->window_num, data->page_offset,
623  data->page_num);
624 
625  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
626  page_group = g_strdup_printf(PAGE_STRING,
627  data->page_offset + data->page_num);
628  page_type = g_key_file_get_string(data->key_file, page_group,
629  PAGE_TYPE, &error);
630  if (error)
631  {
632  g_warning("error reading group %s key %s: %s",
633  page_group, PAGE_TYPE, error->message);
634  goto cleanup;
635  }
636 
637  /* See if the page already exists. */
638  page = g_list_nth_data(priv->installed_pages, data->page_num);
639  if (page)
640  {
641  class_type = GNC_PLUGIN_PAGE_GET_CLASS(page)->plugin_name;
642  if (strcmp(page_type, class_type) != 0)
643  {
644  g_warning("error: page types don't match: state %s, existing page %s",
645  page_type, class_type);
646  goto cleanup;
647  }
648  }
649  else
650  {
651  /* create and install the page */
652  page = gnc_plugin_page_recreate_page(GTK_WIDGET(window), page_type,
653  data->key_file, page_group);
654  if (page)
655  {
656  /* Does the page still need to be installed into the window? */
657  if (page->window == NULL)
658  {
660  gnc_main_window_open_page(window, page);
661  }
662 
663  /* Restore the page name */
664  name = g_key_file_get_string(data->key_file, page_group,
665  PAGE_NAME, &error);
666  if (error)
667  {
668  g_warning("error reading group %s key %s: %s",
669  page_group, PAGE_NAME, error->message);
670  /* Fall through and still show the page. */
671  }
672  else
673  {
674  DEBUG("updating page name for %p to %s.", page, name);
675  main_window_update_page_name(page, name);
676  g_free(name);
677  }
678  }
679  }
680 
681  LEAVE("ok");
682 cleanup:
683  if (error)
684  g_error_free(error);
685  if (page_type)
686  g_free(page_type);
687  g_free(page_group);
688 }
689 
690 
699 static void
700 gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *data)
701 {
702  GncMainWindowPrivate *priv;
703  GtkAction *action;
704  gint *pos, *geom, *order;
705  gsize length;
706  gboolean max, visible, desired_visibility;
707  gchar *window_group;
708  gint page_start, page_count, i;
709  GError *error = NULL;
710 
711  /* Setup */
712  ENTER("window %p, data %p (key file %p, window %d)",
713  window, data, data->key_file, data->window_num);
714  window_group = g_strdup_printf(WINDOW_STRING, data->window_num + 1);
715 
716  /* Deal with the uncommon case that the state file defines a window
717  * but no pages. An example to get in such a situation can be found
718  * here: https://bugs.gnucash.org/show_bug.cgi?id=436479#c3
719  * If this happens on the first window, we will open an account hierarchy
720  * to avoid confusing the user by presenting a completely empty window.
721  * If it happens on a later window, we'll just skip restoring that window.
722  */
723  if (!g_key_file_has_group (data->key_file, window_group) ||
724  !g_key_file_has_key (data->key_file, window_group, WINDOW_PAGECOUNT, &error))
725  {
726  if (window)
727  {
729  PINFO ("saved state had an empty first main window\n"
730  "an account hierarchy page was added automatically to avoid confusion");
731  }
732  else
733  PINFO ("saved state had an empty main window, skipping restore");
734 
735  goto cleanup;
736  }
737 
738 
739  /* Get this window's notebook info */
740  page_count = g_key_file_get_integer(data->key_file,
741  window_group, WINDOW_PAGECOUNT, &error);
742  if (error)
743  {
744  g_warning("error reading group %s key %s: %s",
745  window_group, WINDOW_PAGECOUNT, error->message);
746  goto cleanup;
747  }
748  if (page_count == 0)
749  {
750  /* Should never happen, but has during alpha testing. Having this
751  * check doesn't hurt anything. */
752  goto cleanup;
753  }
754  page_start = g_key_file_get_integer(data->key_file,
755  window_group, WINDOW_FIRSTPAGE, &error);
756  if (error)
757  {
758  g_warning("error reading group %s key %s: %s",
759  window_group, WINDOW_FIRSTPAGE, error->message);
760  goto cleanup;
761  }
762 
763  /* Build a window if we don't already have one */
764  if (window == NULL)
765  {
766  DEBUG("Window %d doesn't exist. Creating new window.", data->window_num);
767  DEBUG("active_windows %p.", active_windows);
768  if (active_windows)
769  DEBUG("first window %p.", active_windows->data);
770  window = gnc_main_window_new();
771  }
772 
773  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
774 
775  /* Get the window coordinates, etc. */
776  geom = g_key_file_get_integer_list(data->key_file, window_group,
777  WINDOW_GEOMETRY, &length, &error);
778  if (error)
779  {
780  g_warning("error reading group %s key %s: %s",
781  window_group, WINDOW_GEOMETRY, error->message);
782  g_error_free(error);
783  error = NULL;
784  }
785  else if (length != 2)
786  {
787  g_warning("invalid number of values for group %s key %s",
788  window_group, WINDOW_GEOMETRY);
789  }
790  else
791  {
792  gtk_window_resize(GTK_WINDOW(window), geom[0], geom[1]);
793  DEBUG("window (%p) size %dx%d", window, geom[0], geom[1]);
794  }
795  /* keep the geometry for a test whether the windows position
796  is offscreen */
797 
798  pos = g_key_file_get_integer_list(data->key_file, window_group,
799  WINDOW_POSITION, &length, &error);
800  if (error)
801  {
802  g_warning("error reading group %s key %s: %s",
803  window_group, WINDOW_POSITION, error->message);
804  g_error_free(error);
805  error = NULL;
806  }
807  else if (length != 2)
808  {
809  g_warning("invalid number of values for group %s key %s",
810  window_group, WINDOW_POSITION);
811  }
812  /* Prevent restoring coordinates if this would move the window off-screen */
813  else if ((pos[0] + (geom ? geom[0] : 0) < 0) ||
814  (pos[0] > gdk_screen_width()) ||
815  (pos[1] + (geom ? geom[1] : 0) < 0) ||
816  (pos[1] > gdk_screen_height()))
817  {
818  DEBUG("position %dx%d, size%dx%d is offscreen; will not move",
819  pos[0], pos[1], geom ? geom[0] : 0, geom ? geom[1] : 0);
820  }
821  else
822  {
823  gtk_window_move(GTK_WINDOW(window), pos[0], pos[1]);
824  priv->pos[0] = pos[0];
825  priv->pos[1] = pos[1];
826  DEBUG("window (%p) position %dx%d", window, pos[0], pos[1]);
827  }
828  if (geom)
829  {
830  g_free(geom);
831  }
832  if (pos)
833  {
834  g_free(pos);
835  }
836 
837  max = g_key_file_get_boolean(data->key_file, window_group,
838  WINDOW_MAXIMIZED, &error);
839  if (error)
840  {
841  g_warning("error reading group %s key %s: %s",
842  window_group, WINDOW_MAXIMIZED, error->message);
843  g_error_free(error);
844  error = NULL;
845  }
846  else if (max)
847  {
848  gtk_window_maximize(GTK_WINDOW(window));
849  }
850 
851  /* Common view menu items */
852  action = gnc_main_window_find_action(window, "ViewToolbarAction");
853  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
854  desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
855  TOOLBAR_VISIBLE, &error);
856  if (error)
857  {
858  g_warning("error reading group %s key %s: %s",
859  window_group, TOOLBAR_VISIBLE, error->message);
860  g_error_free(error);
861  error = NULL;
862  }
863  else if (visible != desired_visibility)
864  {
865  gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
866  }
867 
868  action = gnc_main_window_find_action(window, "ViewSummaryAction");
869  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
870  desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
871  SUMMARYBAR_VISIBLE, &error);
872  if (error)
873  {
874  g_warning("error reading group %s key %s: %s",
875  window_group, TOOLBAR_VISIBLE, error->message);
876  g_error_free(error);
877  error = NULL;
878  }
879  else if (visible != desired_visibility)
880  {
881  gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
882  }
883 
884  action = gnc_main_window_find_action(window, "ViewStatusbarAction");
885  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
886  desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
887  STATUSBAR_VISIBLE, &error);
888  if (error)
889  {
890  g_warning("error reading group %s key %s: %s",
891  window_group, TOOLBAR_VISIBLE, error->message);
892  g_error_free(error);
893  error = NULL;
894  }
895  else if (visible != desired_visibility)
896  {
897  gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
898  }
899  priv->restoring_pages = TRUE;
900  /* Now populate the window with pages. */
901  for (i = 0; i < page_count; i++)
902  {
903  data->page_offset = page_start;
904  data->page_num = i;
905  gnc_main_window_restore_page(window, data);
906 
907  /* give the page a chance to display */
908  while (gtk_events_pending ())
909  gtk_main_iteration ();
910  }
911  priv->restoring_pages = FALSE;
912  /* Restore page ordering within the notebook. Use +1 notation so the
913  * numbers in the page order match the page sections, at least for
914  * the one window case. */
915  order = g_key_file_get_integer_list(data->key_file, window_group,
916  WINDOW_PAGEORDER, &length, &error);
917  if (error)
918  {
919  g_warning("error reading group %s key %s: %s",
920  window_group, WINDOW_PAGEORDER, error->message);
921  g_error_free(error);
922  error = NULL;
923  }
924  else if (length != page_count)
925  {
926  g_warning("%s key %s length %" G_GSIZE_FORMAT " differs from window page count %d",
927  window_group, WINDOW_PAGEORDER, length, page_count);
928  }
929  else
930  {
931  /* Dump any list that might exist */
932  g_list_free(priv->usage_order);
933  priv->usage_order = NULL;
934  /* Now rebuild the list from the key file. */
935  for (i = 0; i < length; i++)
936  {
937  gpointer page = g_list_nth_data(priv->installed_pages, order[i] - 1);
938  if (page)
939  {
940  priv->usage_order = g_list_append(priv->usage_order, page);
941  }
942  }
943  gtk_notebook_set_current_page (GTK_NOTEBOOK(priv->notebook),
944  order[0] - 1);
945 
946  g_signal_emit_by_name (window, "page_changed",
947  g_list_nth_data (priv->usage_order, 0));
948  }
949  if (order)
950  {
951  g_free(order);
952  }
953 
954  LEAVE("window %p", window);
955 cleanup:
956  if (error)
957  g_error_free(error);
958  g_free(window_group);
959  if (window)
960  gtk_widget_show (GTK_WIDGET(window));
961 }
962 
963 void
964 gnc_main_window_restore_all_windows(const GKeyFile *keyfile)
965 {
966  gint i, window_count;
967  GError *error = NULL;
969  GncMainWindow *window;
970 
971  /* We use the same struct for reading and for writing, so we cast
972  away the const. */
973  data.key_file = (GKeyFile *) keyfile;
974  window_count = g_key_file_get_integer(data.key_file, STATE_FILE_TOP,
975  WINDOW_COUNT, &error);
976  if (error)
977  {
978  g_warning("error reading group %s key %s: %s",
979  STATE_FILE_TOP, WINDOW_COUNT, error->message);
980  g_error_free(error);
981  LEAVE("can't read count");
982  return;
983  }
984 
985  /* Restore all state information on the open windows. Window
986  numbers in state file are 1-based. GList indices are 0-based. */
987  gnc_set_busy_cursor (NULL, TRUE);
988  for (i = 0; i < window_count; i++)
989  {
990  data.window_num = i;
991  window = g_list_nth_data(active_windows, i);
992  gnc_main_window_restore_window(window, &data);
993  }
994  gnc_unset_busy_cursor (NULL);
995 
996  statusbar_notification_lastmodified();
997 }
998 
999 void
1001 {
1002  GtkAction *action;
1003 
1004  /* The default state should be to have an Account Tree page open
1005  * in the window. */
1006  DEBUG("no saved state file");
1007  if (!window)
1008  window = g_list_nth_data(active_windows, 0);
1009  gtk_widget_show (GTK_WIDGET(window));
1010  action = gnc_main_window_find_action(window, "ViewAccountTreeAction");
1011  gtk_action_activate(action);
1012 }
1013 
1023 static void
1024 gnc_main_window_save_page (GncPluginPage *page, GncMainWindowSaveData *data)
1025 {
1026  gchar *page_group;
1027  const gchar *plugin_name, *page_name;
1028 
1029  ENTER("page %p, data %p (key file %p, window %d, page %d)",
1030  page, data, data->key_file, data->window_num, data->page_num);
1031  plugin_name = gnc_plugin_page_get_plugin_name(page);
1032  page_name = gnc_plugin_page_get_page_name(page);
1033  if (!plugin_name || !page_name)
1034  {
1035  LEAVE("not saving invalid page");
1036  return;
1037  }
1038  page_group = g_strdup_printf(PAGE_STRING, data->page_num++);
1039  g_key_file_set_string(data->key_file, page_group, PAGE_TYPE, plugin_name);
1040  g_key_file_set_string(data->key_file, page_group, PAGE_NAME, page_name);
1041 
1042  gnc_plugin_page_save_page(page, data->key_file, page_group);
1043  g_free(page_group);
1044  LEAVE(" ");
1045 }
1046 
1047 
1056 static void
1057 gnc_main_window_save_window (GncMainWindow *window, GncMainWindowSaveData *data)
1058 {
1059  GncMainWindowPrivate *priv;
1060  GtkAction *action;
1061  gint i, num_pages, coords[4], *order;
1062  gboolean maximized, minimized, visible;
1063  gchar *window_group;
1064 
1065  /* Setup */
1066  ENTER("window %p, data %p (key file %p, window %d)",
1067  window, data, data->key_file, data->window_num);
1068  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1069 
1070  /* Check for bogus window structures. */
1071  num_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook));
1072  if (0 == num_pages)
1073  {
1074  LEAVE("empty window %p", window);
1075  return;
1076  }
1077 
1078  /* Save this window's notebook info */
1079  window_group = g_strdup_printf(WINDOW_STRING, data->window_num++);
1080  g_key_file_set_integer(data->key_file, window_group,
1081  WINDOW_PAGECOUNT, num_pages);
1082  g_key_file_set_integer(data->key_file, window_group,
1083  WINDOW_FIRSTPAGE, data->page_num);
1084 
1085  /* Save page ordering within the notebook. Use +1 notation so the
1086  * numbers in the page order match the page sections, at least for
1087  * the one window case. */
1088  order = g_malloc(sizeof(gint) * num_pages);
1089  for (i = 0; i < num_pages; i++)
1090  {
1091  gpointer page = g_list_nth_data(priv->usage_order, i);
1092  order[i] = g_list_index(priv->installed_pages, page) + 1;
1093  }
1094  g_key_file_set_integer_list(data->key_file, window_group,
1095  WINDOW_PAGEORDER, order, num_pages);
1096  g_free(order);
1097 
1098  /* Save the window coordinates, etc. */
1099  gtk_window_get_position(GTK_WINDOW(window), &coords[0], &coords[1]);
1100  gtk_window_get_size(GTK_WINDOW(window), &coords[2], &coords[3]);
1101  maximized = (gdk_window_get_state(gtk_widget_get_window ((GTK_WIDGET(window))))
1102  & GDK_WINDOW_STATE_MAXIMIZED) != 0;
1103  minimized = (gdk_window_get_state(gtk_widget_get_window ((GTK_WIDGET(window))))
1104  & GDK_WINDOW_STATE_ICONIFIED) != 0;
1105 
1106  if (minimized)
1107  {
1108  gint *pos = priv->pos;
1109  g_key_file_set_integer_list(data->key_file, window_group,
1110  WINDOW_POSITION, &pos[0], 2);
1111  DEBUG("window minimized (%p) position %dx%d", window, pos[0], pos[1]);
1112  }
1113  else
1114  g_key_file_set_integer_list(data->key_file, window_group,
1115  WINDOW_POSITION, &coords[0], 2);
1116  g_key_file_set_integer_list(data->key_file, window_group,
1117  WINDOW_GEOMETRY, &coords[2], 2);
1118  g_key_file_set_boolean(data->key_file, window_group,
1119  WINDOW_MAXIMIZED, maximized);
1120  DEBUG("window (%p) position %dx%d, size %dx%d, %s", window, coords[0], coords[1],
1121  coords[2], coords[3],
1122  maximized ? "maximized" : "not maximized");
1123 
1124  /* Common view menu items */
1125  action = gnc_main_window_find_action(window, "ViewToolbarAction");
1126  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
1127  g_key_file_set_boolean(data->key_file, window_group,
1128  TOOLBAR_VISIBLE, visible);
1129  action = gnc_main_window_find_action(window, "ViewSummaryAction");
1130  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
1131  g_key_file_set_boolean(data->key_file, window_group,
1132  SUMMARYBAR_VISIBLE, visible);
1133  action = gnc_main_window_find_action(window, "ViewStatusbarAction");
1134  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
1135  g_key_file_set_boolean(data->key_file, window_group,
1136  STATUSBAR_VISIBLE, visible);
1137 
1138  /* Save individual pages in this window */
1139  g_list_foreach(priv->installed_pages, (GFunc)gnc_main_window_save_page, data);
1140 
1141  g_free(window_group);
1142  LEAVE("window %p", window);
1143 }
1144 
1145 void
1147 {
1148  GncMainWindowSaveData data;
1149 
1150  /* Set up the iterator data structures */
1151  data.key_file = keyfile;
1152  data.window_num = 1;
1153  data.page_num = 1;
1154 
1155  g_key_file_set_integer(data.key_file,
1156  STATE_FILE_TOP, WINDOW_COUNT,
1157  g_list_length(active_windows));
1158  /* Dump all state information on the open windows */
1159  g_list_foreach(active_windows, (GFunc)gnc_main_window_save_window, &data);
1160 }
1161 
1162 
1163 gboolean
1165 {
1166  GncMainWindowPrivate *priv;
1167  GList *item;
1168 
1169  g_return_val_if_fail(GNC_IS_MAIN_WINDOW(window), TRUE);
1170 
1171  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1172  for (item = priv->installed_pages; item; item = g_list_next(item))
1173  {
1174  if (!gnc_plugin_page_finish_pending(item->data))
1175  {
1176  return FALSE;
1177  }
1178  }
1179  return TRUE;
1180 }
1181 
1182 
1183 gboolean
1185 {
1186  const GList *windows, *item;
1187 
1188  windows = gnc_gobject_tracking_get_list(GNC_MAIN_WINDOW_NAME);
1189  for (item = windows; item; item = g_list_next(item))
1190  {
1191  if (!gnc_main_window_finish_pending(item->data))
1192  {
1193  return FALSE;
1194  }
1195  }
1196  if (gnc_gui_refresh_suspended ())
1197  {
1198  gnc_warning_dialog (NULL, "%s", "An operation is still running, wait for it to complete before quitting.");
1199  return FALSE;
1200  }
1201  return TRUE;
1202 }
1203 
1204 
1215 static gboolean
1216 gnc_main_window_page_exists (GncPluginPage *page)
1217 {
1218  GncMainWindow *window;
1219  GncMainWindowPrivate *priv;
1220  GList *walker;
1221 
1222  for (walker = active_windows; walker; walker = g_list_next(walker))
1223  {
1224  window = walker->data;
1225  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1226  if (g_list_find(priv->installed_pages, page))
1227  {
1228  return TRUE;
1229  }
1230  }
1231  return FALSE;
1232 }
1233 
1234 static gboolean auto_save_countdown (GtkWidget *dialog)
1235 {
1236  GtkWidget *label;
1237  gchar *timeoutstr = NULL;
1238 
1239  /* Stop count down if user closed the dialog since the last time we were called */
1240  if (!GTK_IS_DIALOG (dialog))
1241  return FALSE; /* remove timer */
1242 
1243  /* Stop count down if count down text can't be updated */
1244  label = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), "count-down-label"));
1245  if (!GTK_IS_LABEL (label))
1246  return FALSE; /* remove timer */
1247 
1248  /* Protect against rolling over to MAXUINT */
1249  if (secs_to_save)
1250  --secs_to_save;
1251  DEBUG ("Counting down: %d seconds", secs_to_save);
1252 
1253  timeoutstr = g_strdup_printf (MSG_AUTO_SAVE, secs_to_save);
1254  gtk_label_set_text (GTK_LABEL (label), timeoutstr);
1255  g_free (timeoutstr);
1256 
1257  /* Count down reached 0. Save and close dialog */
1258  if (!secs_to_save)
1259  {
1260  gtk_dialog_response (GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);
1261  return FALSE; /* remove timer */
1262  }
1263 
1264  /* Run another cycle */
1265  return TRUE;
1266 }
1267 
1268 
1278 static gboolean
1279 gnc_main_window_prompt_for_save (GtkWidget *window)
1280 {
1281  QofSession *session;
1282  QofBook *book;
1283  GtkWidget *dialog, *msg_area, *label;
1284  gint response;
1285  const gchar *filename, *tmp;
1286  const gchar *title = _("Save changes to file %s before closing?");
1287  /* This should be the same message as in gnc-file.c */
1288  const gchar *message_hours =
1289  _("If you don't save, changes from the past %d hours and %d minutes will be discarded.");
1290  const gchar *message_days =
1291  _("If you don't save, changes from the past %d days and %d hours will be discarded.");
1292  time64 oldest_change;
1293  gint minutes, hours, days;
1294  guint timer_source = 0;
1295  if (!gnc_current_session_exist())
1296  return FALSE;
1297  session = gnc_get_current_session();
1298  book = qof_session_get_book(session);
1299  if (!qof_book_session_not_saved(book))
1300  return FALSE;
1301  filename = qof_session_get_url(session);
1302  if (!strlen (filename))
1303  filename = _("<unknown>");
1304  if ((tmp = strrchr(filename, '/')) != NULL)
1305  filename = tmp + 1;
1306 
1307  /* Remove any pending auto-save timeouts */
1308  gnc_autosave_remove_timer(book);
1309 
1310  dialog = gtk_message_dialog_new(GTK_WINDOW(window),
1311  GTK_DIALOG_MODAL,
1312  GTK_MESSAGE_WARNING,
1313  GTK_BUTTONS_NONE,
1314  title,
1315  filename);
1316  oldest_change = qof_book_get_session_dirty_time(book);
1317  minutes = (gnc_time (NULL) - oldest_change) / 60 + 1;
1318  hours = minutes / 60;
1319  minutes = minutes % 60;
1320  days = hours / 24;
1321  hours = hours % 24;
1322  if (days > 0)
1323  {
1324  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1325  message_days, days, hours);
1326  }
1327  else if (hours > 0)
1328  {
1329  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1330  message_hours, hours, minutes);
1331  }
1332  else
1333  {
1334  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1335  ngettext("If you don't save, changes from the past %d minute will be discarded.",
1336  "If you don't save, changes from the past %d minutes will be discarded.",
1337  minutes), minutes);
1338  }
1339  gtk_dialog_add_buttons(GTK_DIALOG(dialog),
1340  _("Close _Without Saving"), GTK_RESPONSE_CLOSE,
1341  _("_Cancel"), GTK_RESPONSE_CANCEL,
1342  _("_Save"), GTK_RESPONSE_APPLY,
1343  NULL);
1344  gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);
1345 
1346  /* If requested by the user, add a timeout to the question to save automatically
1347  * if the user doesn't answer after a chosen number of seconds.
1348  */
1349  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_CLOSE_EXPIRES))
1350  {
1351  gchar *timeoutstr = NULL;
1352 
1353  secs_to_save = gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_CLOSE_WAIT_TIME);
1354  timeoutstr = g_strdup_printf (MSG_AUTO_SAVE, secs_to_save);
1355  label = GTK_WIDGET(gtk_label_new (timeoutstr));
1356  g_free (timeoutstr);
1357  gtk_widget_show (label);
1358 
1359  msg_area = gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG(dialog));
1360  gtk_box_pack_end (GTK_BOX(msg_area), label, TRUE, TRUE, 0);
1361  g_object_set (G_OBJECT (label), "xalign", 0.0, NULL);
1362 
1363  g_object_set_data (G_OBJECT (dialog), "count-down-label", label);
1364  timer_source = g_timeout_add_seconds (1, (GSourceFunc)auto_save_countdown, dialog);
1365  }
1366 
1367  response = gtk_dialog_run (GTK_DIALOG (dialog));
1368  if (timer_source)
1369  g_source_remove (timer_source);
1370  gtk_widget_destroy(dialog);
1371 
1372  switch (response)
1373  {
1374  case GTK_RESPONSE_APPLY:
1375  gnc_file_save (GTK_WINDOW (window));
1376  return FALSE;
1377 
1378  case GTK_RESPONSE_CLOSE:
1380  return FALSE;
1381 
1382  default:
1383  return TRUE;
1384  }
1385 }
1386 
1387 
1388 static void
1389 gnc_main_window_add_plugin (gpointer plugin,
1390  gpointer window)
1391 {
1392  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
1393  g_return_if_fail (GNC_IS_PLUGIN (plugin));
1394 
1395  ENTER(" ");
1396  gnc_plugin_add_to_window (GNC_PLUGIN (plugin),
1397  GNC_MAIN_WINDOW (window),
1398  window_type);
1399  LEAVE(" ");
1400 }
1401 
1402 static void
1403 gnc_main_window_remove_plugin (gpointer plugin,
1404  gpointer window)
1405 {
1406  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
1407  g_return_if_fail (GNC_IS_PLUGIN (plugin));
1408 
1409  ENTER(" ");
1410  gnc_plugin_remove_from_window (GNC_PLUGIN (plugin),
1411  GNC_MAIN_WINDOW (window),
1412  window_type);
1413  LEAVE(" ");
1414 }
1415 
1416 
1417 static gboolean
1418 gnc_main_window_timed_quit (gpointer dummy)
1419 {
1420  if (gnc_file_save_in_progress())
1421  return TRUE;
1422 
1423  gnc_shutdown (0);
1424  return FALSE;
1425 }
1426 
1427 static gboolean
1428 gnc_main_window_quit(GncMainWindow *window)
1429 {
1430  QofSession *session;
1431  gboolean needs_save, do_shutdown = TRUE;
1432  if (gnc_current_session_exist())
1433  {
1434  session = gnc_get_current_session();
1435  needs_save =
1437  !gnc_file_save_in_progress();
1438  do_shutdown = !needs_save ||
1439  (needs_save &&
1440  !gnc_main_window_prompt_for_save(GTK_WIDGET(window)));
1441  }
1442  if (do_shutdown)
1443  {
1444  GList *w, *next;
1445 
1446  /* This is not a typical list iteration. There is a possibility
1447  * that the window may be removed from the active_windows list so
1448  * we have to cache the 'next' pointer before executing any code
1449  * in the loop. */
1450  for (w = active_windows; w; w = next)
1451  {
1452  GncMainWindowPrivate *priv;
1453  GncMainWindow *wind = w->data;
1454 
1455  next = g_list_next (w);
1456 
1457  wind->window_quitting = TRUE; // set window_quitting on all windows
1458 
1459  priv = GNC_MAIN_WINDOW_GET_PRIVATE(wind);
1460 
1461  // if there are no pages destroy window
1462  if (priv->installed_pages == NULL)
1463  gtk_widget_destroy (GTK_WIDGET(wind));
1464  }
1465  /* remove the preference callbacks from the main window */
1466  gnc_main_window_remove_prefs (window);
1467  g_timeout_add(250, gnc_main_window_timed_quit, NULL);
1468  return TRUE;
1469  }
1470  return FALSE;
1471 }
1472 
1473 static gboolean
1474 gnc_main_window_delete_event (GtkWidget *window,
1475  GdkEvent *event,
1476  gpointer user_data)
1477 {
1478  static gboolean already_dead = FALSE;
1479 
1480  if (already_dead)
1481  return TRUE;
1482 
1483  if (gnc_list_length_cmp (active_windows, 1) > 0)
1484  {
1485  gint response;
1486  GtkWidget *dialog;
1487  gchar *message = _("This window is closing and will not be restored.");
1488 
1489  dialog = gtk_message_dialog_new (GTK_WINDOW (window),
1490  GTK_DIALOG_DESTROY_WITH_PARENT,
1491  GTK_MESSAGE_QUESTION,
1492  GTK_BUTTONS_NONE,
1493  "%s", _("Close Window?"));
1494  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
1495  "%s", message);
1496 
1497  gtk_dialog_add_buttons (GTK_DIALOG(dialog),
1498  _("_Cancel"), GTK_RESPONSE_CANCEL,
1499  _("_OK"), GTK_RESPONSE_YES,
1500  (gchar *)NULL);
1501  gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_YES);
1502  response = gnc_dialog_run (GTK_DIALOG(dialog), GNC_PREF_WARN_CLOSING_WINDOW_QUESTION);
1503  gtk_widget_destroy (dialog);
1504 
1505  if (response == GTK_RESPONSE_CANCEL)
1506  return TRUE;
1507  }
1508 
1509  if (!gnc_main_window_finish_pending(GNC_MAIN_WINDOW(window)))
1510  {
1511  /* Don't close the window. */
1512  return TRUE;
1513  }
1514 
1515  if (gnc_list_length_cmp (active_windows, 1) > 0)
1516  return FALSE;
1517 
1518  already_dead = gnc_main_window_quit(GNC_MAIN_WINDOW(window));
1519  return TRUE;
1520 }
1521 
1522 
1542 static void
1543 gnc_main_window_event_handler (QofInstance *entity, QofEventId event_type,
1544  gpointer user_data, gpointer event_data)
1545 {
1546  GncMainWindow *window;
1547  GncMainWindowPrivate *priv;
1548  GncPluginPage *page;
1549  GList *item, *next;
1550 
1551  /* hard failures */
1552  g_return_if_fail(GNC_IS_MAIN_WINDOW(user_data));
1553 
1554  /* soft failures */
1555  if (!QOF_CHECK_TYPE(entity, QOF_ID_BOOK))
1556  return;
1557  if (event_type != QOF_EVENT_DESTROY)
1558  return;
1559 
1560  ENTER("entity %p, event %d, window %p, event data %p",
1561  entity, event_type, user_data, event_data);
1562  window = GNC_MAIN_WINDOW(user_data);
1563  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1564 
1565  /* This is not a typical list iteration. We're removing while
1566  * we iterate, so we have to cache the 'next' pointer before
1567  * executing any code in the loop. */
1568  for (item = priv->installed_pages; item; item = next)
1569  {
1570  next = g_list_next(item);
1571  page = GNC_PLUGIN_PAGE(item->data);
1572  if (gnc_plugin_page_has_book (page, (QofBook *)entity))
1574  }
1575 
1576  if (GTK_IS_WIDGET(window) && window->window_quitting)
1577  gtk_widget_destroy (GTK_WIDGET(window));
1578 
1579  LEAVE(" ");
1580 }
1581 
1582 
1599 static gchar *
1600 gnc_main_window_generate_title (GncMainWindow *window)
1601 {
1602  GncMainWindowPrivate *priv;
1603  GncPluginPage *page;
1604  QofBook *book;
1605  gboolean immutable;
1606  gchar *filename = NULL;
1607  const gchar *uri = NULL;
1608  const gchar *dirty = "";
1609  const gchar *readonly_text = NULL;
1610  gchar *readonly;
1611  gchar *title;
1612 
1613  if (gnc_current_session_exist())
1614  {
1615  uri = qof_session_get_url (gnc_get_current_session ());
1616  book = gnc_get_current_book();
1617  if (qof_book_session_not_saved (book))
1618  dirty = "*";
1619  if (qof_book_is_readonly(book))
1620  {
1621  /* Translators: This string is shown in the window title if this
1622  document is, well, read-only. */
1623  readonly_text = _("(read-only)");
1624  }
1625  }
1626  readonly = (readonly_text != NULL)
1627  ? g_strdup_printf(" %s", readonly_text)
1628  : g_strdup("");
1629 
1630  if (!uri || g_strcmp0 (uri, "") == 0)
1631  filename = g_strdup(_("Unsaved Book"));
1632  else
1633  {
1634  if (gnc_uri_targets_local_fs (uri))
1635  {
1636  /* The filename is a true file.
1637  The Gnome HIG 2.0 recommends only the file name (no path) be used. (p15) */
1638  gchar *path = gnc_uri_get_path ( uri );
1639  filename = g_path_get_basename ( path );
1640  g_free ( path );
1641  }
1642  else
1643  {
1644  /* The filename is composed of database connection parameters.
1645  For this we will show access_method://username@database[:port] */
1646  filename = gnc_uri_normalize_uri (uri, FALSE);
1647  }
1648  }
1649 
1650  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1651  page = priv->current_page;
1652  if (page)
1653  {
1654  /* The Gnome HIG 2.0 recommends the application name not be used. (p16)
1655  but several developers prefer to use it anyway. */
1656  title = g_strdup_printf("%s%s%s - %s - GnuCash", dirty, filename, readonly,
1658  }
1659  else
1660  {
1661  title = g_strdup_printf("%s%s%s - GnuCash", dirty, filename, readonly);
1662  }
1663  /* Update the menus based upon whether this is an "immutable" page. */
1664  immutable = page &&
1665  g_object_get_data (G_OBJECT (page), PLUGIN_PAGE_IMMUTABLE);
1667  immutable_page_actions,
1668  "sensitive", !immutable);
1669  /* Trigger sensitivity updtates of other actions such as Save/Revert */
1670  g_signal_emit_by_name (window, "page_changed", page);
1671  g_free( filename );
1672  g_free(readonly);
1673 
1674  return title;
1675 }
1676 
1677 
1687 static void
1688 gnc_main_window_update_title (GncMainWindow *window)
1689 {
1690  gchar *title;
1691 
1692  title = gnc_main_window_generate_title(window);
1693  gtk_window_set_title(GTK_WINDOW(window), title);
1694  g_free(title);
1695 }
1696 
1697 static void
1698 gnc_main_window_update_all_titles (void)
1699 {
1700  g_list_foreach(active_windows,
1701  (GFunc)gnc_main_window_update_title,
1702  NULL);
1703 }
1704 
1705 static void
1706 gnc_main_window_book_dirty_cb (QofBook *book,
1707  gboolean dirty,
1708  gpointer user_data)
1709 {
1710  gnc_main_window_update_all_titles();
1711 
1712  /* Auto-save feature */
1713  gnc_autosave_dirty_handler(book, dirty);
1714 }
1715 
1716 static void
1717 gnc_main_window_attach_to_book (QofSession *session)
1718 {
1719  QofBook *book;
1720 
1721  g_return_if_fail(session);
1722 
1723  book = qof_session_get_book(session);
1724  qof_book_set_dirty_cb(book, gnc_main_window_book_dirty_cb, NULL);
1725  gnc_main_window_update_all_titles();
1726 #ifndef MAC_INTEGRATION
1727  gnc_main_window_update_all_menu_items();
1728 #endif
1729 }
1730 
1731 static guint gnc_statusbar_notification_messageid = 0;
1732 //#define STATUSBAR_NOTIFICATION_AUTOREMOVAL
1733 #ifdef STATUSBAR_NOTIFICATION_AUTOREMOVAL
1734 /* Removes the statusbar notification again that has been pushed to the
1735  * statusbar by generate_statusbar_lastmodified_message. */
1736 static gboolean statusbar_notification_off(gpointer user_data_unused)
1737 {
1738  GncMainWindow *mainwindow = GNC_MAIN_WINDOW (gnc_ui_get_main_window (NULL));
1739  //g_warning("statusbar_notification_off\n");
1740  if (gnc_statusbar_notification_messageid == 0)
1741  return FALSE;
1742 
1743  if (mainwindow)
1744  {
1745  GtkWidget *statusbar = gnc_main_window_get_statusbar(GNC_WINDOW(mainwindow));
1746  gtk_statusbar_remove(GTK_STATUSBAR(statusbar), 0, gnc_statusbar_notification_messageid);
1747  gnc_statusbar_notification_messageid = 0;
1748  }
1749  else
1750  {
1751  g_warning("oops, no GncMainWindow obtained\n");
1752  }
1753  return FALSE; // should not be called again
1754 }
1755 #endif // STATUSBAR_NOTIFICATION_AUTOREMOVAL
1756 
1757 /* Creates a statusbar message stating the last modification time of the opened
1758  * data file. */
1759 static gchar *generate_statusbar_lastmodified_message()
1760 {
1761  gchar *message = NULL;
1762  const gchar *uri = NULL;
1763 
1764  if (gnc_current_session_exist())
1765  {
1766  uri = qof_session_get_url (gnc_get_current_session ());
1767  }
1768 
1769  if (!(uri && strlen (uri)))
1770  return NULL;
1771  else
1772  {
1773  if (gnc_uri_targets_local_fs (uri))
1774  {
1775  /* The filename is a true file. */
1776  gchar *filepath = gnc_uri_get_path ( uri );
1777  gchar *filename = g_path_get_basename ( filepath );
1778  GFile *file = g_file_new_for_uri (uri);
1779  GFileInfo *info = g_file_query_info (file,
1780  G_FILE_ATTRIBUTE_TIME_MODIFIED,
1781  G_FILE_QUERY_INFO_NONE,
1782  NULL, NULL);
1783 
1784  if (info && g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED))
1785  {
1786  guint64 modtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
1787 
1788  /* Translators: This is the date and time that is shown in
1789  the status bar after opening a file: The date and time of
1790  last modification. The string is a format string using
1791  boost::date_time's format flags, see the boost docs for an
1792  explanation of the modifiers. */
1793  char *time_string = gnc_print_time64 (modtime,
1794  _("Last modified on %a, %b %d, %Y at %I:%M %p"));
1795  //g_warning("got time %ld, str=%s\n", mtime, time_string);
1796  /* Translators: This message appears in the status bar after opening the file. */
1797  message = g_strdup_printf(_("File %s opened. %s"),
1798  filename, time_string);
1799  free(time_string);
1800  }
1801  else
1802  {
1803  g_warning("Unable to read mtime for file %s\n", filepath);
1804  // message is still NULL
1805  }
1806  g_free(filename);
1807  g_free(filepath);
1808  g_object_unref (info);
1809  g_object_unref (file);
1810  }
1811  // If the URI is not a file but a database, we can maybe also show
1812  // something useful, but I have no idea how to obtain this information.
1813  }
1814  return message;
1815 }
1816 
1817 static void
1818 statusbar_notification_lastmodified()
1819 {
1820  // First look up the first GncMainWindow to set the statusbar there
1821  GList *iter;
1822  GtkWidget *widget = NULL;
1823  for (iter = active_windows; iter && !(widget && GNC_IS_MAIN_WINDOW(widget));
1824  iter = g_list_next(iter))
1825  {
1826  widget = iter->data;
1827  }
1828  if (widget && GNC_IS_MAIN_WINDOW(widget))
1829  {
1830  // Ok, we found a mainwindow where we can set a statusbar message
1831  GncMainWindow *mainwindow = GNC_MAIN_WINDOW(widget);
1832  GtkWidget *statusbar = gnc_main_window_get_statusbar(GNC_WINDOW(mainwindow));
1833 
1834  gchar *msg = generate_statusbar_lastmodified_message();
1835  if (msg)
1836  {
1837  gnc_statusbar_notification_messageid = gtk_statusbar_push(GTK_STATUSBAR(statusbar), 0, msg);
1838  }
1839  g_free(msg);
1840 
1841 #ifdef STATUSBAR_NOTIFICATION_AUTOREMOVAL
1842  // Also register a timeout callback to remove that statusbar
1843  // notification again after 10 seconds
1844  g_timeout_add(10 * 1000, statusbar_notification_off, NULL); // maybe not needed anyway?
1845 #endif
1846  }
1847  else
1848  {
1849  g_warning("uh oh, no GNC_IS_MAIN_WINDOW\n");
1850  }
1851 }
1852 
1853 
1858 {
1860  gchar *action_name;
1861 
1863  gchar *label;
1864 
1866  gboolean visible;
1867 };
1868 
1869 #ifndef MAC_INTEGRATION
1870 
1883 static void
1884 gnc_main_window_update_one_menu_action (GncMainWindow *window,
1885  struct menu_update *data)
1886 {
1887  GncMainWindowPrivate *priv;
1888  GtkAction* action;
1889 
1890  ENTER("window %p, action %s, label %s, visible %d", window,
1891  data->action_name, data->label, data->visible);
1892  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1893  action = gtk_action_group_get_action(priv->action_group, data->action_name);
1894  if (action)
1895  g_object_set(G_OBJECT(action),
1896  "label", data->label,
1897  "visible", data->visible,
1898  (char *)NULL);
1899  LEAVE(" ");
1900 }
1901 
1914 static void
1915 gnc_main_window_update_radio_button (GncMainWindow *window)
1916 {
1917  GncMainWindowPrivate *priv;
1918  GtkAction *action, *first_action;
1919  GSList *action_list;
1920  gchar *action_name;
1921  gint index;
1922 
1923  ENTER("window %p", window);
1924 
1925  /* Show the new entry in all windows. */
1926  index = g_list_index(active_windows, window);
1927  if (index >= n_radio_entries)
1928  {
1929  LEAVE("window %d, only %d actions", index, n_radio_entries);
1930  return;
1931  }
1932 
1933  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1934  action_name = g_strdup_printf("Window%dAction", index);
1935  action = gtk_action_group_get_action(priv->action_group, action_name);
1936 
1937  /* Block the signal so as not to affect window ordering (top to
1938  * bottom) on the screen */
1939  action_list = gtk_radio_action_get_group(GTK_RADIO_ACTION(action));
1940  if (action_list)
1941  {
1942  first_action = g_slist_last(action_list)->data;
1943  g_signal_handlers_block_by_func(G_OBJECT(first_action),
1944  G_CALLBACK(gnc_main_window_cmd_window_raise),
1945  window);
1946  DEBUG("blocked signal on %p, set %p active, window %p", first_action,
1947  action, window);
1948  gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
1949  g_signal_handlers_unblock_by_func(G_OBJECT(first_action),
1950  G_CALLBACK(gnc_main_window_cmd_window_raise),
1951  window);
1952  }
1953  g_free(action_name);
1954  LEAVE(" ");
1955 }
1956 
1970 static void
1971 gnc_main_window_update_menu_item (GncMainWindow *window)
1972 {
1973  struct menu_update data;
1974  gchar **strings, *title, *expanded;
1975  gint index;
1976 
1977  ENTER("window %p", window);
1978  index = g_list_index(active_windows, window);
1979  if (index > n_radio_entries)
1980  {
1981  LEAVE("skip window %d (only %d entries)", index, n_radio_entries);
1982  return;
1983  }
1984 
1985  /* Figure out the label name. Add the accelerator if possible. */
1986  title = gnc_main_window_generate_title(window);
1987  strings = g_strsplit(title, "_", 0);
1988  g_free(title);
1989  expanded = g_strjoinv("__", strings);
1990  if (index < 10)
1991  {
1992  data.label = g_strdup_printf("_%d %s", (index + 1) % 10, expanded);
1993  g_free(expanded);
1994  }
1995  else
1996  {
1997  data.label = expanded;
1998  }
1999  g_strfreev(strings);
2000 
2001  data.visible = TRUE;
2002  data.action_name = g_strdup_printf("Window%dAction", index);
2003  g_list_foreach(active_windows,
2004  (GFunc)gnc_main_window_update_one_menu_action,
2005  &data);
2006  g_free(data.action_name);
2007  g_free(data.label);
2008 
2009  LEAVE(" ");
2010 }
2011 #endif /* !MAC_INTEGRATION */
2012 
2021 #ifndef MAC_INTEGRATION
2022 static void
2023 gnc_main_window_update_all_menu_items (void)
2024 {
2025  struct menu_update data;
2026  gchar *label;
2027  gint i;
2028 
2029  ENTER("");
2030  /* First update the entries for all existing windows */
2031  g_list_foreach(active_windows,
2032  (GFunc)gnc_main_window_update_menu_item,
2033  NULL);
2034  g_list_foreach(active_windows,
2035  (GFunc)gnc_main_window_update_radio_button,
2036  NULL);
2037 
2038  /* Now hide any entries that aren't being used. */
2039  data.visible = FALSE;
2040  for (i = g_list_length(active_windows); i < n_radio_entries; i++)
2041  {
2042  data.action_name = g_strdup_printf("Window%dAction", i);
2043  label = g_strdup_printf("Window _%d", (i - 1) % 10);
2044  data.label = gettext(label);
2045 
2046  g_list_foreach(active_windows,
2047  (GFunc)gnc_main_window_update_one_menu_action,
2048  &data);
2049 
2050  g_free(data.action_name);
2051  g_free(label);
2052  }
2053  LEAVE(" ");
2054 }
2055 #endif /* !MAC_INTEGRATION */
2056 
2068 static void
2069 gnc_main_window_update_tab_close_one_page (GncPluginPage *page,
2070  gpointer user_data)
2071 {
2072  gboolean *new_value = user_data;
2073  GtkWidget * close_button;
2074 
2075  ENTER("page %p, visible %d", page, *new_value);
2076  close_button = g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON);
2077  if (!close_button)
2078  {
2079  LEAVE("no close button");
2080  return;
2081  }
2082 
2083  if (*new_value)
2084  gtk_widget_show (close_button);
2085  else
2086  gtk_widget_hide (close_button);
2087  LEAVE(" ");
2088 }
2089 
2090 
2103 static void
2104 gnc_main_window_update_tab_close (gpointer prefs, gchar *pref, gpointer user_data)
2105 {
2106  gboolean new_value;
2107 
2108  ENTER(" ");
2109  new_value = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SHOW_CLOSE_BUTTON);
2110  gnc_main_window_foreach_page(
2111  gnc_main_window_update_tab_close_one_page,
2112  &new_value);
2113  LEAVE(" ");
2114 }
2115 
2116 
2125 static void
2126 gnc_main_window_update_tab_color_one_page (GncPluginPage *page,
2127  gpointer user_data)
2128 {
2129  const gchar *color_string;
2130 
2131  ENTER("page %p", page);
2132  color_string = gnc_plugin_page_get_page_color(page);
2133  main_window_update_page_color (page, color_string);
2134  LEAVE(" ");
2135 }
2136 
2137 
2148 static void
2149 gnc_main_window_update_tab_color (gpointer gsettings, gchar *pref, gpointer user_data)
2150 {
2151  GncMainWindowPrivate *priv;
2152  GncMainWindow *window;
2153 
2154  ENTER(" ");
2155  g_return_if_fail(GNC_IS_MAIN_WINDOW(user_data));
2156  window = user_data;
2157  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2158  if (g_strcmp0 (GNC_PREF_TAB_COLOR, pref) == 0)
2159  priv->show_color_tabs = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_COLOR);
2160  gnc_main_window_foreach_page (gnc_main_window_update_tab_color_one_page, window);
2161  LEAVE(" ");
2162 }
2163 
2164 
2168 typedef struct
2169 {
2170  gint tab_width;
2171  gboolean tabs_left_right;
2172 } TabWidth;
2173 
2174 static TabWidth *
2175 populate_tab_width_struct (void)
2176 {
2177  TabWidth *tw;
2178 
2179  tw = g_new0 (TabWidth, 1);
2180  tw->tab_width = gnc_prefs_get_float (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_WIDTH);
2181  tw->tabs_left_right = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT) ||
2182  gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT);
2183 
2184  return tw;
2185 }
2186 
2208 static void
2209 gnc_main_window_set_tab_ellipsize (GtkWidget *label, gint tab_width, gboolean tab_left_right)
2210 {
2211  const gchar *lab_text = gtk_label_get_text (GTK_LABEL(label));
2212 
2213  if (tab_width != 0)
2214  {
2215  gint text_length = g_utf8_strlen (lab_text, -1);
2216  if (text_length < tab_width)
2217  {
2218  if (tab_left_right) // tabs position is left or right
2219  gtk_label_set_width_chars (GTK_LABEL(label), tab_width);
2220  else // tabs position is top or bottom
2221  gtk_label_set_width_chars (GTK_LABEL(label), text_length);
2222 
2223  gtk_label_set_ellipsize (GTK_LABEL(label), PANGO_ELLIPSIZE_NONE);
2224  }
2225  else
2226  {
2227  gtk_label_set_width_chars (GTK_LABEL(label), tab_width);
2228  gtk_label_set_ellipsize (GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
2229  }
2230  }
2231  else
2232  {
2233  gtk_label_set_width_chars (GTK_LABEL(label), 15);
2234  gtk_label_set_ellipsize (GTK_LABEL(label), PANGO_ELLIPSIZE_NONE);
2235  }
2236 }
2237 
2238 
2249 static void
2250 gnc_main_window_update_tab_width_one_page (GncPluginPage *page,
2251  gpointer user_data)
2252 {
2253  TabWidth *tw = user_data;
2254  GtkWidget *label;
2255 
2256  ENTER("page %p, tab width %d, tabs on left or right %d",
2257  page, tw->tab_width, tw->tabs_left_right);
2258 
2259  label = g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_TAB_LABEL);
2260  if (!label)
2261  {
2262  LEAVE("no label");
2263  return;
2264  }
2265  gnc_main_window_set_tab_ellipsize (label, tw->tab_width, tw->tabs_left_right);
2266  LEAVE(" ");
2267 }
2268 
2269 
2282 static void
2283 gnc_main_window_update_tab_width (gpointer prefs, gchar *pref, gpointer user_data)
2284 {
2285  TabWidth *tw;
2286 
2287  ENTER(" ");
2288 
2289  tw = populate_tab_width_struct ();
2290 
2291  gnc_main_window_foreach_page (gnc_main_window_update_tab_width_one_page, tw);
2292  g_free (tw);
2293 
2294  LEAVE(" ");
2295 }
2296 
2297 
2298 /************************************************************
2299  * Tab Label Implementation *
2300  ************************************************************/
2301 static gboolean
2302 main_window_find_tab_items (GncMainWindow *window,
2303  GncPluginPage *page,
2304  GtkWidget **label_p,
2305  GtkWidget **entry_p)
2306 {
2307  GncMainWindowPrivate *priv;
2308  GtkWidget *tab_hbox, *widget, *tab_widget;
2309  GList *children, *tmp;
2310 
2311  ENTER("window %p, page %p, label_p %p, entry_p %p",
2312  window, page, label_p, entry_p);
2313  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2314  *label_p = *entry_p = NULL;
2315 
2316  if (!page->notebook_page)
2317  {
2318  LEAVE("invalid notebook_page");
2319  return FALSE;
2320  }
2321 
2322  tab_widget = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
2323  page->notebook_page);
2324  if (GTK_IS_EVENT_BOX (tab_widget))
2325  tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
2326  else if (GTK_IS_BOX (tab_widget))
2327  tab_hbox = tab_widget;
2328  else
2329  {
2330  PWARN ("Unknown widget for tab label %p", tab_widget);
2331  return FALSE;
2332  }
2333 
2334  children = gtk_container_get_children(GTK_CONTAINER(tab_hbox));
2335  for (tmp = children; tmp; tmp = g_list_next(tmp))
2336  {
2337  widget = tmp->data;
2338  if (GTK_IS_LABEL(widget))
2339  {
2340  *label_p = widget;
2341  }
2342  else if (GTK_IS_ENTRY(widget))
2343  {
2344  *entry_p = widget;
2345  }
2346  }
2347  g_list_free(children);
2348 
2349  LEAVE("label %p, entry %p", *label_p, *entry_p);
2350  return (*label_p && *entry_p);
2351 }
2352 
2353 static gboolean
2354 main_window_find_tab_widget (GncMainWindow *window,
2355  GncPluginPage *page,
2356  GtkWidget **widget_p)
2357 {
2358  GncMainWindowPrivate *priv;
2359 
2360  ENTER("window %p, page %p, widget %p",
2361  window, page, widget_p);
2362  *widget_p = NULL;
2363 
2364  if (!page->notebook_page)
2365  {
2366  LEAVE("invalid notebook_page");
2367  return FALSE;
2368  }
2369 
2370  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2371  *widget_p = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
2372  page->notebook_page);
2373 
2374  LEAVE("widget %p", *widget_p);
2375  return TRUE;
2376 }
2377 
2378 void
2380  const gchar *name_in)
2381 {
2382  GncMainWindow *window;
2383  GncMainWindowPrivate *priv;
2384  GtkWidget *label, *entry;
2385  gchar *name, *old_page_name, *old_page_long_name;
2386  TabWidth *tw;
2387 
2388  ENTER(" ");
2389 
2390  if ((name_in == NULL) || (*name_in == '\0'))
2391  {
2392  LEAVE("no string");
2393  return;
2394  }
2395  name = g_strstrip(g_strdup(name_in));
2396 
2397  /* Optimization, if the name hasn't changed, don't update X. */
2398  if (*name == '\0' || 0 == strcmp(name, gnc_plugin_page_get_page_name(page)))
2399  {
2400  g_free(name);
2401  LEAVE("empty string or name unchanged");
2402  return;
2403  }
2404 
2405  old_page_name = g_strdup( gnc_plugin_page_get_page_name(page));
2406  old_page_long_name = g_strdup( gnc_plugin_page_get_page_long_name(page));
2407 
2408  /* Update the plugin */
2409  gnc_plugin_page_set_page_name(page, name);
2410 
2411  /* Update the notebook tab */
2412  window = GNC_MAIN_WINDOW(page->window);
2413  if (!window)
2414  {
2415  g_free(old_page_name);
2416  g_free(old_page_long_name);
2417  g_free(name);
2418  LEAVE("no window widget available");
2419  return;
2420  }
2421 
2422  if (main_window_find_tab_items(window, page, &label, &entry))
2423  gtk_label_set_text(GTK_LABEL(label), name);
2424 
2425  /* Adjust the label width for new text */
2426  tw = populate_tab_width_struct ();
2427  gnc_main_window_update_tab_width_one_page (page, tw);
2428  g_free (tw);
2429 
2430  /* Update Tooltip on notebook Tab */
2431  if (old_page_long_name && old_page_name
2432  && g_strrstr(old_page_long_name, old_page_name) != NULL)
2433  {
2434  gchar *new_page_long_name;
2435  gint string_position;
2436  GtkWidget *tab_widget;
2437 
2438  string_position = strlen(old_page_long_name) - strlen(old_page_name);
2439  new_page_long_name = g_strconcat(g_strndup(old_page_long_name, string_position), name, NULL);
2440 
2441  gnc_plugin_page_set_page_long_name(page, new_page_long_name);
2442 
2443  if (main_window_find_tab_widget(window, page, &tab_widget))
2444  gtk_widget_set_tooltip_text(tab_widget, new_page_long_name);
2445 
2446  g_free(new_page_long_name);
2447  }
2448 
2449  /* Update the notebook menu */
2450  if (page->notebook_page)
2451  {
2452  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2453  label = gtk_notebook_get_menu_label (GTK_NOTEBOOK(priv->notebook),
2454  page->notebook_page);
2455  gtk_label_set_text(GTK_LABEL(label), name);
2456  }
2457 
2458  /* Force an update of the window title */
2459  gnc_main_window_update_title(window);
2460  g_free(old_page_long_name);
2461  g_free(old_page_name);
2462  g_free(name);
2463  LEAVE("done");
2464 }
2465 
2466 
2467 void
2469  const gchar *color_in)
2470 {
2471  GncMainWindow *window;
2472  GncMainWindowPrivate *priv;
2473  GtkWidget *tab_widget;
2474  GdkRGBA tab_color;
2475  gchar *color_string = NULL;
2476  gboolean want_color = FALSE;
2477 
2478  ENTER(" ");
2479  if (color_in)
2480  color_string = g_strstrip(g_strdup(color_in));
2481 
2482  if (color_string && *color_string != '\0')
2483  want_color = TRUE;
2484 
2485  /* Update the plugin */
2486  window = GNC_MAIN_WINDOW(page->window);
2487  if (want_color)
2488  gnc_plugin_page_set_page_color(page, color_string);
2489  else
2490  gnc_plugin_page_set_page_color(page, NULL);
2491 
2492  /* Update the notebook tab */
2493  main_window_find_tab_widget (window, page, &tab_widget);
2494  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2495 
2496  if (want_color && gdk_rgba_parse(&tab_color, color_string) && priv->show_color_tabs)
2497  {
2498  GtkCssProvider *provider = gtk_css_provider_new();
2499  GtkStyleContext *stylectxt;
2500  gchar *col_str, *widget_css;
2501 
2502  if (!GTK_IS_EVENT_BOX (tab_widget))
2503  {
2504  GtkWidget *event_box = gtk_event_box_new ();
2505  g_object_ref (tab_widget);
2506  gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
2507  page->notebook_page, event_box);
2508  gtk_container_add (GTK_CONTAINER(event_box), tab_widget);
2509  g_object_unref (tab_widget);
2510  tab_widget = event_box;
2511  }
2512 
2513  stylectxt = gtk_widget_get_style_context (GTK_WIDGET (tab_widget));
2514  col_str = gdk_rgba_to_string (&tab_color);
2515  widget_css = g_strconcat ("*{\n background-color:", col_str, ";\n}\n", NULL);
2516 
2517  gtk_css_provider_load_from_data (provider, widget_css, -1, NULL);
2518  gtk_style_context_add_provider (stylectxt, GTK_STYLE_PROVIDER (provider),
2519  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
2520  g_object_unref (provider);
2521  g_free (col_str);
2522  g_free (widget_css);
2523  }
2524  else
2525  {
2526  if (GTK_IS_EVENT_BOX (tab_widget))
2527  {
2528  GtkWidget *tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
2529  g_object_ref (tab_hbox);
2530  gtk_container_remove (GTK_CONTAINER(tab_widget), tab_hbox);
2531  gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
2532  page->notebook_page, tab_hbox);
2533  g_object_unref (tab_hbox);
2534  }
2535  }
2536  g_free(color_string);
2537  LEAVE("done");
2538 }
2539 
2540 
2541 void
2543  gboolean read_only)
2544 {
2545  GncMainWindow *window;
2546  GncMainWindowPrivate *priv;
2547  GtkWidget *tab_widget;
2548  GtkWidget *image = NULL;
2549  GList *children;
2550  gchar *image_name = NULL;
2551  const gchar *icon_name;
2552 
2553  ENTER(" ");
2554 
2555  g_return_if_fail (page && page->window);
2556 
2557  if (!GNC_IS_MAIN_WINDOW (page->window))
2558  return;
2559 
2560  window = GNC_MAIN_WINDOW(page->window);
2561 
2562  /* Get the notebook tab widget */
2563  main_window_find_tab_widget (window, page, &tab_widget);
2564  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2565 
2566  if (!tab_widget)
2567  {
2568  LEAVE("no tab widget");
2569  return;
2570  }
2571 
2572  if (GTK_IS_EVENT_BOX(tab_widget))
2573  tab_widget = gtk_bin_get_child (GTK_BIN(tab_widget));
2574 
2575  children = gtk_container_get_children (GTK_CONTAINER(tab_widget));
2576  /* For each, walk the list of container children to get image widget */
2577  for (GList *child = children; child; child = g_list_next (child))
2578  {
2579  GtkWidget *widget = child->data;
2580  if (GTK_IS_IMAGE(widget))
2581  image = widget;
2582  }
2583  g_list_free (children);
2584 
2585  if (!image)
2586  {
2587  LEAVE("no image to replace");
2588  return;
2589  }
2590 
2591  g_object_get (image, "icon-name", &image_name, NULL);
2592 
2593  if (read_only)
2594  icon_name = "changes-prevent-symbolic";
2595  else
2596  icon_name = GNC_PLUGIN_PAGE_GET_CLASS(page)->tab_icon;
2597 
2598  if (g_strcmp0 (icon_name, image_name) == 0)
2599  {
2600  LEAVE("page icon the same, no need to replace");
2601  g_free (image_name);
2602  return;
2603  }
2604  gtk_container_remove (GTK_CONTAINER(tab_widget), image);
2605  image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
2606  gtk_widget_show (image);
2607 
2608  gtk_container_add (GTK_CONTAINER(tab_widget), image);
2609  gtk_widget_set_margin_start (GTK_WIDGET(image), 5);
2610  gtk_box_reorder_child (GTK_BOX(tab_widget), image, 0);
2611 
2612  g_free (image_name);
2613  LEAVE("done");
2614 }
2615 
2616 
2617 static void
2618 gnc_main_window_tab_entry_activate (GtkWidget *entry,
2619  GncPluginPage *page)
2620 {
2621  GtkWidget *label, *entry2;
2622 
2623  g_return_if_fail(GTK_IS_ENTRY(entry));
2624  g_return_if_fail(GNC_IS_PLUGIN_PAGE(page));
2625 
2626  ENTER("");
2627  if (!main_window_find_tab_items(GNC_MAIN_WINDOW(page->window),
2628  page, &label, &entry2))
2629  {
2630  LEAVE("can't find required widgets");
2631  return;
2632  }
2633 
2634  main_window_update_page_name(page, gtk_entry_get_text(GTK_ENTRY(entry)));
2635 
2636  gtk_widget_hide(entry);
2637  gtk_widget_show(label);
2638  LEAVE("");
2639 }
2640 
2641 
2642 static gboolean
2643 gnc_main_window_tab_entry_editing_done (GtkWidget *entry,
2644  GncPluginPage *page)
2645 {
2646  ENTER("");
2647  gnc_main_window_tab_entry_activate(entry, page);
2648  LEAVE("");
2649  return FALSE;
2650 }
2651 
2652 static gboolean
2653 gnc_main_window_tab_entry_focus_out_event (GtkWidget *entry,
2654  GdkEvent *event,
2655  GncPluginPage *page)
2656 {
2657  ENTER("");
2658  gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
2659  LEAVE("");
2660  return FALSE;
2661 }
2662 
2663 static gboolean
2664 gnc_main_window_tab_entry_key_press_event (GtkWidget *entry,
2665  GdkEventKey *event,
2666  GncPluginPage *page)
2667 {
2668  if (event->keyval == GDK_KEY_Escape)
2669  {
2670  GtkWidget *label, *entry2;
2671 
2672  g_return_val_if_fail(GTK_IS_ENTRY(entry), FALSE);
2673  g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
2674 
2675  ENTER("");
2676  if (!main_window_find_tab_items(GNC_MAIN_WINDOW(page->window),
2677  page, &label, &entry2))
2678  {
2679  LEAVE("can't find required widgets");
2680  return FALSE;
2681  }
2682 
2683  gtk_entry_set_text(GTK_ENTRY(entry), gtk_label_get_text(GTK_LABEL(label)));
2684  gtk_widget_hide(entry);
2685  gtk_widget_show(label);
2686  LEAVE("");
2687  }
2688  return FALSE;
2689 }
2690 
2691 /************************************************************
2692  * Widget Implementation *
2693  ************************************************************/
2694 
2695 
2696 
2704 static void
2705 gnc_main_window_class_init (GncMainWindowClass *klass)
2706 {
2707  GObjectClass *object_class = G_OBJECT_CLASS (klass);
2708  GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS(klass);
2709 
2710  parent_class = g_type_class_peek_parent (klass);
2711 
2712  window_type = g_quark_from_static_string ("gnc-main-window");
2713 
2714  object_class->finalize = gnc_main_window_finalize;
2715 
2716  /* GtkWidget signals */
2717  gtkwidget_class->destroy = gnc_main_window_destroy;
2718 
2730  main_window_signals[PAGE_ADDED] =
2731  g_signal_new ("page_added",
2732  G_OBJECT_CLASS_TYPE (object_class),
2733  G_SIGNAL_RUN_FIRST,
2734  G_STRUCT_OFFSET (GncMainWindowClass, page_added),
2735  NULL, NULL,
2736  g_cclosure_marshal_VOID__OBJECT,
2737  G_TYPE_NONE, 1,
2738  G_TYPE_OBJECT);
2739 
2750  main_window_signals[PAGE_CHANGED] =
2751  g_signal_new ("page_changed",
2752  G_OBJECT_CLASS_TYPE (object_class),
2753  G_SIGNAL_RUN_FIRST,
2754  G_STRUCT_OFFSET (GncMainWindowClass, page_changed),
2755  NULL, NULL,
2756  g_cclosure_marshal_VOID__OBJECT,
2757  G_TYPE_NONE, 1,
2758  G_TYPE_OBJECT);
2759 
2760  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
2761  GNC_PREF_SHOW_CLOSE_BUTTON,
2762  gnc_main_window_update_tab_close,
2763  NULL);
2764  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
2765  GNC_PREF_TAB_WIDTH,
2766  gnc_main_window_update_tab_width,
2767  NULL);
2768 
2769  gnc_hook_add_dangler(HOOK_BOOK_SAVED,
2770  (GFunc)gnc_main_window_update_all_titles, NULL, NULL);
2771  gnc_hook_add_dangler(HOOK_BOOK_OPENED,
2772  (GFunc)gnc_main_window_attach_to_book, NULL, NULL);
2773 
2774 }
2775 
2776 
2785 static void
2786 gnc_main_window_init (GncMainWindow *window, void *data)
2787 {
2788  GncMainWindowPrivate *priv;
2789 
2790  GncMainWindowClass *klass = (GncMainWindowClass*)data;
2791 
2792  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2793  priv->merged_actions_table =
2794  g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
2795 
2796  // Set the name for this dialog so it can be easily manipulated with css
2797  gtk_widget_set_name (GTK_WIDGET(window), "gnc-id-main-window");
2798 
2799  priv->event_handler_id =
2800  qof_event_register_handler(gnc_main_window_event_handler, window);
2801 
2802  priv->restoring_pages = FALSE;
2803 
2804  /* Get the show_color_tabs value preference */
2805  priv->show_color_tabs = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_COLOR);
2806 
2807  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
2808  GNC_PREF_TAB_COLOR,
2809  gnc_main_window_update_tab_color,
2810  window);
2811 
2812  gnc_main_window_setup_window (window);
2813  gnc_gobject_tracking_remember(G_OBJECT(window),
2814  G_OBJECT_CLASS(klass));
2815 }
2816 
2817 
2828 static void
2829 gnc_main_window_finalize (GObject *object)
2830 {
2831  g_return_if_fail (object != NULL);
2832  g_return_if_fail (GNC_IS_MAIN_WINDOW (object));
2833 
2834  if (active_windows == NULL)
2835  {
2836  /* Oops. User killed last window and we didn't catch it. */
2837  g_idle_add((GSourceFunc)gnc_shutdown, 0);
2838  }
2839 
2841  G_OBJECT_CLASS (parent_class)->finalize (object);
2842 }
2843 
2844 
2845 static void
2846 gnc_main_window_remove_prefs (GncMainWindow *window)
2847 {
2848  // remove the registered preference callbacks setup in this file.
2849  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2850  GNC_PREF_TAB_COLOR,
2851  gnc_main_window_update_tab_color,
2852  window);
2853 
2854  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2855  GNC_PREF_SHOW_CLOSE_BUTTON,
2856  gnc_main_window_update_tab_close,
2857  NULL);
2858 
2859  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2860  GNC_PREF_TAB_WIDTH,
2861  gnc_main_window_update_tab_width,
2862  NULL);
2863 
2864  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2865  GNC_PREF_TAB_POSITION_TOP,
2866  gnc_main_window_update_tab_position,
2867  window);
2868 
2869  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2870  GNC_PREF_TAB_POSITION_BOTTOM,
2871  gnc_main_window_update_tab_position,
2872  window);
2873 
2874  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2875  GNC_PREF_TAB_POSITION_LEFT,
2876  gnc_main_window_update_tab_position,
2877  window);
2878 
2879  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2880  GNC_PREF_TAB_POSITION_RIGHT,
2881  gnc_main_window_update_tab_position,
2882  window);
2883 
2884  // remove the registered negative color preference callback.
2885  if (gnc_prefs_get_reg_negative_color_pref_id() > 0 && window->window_quitting)
2886  {
2887  gnc_prefs_remove_cb_by_id (GNC_PREFS_GROUP_GENERAL,
2889  gnc_prefs_set_reg_negative_color_pref_id (0);
2890  }
2891 
2892  // remove the registered auto_raise_lists preference callback.
2893  if (gnc_prefs_get_reg_auto_raise_lists_id() > 0 && window->window_quitting)
2894  {
2895  gnc_prefs_remove_cb_by_id (GNC_PREFS_GROUP_GENERAL_REGISTER,
2897  gnc_prefs_set_reg_auto_raise_lists_id (0);
2898  }
2899 }
2900 
2901 
2902 static void
2903 gnc_main_window_destroy (GtkWidget *widget)
2904 {
2905  GncMainWindow *window;
2906  GncMainWindowPrivate *priv;
2907  GncPluginManager *manager;
2908  GList *plugins;
2909 
2910  g_return_if_fail (widget != NULL);
2911  g_return_if_fail (GNC_IS_MAIN_WINDOW (widget));
2912 
2913  window = GNC_MAIN_WINDOW (widget);
2914 
2915  active_windows = g_list_remove (active_windows, window);
2916 
2917  /* Do these things once */
2918  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2919  if (priv->merged_actions_table)
2920  {
2921 
2922  /* Close any pages in this window */
2923  while (priv->current_page)
2925 
2926  if (gnc_window_get_progressbar_window() == GNC_WINDOW(window))
2927  gnc_window_set_progressbar_window(NULL);
2928 #ifndef MAC_INTEGRATION
2929  /* Update the "Windows" menu in all other windows */
2930  gnc_main_window_update_all_menu_items();
2931 #endif
2932  /* remove the preference callbacks from the main window */
2933  gnc_main_window_remove_prefs (window);
2934 
2936  priv->event_handler_id = 0;
2937 
2938  g_hash_table_destroy (priv->merged_actions_table);
2939  priv->merged_actions_table = NULL;
2940 
2941  /* GncPluginManager stuff */
2942  manager = gnc_plugin_manager_get ();
2943  plugins = gnc_plugin_manager_get_plugins (manager);
2944  g_list_foreach (plugins, gnc_main_window_remove_plugin, window);
2945  g_list_free (plugins);
2946  }
2947  GTK_WIDGET_CLASS (parent_class)->destroy (widget);
2948 }
2949 
2950 
2951 static gboolean
2952 gnc_main_window_key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
2953 {
2954  GncMainWindowPrivate *priv;
2955  GdkModifierType modifiers;
2956 
2957  g_return_val_if_fail (GNC_IS_MAIN_WINDOW(widget), FALSE);
2958 
2959  priv = GNC_MAIN_WINDOW_GET_PRIVATE(widget);
2960 
2961  modifiers = gtk_accelerator_get_default_mod_mask ();
2962 
2963  if ((event->state & modifiers) == (GDK_CONTROL_MASK | GDK_MOD1_MASK)) // Ctrl+Alt+
2964  {
2965  const gchar *account_key = C_ ("lower case key for short cut to 'Accounts'", "a");
2966  guint account_keyval = gdk_keyval_from_name (account_key);
2967 
2968  if ((account_keyval == event->keyval) || (account_keyval == gdk_keyval_to_lower (event->keyval)))
2969  {
2970  gint page = 0;
2971 
2972  for (GList *item = priv->installed_pages; item; item = g_list_next (item))
2973  {
2974  const gchar *pname = gnc_plugin_page_get_plugin_name (GNC_PLUGIN_PAGE(item->data));
2975 
2976  if (g_strcmp0 (pname, "GncPluginPageAccountTree") == 0)
2977  {
2978  gtk_notebook_set_current_page (GTK_NOTEBOOK(priv->notebook), page);
2979  return TRUE;
2980  }
2981  page++;
2982  }
2983  }
2984  else if ((GDK_KEY_Menu == event->keyval) || (GDK_KEY_space == event->keyval))
2985  {
2986  GList *menu = gtk_menu_get_for_attach_widget (GTK_WIDGET(priv->notebook));
2987 
2988  if (menu)
2989  {
2990  gtk_menu_popup_at_widget (GTK_MENU(menu->data),
2991  GTK_WIDGET(priv->notebook),
2992  GDK_GRAVITY_SOUTH,
2993  GDK_GRAVITY_SOUTH,
2994  NULL);
2995  return TRUE;
2996  }
2997  }
2998  }
2999  return FALSE;
3000 }
3001 
3002 
3003 /* Create a new gnc main window plugin.
3004  */
3005 GncMainWindow *
3007 {
3008  GncMainWindow *window;
3009  GtkWindow *old_window;
3010 
3011  window = g_object_new (GNC_TYPE_MAIN_WINDOW, NULL);
3012  gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
3013 
3014  old_window = gnc_ui_get_main_window (NULL);
3015  if (old_window)
3016  {
3017  gint width, height;
3018  gtk_window_get_size (old_window, &width, &height);
3019  gtk_window_resize (GTK_WINDOW (window), width, height);
3020  if ((gdk_window_get_state((gtk_widget_get_window (GTK_WIDGET(old_window))))
3021  & GDK_WINDOW_STATE_MAXIMIZED) != 0)
3022  {
3023  gtk_window_maximize (GTK_WINDOW (window));
3024  }
3025  }
3026  active_windows = g_list_append (active_windows, window);
3027  gnc_main_window_update_title(window);
3028  window->window_quitting = FALSE;
3029  window->just_plugin_prefs = FALSE;
3030 #ifdef MAC_INTEGRATION
3031  gnc_quartz_set_menu(window);
3032 #else
3033  gnc_main_window_update_all_menu_items();
3034 #endif
3035  gnc_engine_add_commit_error_callback( gnc_main_window_engine_commit_error_callback, window );
3036 
3037  // set up a callback for notebook navigation
3038  g_signal_connect (G_OBJECT(window), "key-press-event",
3039  G_CALLBACK(gnc_main_window_key_press_event),
3040  NULL);
3041 
3042  return window;
3043 }
3044 
3045 /************************************************************
3046  * Utility Functions *
3047  ************************************************************/
3048 
3049 static void
3050 gnc_main_window_engine_commit_error_callback( gpointer data,
3051  QofBackendError errcode )
3052 {
3053  GncMainWindow* window = GNC_MAIN_WINDOW(data);
3054  GtkWidget* dialog;
3055  const gchar *reason = _("Unable to save to database.");
3056  if ( errcode == ERR_BACKEND_READONLY )
3057  reason = _("Unable to save to database: Book is marked read-only.");
3058  dialog = gtk_message_dialog_new( GTK_WINDOW(window),
3059  GTK_DIALOG_DESTROY_WITH_PARENT,
3060  GTK_MESSAGE_ERROR,
3061  GTK_BUTTONS_CLOSE,
3062  "%s",
3063  reason );
3064  gtk_dialog_run(GTK_DIALOG (dialog));
3065  gtk_widget_destroy(dialog);
3066 
3067 }
3068 
3086 static void
3087 gnc_main_window_connect (GncMainWindow *window,
3088  GncPluginPage *page,
3089  GtkWidget *tab_hbox,
3090  GtkWidget *menu_label)
3091 {
3092  GncMainWindowPrivate *priv;
3093  GtkNotebook *notebook;
3094  gint current_position = -1;
3095 
3096  page->window = GTK_WIDGET(window);
3097  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3098  notebook = GTK_NOTEBOOK (priv->notebook);
3099 
3100  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_OPEN_ADJACENT))
3101  current_position = g_list_index (priv->installed_pages, priv->current_page) + 1;
3102 
3103  priv->installed_pages = g_list_insert (priv->installed_pages, page, current_position);
3104  priv->usage_order = g_list_prepend (priv->usage_order, page);
3105  gtk_notebook_insert_page_menu (notebook, page->notebook_page,
3106  tab_hbox, menu_label, current_position);
3107  gtk_notebook_set_tab_reorderable (notebook, page->notebook_page, TRUE);
3108  gnc_plugin_page_inserted (page);
3109  gtk_notebook_set_current_page (notebook, current_position);
3110 
3111  if (GNC_PLUGIN_PAGE_GET_CLASS(page)->window_changed)
3112  (GNC_PLUGIN_PAGE_GET_CLASS(page)->window_changed)(page, GTK_WIDGET(window));
3113  g_signal_emit (window, main_window_signals[PAGE_ADDED], 0, page);
3114 
3115  g_signal_connect(G_OBJECT(page->notebook_page), "popup-menu",
3116  G_CALLBACK(gnc_main_window_popup_menu_cb), page);
3117  g_signal_connect_after(G_OBJECT(page->notebook_page), "button-press-event",
3118  G_CALLBACK(gnc_main_window_button_press_cb), page);
3119 }
3120 
3121 
3135 static void
3136 gnc_main_window_disconnect (GncMainWindow *window,
3137  GncPluginPage *page)
3138 {
3139  GncMainWindowPrivate *priv;
3140  GtkNotebook *notebook;
3141  GncPluginPage *new_page;
3142  gint page_num;
3143 
3144  /* Disconnect the callbacks */
3145  g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
3146  G_CALLBACK(gnc_main_window_popup_menu_cb), page);
3147  g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
3148  G_CALLBACK(gnc_main_window_button_press_cb), page);
3149 
3150  // Remove the page_changed signal callback
3151  gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(page));
3152 
3153  /* Disconnect the page and summarybar from the window */
3154  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3155  if (priv->current_page == page)
3156  {
3157  gnc_plugin_page_unmerge_actions (page, window->ui_merge);
3158  gnc_plugin_page_unselected (page);
3159  priv->current_page = NULL;
3160  }
3161 
3162  /* Remove it from the list of pages in the window */
3163  priv->installed_pages = g_list_remove (priv->installed_pages, page);
3164  priv->usage_order = g_list_remove (priv->usage_order, page);
3165 
3166  /* Switch to the last recently used page */
3167  notebook = GTK_NOTEBOOK (priv->notebook);
3168  if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_NEXT_RECENT))
3169  {
3170  new_page = g_list_nth_data (priv->usage_order, 0);
3171  if (new_page)
3172  {
3173  page_num = gtk_notebook_page_num(notebook, new_page->notebook_page);
3174  gtk_notebook_set_current_page(notebook, page_num);
3175  /* This may have caused WebKit to schedule a timer interrupt which it
3176  sometimes forgets to cancel before deleting the object. See
3177  <https://bugs.webkit.org/show_bug.cgi?id=119003>. Get around this
3178  by flushing all events to get rid of the timer interrupt. */
3179  while (gtk_events_pending())
3180  gtk_main_iteration();
3181  }
3182  }
3183 
3184  /* Remove the page from the notebook */
3185  page_num = gtk_notebook_page_num(notebook, page->notebook_page);
3186  gtk_notebook_remove_page (notebook, page_num);
3187 
3188  if ( gtk_notebook_get_current_page(notebook) == -1)
3189  {
3190  /* Need to synthesize a page changed signal when the last
3191  * page is removed. The notebook doesn't generate a signal
3192  * for this, therefore the switch_page code in this file
3193  * never gets called to generate this signal. */
3194  gnc_main_window_switch_page(notebook, NULL, -1, window);
3195  //g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, NULL);
3196  }
3197 
3198  gnc_plugin_page_removed (page);
3199 
3200  gtk_ui_manager_ensure_update (window->ui_merge);
3201  gnc_window_set_status (GNC_WINDOW(window), page, NULL);
3202 }
3203 
3204 
3205 /************************************************************
3206  * *
3207  ************************************************************/
3208 
3209 
3210 void
3212 {
3213  GncMainWindow *window;
3214  GncMainWindowPrivate *priv;
3215  GtkNotebook *notebook;
3216  gint page_num;
3217 
3218  window = GNC_MAIN_WINDOW (page->window);
3219  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3220  notebook = GTK_NOTEBOOK (priv->notebook);
3221  page_num = gtk_notebook_page_num(notebook, page->notebook_page);
3222  gtk_notebook_set_current_page (notebook, page_num);
3223  gtk_window_present(GTK_WINDOW(window));
3224 }
3225 
3226 
3227 /* Display a data plugin page in a window. If the page already
3228  * exists in any window, then that window will be brought to the
3229  * front and the notebook switch to display the specified page. If
3230  * the page is new then it will be added to the specified window. If
3231  * the window is NULL, the new page will be added to the first
3232  * window.
3233  */
3234 void
3236  GncPluginPage *page)
3237 {
3238  GncMainWindowPrivate *priv;
3239  GtkWidget *tab_hbox;
3240  GtkWidget *label, *entry;
3241  const gchar *icon, *text, *color_string, *lab_text;
3242  GtkWidget *image;
3243  GList *tmp;
3244  TabWidth *tw;
3245 
3246  ENTER("window %p, page %p", window, page);
3247  if (window)
3248  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3249  g_return_if_fail (GNC_IS_PLUGIN_PAGE (page));
3250  g_return_if_fail (gnc_plugin_page_has_books(page));
3251 
3252  if (gnc_main_window_page_exists(page))
3253  {
3255  return;
3256  }
3257 
3258  /* Does the page want to be in a new window? */
3260  {
3261  /* See if there's a blank window. If so, use that. */
3262  for (tmp = active_windows; tmp; tmp = g_list_next(tmp))
3263  {
3264  window = GNC_MAIN_WINDOW(tmp->data);
3265  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3266  if (priv->installed_pages == NULL)
3267  {
3268  break;
3269  }
3270  }
3271  if (tmp == NULL)
3272  window = gnc_main_window_new ();
3273  gtk_widget_show(GTK_WIDGET(window));
3274  }
3275  else if ((window == NULL) && active_windows)
3276  {
3277  window = active_windows->data;
3278  }
3279 
3280  page->window = GTK_WIDGET(window);
3282  g_object_set_data (G_OBJECT (page->notebook_page),
3283  PLUGIN_PAGE_LABEL, page);
3284 
3285  /*
3286  * The page tab.
3287  */
3288  icon = GNC_PLUGIN_PAGE_GET_CLASS(page)->tab_icon;
3289  lab_text = gnc_plugin_page_get_page_name(page);
3290  label = gtk_label_new (lab_text);
3291  g_object_set_data (G_OBJECT (page), PLUGIN_PAGE_TAB_LABEL, label);
3292 
3293  tw = populate_tab_width_struct ();
3294  gnc_main_window_update_tab_width_one_page (page, tw);
3295  g_free (tw);
3296 
3297  gtk_widget_show (label);
3298 
3299  tab_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
3300 
3301  if (g_strcmp0 (gnc_plugin_page_get_plugin_name (page), "GncPluginPageAccountTree") == 0)
3302  gtk_widget_set_name (GTK_WIDGET(tab_hbox), "gnc-id-account-page-tab-box");
3303 
3304  gtk_box_set_homogeneous (GTK_BOX (tab_hbox), FALSE);
3305  gtk_widget_show (tab_hbox);
3306 
3307  if (icon != NULL)
3308  {
3309  image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU);
3310  gtk_widget_show (image);
3311  gtk_box_pack_start (GTK_BOX (tab_hbox), image, FALSE, FALSE, 0);
3312  gtk_widget_set_margin_start (GTK_WIDGET(image), 5);
3313  gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0);
3314  }
3315  else
3316  gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0);
3317 
3319  if (text)
3320  {
3321  gtk_widget_set_tooltip_text(tab_hbox, text);
3322  }
3323 
3324  entry = gtk_entry_new();
3325  gtk_widget_hide (entry);
3326  gtk_box_pack_start (GTK_BOX (tab_hbox), entry, TRUE, TRUE, 0);
3327  g_signal_connect(G_OBJECT(entry), "activate",
3328  G_CALLBACK(gnc_main_window_tab_entry_activate), page);
3329  g_signal_connect(G_OBJECT(entry), "focus-out-event",
3330  G_CALLBACK(gnc_main_window_tab_entry_focus_out_event),
3331  page);
3332  g_signal_connect(G_OBJECT(entry), "key-press-event",
3333  G_CALLBACK(gnc_main_window_tab_entry_key_press_event),
3334  page);
3335  g_signal_connect(G_OBJECT(entry), "editing-done",
3336  G_CALLBACK(gnc_main_window_tab_entry_editing_done),
3337  page);
3338 
3339  /* Add close button - Not for immutable pages */
3340  if (!g_object_get_data (G_OBJECT (page), PLUGIN_PAGE_IMMUTABLE))
3341  {
3342  GtkWidget *close_image, *close_button;
3343  GtkRequisition requisition;
3344 
3345  close_button = gtk_button_new();
3346  gtk_button_set_relief(GTK_BUTTON(close_button), GTK_RELIEF_NONE);
3347  close_image = gtk_image_new_from_icon_name ("window-close", GTK_ICON_SIZE_MENU);
3348  gtk_widget_show(close_image);
3349  gtk_widget_get_preferred_size (close_image, &requisition, NULL);
3350  gtk_widget_set_size_request(close_button, requisition.width + 4,
3351  requisition.height + 2);
3352  gtk_container_add(GTK_CONTAINER(close_button), close_image);
3353  if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_SHOW_CLOSE_BUTTON))
3354  gtk_widget_show (close_button);
3355  else
3356  gtk_widget_hide (close_button);
3357 
3358  g_signal_connect_swapped (G_OBJECT (close_button), "clicked",
3359  G_CALLBACK(gnc_main_window_close_page), page);
3360 
3361  gtk_box_pack_start (GTK_BOX (tab_hbox), close_button, FALSE, FALSE, 0);
3362  gtk_widget_set_margin_end (GTK_WIDGET(close_button), 5);
3363  g_object_set_data (G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON, close_button);
3364  }
3365 
3366  /*
3367  * The popup menu
3368  */
3369  label = gtk_label_new (gnc_plugin_page_get_page_name(page));
3370 
3371  /*
3372  * Now install it all in the window.
3373  */
3374  gnc_main_window_connect(window, page, tab_hbox, label);
3375 
3376  color_string = gnc_plugin_page_get_page_color(page);
3377  main_window_update_page_color (page, color_string);
3378  LEAVE("");
3379 }
3380 
3381 
3382 /* Remove a data plugin page from a window and display the previous
3383  * page. If the page removed was the last page in the window, and
3384  * there is more than one window open, then the entire window will be
3385  * destroyed.
3386  */
3387 void
3389 {
3390  GncMainWindow *window;
3391  GncMainWindowPrivate *priv;
3392 
3393  if (!page || !page->notebook_page)
3394  return;
3395 
3396  if (!gnc_plugin_page_finish_pending(page))
3397  return;
3398 
3399  if (!GNC_IS_MAIN_WINDOW (page->window))
3400  return;
3401 
3402  window = GNC_MAIN_WINDOW (page->window);
3403  if (!window)
3404  {
3405  g_warning("Page is not in a window.");
3406  return;
3407  }
3408 
3409  gnc_main_window_disconnect(window, page);
3411  g_object_unref(page);
3412 
3413  /* If this isn't the last window, go ahead and destroy the window. */
3414  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3415  if (priv->installed_pages == NULL)
3416  {
3417  if (window->window_quitting)
3418  {
3420  GList *plugins = gnc_plugin_manager_get_plugins (manager);
3421 
3422  /* remove only the preference callbacks from the window plugins */
3423  window->just_plugin_prefs = TRUE;
3424  g_list_foreach (plugins, gnc_main_window_remove_plugin, window);
3425  window->just_plugin_prefs = FALSE;
3426  g_list_free (plugins);
3427 
3428  /* remove the preference callbacks from the main window */
3429  gnc_main_window_remove_prefs (window);
3430  }
3431  if (window && (gnc_list_length_cmp (active_windows, 1) > 0))
3432  gtk_widget_destroy (GTK_WIDGET(window));
3433  }
3434 }
3435 
3436 
3437 /* Retrieve a pointer to the page that is currently at the front of
3438  * the specified window. Any plugin that needs to manipulate its
3439  * menus based upon the currently selected menu page should connect
3440  * to the "page_changed" signal on a window. The callback function
3441  * from that signal can then call this function to obtain a pointer
3442  * to the current page.
3443  */
3444 GncPluginPage *
3446 {
3447  GncMainWindowPrivate *priv;
3448 
3449  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3450  return priv->current_page;
3451 }
3452 
3453 
3454 /* Manually add a set of actions to the specified window. Plugins
3455  * whose user interface is not hard coded (e.g. the menu-additions
3456  * plugin) must create their actions at run time, then use this
3457  * function to install them into the window.
3458  */
3459 void
3461  const gchar *group_name,
3462  GtkActionGroup *group,
3463  guint merge_id)
3464 {
3465  GncMainWindowPrivate *priv;
3466  MergedActionEntry *entry;
3467 
3468  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3469  g_return_if_fail (group_name != NULL);
3470  g_return_if_fail (GTK_IS_ACTION_GROUP(group));
3471  g_return_if_fail (merge_id > 0);
3472 
3473  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3474  entry = g_new0 (MergedActionEntry, 1);
3475  entry->action_group = group;
3476  entry->merge_id = merge_id;
3477  gtk_ui_manager_ensure_update (window->ui_merge);
3478  g_hash_table_insert (priv->merged_actions_table, g_strdup (group_name), entry);
3479 }
3480 
3481 
3482 /* Add a set of actions to the specified window. This function
3483  * should not need to be called directly by plugin implementors.
3484  * Correctly assigning values to the GncPluginClass fields during
3485  * plugin initialization will cause this routine to be automatically
3486  * called.
3487  */
3488 void
3490  const gchar *group_name,
3491  GtkActionEntry *actions,
3492  guint n_actions,
3493  GtkToggleActionEntry *toggle_actions,
3494  guint n_toggle_actions,
3495  const gchar *filename,
3496  gpointer user_data)
3497 {
3498  GncMainWindowPrivate *priv;
3500  MergedActionEntry *entry;
3501  GError *error = NULL;
3502  gchar *pathname;
3503 
3504  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3505  g_return_if_fail (group_name != NULL);
3506  g_return_if_fail (actions != NULL);
3507  g_return_if_fail (n_actions > 0);
3508  g_return_if_fail (filename != NULL);
3509 
3510  pathname = gnc_filepath_locate_ui_file (filename);
3511  if (pathname == NULL)
3512  return;
3513 
3514  data = g_new0 (GncMainWindowActionData, 1);
3515  data->window = window;
3516  data->data = user_data;
3517 
3518  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3519  entry = g_new0 (MergedActionEntry, 1);
3520  entry->action_group = gtk_action_group_new (group_name);
3521  gtk_action_group_set_translation_domain (entry->action_group, PROJECT_NAME);
3522  gtk_action_group_add_actions (entry->action_group, actions, n_actions, data);
3523  if (toggle_actions != NULL && n_toggle_actions > 0)
3524  {
3525  gtk_action_group_add_toggle_actions (entry->action_group,
3526  toggle_actions, n_toggle_actions,
3527  data);
3528  }
3529  gtk_ui_manager_insert_action_group (window->ui_merge, entry->action_group, 0);
3530  entry->merge_id = gtk_ui_manager_add_ui_from_file (window->ui_merge, pathname, &error);
3531  g_assert(entry->merge_id || error);
3532  if (entry->merge_id)
3533  {
3534  gtk_ui_manager_ensure_update (window->ui_merge);
3535  g_hash_table_insert (priv->merged_actions_table, g_strdup (group_name), entry);
3536  }
3537  else
3538  {
3539  g_critical("Failed to load ui file.\n Filename %s\n Error %s",
3540  filename, error->message);
3541  g_error_free(error);
3542  g_free(entry);
3543  }
3544  g_free(pathname);
3545 }
3546 
3547 
3548 /* Remove a set of actions from the specified window. This function
3549  * should not need to be called directly by plugin implementors. It
3550  * will automatically be called when a plugin is removed from a
3551  * window.
3552  */
3553 void
3555  const gchar *group_name)
3556 {
3557  GncMainWindowPrivate *priv;
3558  MergedActionEntry *entry;
3559 
3560  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3561  g_return_if_fail (group_name != NULL);
3562 
3563  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3564  if (priv->merged_actions_table == NULL)
3565  return;
3566  entry = g_hash_table_lookup (priv->merged_actions_table, group_name);
3567 
3568  if (entry == NULL)
3569  return;
3570 
3571  gtk_ui_manager_remove_action_group (window->ui_merge, entry->action_group);
3572  gtk_ui_manager_remove_ui (window->ui_merge, entry->merge_id);
3573  gtk_ui_manager_ensure_update (window->ui_merge);
3574 
3575  g_hash_table_remove (priv->merged_actions_table, group_name);
3576 }
3577 
3578 
3579 /* Force a full update of the user interface for the specified
3580  * window. This can be an expensive function, but is needed because
3581  * the gtk ui manager doesn't always seem to update properly when
3582  * actions are changed.
3583  */
3584 void
3586 {
3587  GtkActionGroup *force;
3588 
3589  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3590 
3591  /* Unfortunately gtk_ui_manager_ensure_update doesn't work
3592  * here. Force a full update by adding and removing an empty
3593  * action group.
3594  */
3595  force = gtk_action_group_new("force_update");
3596  gtk_ui_manager_insert_action_group (window->ui_merge, force, 0);
3597  gtk_ui_manager_ensure_update (window->ui_merge);
3598  gtk_ui_manager_remove_action_group (window->ui_merge, force);
3599  g_object_unref(force);
3600 }
3601 
3602 
3603 GtkAction *
3604 gnc_main_window_find_action (GncMainWindow *window, const gchar *name)
3605 {
3606  GtkAction *action = NULL;
3607  const GList *groups, *tmp;
3608 
3609  groups = gtk_ui_manager_get_action_groups(window->ui_merge);
3610  for (tmp = groups; tmp; tmp = g_list_next(tmp))
3611  {
3612  action = gtk_action_group_get_action(GTK_ACTION_GROUP(tmp->data), name);
3613  if (action)
3614  break;
3615  }
3616  return action;
3617 }
3618 
3619 
3620 /* Retrieve a specific set of user interface actions from a window.
3621  * This function can be used to get an group of action to be
3622  * manipulated when the front page of a window has changed.
3623  */
3624 GtkActionGroup *
3626  const gchar *group_name)
3627 {
3628  GncMainWindowPrivate *priv;
3629  MergedActionEntry *entry;
3630 
3631  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), NULL);
3632  g_return_val_if_fail (group_name != NULL, NULL);
3633 
3634  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3635  if (priv->merged_actions_table == NULL)
3636  return NULL;
3637  entry = g_hash_table_lookup (priv->merged_actions_table, group_name);
3638 
3639  if (entry == NULL)
3640  return NULL;
3641 
3642  return entry->action_group;
3643 }
3644 
3645 static void
3646 gnc_main_window_update_tab_position (gpointer prefs, gchar *pref, gpointer user_data)
3647 {
3648  GncMainWindow *window;
3649  GtkPositionType position = GTK_POS_TOP;
3650  GncMainWindowPrivate *priv;
3651  GtkAction *first_action;
3652  GtkAction *position_action;
3653  gsize i;
3654 
3655  g_return_if_fail (GNC_IS_MAIN_WINDOW(user_data));
3656 
3657  window = GNC_MAIN_WINDOW(user_data);
3658 
3659  ENTER ("window %p", window);
3660 
3661  /* Ignore notification of the preference that is being set to false when
3662  * the choice of tab position changes. */
3663  if (pref && !gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, pref))
3664  return;
3665 
3666  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_BOTTOM))
3667  position = GTK_POS_BOTTOM;
3668  else if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT))
3669  position = GTK_POS_LEFT;
3670  else if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT))
3671  position = GTK_POS_RIGHT;
3672 
3673  priv = GNC_MAIN_WINDOW_GET_PRIVATE (window);
3674  gtk_notebook_set_tab_pos (GTK_NOTEBOOK (priv->notebook), position);
3675 
3676  /* Groups of radio actions use the first action for the callback and all
3677  * change events so block/unblock signals on the first radio action. */
3678  first_action = gtk_action_group_get_action (priv->action_group,
3679  tab_pos_radio_entries[0].name);
3680 
3681  for (i = n_tab_pos_radio_entries - 1; i > 0; i--)
3682  if (tab_pos_radio_entries[i].value == position)
3683  break;
3684 
3685  position_action = gtk_action_group_get_action (priv->action_group,
3686  tab_pos_radio_entries[i].name);
3687 
3688  g_signal_handlers_block_by_func (G_OBJECT (first_action),
3689  G_CALLBACK (gnc_main_window_cmd_view_tab_position),
3690  window);
3691  gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (position_action), TRUE);
3692  g_signal_handlers_unblock_by_func (G_OBJECT (first_action),
3693  G_CALLBACK (gnc_main_window_cmd_view_tab_position),
3694  window);
3695 
3696  gnc_main_window_update_tab_width (NULL, GNC_PREF_TAB_WIDTH, NULL);
3697 
3698  LEAVE ("");
3699 }
3700 
3701 /*
3702  * Based on code from Epiphany (src/ephy-window.c)
3703  */
3704 static void
3705 gnc_main_window_update_edit_actions_sensitivity (GncMainWindow *window, gboolean hide)
3706 {
3707  GncMainWindowPrivate *priv;
3708  GncPluginPage *page;
3709  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
3710  GtkAction *action;
3711  gboolean can_copy = FALSE, can_cut = FALSE, can_paste = FALSE;
3712 
3713  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3714  page = priv->current_page;
3715  if (page && GNC_PLUGIN_PAGE_GET_CLASS(page)->update_edit_menu_actions)
3716  {
3717  (GNC_PLUGIN_PAGE_GET_CLASS(page)->update_edit_menu_actions)(page, hide);
3718  return;
3719  }
3720 
3721  if (GTK_IS_EDITABLE (widget))
3722  {
3723  gboolean has_selection;
3724 
3725  has_selection = gtk_editable_get_selection_bounds
3726  (GTK_EDITABLE (widget), NULL, NULL);
3727 
3728  can_copy = has_selection;
3729  can_cut = has_selection;
3730  can_paste = TRUE;
3731  }
3732  else if (GTK_IS_TEXT_VIEW (widget))
3733  {
3734  gboolean has_selection;
3735  GtkTextBuffer *text_buffer;
3736 
3737  text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
3738  has_selection = gtk_text_buffer_get_selection_bounds
3739  (text_buffer, NULL, NULL);
3740 
3741  can_copy = has_selection;
3742  can_cut = has_selection;
3743  can_paste = TRUE;
3744  }
3745  else
3746  {
3747 #ifdef ORIGINAL_EPIPHANY_CODE
3748  /* For now we assume all actions are possible */
3749  can_copy = can_cut = can_paste = TRUE;
3750 #else
3751  /* If its not a GtkEditable, we don't know what to do
3752  * with it. */
3753  can_copy = can_cut = can_paste = FALSE;
3754 #endif
3755  }
3756 
3757  action = gnc_main_window_find_action (window, "EditCopyAction");
3758  gtk_action_set_sensitive (action, can_copy);
3759  gtk_action_set_visible (action, !hide || can_copy);
3760  action = gnc_main_window_find_action (window, "EditCutAction");
3761  gtk_action_set_sensitive (action, can_cut);
3762  gtk_action_set_visible (action, !hide || can_cut);
3763  action = gnc_main_window_find_action (window, "EditPasteAction");
3764  gtk_action_set_sensitive (action, can_paste);
3765  gtk_action_set_visible (action, !hide || can_paste);
3766 }
3767 
3768 static void
3769 gnc_main_window_enable_edit_actions_sensitivity (GncMainWindow *window)
3770 {
3771  GtkAction *action;
3772 
3773  action = gnc_main_window_find_action (window, "EditCopyAction");
3774  gtk_action_set_sensitive (action, TRUE);
3775  gtk_action_set_visible (action, TRUE);
3776  action = gnc_main_window_find_action (window, "EditCutAction");
3777  gtk_action_set_sensitive (action, TRUE);
3778  gtk_action_set_visible (action, TRUE);
3779  action = gnc_main_window_find_action (window, "EditPasteAction");
3780  gtk_action_set_sensitive (action, TRUE);
3781  gtk_action_set_visible (action, TRUE);
3782 }
3783 
3784 static void
3785 gnc_main_window_edit_menu_show_cb (GtkWidget *menu,
3786  GncMainWindow *window)
3787 {
3788  gnc_main_window_update_edit_actions_sensitivity (window, FALSE);
3789 }
3790 
3791 static void
3792 gnc_main_window_edit_menu_hide_cb (GtkWidget *menu,
3793  GncMainWindow *window)
3794 {
3795  gnc_main_window_enable_edit_actions_sensitivity (window);
3796 }
3797 
3798 static void
3799 gnc_main_window_init_menu_updaters (GncMainWindow *window)
3800 {
3801  GtkWidget *edit_menu_item, *edit_menu;
3802 
3803  edit_menu_item = gtk_ui_manager_get_widget
3804  (window->ui_merge, "/menubar/Edit");
3805  edit_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (edit_menu_item));
3806 
3807  g_signal_connect (edit_menu, "show",
3808  G_CALLBACK (gnc_main_window_edit_menu_show_cb), window);
3809  g_signal_connect (edit_menu, "hide",
3810  G_CALLBACK (gnc_main_window_edit_menu_hide_cb), window);
3811 }
3812 
3813 static void
3814 gnc_main_window_window_menu (GncMainWindow *window)
3815 {
3816  guint merge_id;
3817 #ifdef MAC_INTEGRATION
3818  gchar *filename = gnc_filepath_locate_ui_file("gnc-windows-menu-ui-quartz.xml");
3819 #else
3820  gchar *filename = gnc_filepath_locate_ui_file("gnc-windows-menu-ui.xml");
3821  GncMainWindowPrivate *priv;
3822 #endif
3823  GError *error = NULL;
3824  g_assert(filename);
3825  merge_id = gtk_ui_manager_add_ui_from_file(window->ui_merge, filename,
3826  &error);
3827  g_free(filename);
3828  g_assert(merge_id);
3829 #ifndef MAC_INTEGRATION
3830  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3831  gtk_action_group_add_radio_actions (priv->action_group,
3832  radio_entries, n_radio_entries,
3833  0,
3834  G_CALLBACK(gnc_main_window_cmd_window_raise),
3835  window);
3836 #endif
3837 };
3838 
3839 /* This is used to prevent the tab having focus */
3840 static gboolean
3841 gnc_main_window_page_focus_in (GtkWidget *widget, GdkEvent *event,
3842  gpointer user_data)
3843 {
3844  GncMainWindow *window = user_data;
3846 
3847  g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, page);
3848  return FALSE;
3849 }
3850 
3851 static void
3852 gnc_main_window_setup_window (GncMainWindow *window)
3853 {
3854  GncMainWindowPrivate *priv;
3855  GtkWidget *main_vbox;
3856  guint merge_id;
3857  GncPluginManager *manager;
3858  GList *plugins;
3859  GError *error = NULL;
3860  gchar *filename;
3861 
3862  ENTER(" ");
3863 
3864  /* Catch window manager delete signal */
3865  g_signal_connect (G_OBJECT (window), "delete-event",
3866  G_CALLBACK (gnc_main_window_delete_event), window);
3867 
3868  /* Create widgets and add them to the window */
3869  main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3870  gtk_box_set_homogeneous (GTK_BOX (main_vbox), FALSE);
3871  gtk_widget_show (main_vbox);
3872  gtk_container_add (GTK_CONTAINER (window), main_vbox);
3873 
3874  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3875  priv->menu_dock = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3876  gtk_box_set_homogeneous (GTK_BOX (priv->menu_dock), FALSE);
3877  gtk_widget_show (priv->menu_dock);
3878  gtk_box_pack_start (GTK_BOX (main_vbox), priv->menu_dock,
3879  FALSE, TRUE, 0);
3880 
3881  priv->notebook = gtk_notebook_new ();
3882  g_object_set(G_OBJECT(priv->notebook),
3883  "scrollable", TRUE,
3884  "enable-popup", TRUE,
3885  (char *)NULL);
3886  gtk_widget_show (priv->notebook);
3887  g_signal_connect (G_OBJECT (priv->notebook), "switch-page",
3888  G_CALLBACK (gnc_main_window_switch_page), window);
3889  g_signal_connect (G_OBJECT (priv->notebook), "page-reordered",
3890  G_CALLBACK (gnc_main_window_page_reordered), window);
3891  g_signal_connect (G_OBJECT (priv->notebook), "focus-in-event",
3892  G_CALLBACK (gnc_main_window_page_focus_in), window);
3893  gtk_box_pack_start (GTK_BOX (main_vbox), priv->notebook,
3894  TRUE, TRUE, 0);
3895 
3896  priv->statusbar = gtk_statusbar_new ();
3897  gtk_widget_show (priv->statusbar);
3898  gtk_box_pack_start (GTK_BOX (main_vbox), priv->statusbar,
3899  FALSE, TRUE, 0);
3900 
3901  priv->progressbar = gtk_progress_bar_new ();
3902  gtk_progress_bar_set_show_text (GTK_PROGRESS_BAR(priv->progressbar), TRUE);
3903  gtk_progress_bar_set_text(GTK_PROGRESS_BAR(priv->progressbar), " ");
3904  gtk_widget_show (priv->progressbar);
3905  gtk_box_pack_start (GTK_BOX (priv->statusbar), priv->progressbar,
3906  FALSE, TRUE, 0);
3907  gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(priv->progressbar),
3908  0.01);
3909 
3910  window->ui_merge = gtk_ui_manager_new ();
3911 
3912  /* Create menu and toolbar information */
3913  priv->action_group = gtk_action_group_new ("MainWindowActions");
3914  gtk_action_group_set_translation_domain (priv->action_group, PROJECT_NAME);
3915  gtk_action_group_add_actions (priv->action_group, gnc_menu_actions,
3916  gnc_menu_n_actions, window);
3917  gtk_action_group_add_toggle_actions (priv->action_group,
3918  toggle_actions, n_toggle_actions,
3919  window);
3921  initially_insensitive_actions,
3922  "sensitive", FALSE);
3924  always_insensitive_actions,
3925  "sensitive", FALSE);
3927  always_hidden_actions,
3928  "visible", FALSE);
3930  gnc_menu_important_actions);
3931  gtk_ui_manager_insert_action_group (window->ui_merge, priv->action_group, 0);
3932 
3933  g_signal_connect (G_OBJECT (window->ui_merge), "add_widget",
3934  G_CALLBACK (gnc_main_window_add_widget), window);
3935 
3936  /* Use the "connect-proxy" signal for tooltip display in the status bar */
3937  g_signal_connect (G_OBJECT (window->ui_merge), "connect-proxy",
3938  G_CALLBACK (gnc_window_connect_proxy), priv->statusbar);
3939 
3940  filename = gnc_filepath_locate_ui_file("gnc-main-window-ui.xml");
3941 
3942  /* Can't do much without a ui. */
3943  g_assert (filename);
3944 
3945  merge_id = gtk_ui_manager_add_ui_from_file (window->ui_merge,
3946  filename, &error);
3947  g_assert(merge_id || error);
3948  if (merge_id)
3949  {
3950  gtk_action_group_add_radio_actions (priv->action_group,
3951  tab_pos_radio_entries,
3952  n_tab_pos_radio_entries,
3953  0,
3954  G_CALLBACK(gnc_main_window_cmd_view_tab_position),
3955  window);
3956  gtk_window_add_accel_group (GTK_WINDOW (window),
3957  gtk_ui_manager_get_accel_group(window->ui_merge));
3958  gtk_ui_manager_ensure_update (window->ui_merge);
3959  }
3960  else
3961  {
3962  g_critical("Failed to load ui file.\n Filename %s\n Error %s",
3963  filename, error->message);
3964  g_error_free(error);
3965  g_assert(merge_id != 0);
3966  }
3967  g_free(filename);
3968  gnc_main_window_window_menu(window);
3969  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
3970  GNC_PREF_TAB_POSITION_TOP,
3971  gnc_main_window_update_tab_position,
3972  window);
3973  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
3974  GNC_PREF_TAB_POSITION_BOTTOM,
3975  gnc_main_window_update_tab_position,
3976  window);
3977  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
3978  GNC_PREF_TAB_POSITION_LEFT,
3979  gnc_main_window_update_tab_position,
3980  window);
3981  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
3982  GNC_PREF_TAB_POSITION_RIGHT,
3983  gnc_main_window_update_tab_position,
3984  window);
3985  gnc_main_window_update_tab_position(NULL, NULL, window);
3986 
3987  gnc_main_window_init_menu_updaters(window);
3988 
3989  /* Testing */
3990  /* Now update the "eXtensions" menu */
3991  if (!gnc_prefs_is_extra_enabled())
3992  {
3993  GtkAction* action;
3994 
3995  action = gtk_action_group_get_action(priv->action_group,
3996  "ExtensionsAction");
3997  gtk_action_set_visible(action, FALSE);
3998  }
3999 
4000  /* GncPluginManager stuff */
4001  manager = gnc_plugin_manager_get ();
4002  plugins = gnc_plugin_manager_get_plugins (manager);
4003  g_list_foreach (plugins, gnc_main_window_add_plugin, window);
4004  g_list_free (plugins);
4005 
4006  g_signal_connect (G_OBJECT (manager), "plugin-added",
4007  G_CALLBACK (gnc_main_window_plugin_added), window);
4008  g_signal_connect (G_OBJECT (manager), "plugin-removed",
4009  G_CALLBACK (gnc_main_window_plugin_removed), window);
4010 
4011  LEAVE(" ");
4012 }
4013 
4014 #ifdef MAC_INTEGRATION
4015 /* Event handlers for the shutdown process. Gnc_quartz_shutdown is
4016  * connected to NSApplicationWillTerminate, the last chance to do
4017  * anything before quitting. The problem is that it's launched from a
4018  * CFRunLoop, not a g_main_loop, and if we call anything that would
4019  * affect the main_loop we get an assert that we're in a subidiary
4020  * loop.
4021  */
4022 static void
4023 gnc_quartz_shutdown (GtkosxApplication *theApp, gpointer data)
4024 {
4025  /* Do Nothing. It's too late. */
4026 }
4027 /* Should quit responds to NSApplicationBlockTermination; returning TRUE means
4028  * "don't terminate", FALSE means "do terminate". gnc_main_window_quit() queues
4029  * a timer that starts an orderly shutdown in 250ms and if we tell macOS it's OK
4030  * to quit GnuCash gets terminated instead of doing its orderly shutdown,
4031  * leaving the book locked.
4032  */
4033 static gboolean
4034 gnc_quartz_should_quit (GtkosxApplication *theApp, GncMainWindow *window)
4035 {
4037  gnc_main_window_quit (window);
4038  return TRUE;
4039 }
4040 
4041 static void
4042 gnc_quartz_set_menu(GncMainWindow* window)
4043 {
4044  GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
4045  GtkWidget *menu;
4046  GtkWidget *item;
4047 
4048  menu = gtk_ui_manager_get_widget (window->ui_merge, "/menubar");
4049  if (GTK_IS_MENU_ITEM (menu))
4050  menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
4051  gtk_widget_hide(menu);
4052  gtkosx_application_set_menu_bar (theApp, GTK_MENU_SHELL (menu));
4053 
4054  item = gtk_ui_manager_get_widget (window->ui_merge,
4055  "/menubar/File/FileQuit");
4056  if (GTK_IS_MENU_ITEM (item))
4057  gtk_widget_hide (GTK_WIDGET (item));
4058 
4059  item = gtk_ui_manager_get_widget (window->ui_merge,
4060  "/menubar/Help/HelpAbout");
4061  if (GTK_IS_MENU_ITEM (item))
4062  {
4063  gtkosx_application_insert_app_menu_item (theApp, GTK_WIDGET (item), 0);
4064  }
4065 
4066  item = gtk_ui_manager_get_widget (window->ui_merge,
4067  "/menubar/Edit/EditPreferences");
4068  if (GTK_IS_MENU_ITEM (item))
4069  {
4070  gtkosx_application_insert_app_menu_item (theApp,
4071  gtk_separator_menu_item_new (), 1);
4072  gtkosx_application_insert_app_menu_item (theApp, GTK_WIDGET (item), 2);
4073  }
4074 
4075  item = gtk_ui_manager_get_widget (window->ui_merge,
4076  "/menubar/Help");
4077  gtkosx_application_set_help_menu(theApp, GTK_MENU_ITEM(item));
4078  item = gtk_ui_manager_get_widget (window->ui_merge,
4079  "/menubar/Windows");
4080  gtkosx_application_set_window_menu(theApp, GTK_MENU_ITEM(item));
4081  g_signal_connect(theApp, "NSApplicationBlockTermination",
4082  G_CALLBACK(gnc_quartz_should_quit), window);
4083  gtkosx_application_set_use_quartz_accelerators (theApp, FALSE);
4084  g_object_unref (theApp);
4085 
4086 }
4087 #endif //MAC_INTEGRATION
4088 
4089 /* Callbacks */
4090 static void
4091 gnc_main_window_add_widget (GtkUIManager *merge,
4092  GtkWidget *widget,
4093  GncMainWindow *window)
4094 {
4095  GncMainWindowPrivate *priv;
4096 
4097  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4098  if (GTK_IS_TOOLBAR (widget))
4099  {
4100  priv->toolbar = widget;
4101  gtk_toolbar_set_style (GTK_TOOLBAR(priv->toolbar),
4102  GTK_TOOLBAR_BOTH);
4103  gtk_toolbar_set_icon_size (GTK_TOOLBAR(priv->toolbar),
4104  GTK_ICON_SIZE_SMALL_TOOLBAR);
4105  }
4106 
4107  gtk_box_pack_start (GTK_BOX (priv->menu_dock), widget, FALSE, FALSE, 0);
4108  gtk_widget_show (widget);
4109 }
4110 
4123 static gboolean
4124 gnc_main_window_show_summarybar (GncMainWindow *window, GtkAction *action)
4125 {
4126  GncMainWindowPrivate *priv;
4127 
4128  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4129  if (action == NULL)
4130  action = gtk_action_group_get_action(priv->action_group,
4131  "ViewSummaryAction");
4132  if (action == NULL)
4133  return TRUE;
4134  return gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
4135 }
4136 
4146 static void
4147 gnc_main_window_switch_page (GtkNotebook *notebook,
4148  gpointer *notebook_page,
4149  gint pos,
4150  GncMainWindow *window)
4151 {
4152  GncMainWindowPrivate *priv;
4153  GtkWidget *child;
4154  GncPluginPage *page;
4155  gboolean visible;
4156 
4157  ENTER("Notebook %p, page, %p, index %d, window %p",
4158  notebook, notebook_page, pos, window);
4159  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
4160 
4161  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4162  if (priv->current_page != NULL)
4163  {
4164  page = priv->current_page;
4165  gnc_plugin_page_unmerge_actions (page, window->ui_merge);
4166  gnc_plugin_page_unselected (page);
4167  }
4168 
4169  child = gtk_notebook_get_nth_page (notebook, pos);
4170  if (child)
4171  {
4172  page = g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL);
4173  }
4174  else
4175  {
4176  page = NULL;
4177  }
4178 
4179  priv->current_page = page;
4180 
4181  if (page != NULL)
4182  {
4183  /* Update the user interface (e.g. menus and toolbars */
4184  gnc_plugin_page_merge_actions (page, window->ui_merge);
4185  visible = gnc_main_window_show_summarybar(window, NULL);
4187 
4188  /* Allow page specific actions */
4189  gnc_plugin_page_selected (page);
4190  gnc_window_update_status (GNC_WINDOW(window), page);
4191 
4192  /* Update the page reference info */
4193  priv->usage_order = g_list_remove (priv->usage_order, page);
4194  priv->usage_order = g_list_prepend (priv->usage_order, page);
4195  }
4196 
4198  multiple_page_actions,
4199  "sensitive",
4200  g_list_length(priv->installed_pages) > 1);
4201 
4202  gnc_main_window_update_title(window);
4203 #ifndef MAC_INTEGRATION
4204  gnc_main_window_update_menu_item(window);
4205 #endif
4206  g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, page);
4207  LEAVE(" ");
4208 }
4209 
4216 static void
4217 gnc_main_window_page_reordered (GtkNotebook *notebook,
4218  GtkWidget *child,
4219  guint pos,
4220  GncMainWindow *window)
4221 {
4222  GncMainWindowPrivate *priv;
4223  GncPluginPage *page;
4224  GList *old_link;
4225 
4226  ENTER("Notebook %p, child %p, index %d, window %p",
4227  notebook, child, pos, window);
4228  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
4229 
4230  if (!child) return;
4231 
4232  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4233 
4234  page = g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL);
4235  if (!page) return;
4236 
4237  old_link = g_list_find (priv->installed_pages, page);
4238  if (!old_link) return;
4239 
4240  priv->installed_pages = g_list_delete_link (priv->installed_pages,
4241  old_link);
4242  priv->installed_pages = g_list_insert (priv->installed_pages,
4243  page, pos);
4244 
4245  LEAVE(" ");
4246 }
4247 
4248 static void
4249 gnc_main_window_plugin_added (GncPlugin *manager,
4250  GncPlugin *plugin,
4251  GncMainWindow *window)
4252 {
4253  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
4254  g_return_if_fail (GNC_IS_PLUGIN (plugin));
4255 
4256  gnc_plugin_add_to_window (plugin, window, window_type);
4257 }
4258 
4259 static void
4260 gnc_main_window_plugin_removed (GncPlugin *manager,
4261  GncPlugin *plugin,
4262  GncMainWindow *window)
4263 {
4264  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
4265  g_return_if_fail (GNC_IS_PLUGIN (plugin));
4266 
4267  gnc_plugin_remove_from_window (plugin, window, window_type);
4268 }
4269 
4270 
4271 /* Command callbacks */
4272 static void
4273 gnc_main_window_cmd_page_setup (GtkAction *action,
4274  GncMainWindow *window)
4275 {
4276  GtkWindow *gtk_window;
4277 
4278  g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
4279 
4280  gtk_window = gnc_window_get_gtk_window(GNC_WINDOW(window));
4281  gnc_ui_page_setup(gtk_window);
4282 }
4283 
4284 gboolean
4286 {
4287  QofBook *book = gnc_get_current_book ();
4288  gboolean use_split_action_for_num_before =
4290  gboolean use_book_currency_before =
4292  gint use_read_only_threshold_before =
4294  gboolean use_split_action_for_num_after;
4295  gboolean use_book_currency_after;
4296  gint use_read_only_threshold_after;
4297  gboolean return_val = FALSE;
4298  GList *results = NULL, *iter;
4299 
4300  if (!options) return return_val;
4301 
4302  results = gnc_option_db_commit (options);
4303  for (iter = results; iter; iter = iter->next)
4304  {
4305  GtkWidget *dialog = gtk_message_dialog_new(gnc_ui_get_main_window (NULL),
4306  0,
4307  GTK_MESSAGE_ERROR,
4308  GTK_BUTTONS_OK,
4309  "%s",
4310  (char*)iter->data);
4311  gtk_dialog_run(GTK_DIALOG(dialog));
4312  gtk_widget_destroy(dialog);
4313  g_free (iter->data);
4314  }
4315  g_list_free (results);
4316  qof_book_begin_edit (book);
4317  qof_book_save_options (book, gnc_option_db_save, options, TRUE);
4318  use_split_action_for_num_after =
4320  use_book_currency_after = gnc_book_use_book_currency (book);
4321 
4322  // mark cached value as invalid so we get new value
4323  book->cached_num_days_autoreadonly_isvalid = FALSE;
4324  use_read_only_threshold_after = qof_book_get_num_days_autoreadonly (book);
4325 
4326  if (use_split_action_for_num_before != use_split_action_for_num_after)
4327  {
4329  use_split_action_for_num_after);
4330  return_val = TRUE;
4331  }
4332  if (use_book_currency_before != use_book_currency_after)
4333  {
4334  gnc_book_option_book_currency_selected_cb (use_book_currency_after);
4335  return_val = TRUE;
4336  }
4337  if (use_read_only_threshold_before != use_read_only_threshold_after)
4338  return_val = TRUE;
4339 
4340  qof_book_commit_edit (book);
4341  return return_val;
4342 }
4343 
4344 static void
4345 gnc_book_options_dialog_apply_cb(GNCOptionWin * optionwin,
4346  gpointer user_data)
4347 {
4348  GNCOptionDB * options = user_data;
4349 
4350  if (!options) return;
4351 
4353  gnc_gui_refresh_all ();
4354 }
4355 
4356 static void
4357 gnc_book_options_dialog_close_cb(GNCOptionWin * optionwin,
4358  gpointer user_data)
4359 {
4360  GNCOptionDB * options = user_data;
4361 
4362  gnc_options_dialog_destroy(optionwin);
4363  gnc_option_db_destroy(options);
4364 }
4365 
4369 void
4371 {
4372  gnc_suspend_gui_refresh ();
4373  if (num_action)
4374  {
4375  /* Set a feature flag in the book for use of the split action field as number.
4376  * This will prevent older GnuCash versions that don't support this feature
4377  * from opening this file. */
4378  gnc_features_set_used (gnc_get_current_book(),
4379  GNC_FEATURE_NUM_FIELD_SOURCE);
4380  }
4381  gnc_book_option_num_field_source_change (num_action);
4382  gnc_resume_gui_refresh ();
4383 }
4384 
4388 void
4389 gnc_book_option_book_currency_selected_cb (gboolean use_book_currency)
4390 {
4391  gnc_suspend_gui_refresh ();
4392  if (use_book_currency)
4393  {
4394  /* Set a feature flag in the book for use of book currency. This will
4395  * prevent older GnuCash versions that don't support this feature from
4396  * opening this file. */
4397  gnc_features_set_used (gnc_get_current_book(),
4398  GNC_FEATURE_BOOK_CURRENCY);
4399  }
4400  gnc_book_option_book_currency_selected (use_book_currency);
4401  gnc_resume_gui_refresh ();
4402 }
4403 
4404 static gboolean
4405 show_handler (const char *class_name, gint component_id,
4406  gpointer user_data, gpointer iter_data)
4407 {
4408  GNCOptionWin *optwin = user_data;
4409  GtkWidget *widget;
4410 
4411  if (!optwin)
4412  return(FALSE);
4413 
4414  widget = gnc_options_dialog_widget(optwin);
4415 
4416  gtk_window_present(GTK_WINDOW(widget));
4417  return(TRUE);
4418 }
4419 
4420 GtkWidget *
4421 gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent)
4422 {
4423  QofBook *book = gnc_get_current_book ();
4424  GNCOptionDB *options;
4425  GNCOptionWin *optionwin;
4426 
4427  options = gnc_option_db_new_for_type (QOF_ID_BOOK);
4428  qof_book_load_options (book, gnc_option_db_load, options);
4429  gnc_option_db_clean (options);
4430 
4431  /* Only allow one Book Options dialog if called from file->properties
4432  menu */
4433  if (gnc_forall_gui_components(DIALOG_BOOK_OPTIONS_CM_CLASS,
4434  show_handler, NULL))
4435  {
4436  return NULL;
4437  }
4438  optionwin = gnc_options_dialog_new_modal (
4439  modal,
4440  (title ? title : _( "Book Options")),
4441  DIALOG_BOOK_OPTIONS_CM_CLASS, parent);
4442  gnc_options_dialog_build_contents (optionwin, options);
4443 
4445 
4446  gnc_options_dialog_set_apply_cb (optionwin,
4447  gnc_book_options_dialog_apply_cb,
4448  (gpointer)options);
4449  gnc_options_dialog_set_close_cb (optionwin,
4450  gnc_book_options_dialog_close_cb,
4451  (gpointer)options);
4452  if (modal)
4454  return gnc_options_dialog_widget (optionwin);
4455 }
4456 
4457 static void
4458 gnc_main_window_cmd_file_properties (GtkAction *action, GncMainWindow *window)
4459 {
4460  gnc_book_options_dialog_cb (FALSE, NULL, GTK_WINDOW (window));
4461 }
4462 
4463 static void
4464 gnc_main_window_cmd_file_close (GtkAction *action, GncMainWindow *window)
4465 {
4466  GncMainWindowPrivate *priv;
4467  GncPluginPage *page;
4468 
4469  g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
4470 
4471  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4472  page = priv->current_page;
4474 }
4475 
4476 static void
4477 gnc_main_window_cmd_file_quit (GtkAction *action, GncMainWindow *window)
4478 {
4480  return;
4481 
4482  gnc_main_window_quit(window);
4483 }
4484 
4485 static void
4486 gnc_main_window_cmd_edit_cut (GtkAction *action, GncMainWindow *window)
4487 {
4488  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW(window));
4489 
4490  if (GTK_IS_EDITABLE(widget))
4491  {
4492  gtk_editable_cut_clipboard (GTK_EDITABLE(widget));
4493  }
4494  else if (GTK_IS_TEXT_VIEW(widget))
4495  {
4496  GtkTextBuffer *text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
4497  GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(widget),
4498  GDK_SELECTION_CLIPBOARD);
4499  gboolean editable = gtk_text_view_get_editable (GTK_TEXT_VIEW(widget));
4500 
4501  if (clipboard)
4502  gtk_text_buffer_cut_clipboard (text_buffer, clipboard, editable);
4503  }
4504 }
4505 
4506 static void
4507 gnc_main_window_cmd_edit_copy (GtkAction *action, GncMainWindow *window)
4508 {
4509  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW(window));
4510 
4511  if (GTK_IS_EDITABLE(widget))
4512  {
4513  gtk_editable_copy_clipboard (GTK_EDITABLE(widget));
4514  }
4515  else if (GTK_IS_TEXT_VIEW(widget))
4516  {
4517  GtkTextBuffer *text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
4518  GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(widget),
4519  GDK_SELECTION_CLIPBOARD);
4520  if (clipboard)
4521  gtk_text_buffer_copy_clipboard (text_buffer, clipboard);
4522  }
4523 }
4524 
4525 static void
4526 gnc_main_window_cmd_edit_paste (GtkAction *action, GncMainWindow *window)
4527 {
4528  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW(window));
4529 
4530  if (GTK_IS_EDITABLE(widget))
4531  {
4532  gtk_editable_paste_clipboard (GTK_EDITABLE(widget));
4533  }
4534  else if (GTK_IS_TEXT_VIEW(widget))
4535  {
4536  GtkTextBuffer *text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
4537  GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(widget),
4538  GDK_SELECTION_CLIPBOARD);
4539  gboolean editable = gtk_text_view_get_editable (GTK_TEXT_VIEW(widget));
4540 
4541  if (clipboard)
4542  gtk_text_buffer_paste_clipboard (text_buffer, clipboard, NULL, editable);
4543  }
4544 }
4545 
4546 static void
4547 gnc_main_window_cmd_edit_preferences (GtkAction *action, GncMainWindow *window)
4548 {
4549  gnc_preferences_dialog (GTK_WINDOW(window));
4550 }
4551 
4552 static void
4553 gnc_main_window_cmd_view_refresh (GtkAction *action, GncMainWindow *window)
4554 {
4555 }
4556 
4557 static void
4558 gnc_main_window_cmd_actions_reset_warnings (GtkAction *action, GncMainWindow *window)
4559 {
4560  gnc_reset_warnings_dialog(GTK_WINDOW(window));
4561 }
4562 
4563 static void
4564 gnc_main_window_cmd_actions_rename_page (GtkAction *action, GncMainWindow *window)
4565 {
4566  GncMainWindowPrivate *priv;
4567  GncPluginPage *page;
4568  GtkWidget *label, *entry;
4569 
4570  ENTER(" ");
4571  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4572  page = priv->current_page;
4573  if (!page)
4574  {
4575  LEAVE("No current page");
4576  return;
4577  }
4578 
4579  if (!main_window_find_tab_items(window, page, &label, &entry))
4580  {
4581  LEAVE("can't find required widgets");
4582  return;
4583  }
4584 
4585  gtk_entry_set_text(GTK_ENTRY(entry), gtk_label_get_text(GTK_LABEL(label)));
4586  gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
4587  gtk_widget_hide(label);
4588  gtk_widget_show(entry);
4589  gtk_widget_grab_focus(entry);
4590  LEAVE("opened for editing");
4591 }
4592 
4593 static void
4594 gnc_main_window_cmd_view_toolbar (GtkAction *action, GncMainWindow *window)
4595 {
4596  GncMainWindowPrivate *priv;
4597 
4598  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4599  if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)))
4600  {
4601  gtk_widget_show (priv->toolbar);
4602  }
4603  else
4604  {
4605  gtk_widget_hide (priv->toolbar);
4606  }
4607 }
4608 
4609 static void
4610 gnc_main_window_cmd_view_summary (GtkAction *action, GncMainWindow *window)
4611 {
4612  GncMainWindowPrivate *priv;
4613  GList *item;
4614  gboolean visible;
4615 
4616  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4617  visible = gnc_main_window_show_summarybar(window, action);
4618  for (item = priv->installed_pages; item; item = g_list_next(item))
4619  {
4621  }
4622 }
4623 
4624 static void
4625 gnc_main_window_cmd_view_statusbar (GtkAction *action, GncMainWindow *window)
4626 {
4627  GncMainWindowPrivate *priv;
4628 
4629  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4630  if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)))
4631  {
4632  gtk_widget_show (priv->statusbar);
4633  }
4634  else
4635  {
4636  gtk_widget_hide (priv->statusbar);
4637  }
4638 }
4639 
4640 static void
4641 gnc_main_window_cmd_window_new (GtkAction *action, GncMainWindow *window)
4642 {
4643  GncMainWindow *new_window;
4644 
4645  /* Create the new window */
4646  ENTER(" ");
4647  new_window = gnc_main_window_new ();
4648  gtk_widget_show(GTK_WIDGET(new_window));
4649  LEAVE(" ");
4650 }
4651 
4652 static void
4653 gnc_main_window_cmd_window_move_page (GtkAction *action, GncMainWindow *window)
4654 {
4655  GncMainWindowPrivate *priv;
4656  GncMainWindow *new_window;
4657  GncPluginPage *page;
4658  GtkNotebook *notebook;
4659  GtkWidget *tab_widget, *menu_widget;
4660 
4661  ENTER("action %p,window %p", action, window);
4662 
4663  /* Setup */
4664  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4665  page = priv->current_page;
4666  if (!page)
4667  {
4668  LEAVE("invalid page");
4669  return;
4670  }
4671  if (!page->notebook_page)
4672  {
4673  LEAVE("invalid notebook_page");
4674  return;
4675  }
4676 
4677  notebook = GTK_NOTEBOOK (priv->notebook);
4678  tab_widget = gtk_notebook_get_tab_label (notebook, page->notebook_page);
4679  menu_widget = gtk_notebook_get_menu_label (notebook, page->notebook_page);
4680 
4681  // Remove the page_changed signal callback
4682  gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(page));
4683 
4684  /* Ref the page components, then remove it from its old window */
4685  g_object_ref(page);
4686  g_object_ref(tab_widget);
4687  g_object_ref(menu_widget);
4688  g_object_ref(page->notebook_page);
4689  gnc_main_window_disconnect(window, page);
4690 
4691  /* Create the new window */
4692  new_window = gnc_main_window_new ();
4693  gtk_widget_show(GTK_WIDGET(new_window));
4694 
4695  /* Now add the page to the new window */
4696  gnc_main_window_connect (new_window, page, tab_widget, menu_widget);
4697 
4698  /* Unref the page components now that we're done */
4699  g_object_unref(page->notebook_page);
4700  g_object_unref(menu_widget);
4701  g_object_unref(tab_widget);
4702  g_object_unref(page);
4703 
4704  /* just a little debugging. :-) */
4705  DEBUG("Moved page %p from window %p to new window %p",
4706  page, window, new_window);
4707  DEBUG("Old window current is %p, new window current is %p",
4708  priv->current_page, priv->current_page);
4709 
4710  LEAVE("page moved");
4711 }
4712 
4713 static void
4714 gnc_main_window_cmd_view_tab_position (GtkAction *action,
4715  GtkRadioAction *current,
4716  GncMainWindow *window)
4717 {
4718  GtkPositionType value = gtk_radio_action_get_current_value(current);
4719 
4720  if (value != GTK_POS_TOP && gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_TOP))
4721  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_TOP, FALSE);
4722 
4723  if (value != GTK_POS_BOTTOM && gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_BOTTOM))
4724  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_BOTTOM, FALSE);
4725 
4726  if (value != GTK_POS_LEFT && gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT))
4727  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT, FALSE);
4728 
4729  if (value != GTK_POS_RIGHT && gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT))
4730  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT, FALSE);
4731 
4732  switch (value)
4733  {
4734  case GTK_POS_TOP:
4735  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_TOP, TRUE);
4736  break;
4737 
4738  case GTK_POS_BOTTOM:
4739  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_BOTTOM, TRUE);
4740  break;
4741 
4742  case GTK_POS_LEFT:
4743  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT, TRUE);
4744  break;
4745 
4746  case GTK_POS_RIGHT:
4747  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT, TRUE);
4748  break;
4749  }
4750 }
4751 
4752 #ifndef MAC_INTEGRATION
4753 static void
4754 gnc_main_window_cmd_window_raise (GtkAction *action,
4755  GtkRadioAction *current,
4756  GncMainWindow *old_window)
4757 {
4758  GncMainWindow *new_window;
4759  gint value;
4760 
4761  g_return_if_fail(GTK_IS_ACTION(action));
4762  g_return_if_fail(GTK_IS_RADIO_ACTION(current));
4763  g_return_if_fail(GNC_IS_MAIN_WINDOW(old_window));
4764 
4765  ENTER("action %p, current %p, window %p", action, current, old_window);
4766  value = gtk_radio_action_get_current_value(current);
4767  new_window = g_list_nth_data(active_windows, value);
4768  gtk_window_present(GTK_WINDOW(new_window));
4769  /* revert the change in the radio group
4770  * impossible while handling "changed" (G_SIGNAL_NO_RECURSE) */
4771  g_idle_add((GSourceFunc)gnc_main_window_update_radio_button, old_window);
4772  LEAVE(" ");
4773 }
4774 #endif /* !MAC_INTEGRATION */
4775 
4776 static void
4777 gnc_main_window_cmd_help_tutorial (GtkAction *action, GncMainWindow *window)
4778 {
4779  gnc_gnome_help (GTK_WINDOW(window), HF_GUIDE, NULL);
4780 }
4781 
4782 static void
4783 gnc_main_window_cmd_help_contents (GtkAction *action, GncMainWindow *window)
4784 {
4785  gnc_gnome_help (GTK_WINDOW(window), HF_HELP, NULL);
4786 }
4787 
4797 static gchar *
4798 get_file (const gchar *partial)
4799 {
4800  gchar *filename, *text = NULL;
4801  gsize length;
4802 
4803  filename = gnc_filepath_locate_doc_file(partial);
4804  if (filename && g_file_get_contents(filename, &text, &length, NULL))
4805  {
4806  if (length)
4807  {
4808  g_free(filename);
4809  return text;
4810  }
4811  g_free(text);
4812  }
4813  g_free (filename);
4814  return NULL;
4815 }
4816 
4817 
4827 static gchar **
4828 get_file_strsplit (const gchar *partial)
4829 {
4830  gchar *text, **lines;
4831 
4832  text = get_file(partial);
4833  if (!text)
4834  return NULL;
4835 
4836  lines = g_strsplit_set(text, "\r\n", -1);
4837  g_free(text);
4838  return lines;
4839 }
4846 static gboolean
4847 url_signal_cb (GtkAboutDialog *dialog, gchar *uri, gpointer data)
4848 {
4849  gnc_launch_doclink (GTK_WINDOW(dialog), uri);
4850  return TRUE;
4851 }
4852 
4853 static gboolean
4854 link_button_cb (GtkLinkButton *button, gpointer user_data)
4855 {
4856  const gchar *uri = gtk_link_button_get_uri (button);
4857  gnc_launch_doclink (GTK_WINDOW(user_data), uri);
4858  return TRUE;
4859 }
4860 
4861 static void
4862 add_about_paths (GtkDialog *dialog)
4863 {
4864  GtkWidget *page_vbox = gnc_get_dialog_widget_from_id (dialog, "page_vbox");
4865  GtkWidget *grid;
4866  GList *paths;
4867  gint i = 0;
4868 
4869  if (!page_vbox)
4870  {
4871  PWARN("Unable to find AboutDialog 'page_vbox' Widget");
4872  return;
4873  }
4874 
4875  grid = gtk_grid_new ();
4876  paths = gnc_list_all_paths ();
4877 
4878  for (GList *path_node = paths; path_node; path_node = g_list_next (path_node))
4879  {
4880  EnvPaths *ep = (EnvPaths*)path_node->data;
4881 
4882  gchar *env_name = g_strconcat (ep->env_name, ":", NULL);
4883  GtkWidget *label = gtk_label_new (env_name);
4884  const gchar *uri = gnc_uri_create_uri ("file", NULL, 0, NULL, NULL, ep->env_path);
4885  gchar *display_uri = gnc_doclink_get_unescaped_just_uri (uri);
4886  GtkWidget *widget = gtk_link_button_new_with_label (uri, display_uri);
4887 
4888  gtk_grid_attach (GTK_GRID(grid), label, 0, i, 1, 1);
4889  gtk_widget_set_halign (label, GTK_ALIGN_END);
4890  gtk_grid_attach (GTK_GRID(grid), widget, 1, i, 1, 1);
4891  gtk_widget_set_halign (widget, GTK_ALIGN_START);
4892  gtk_widget_set_margin_top (widget, 0);
4893  gtk_widget_set_margin_bottom (widget, 0);
4894 
4895  if (ep->modifiable)
4896  {
4897  GtkWidget *mod_lab = gtk_label_new (_("(user modifiable)"));
4898  gtk_grid_attach (GTK_GRID(grid), mod_lab, 2, i, 1, 1);
4899  gtk_widget_show (mod_lab);
4900  }
4901  g_signal_connect (widget, "activate-link",
4902  G_CALLBACK(link_button_cb), dialog);
4903  i++;
4904 
4905  g_free (display_uri);
4906  g_free (env_name);
4907  }
4908  gtk_container_add_with_properties (GTK_CONTAINER(page_vbox), grid,
4909  "position", 1, NULL);
4910  gtk_widget_show_all (grid);
4911  g_list_free_full (paths, g_free);
4912 }
4913 
4920 static void
4921 gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window)
4922 {
4923  /* Translators: %s will be replaced with the current year */
4924  gchar *copyright = g_strdup_printf(_("Copyright © 1997-%s The GnuCash contributors."),
4925  GNC_VCS_REV_YEAR);
4926  gchar **authors = get_file_strsplit("AUTHORS");
4927  gchar **documenters = get_file_strsplit("DOCUMENTERS");
4928  gchar *license = get_file("LICENSE");
4929  GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
4930  GdkPixbuf *logo = gtk_icon_theme_load_icon (icon_theme,
4931  GNC_ICON_APP,
4932  128,
4933  GTK_ICON_LOOKUP_USE_BUILTIN,
4934  NULL);
4935  gchar *version = g_strdup_printf ("%s: %s\n%s: %s\nFinance::Quote: %s",
4936  _("Version"), gnc_version(),
4937  _("Build ID"), gnc_build_id(),
4940  : "-");
4941  GtkDialog *dialog = GTK_DIALOG (gtk_about_dialog_new ());
4942  g_object_set (G_OBJECT (dialog),
4943  "authors", authors,
4944  "documenters", documenters,
4945  "comments", _("Accounting for personal and small business finance."),
4946  "copyright", copyright,
4947  "license", license,
4948  "logo", logo,
4949  "name", "GnuCash",
4950  /* Translators: the following string will be shown in Help->About->Credits
4951  Enter your name or that of your team and an email contact for feedback.
4952  The string can have multiple rows, so you can also add a list of
4953  contributors. */
4954  "translator-credits", _("translator-credits"),
4955  "version", version,
4956  "website", PACKAGE_URL,
4957  "website-label", _("Visit the GnuCash website."),
4958  NULL);
4959 
4960  g_free(version);
4961  g_free(copyright);
4962  if (license)
4963  g_free(license);
4964  if (documenters)
4965  g_strfreev(documenters);
4966  if (authors)
4967  g_strfreev(authors);
4968  g_object_unref (logo);
4969  g_signal_connect (dialog, "activate-link",
4970  G_CALLBACK (url_signal_cb), NULL);
4971 
4972  // Add environment paths
4973  add_about_paths (dialog);
4974 
4975  /* Set dialog to resize. */
4976  gtk_window_set_resizable(GTK_WINDOW (dialog), TRUE);
4977 
4978  gtk_window_set_transient_for (GTK_WINDOW (dialog),
4979  GTK_WINDOW (window));
4980  gtk_dialog_run (dialog);
4981  gtk_widget_destroy (GTK_WIDGET (dialog));
4982 }
4983 
4984 
4985 /************************************************************
4986  * *
4987  ************************************************************/
4988 
4989 void
4991 {
4992  GList *window_iter;
4993 #ifdef MAC_INTEGRATION
4994  GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
4995 #endif
4996  for (window_iter = active_windows; window_iter != NULL; window_iter = window_iter->next)
4997  {
4998  gtk_widget_show(GTK_WIDGET(window_iter->data));
4999  }
5000 #ifdef MAC_INTEGRATION
5001  g_signal_connect(theApp, "NSApplicationWillTerminate",
5002  G_CALLBACK(gnc_quartz_shutdown), NULL);
5003  gtkosx_application_ready(theApp);
5004  g_object_unref (theApp);
5005 #endif
5006 }
5007 
5008 GtkWindow *
5009 gnc_ui_get_gtk_window (GtkWidget *widget)
5010 {
5011  GtkWidget *toplevel;
5012 
5013  if (!widget)
5014  return NULL;
5015 
5016  toplevel = gtk_widget_get_toplevel (widget);
5017  if (toplevel && GTK_IS_WINDOW (toplevel))
5018  return GTK_WINDOW (toplevel);
5019  else
5020  return NULL;
5021 }
5022 
5023 GtkWindow *
5024 gnc_ui_get_main_window (GtkWidget *widget)
5025 {
5026  GList *window;
5027 
5028  GtkWindow *toplevel = gnc_ui_get_gtk_window (widget);
5029  while (toplevel && !GNC_IS_MAIN_WINDOW (toplevel))
5030  toplevel = gtk_window_get_transient_for(toplevel);
5031 
5032  if (toplevel)
5033  return toplevel;
5034 
5035  for (window = active_windows; window; window = window->next)
5036  if (gtk_window_is_active (GTK_WINDOW (window->data)))
5037  return window->data;
5038 
5039  for (window = active_windows; window; window = window->next)
5040  if (gtk_widget_get_mapped (GTK_WIDGET(window->data)))
5041  return window->data;
5042 
5043  return NULL;
5044 }
5045 
5046 
5052 static GtkWindow *
5053 gnc_main_window_get_gtk_window (GncWindow *window)
5054 {
5055  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), NULL);
5056  return GTK_WINDOW(window);
5057 }
5058 
5059 
5065 static GtkWidget *
5066 gnc_main_window_get_statusbar (GncWindow *window_in)
5067 {
5068  GncMainWindowPrivate *priv;
5069  GncMainWindow *window;
5070 
5071  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), NULL);
5072 
5073  window = GNC_MAIN_WINDOW(window_in);
5074  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
5075  return priv->statusbar;
5076 }
5077 
5078 
5084 static GtkWidget *
5085 gnc_main_window_get_progressbar (GncWindow *window_in)
5086 {
5087  GncMainWindowPrivate *priv;
5088  GncMainWindow *window;
5089 
5090  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), NULL);
5091 
5092  window = GNC_MAIN_WINDOW(window_in);
5093  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
5094  return priv->progressbar;
5095 }
5096 
5097 
5098 static void
5099 gnc_main_window_all_ui_set_sensitive (GncWindow *unused, gboolean sensitive)
5100 {
5101  GncMainWindow *window;
5102  GncMainWindowPrivate *priv;
5103  GList *groupp, *groups, *winp, *tmp;
5104  GtkWidget *close_button;
5105 
5106  for (winp = active_windows; winp; winp = g_list_next(winp))
5107  {
5108  window = winp->data;
5109  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
5110 
5111  groups = gtk_ui_manager_get_action_groups(window->ui_merge);
5112  for (groupp = groups; groupp; groupp = g_list_next(groupp))
5113  {
5114  gtk_action_group_set_sensitive(GTK_ACTION_GROUP(groupp->data), sensitive);
5115  }
5116 
5117  for (tmp = priv->installed_pages; tmp; tmp = g_list_next(tmp))
5118  {
5119  close_button = g_object_get_data(tmp->data, PLUGIN_PAGE_CLOSE_BUTTON);
5120  if (!close_button)
5121  continue;
5122  gtk_widget_set_sensitive (close_button, sensitive);
5123  }
5124  }
5125 }
5126 
5127 
5132 static void
5133 gnc_window_main_window_init (GncWindowIface *iface)
5134 {
5135  iface->get_gtk_window = gnc_main_window_get_gtk_window;
5136  iface->get_statusbar = gnc_main_window_get_statusbar;
5137  iface->get_progressbar = gnc_main_window_get_progressbar;
5138  iface->ui_set_sensitive = gnc_main_window_all_ui_set_sensitive;
5139 }
5140 
5141 
5142 /* Set the window where all progressbar updates should occur. This
5143  * is a wrapper around the gnc_window_set_progressbar_window()
5144  * function.
5145  */
5146 void
5148 {
5149  GncWindow *gncwin;
5150  gncwin = GNC_WINDOW(window);
5151  gnc_window_set_progressbar_window(gncwin);
5152 }
5153 
5154 
5167 static void
5168 do_popup_menu(GncPluginPage *page, GdkEventButton *event)
5169 {
5170  GtkUIManager *ui_merge;
5171  GtkWidget *menu;
5172 
5173  g_return_if_fail(GNC_IS_PLUGIN_PAGE(page));
5174 
5175  ENTER("page %p, event %p", page, event);
5176  ui_merge = gnc_plugin_page_get_ui_merge(page);
5177  if (ui_merge == NULL)
5178  {
5179  LEAVE("no ui merge");
5180  return;
5181  }
5182 
5183  menu = gtk_ui_manager_get_widget(ui_merge, "/MainPopup");
5184  if (!menu)
5185  {
5186  LEAVE("no menu");
5187  return;
5188  }
5189  gtk_menu_popup_at_pointer (GTK_MENU(menu), (GdkEvent *) event);
5190 
5191  LEAVE(" ");
5192 }
5193 
5194 
5208 gboolean
5210  GncPluginPage *page)
5211 {
5212  ENTER("widget %p, page %p", widget, page);
5213  do_popup_menu(page, NULL);
5214  LEAVE(" ");
5215  return TRUE;
5216 }
5217 
5218 
5219 /* Callback function invoked when the user clicks in the content of
5220  * any Gnucash window. If this was a "right-click" then Gnucash will
5221  * popup the contextual menu.
5222  */
5223 gboolean
5224 gnc_main_window_button_press_cb (GtkWidget *whatever,
5225  GdkEventButton *event,
5226  GncPluginPage *page)
5227 {
5228  g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
5229 
5230  ENTER("widget %p, event %p, page %p", whatever, event, page);
5231  /* Ignore double-clicks and triple-clicks */
5232  if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
5233  {
5234  do_popup_menu(page, event);
5235  LEAVE("menu shown");
5236  return TRUE;
5237  }
5238 
5239  LEAVE("other click");
5240  return FALSE;
5241 }
5242 
5243 void
5245  gboolean sensitive)
5246 {
5247  GList *tmp;
5248  GtkAction *action;
5249 
5250  for (tmp = active_windows; tmp; tmp = g_list_next(tmp))
5251  {
5252  action = gnc_main_window_find_action (tmp->data, action_name);
5253  gtk_action_set_sensitive (action, sensitive);
5254  }
5255 }
5256 
5258 {
5259  g_assert(window);
5260  return window->ui_merge;
5261 }
5262 
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:512
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.
gtk helper routines.
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:506
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_...
This data structure allows the passing of the tab width and whether the tab layout is on the left or ...
#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:1094
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.
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.
GtkWidget * gnc_get_dialog_widget_from_id(GtkDialog *dialog, const gchar *id)
Find the Widget defined by &#39;id&#39; in the dialog.
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:469
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...
gboolean gnc_prefs_set_bool(const gchar *group, const gchar *pref_name, gboolean value)
Store a boolean value into the preferences backend.
Definition: gnc-prefs.c:277
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:461
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 ...
GList * gnc_list_all_paths(void)
Returns a GList* of the environment variables used by GnuCash.
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:582
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:1305
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:1311
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.
gchar * gnc_uri_create_uri(const gchar *scheme, const gchar *hostname, gint32 port, const gchar *username, const gchar *password, const gchar *path)
Composes a normalized uri starting from its separate components.
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.