GnuCash  5.6-150-g038405b370+
gnc-main-window.cpp
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 <glib/gi18n.h>
36 #include <gtk/gtk.h>
37 #include <gdk/gdk.h>
38 #include <gdk/gdkkeysyms.h>
39 #include "dialog-options.hpp"
40 #include <libguile.h>
41 
42 #include <config.h>
43 
44 
45 #include "gnc-plugin.h"
46 #include "gnc-plugin-manager.h"
47 #include "gnc-main-window.h"
48 
49 #include "dialog-preferences.h"
50 #include "dialog-reset-warnings.h"
51 #include "dialog-transfer.h"
52 #include "dialog-utils.h"
53 #include "engine-helpers.h"
54 #include "file-utils.h"
55 #include "gnc-component-manager.h"
56 #include "dialog-doclink-utils.h"
57 #include "gnc-engine.h"
58 #include "gnc-features.h"
59 #include "gnc-file.h"
60 #include "gnc-filepath-utils.h"
61 #include "gnc-gkeyfile-utils.h"
62 #include "gnc-gnome-utils.h"
63 #include "gnc-gobject-utils.h"
64 #include "gnc-gui-query.h"
65 #include "gnc-gtk-utils.h"
66 #include "gnc-hooks.h"
67 #include "gnc-icons.h"
68 #include "gnc-session.h"
69 #include "gnc-state.h"
70 #include "gnc-ui.h"
71 #include "gnc-ui-util.h"
72 #include <gnc-glib-utils.h>
73 #include "gnc-uri-utils.h"
74 #include "gnc-version.h"
75 #include "gnc-warnings.h"
76 #include "gnc-window.h"
77 #include "gnc-prefs.h"
78 #include "gnc-optiondb.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  MENU_CHANGED,
96  LAST_SIGNAL
97 };
98 
101 #define PLUGIN_PAGE_LABEL "plugin-page"
102 
103 #define PLUGIN_PAGE_CLOSE_BUTTON "close-button"
104 #define PLUGIN_PAGE_TAB_LABEL "label"
105 
106 #define GNC_PREF_SHOW_CLOSE_BUTTON "tab-close-buttons"
107 #define GNC_PREF_TAB_NEXT_RECENT "tab-next-recent"
108 #define GNC_PREF_TAB_POSITION_TOP "tab-position-top"
109 #define GNC_PREF_TAB_POSITION_BOTTOM "tab-position-bottom"
110 #define GNC_PREF_TAB_POSITION_LEFT "tab-position-left"
111 #define GNC_PREF_TAB_POSITION_RIGHT "tab-position-right"
112 #define GNC_PREF_TAB_WIDTH "tab-width"
113 #define GNC_PREF_TAB_COLOR "show-account-color-tabs"
114 #define GNC_PREF_SAVE_CLOSE_EXPIRES "save-on-close-expires"
115 #define GNC_PREF_SAVE_CLOSE_WAIT_TIME "save-on-close-wait-time"
116 #define GNC_PREF_TAB_OPEN_ADJACENT "tab-open-adjacent"
117 
118 #define GNC_MAIN_WINDOW_NAME "GncMainWindow"
119 
120 #define DIALOG_BOOK_OPTIONS_CM_CLASS "dialog-book-options"
121 
133 extern gboolean gnc_book_options_dialog_apply_helper(GncOptionDB * options);
134 
136 [[maybe_unused]] constexpr auto gnc_main_window_max_number {10};
137 
138 /* Static Globals *******************************************************/
139 
141 static QofLogModule log_module = GNC_MOD_GUI;
143 static GQuark window_type = 0;
146 static GList *active_windows = nullptr;
149 static guint secs_to_save = 0;
150 #define MSG_AUTO_SAVE _("Changes will be saved automatically in %u seconds")
151 
152 /* Declarations *********************************************************/
153 static void gnc_main_window_constructed (GObject *object);
154 static void gnc_main_window_finalize (GObject *object);
155 static void gnc_main_window_destroy (GtkWidget *widget);
156 
157 static void gnc_main_window_setup_window (GncMainWindow *window);
158 static void gnc_window_main_window_init (GncWindowInterface *iface);
159 #ifndef MAC_INTEGRATION
160 static void gnc_main_window_update_all_menu_items (void);
161 #endif
162 
163 /* Callbacks */
164 static void gnc_main_window_switch_page (GtkNotebook *notebook, gpointer *notebook_page, gint pos, GncMainWindow *window);
165 static void gnc_main_window_page_reordered (GtkNotebook *notebook, GtkWidget *child, guint pos, GncMainWindow *window);
166 static void gnc_main_window_plugin_added (GncPlugin *manager, GncPlugin *plugin, GncMainWindow *window);
167 static void gnc_main_window_plugin_removed (GncPlugin *manager, GncPlugin *plugin, GncMainWindow *window);
168 static void gnc_main_window_engine_commit_error_callback( gpointer data, QofBackendError errcode );
169 
170 /* Command callbacks */
171 static void gnc_main_window_cmd_redirect (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
172 static void gnc_main_window_cmd_page_setup (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
173 static void gnc_main_window_cmd_file_properties (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
174 static void gnc_main_window_cmd_file_close (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
175 static void gnc_main_window_cmd_file_quit (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
176 static void gnc_main_window_cmd_edit_cut (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
177 static void gnc_main_window_cmd_edit_copy (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
178 static void gnc_main_window_cmd_edit_paste (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
179 static void gnc_main_window_cmd_edit_preferences (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
180 static void gnc_main_window_cmd_view_refresh (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
181 static void gnc_main_window_cmd_view_toolbar (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
182 static void gnc_main_window_cmd_view_summary (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
183 static void gnc_main_window_cmd_view_statusbar (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
184 
185 static void gnc_main_window_cmd_view_tab_position (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
186 
187 static void gnc_main_window_cmd_actions_reset_warnings (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
188 static void gnc_main_window_cmd_actions_rename_page (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
189 static void gnc_main_window_cmd_window_new (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
190 static void gnc_main_window_cmd_window_move_page (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
191 #ifndef MAC_INTEGRATION
192 static void gnc_main_window_cmd_window_raise (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
193 #endif
194 static void gnc_main_window_cmd_help_tutorial (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
195 static void gnc_main_window_cmd_help_contents (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
196 static void gnc_main_window_cmd_help_about (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
197 
198 static void do_popup_menu(GncPluginPage *page, GdkEventButton *event);
199 static GtkWidget *gnc_main_window_get_statusbar (GncWindow *window_in);
200 static void statusbar_notification_lastmodified (void);
201 static void gnc_main_window_update_tab_position (gpointer prefs, gchar *pref, gpointer user_data);
202 static void gnc_main_window_remove_prefs (GncMainWindow *window);
203 
204 #ifdef MAC_INTEGRATION
205 static void gnc_quartz_shutdown (GtkosxApplication *theApp, gpointer data);
206 static gboolean gnc_quartz_should_quit (GtkosxApplication *theApp, GncMainWindow *window);
207 static void gnc_quartz_set_menu (GncMainWindow* window);
208 #endif
209 static void gnc_main_window_init_menu_updaters (GncMainWindow *window);
210 
212 {
213  GtkApplicationWindow gtk_application_window;
214  gboolean window_quitting;
215  gboolean just_plugin_prefs;
216 };
217 
220 typedef struct
221 {
226  GtkWidget *menu_dock;
228  GtkWidget *menubar;
230  GMenuModel *menubar_model;
233  GtkWidget *toolbar;
235  GtkWidget *notebook;
237  gboolean show_color_tabs;
241  GtkWidget *statusbar;
245  GtkWidget *progressbar;
249  GList *usage_order;
255  gint pos[2];
257  gboolean restoring_pages;
258 
259  const gchar *previous_plugin_page_name;
260  const gchar *previous_menu_qualifier;
261 
263  GtkAccelGroup *accel_group;
264 
265  GHashTable *display_item_hash;
266 
268 
269 G_DEFINE_TYPE_WITH_CODE(GncMainWindow, gnc_main_window, GTK_TYPE_APPLICATION_WINDOW,
270  G_ADD_PRIVATE (GncMainWindow)
271  G_IMPLEMENT_INTERFACE (GNC_TYPE_WINDOW,
272  gnc_window_main_window_init))
273 
274 #define GNC_MAIN_WINDOW_GET_PRIVATE(o) \
275  ((GncMainWindowPrivate*)gnc_main_window_get_instance_private((GncMainWindow*)o))
276 
279 static guint main_window_signals[LAST_SIGNAL] = { 0 };
280 
285 static GActionEntry gnc_menu_actions [] =
286 {
287  { "FilePageSetupAction", gnc_main_window_cmd_page_setup, nullptr, nullptr, nullptr },
288  { "FilePropertiesAction", gnc_main_window_cmd_file_properties, nullptr, nullptr, nullptr },
289  { "FileCloseAction", gnc_main_window_cmd_file_close, nullptr, nullptr, nullptr },
290  { "FilePrintAction", gnc_main_window_cmd_redirect, nullptr, nullptr, nullptr },
291  { "FileQuitAction", gnc_main_window_cmd_file_quit, nullptr, nullptr, nullptr },
292 
293  { "EditCutAction", gnc_main_window_cmd_edit_cut, nullptr, nullptr, nullptr },
294  { "EditCopyAction", gnc_main_window_cmd_edit_copy, nullptr, nullptr, nullptr },
295  { "EditPasteAction", gnc_main_window_cmd_edit_paste, nullptr, nullptr, nullptr },
296  { "EditPreferencesAction", gnc_main_window_cmd_edit_preferences, nullptr, nullptr, nullptr },
297 
298  { "ActionsForgetWarningsAction", gnc_main_window_cmd_actions_reset_warnings, nullptr, nullptr, nullptr },
299  { "ActionsRenamePageAction", gnc_main_window_cmd_actions_rename_page, nullptr, nullptr, nullptr },
300 
301  { "TransactionAction", nullptr, nullptr, nullptr, nullptr },
302 
303  { "ViewSortByAction", nullptr, nullptr, nullptr, nullptr },
304  { "ViewFilterByAction", nullptr, nullptr, nullptr, nullptr },
305  { "ViewRefreshAction", gnc_main_window_cmd_view_refresh, nullptr, nullptr, nullptr },
306  { "ViewToolbarAction", gnc_main_window_cmd_view_toolbar, nullptr, "true", nullptr },
307  { "ViewSummaryAction", gnc_main_window_cmd_view_summary, nullptr, "true", nullptr },
308  { "ViewStatusbarAction", gnc_main_window_cmd_view_statusbar, nullptr, "true", nullptr },
309  { "ViewTabPositionAction", gnc_main_window_cmd_view_tab_position, "i", "@i 0", nullptr },
310 
311  { "ScheduledAction", nullptr, nullptr, nullptr, nullptr },
312 
313  { "ExtensionsAction", nullptr, nullptr, nullptr, nullptr },
314 
315  { "WindowNewAction", gnc_main_window_cmd_window_new, nullptr, nullptr, nullptr },
316  { "WindowMovePageAction", gnc_main_window_cmd_window_move_page, nullptr, nullptr, nullptr },
317 #ifndef MAC_INTEGRATION
318  { "WindowAction", gnc_main_window_cmd_window_raise, "i", "@i 0", nullptr },
319 #endif
320  { "HelpTutorialAction", gnc_main_window_cmd_help_tutorial, nullptr, nullptr, nullptr },
321  { "HelpContentsAction", gnc_main_window_cmd_help_contents, nullptr, nullptr, nullptr },
322  { "HelpAboutAction", gnc_main_window_cmd_help_about, nullptr, nullptr, nullptr },
323 };
325 static guint gnc_menu_n_actions = G_N_ELEMENTS(gnc_menu_actions);
326 
331 static const gchar *always_insensitive_actions[] =
332 {
333  "FilePrintAction",
334  nullptr
335 };
336 
337 
341 static const gchar *initially_insensitive_actions[] =
342 {
343  "FileCloseAction",
344  nullptr
345 };
346 
347 
352 static const gchar *always_hidden_actions[] =
353 {
354  "ViewSortByAction",
355  "ViewFilterByAction",
356  nullptr
357 };
358 
359 
362 static const gchar *immutable_page_actions[] =
363 {
364  "FileCloseAction",
365  nullptr
366 };
367 
368 
371 static const gchar *multiple_page_actions[] =
372 {
373  "WindowMovePageAction",
374  nullptr
375 };
376 
377 
378 /************************************************************
379  * *
380  ************************************************************/
381 #define WINDOW_COUNT "WindowCount"
382 #define WINDOW_STRING "Window %d"
383 #define WINDOW_GEOMETRY "WindowGeometry"
384 #define WINDOW_POSITION "WindowPosition"
385 #define WINDOW_MAXIMIZED "WindowMaximized"
386 #define TOOLBAR_VISIBLE "ToolbarVisible"
387 #define STATUSBAR_VISIBLE "StatusbarVisible"
388 #define SUMMARYBAR_VISIBLE "SummarybarVisible"
389 #define WINDOW_FIRSTPAGE "FirstPage"
390 #define WINDOW_PAGECOUNT "PageCount"
391 #define WINDOW_PAGEORDER "PageOrder"
392 #define PAGE_TYPE "PageType"
393 #define PAGE_NAME "PageName"
394 #define PAGE_STRING "Page %d"
395 
396 typedef struct
397 {
398  GKeyFile *key_file;
399  const gchar *group_name;
400  gint window_num;
401  gint page_num;
402  gint page_offset;
404 
405 
406 gboolean
407 gnc_main_window_is_restoring_pages (GncMainWindow *window)
408 {
409  GncMainWindowPrivate *priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
410  return priv->restoring_pages;
411 }
412 
413 
414 /* Iterator function to walk all pages in all windows, calling the
415  * specified function for each page. */
416 void
417 gnc_main_window_foreach_page (GncMainWindowPageFunc fn, gpointer user_data)
418 {
419  ENTER(" ");
420  for (auto w = active_windows; w; w = g_list_next(w))
421  {
422  auto window{static_cast<GncMainWindow*>(w->data)};
423  auto priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
424  for (auto p = priv->installed_pages; p; p = g_list_next(p))
425  {
426  auto page{static_cast<GncPluginPage*>(p->data)};
427  fn(page, user_data);
428  }
429  }
430  LEAVE(" ");
431 }
432 
433 
448 static gboolean
449 gnc_main_window_restore_page (GncMainWindow *window,
450  GncMainWindowSaveData *data)
451 {
452  GncMainWindowPrivate *priv;
453  GncPluginPage *page = nullptr;
454  gchar *page_group, *page_type = nullptr, *name = nullptr;
455  const gchar *class_type;
456  GError *error = nullptr;
457 
458  ENTER("window %p, data %p (key file %p, window %d, page start %d, page num %d)",
459  window, data, data->key_file, data->window_num, data->page_offset,
460  data->page_num);
461 
462  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
463  page_group = g_strdup_printf(PAGE_STRING,
464  data->page_offset + data->page_num);
465  page_type = g_key_file_get_string(data->key_file, page_group,
466  PAGE_TYPE, &error);
467  if (error)
468  {
469  g_warning("error reading group %s key %s: %s",
470  page_group, PAGE_TYPE, error->message);
471  goto cleanup;
472  }
473 
474  /* See if the page already exists. */
475  page = static_cast<GncPluginPage*>(g_list_nth_data(priv->installed_pages,
476  data->page_num));
477  if (page)
478  {
479  class_type = GNC_PLUGIN_PAGE_GET_CLASS(page)->plugin_name;
480  if (strcmp(page_type, class_type) != 0)
481  {
482  g_warning("error: page types don't match: state %s, existing page %s",
483  page_type, class_type);
484  goto cleanup;
485  }
486  }
487  else
488  {
489  /* create and install the page */
490  page = gnc_plugin_page_recreate_page(GTK_WIDGET(window), page_type,
491  data->key_file, page_group);
492  if (page)
493  {
494  /* Does the page still need to be installed into the window? */
495  if (page->window == nullptr)
496  {
498  gnc_main_window_open_page(window, page);
499  }
500 
501  /* Restore the page name */
502  name = g_key_file_get_string(data->key_file, page_group,
503  PAGE_NAME, &error);
504  if (error)
505  {
506  g_warning("error reading group %s key %s: %s",
507  page_group, PAGE_NAME, error->message);
508  /* Fall through and still show the page. */
509  }
510  else
511  {
512  DEBUG("updating page name for %p to %s.", page, name);
513  main_window_update_page_name(page, name);
514  g_free(name);
515  }
516  }
517  }
518 
519  LEAVE("ok");
520 cleanup:
521  if (error)
522  g_error_free(error);
523  if (page_type)
524  g_free(page_type);
525  g_free(page_group);
526 
527  return (page ? true : false);
528 }
529 
530 static bool
531 intersects_some_monitor(const GdkRectangle& rect)
532 {
533  auto display = gdk_display_get_default();
534  if (!display)
535  return false;
536 
537  int n = gdk_display_get_n_monitors(display);
538  for (int i = 0; i < n; ++i)
539  {
540  auto monitor = gdk_display_get_monitor(display, i);
541  GdkRectangle monitor_geometry;
542  gdk_monitor_get_geometry(monitor, &monitor_geometry);
543  DEBUG("Monitor %d: position (%d,%d), size %dx%d\n", i,
544  monitor_geometry.x, monitor_geometry.y,
545  monitor_geometry.width, monitor_geometry.height);
546  if (gdk_rectangle_intersect(&rect, &monitor_geometry, nullptr))
547  return true;
548  }
549 
550  return false;
551 }
552 
553 static void
554 set_window_geometry(GncMainWindow *window, GncMainWindowSaveData *data, gchar *window_group)
555 {
556  gsize length;
557  GError *error = nullptr;
558  gint *geom = g_key_file_get_integer_list(data->key_file, window_group,
559  WINDOW_GEOMETRY, &length, &error);
560  if (error)
561  {
562  g_warning("error reading group %s key %s: %s",
563  window_group, WINDOW_GEOMETRY, error->message);
564  g_error_free(error);
565  error = nullptr;
566  }
567  else if (length != 2)
568  {
569  g_warning("invalid number of values for group %s key %s",
570  window_group, WINDOW_GEOMETRY);
571  }
572  else
573  {
574  gtk_window_resize(GTK_WINDOW(window), geom[0], geom[1]);
575  DEBUG("window (%p) size %dx%d", window, geom[0], geom[1]);
576  }
577 
578  /* keep the geometry for a test whether the windows position
579  is offscreen */
580  gint *pos = g_key_file_get_integer_list(data->key_file, window_group,
581  WINDOW_POSITION, &length, &error);
582  if (error)
583  {
584  g_warning("error reading group %s key %s: %s",
585  window_group, WINDOW_POSITION, error->message);
586  g_error_free(error);
587  error = nullptr;
588  }
589  else if (length != 2)
590  {
591  g_warning("invalid number of values for group %s key %s",
592  window_group, WINDOW_POSITION);
593  }
594  else if (pos)
595  {
596  // Prevent restoring coordinates if this would move the window off-screen
597  // If missing geom, use height=width=1 to make the intersection check work
598  GdkRectangle geometry{pos[0], pos[1], geom ? geom[0] : 1, geom ? geom[1] : 1};
599  if (intersects_some_monitor(geometry))
600  {
601  gtk_window_move(GTK_WINDOW(window), geometry.x, geometry.y);
602  auto priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
603  priv->pos[0] = geometry.x;
604  priv->pos[1] = geometry.y;
605  DEBUG("window (%p) position (%d,%d)", window, geometry.x, geometry.y);
606  }
607  else
608  {
609  DEBUG("position (%d,%d), size %dx%d is offscreen; will not move",
610  geometry.x, geometry.y, geometry.width, geometry.height);
611  }
612  }
613  g_free(geom);
614  g_free(pos);
615 
616  gboolean max = g_key_file_get_boolean(data->key_file, window_group,
617  WINDOW_MAXIMIZED, &error);
618  if (error)
619  {
620  g_warning("error reading group %s key %s: %s",
621  window_group, WINDOW_MAXIMIZED, error->message);
622  g_error_free(error);
623  error = nullptr;
624  }
625  else if (max)
626  {
627  gtk_window_maximize(GTK_WINDOW(window));
628  }
629 }
630 
639 static void
640 gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *data)
641 {
642  GncMainWindowPrivate *priv;
643  GAction *action;
644  gint *order;
645  gsize length;
646  gsize page_start, page_count, i;
647  GError *error = nullptr;
648  GSList *added_page_offsets = nullptr;
649  gint offset = 0;
650 
651  /* Setup */
652  ENTER("window %p, data %p (key file %p, window %d)",
653  window, data, data->key_file, data->window_num);
654  gchar *window_group = g_strdup_printf(WINDOW_STRING, data->window_num + 1);
655 
656  /* Deal with the uncommon case that the state file defines a window
657  * but no pages. An example to get in such a situation can be found
658  * here: https://bugs.gnucash.org/show_bug.cgi?id=436479#c3
659  * If this happens on the first window, we will open an account hierarchy
660  * to avoid confusing the user by presenting a completely empty window.
661  * If it happens on a later window, we'll just skip restoring that window.
662  */
663  if (!g_key_file_has_group (data->key_file, window_group) ||
664  !g_key_file_has_key (data->key_file, window_group, WINDOW_PAGECOUNT, &error))
665  {
666  if (window)
667  {
669  PINFO ("saved state had an empty first main window\n"
670  "an account hierarchy page was added automatically to avoid confusion");
671  }
672  else
673  PINFO ("saved state had an empty main window, skipping restore");
674 
675  goto cleanup;
676  }
677 
678 
679  /* Get this window's notebook info */
680  page_count = g_key_file_get_integer(data->key_file,
681  window_group, WINDOW_PAGECOUNT, &error);
682  if (error)
683  {
684  g_warning("error reading group %s key %s: %s",
685  window_group, WINDOW_PAGECOUNT, error->message);
686  goto cleanup;
687  }
688  if (page_count == 0)
689  {
690  /* Should never happen, but has during alpha testing. Having this
691  * check doesn't hurt anything. */
692  goto cleanup;
693  }
694  page_start = g_key_file_get_integer(data->key_file,
695  window_group, WINDOW_FIRSTPAGE, &error);
696  if (error)
697  {
698  g_warning("error reading group %s key %s: %s",
699  window_group, WINDOW_FIRSTPAGE, error->message);
700  goto cleanup;
701  }
702 
703  /* Build a window if we don't already have one */
704  if (window == nullptr)
705  {
706  DEBUG("Window %d doesn't exist. Creating new window.", data->window_num);
707  DEBUG("active_windows %p.", active_windows);
708  if (active_windows)
709  DEBUG("first window %p.", active_windows->data);
710  window = gnc_main_window_new();
711  }
712 
713  /* Get the window coordinates, etc. */
714  set_window_geometry(window, data, window_group);
715 
716  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
717 
718  // need to add the accelerator keys
719  gnc_add_accelerator_keys_for_menu (GTK_WIDGET(priv->menubar), priv->menubar_model, priv->accel_group);
720 
721  /* Common view menu items */
722  action = gnc_main_window_find_action (window, "ViewToolbarAction");
723  if (action)
724  {
725  GVariant *state = g_action_get_state (G_ACTION(action));
726  gboolean visible = g_variant_get_boolean (state);
727  gboolean desired_visibility = g_key_file_get_boolean (data->key_file, window_group,
728  TOOLBAR_VISIBLE, &error);
729 
730  if (error)
731  {
732  g_warning ("error reading group %s key %s: %s",
733  window_group, TOOLBAR_VISIBLE, error->message);
734  g_error_free (error);
735  error = nullptr;
736  }
737  else if (visible != desired_visibility)
738  {
739  g_action_activate (action, nullptr);
740  }
741  g_variant_unref (state);
742  }
743 
744  action = gnc_main_window_find_action (window, "ViewSummaryAction");
745  if (action)
746  {
747  GVariant *state = g_action_get_state (G_ACTION(action));
748  gboolean visible = g_variant_get_boolean (state);
749  gboolean desired_visibility = g_key_file_get_boolean (data->key_file, window_group,
750  SUMMARYBAR_VISIBLE, &error);
751 
752  if (error)
753  {
754  g_warning ("error reading group %s key %s: %s",
755  window_group, SUMMARYBAR_VISIBLE, error->message);
756  g_error_free (error);
757  error = nullptr;
758  }
759  else if (visible != desired_visibility)
760  {
761  g_action_activate (action, nullptr);
762  }
763  g_variant_unref (state);
764  }
765 
766  action = gnc_main_window_find_action (window, "ViewStatusbarAction");
767  if (action)
768  {
769  GVariant *state = g_action_get_state (G_ACTION(action));
770  gboolean visible = g_variant_get_boolean (state);
771  gboolean desired_visibility = g_key_file_get_boolean (data->key_file, window_group,
772  STATUSBAR_VISIBLE, &error);
773 
774  if (error)
775  {
776  g_warning ("error reading group %s key %s: %s",
777  window_group, STATUSBAR_VISIBLE, error->message);
778  g_error_free (error);
779  error = nullptr;
780  }
781  else if (visible != desired_visibility)
782  {
783  g_action_activate (action, nullptr);
784  }
785  g_variant_unref (state);
786  }
787  priv->restoring_pages = TRUE;
788  /* Now populate the window with pages. */
789  for (i = 0; i < page_count; i++)
790  {
791  data->page_offset = page_start;
792  data->page_num = i;
793  gboolean page_added = gnc_main_window_restore_page (window, data);
794 
795  if (!page_added) // if page not added, increase offset to compensate
796  {
797  offset ++;
798  added_page_offsets = g_slist_append (added_page_offsets,
799  GINT_TO_POINTER(-1));
800  }
801  else
802  added_page_offsets = g_slist_append (added_page_offsets,
803  GINT_TO_POINTER(offset));
804 
805  /* give the page a chance to display */
806  while (gtk_events_pending ())
807  gtk_main_iteration ();
808  }
809  priv->restoring_pages = FALSE;
810  /* Restore page ordering within the notebook. Use +1 notation so the
811  * numbers in the page order match the page sections, at least for
812  * the one window case. */
813  order = g_key_file_get_integer_list (data->key_file, window_group,
814  WINDOW_PAGEORDER, &length, &error);
815  if (error)
816  {
817  g_warning("error reading group %s key %s: %s",
818  window_group, WINDOW_PAGEORDER, error->message);
819  g_error_free(error);
820  error = nullptr;
821  }
822  else if (length != page_count)
823  {
824  g_warning("%s key %s length %" G_GSIZE_FORMAT " differs from window page count %" G_GSIZE_FORMAT,
825  window_group, WINDOW_PAGEORDER, length, page_count);
826  }
827  else
828  {
829  /* Dump any list that might exist */
830  g_list_free(priv->usage_order);
831  priv->usage_order = nullptr;
832 
833  gint default_page_position = -1;
834 
835  /* Now rebuild the list from the key file, skipping pages not added */
836  for (i = 0; i < length; i++)
837  {
838  gint zero_based_page_number = order[i] - 1;
839 
840  gint offset = GPOINTER_TO_INT(g_slist_nth_data (added_page_offsets,
841  zero_based_page_number));
842 
843  if (offset == -1)
844  continue;
845 
846  gpointer page = g_list_nth_data (priv->installed_pages,
847  zero_based_page_number - offset);
848 
849  if (default_page_position == -1)
850  default_page_position = zero_based_page_number - offset;
851 
852  if (page)
853  priv->usage_order = g_list_append (priv->usage_order, page);
854  }
855  gtk_notebook_set_current_page (GTK_NOTEBOOK(priv->notebook),
856  default_page_position);
857 
858  g_signal_emit_by_name (window, "page_changed",
859  g_list_nth_data (priv->usage_order, 0));
860  }
861  if (order)
862  {
863  g_free(order);
864  }
865 
866  LEAVE("window %p", window);
867 cleanup:
868  g_slist_free (added_page_offsets);
869  if (error)
870  g_error_free(error);
871  g_free(window_group);
872  if (window)
873  gtk_widget_show (GTK_WIDGET(window));
874 }
875 
876 void
877 gnc_main_window_restore_all_windows(const GKeyFile *keyfile)
878 {
879  gint i, window_count;
880  GError *error = nullptr;
882 
883  /* We use the same struct for reading and for writing, so we cast
884  away the const. */
885  data.key_file = (GKeyFile *) keyfile;
886  window_count = g_key_file_get_integer(data.key_file, STATE_FILE_TOP,
887  WINDOW_COUNT, &error);
888  if (error)
889  {
890  g_warning("error reading group %s key %s: %s",
891  STATE_FILE_TOP, WINDOW_COUNT, error->message);
892  g_error_free(error);
893  LEAVE("can't read count");
894  return;
895  }
896 
897  /* Restore all state information on the open windows. Window
898  numbers in state file are 1-based. GList indices are 0-based. */
899  gnc_set_busy_cursor (nullptr, TRUE);
900  for (i = 0; i < window_count; i++)
901  {
902  data.window_num = i;
903  auto window{static_cast<GncMainWindow*>(g_list_nth_data(active_windows,
904  i))};
905  gnc_main_window_restore_window(window, &data);
906  }
907  gnc_unset_busy_cursor (nullptr);
908 
909  statusbar_notification_lastmodified();
910 }
911 
912 void
914 {
915  GAction *action;
916 
917  /* The default state should be to have an Account Tree page open
918  * in the window. */
919  DEBUG("no saved state file");
920  if (!window)
921  window = static_cast<GncMainWindow*>(g_list_nth_data(active_windows, 0));
922  gtk_widget_show (GTK_WIDGET(window));
923  action = gnc_main_window_find_action_in_group (window,
924  "gnc-plugin-account-tree-actions",
925  "ViewAccountTreeAction");
926  g_action_activate (action, nullptr);
927 }
928 
938 static void
939 gnc_main_window_save_page (GncPluginPage *page, GncMainWindowSaveData *data)
940 {
941  gchar *page_group;
942  const gchar *plugin_name, *page_name;
943 
944  ENTER("page %p, data %p (key file %p, window %d, page %d)",
945  page, data, data->key_file, data->window_num, data->page_num);
946  plugin_name = gnc_plugin_page_get_plugin_name(page);
947  page_name = gnc_plugin_page_get_page_name(page);
948  if (!plugin_name || !page_name)
949  {
950  LEAVE("not saving invalid page");
951  return;
952  }
953  page_group = g_strdup_printf(PAGE_STRING, data->page_num++);
954  g_key_file_set_string(data->key_file, page_group, PAGE_TYPE, plugin_name);
955  g_key_file_set_string(data->key_file, page_group, PAGE_NAME, page_name);
956 
957  gnc_plugin_page_save_page(page, data->key_file, page_group);
958  g_free(page_group);
959  LEAVE(" ");
960 }
961 
962 
971 static void
972 gnc_main_window_save_window (GncMainWindow *window, GncMainWindowSaveData *data)
973 {
974  GncMainWindowPrivate *priv;
975  GAction *action;
976  gint i, num_pages, coords[4], *order;
977  gboolean maximized, minimized, visible = true;
978  gchar *window_group;
979 
980  /* Setup */
981  ENTER("window %p, data %p (key file %p, window %d)",
982  window, data, data->key_file, data->window_num);
983  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
984 
985  /* Check for bogus window structures. */
986  num_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook));
987  if (0 == num_pages)
988  {
989  LEAVE("empty window %p", window);
990  return;
991  }
992 
993  /* Save this window's notebook info */
994  window_group = g_strdup_printf(WINDOW_STRING, data->window_num++);
995  g_key_file_set_integer(data->key_file, window_group,
996  WINDOW_PAGECOUNT, num_pages);
997  g_key_file_set_integer(data->key_file, window_group,
998  WINDOW_FIRSTPAGE, data->page_num);
999 
1000  /* Save page ordering within the notebook. Use +1 notation so the
1001  * numbers in the page order match the page sections, at least for
1002  * the one window case. */
1003  order = static_cast<int*>(g_malloc(sizeof(gint) * num_pages));
1004  for (i = 0; i < num_pages; i++)
1005  {
1006  gpointer page = g_list_nth_data(priv->usage_order, i);
1007  order[i] = g_list_index(priv->installed_pages, page) + 1;
1008  }
1009  g_key_file_set_integer_list(data->key_file, window_group,
1010  WINDOW_PAGEORDER, order, num_pages);
1011  g_free(order);
1012 
1013  /* Save the window coordinates, etc. */
1014  gtk_window_get_position(GTK_WINDOW(window), &coords[0], &coords[1]);
1015  gtk_window_get_size(GTK_WINDOW(window), &coords[2], &coords[3]);
1016  maximized = (gdk_window_get_state(gtk_widget_get_window ((GTK_WIDGET(window))))
1017  & GDK_WINDOW_STATE_MAXIMIZED) != 0;
1018  minimized = (gdk_window_get_state(gtk_widget_get_window ((GTK_WIDGET(window))))
1019  & GDK_WINDOW_STATE_ICONIFIED) != 0;
1020 
1021  if (minimized)
1022  {
1023  gint *pos = priv->pos;
1024  g_key_file_set_integer_list(data->key_file, window_group,
1025  WINDOW_POSITION, &pos[0], 2);
1026  DEBUG("window minimized (%p) position (%d,%d)", window, pos[0], pos[1]);
1027  }
1028  else
1029  g_key_file_set_integer_list(data->key_file, window_group,
1030  WINDOW_POSITION, &coords[0], 2);
1031  g_key_file_set_integer_list(data->key_file, window_group,
1032  WINDOW_GEOMETRY, &coords[2], 2);
1033  g_key_file_set_boolean(data->key_file, window_group,
1034  WINDOW_MAXIMIZED, maximized);
1035  DEBUG("window (%p) position (%d,%d), size %dx%d, %s", window, coords[0], coords[1],
1036  coords[2], coords[3],
1037  maximized ? "maximized" : "not maximized");
1038 
1039  /* Common view menu items */
1040  action = gnc_main_window_find_action (window, "ViewToolbarAction");
1041  if (action)
1042  {
1043  GVariant *state = g_action_get_state (G_ACTION(action));
1044  visible = g_variant_get_boolean (state);
1045  g_variant_unref (state);
1046  }
1047  g_key_file_set_boolean (data->key_file, window_group,
1048  TOOLBAR_VISIBLE, visible);
1049  action = gnc_main_window_find_action (window, "ViewSummaryAction");
1050  if (action)
1051  {
1052  GVariant *state = g_action_get_state (G_ACTION(action));
1053  visible = g_variant_get_boolean (state);
1054  g_variant_unref (state);
1055  }
1056  g_key_file_set_boolean (data->key_file, window_group,
1057  SUMMARYBAR_VISIBLE, visible);
1058  action = gnc_main_window_find_action (window, "ViewStatusbarAction");
1059  if (action)
1060  {
1061  GVariant *state = g_action_get_state (G_ACTION(action));
1062  visible = g_variant_get_boolean (state);
1063  g_variant_unref (state);
1064  }
1065  g_key_file_set_boolean (data->key_file, window_group,
1066  STATUSBAR_VISIBLE, visible);
1067 
1068  /* Save individual pages in this window */
1069  g_list_foreach (priv->installed_pages, (GFunc)gnc_main_window_save_page, data);
1070 
1071  g_free(window_group);
1072  LEAVE("window %p", window);
1073 }
1074 
1075 void
1077 {
1078  GncMainWindowSaveData data;
1079 
1080  /* Set up the iterator data structures */
1081  data.key_file = keyfile;
1082  data.window_num = 1;
1083  data.page_num = 1;
1084 
1085  g_key_file_set_integer(data.key_file,
1086  STATE_FILE_TOP, WINDOW_COUNT,
1087  g_list_length(active_windows));
1088  /* Dump all state information on the open windows */
1089  g_list_foreach(active_windows, (GFunc)gnc_main_window_save_window, &data);
1090 }
1091 
1092 
1093 gboolean
1094 gnc_main_window_finish_pending (GncMainWindow *window)
1095 {
1096  GncMainWindowPrivate *priv;
1097  GList *item;
1098 
1099  g_return_val_if_fail(GNC_IS_MAIN_WINDOW(window), TRUE);
1100 
1101  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1102  for (item = priv->installed_pages; item; item = g_list_next(item))
1103  {
1104  if (!gnc_plugin_page_finish_pending(static_cast<GncPluginPage*>(item->data)))
1105  {
1106  return FALSE;
1107  }
1108  }
1109  return TRUE;
1110 }
1111 
1112 
1113 gboolean
1115 {
1116  const GList *windows, *item;
1117 
1118  windows = gnc_gobject_tracking_get_list(GNC_MAIN_WINDOW_NAME);
1119  for (item = windows; item; item = g_list_next(item))
1120  {
1121  if (!gnc_main_window_finish_pending(static_cast<GncMainWindow*>(item->data)))
1122  {
1123  return FALSE;
1124  }
1125  }
1126  if (gnc_gui_refresh_suspended ())
1127  {
1128  gnc_warning_dialog (nullptr, "%s", "An operation is still running, wait for it to complete before quitting.");
1129  return FALSE;
1130  }
1131  return TRUE;
1132 }
1133 
1134 
1145 static gboolean
1146 gnc_main_window_page_exists (GncPluginPage *page)
1147 {
1148  GncMainWindowPrivate *priv;
1149  GList *walker;
1150 
1151  for (walker = active_windows; walker; walker = g_list_next(walker))
1152  {
1153  auto window{static_cast<GncMainWindow*>(walker->data)};
1154  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1155  if (g_list_find(priv->installed_pages, page))
1156  {
1157  return TRUE;
1158  }
1159  }
1160  return FALSE;
1161 }
1162 
1163 static gboolean auto_save_countdown (GtkWidget *dialog)
1164 {
1165  GtkWidget *label;
1166  gchar *timeoutstr = nullptr;
1167 
1168  /* Stop count down if user closed the dialog since the last time we were called */
1169  if (!GTK_IS_DIALOG (dialog))
1170  return FALSE; /* remove timer */
1171 
1172  /* Stop count down if count down text can't be updated */
1173  label = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), "count-down-label"));
1174  if (!GTK_IS_LABEL (label))
1175  return FALSE; /* remove timer */
1176 
1177  /* Protect against rolling over to MAXUINT */
1178  if (secs_to_save)
1179  --secs_to_save;
1180  DEBUG ("Counting down: %d seconds", secs_to_save);
1181 
1182  timeoutstr = g_strdup_printf (MSG_AUTO_SAVE, secs_to_save);
1183  gtk_label_set_text (GTK_LABEL (label), timeoutstr);
1184  g_free (timeoutstr);
1185 
1186  /* Count down reached 0. Save and close dialog */
1187  if (!secs_to_save)
1188  {
1189  gtk_dialog_response (GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);
1190  return FALSE; /* remove timer */
1191  }
1192 
1193  /* Run another cycle */
1194  return TRUE;
1195 }
1196 
1197 
1207 static gboolean
1208 gnc_main_window_prompt_for_save (GtkWidget *window)
1209 {
1210  QofSession *session;
1211  QofBook *book;
1212  GtkWidget *dialog, *msg_area, *label;
1213  gint response;
1214  const gchar *filename, *tmp;
1215  const gchar *title = _("Save changes to file %s before closing?");
1216  /* This should be the same message as in gnc-file.c */
1217  const gchar *message_hours =
1218  _("If you don't save, changes from the past %d hours and %d minutes will be discarded.");
1219  const gchar *message_days =
1220  _("If you don't save, changes from the past %d days and %d hours will be discarded.");
1221  time64 oldest_change;
1222  gint minutes, hours, days;
1223  guint timer_source = 0;
1224  if (!gnc_current_session_exist())
1225  return FALSE;
1226  session = gnc_get_current_session();
1227  book = qof_session_get_book(session);
1228  if (!qof_book_session_not_saved(book))
1229  return FALSE;
1230  filename = qof_session_get_url(session);
1231  if (!strlen (filename))
1232  filename = _("<unknown>");
1233  if ((tmp = strrchr(filename, '/')) != nullptr)
1234  filename = tmp + 1;
1235 
1236  /* Remove any pending auto-save timeouts */
1237  gnc_autosave_remove_timer(book);
1238 
1239  dialog = gtk_message_dialog_new(GTK_WINDOW(window),
1240  GTK_DIALOG_MODAL,
1241  GTK_MESSAGE_WARNING,
1242  GTK_BUTTONS_NONE,
1243  title,
1244  filename);
1245  oldest_change = qof_book_get_session_dirty_time(book);
1246  minutes = (gnc_time (nullptr) - oldest_change) / 60 + 1;
1247  hours = minutes / 60;
1248  minutes = minutes % 60;
1249  days = hours / 24;
1250  hours = hours % 24;
1251  if (days > 0)
1252  {
1253  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1254  message_days, days, hours);
1255  }
1256  else if (hours > 0)
1257  {
1258  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1259  message_hours, hours, minutes);
1260  }
1261  else
1262  {
1263  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1264  ngettext("If you don't save, changes from the past %d minute will be discarded.",
1265  "If you don't save, changes from the past %d minutes will be discarded.",
1266  minutes), minutes);
1267  }
1268  gtk_dialog_add_buttons(GTK_DIALOG(dialog),
1269  _("Close _Without Saving"), GTK_RESPONSE_CLOSE,
1270  _("_Cancel"), GTK_RESPONSE_CANCEL,
1271  _("_Save"), GTK_RESPONSE_APPLY,
1272  nullptr);
1273  gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);
1274 
1275  /* If requested by the user, add a timeout to the question to save automatically
1276  * if the user doesn't answer after a chosen number of seconds.
1277  */
1278  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_CLOSE_EXPIRES))
1279  {
1280  gchar *timeoutstr = nullptr;
1281 
1282  secs_to_save = gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_CLOSE_WAIT_TIME);
1283  timeoutstr = g_strdup_printf (MSG_AUTO_SAVE, secs_to_save);
1284  label = GTK_WIDGET(gtk_label_new (timeoutstr));
1285  g_free (timeoutstr);
1286  gtk_widget_show (label);
1287 
1288  msg_area = gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG(dialog));
1289  gtk_box_pack_end (GTK_BOX(msg_area), label, TRUE, TRUE, 0);
1290  g_object_set (G_OBJECT (label), "xalign", 0.0, nullptr);
1291 
1292  g_object_set_data (G_OBJECT (dialog), "count-down-label", label);
1293  timer_source = g_timeout_add_seconds (1, (GSourceFunc)auto_save_countdown, dialog);
1294  }
1295 
1296  response = gtk_dialog_run (GTK_DIALOG (dialog));
1297  if (timer_source)
1298  g_source_remove (timer_source);
1299  gtk_widget_destroy(dialog);
1300 
1301  switch (response)
1302  {
1303  case GTK_RESPONSE_APPLY:
1304  gnc_file_save (GTK_WINDOW (window));
1305  return FALSE;
1306 
1307  case GTK_RESPONSE_CLOSE:
1309  return FALSE;
1310 
1311  default:
1312  return TRUE;
1313  }
1314 }
1315 
1316 
1317 static void
1318 gnc_main_window_add_plugin (gpointer plugin,
1319  gpointer window)
1320 {
1321  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
1322  g_return_if_fail (GNC_IS_PLUGIN (plugin));
1323 
1324  ENTER(" ");
1325  gnc_plugin_add_to_window (GNC_PLUGIN (plugin),
1326  GNC_MAIN_WINDOW (window),
1327  window_type);
1328  LEAVE(" ");
1329 }
1330 
1331 static void
1332 gnc_main_window_remove_plugin (gpointer plugin,
1333  gpointer window)
1334 {
1335  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
1336  g_return_if_fail (GNC_IS_PLUGIN (plugin));
1337 
1338  ENTER(" ");
1339  gnc_plugin_remove_from_window (GNC_PLUGIN (plugin),
1340  GNC_MAIN_WINDOW (window),
1341  window_type);
1342  LEAVE(" ");
1343 }
1344 
1345 
1346 static gboolean
1347 gnc_main_window_timed_quit (gpointer dummy)
1348 {
1349  if (gnc_file_save_in_progress())
1350  return TRUE;
1351 
1352  gnc_shutdown (0);
1353  return FALSE;
1354 }
1355 
1356 static gboolean
1357 gnc_main_window_quit(GncMainWindow *window)
1358 {
1359  QofSession *session;
1360  gboolean needs_save, do_shutdown = TRUE;
1361  if (gnc_current_session_exist())
1362  {
1363  session = gnc_get_current_session();
1364  needs_save =
1366  !gnc_file_save_in_progress();
1367  do_shutdown = !needs_save ||
1368  (needs_save &&
1369  !gnc_main_window_prompt_for_save(GTK_WIDGET(window)));
1370  }
1371  if (do_shutdown)
1372  {
1373  GList *w, *next;
1374 
1375  /* This is not a typical list iteration. There is a possibility
1376  * that the window may be removed from the active_windows list so
1377  * we have to cache the 'next' pointer before executing any code
1378  * in the loop. */
1379  for (w = active_windows; w; w = next)
1380  {
1381  GncMainWindowPrivate *priv;
1382  GncMainWindow *window = static_cast<GncMainWindow*>(w->data);
1383 
1384  next = g_list_next (w);
1385 
1386  window->window_quitting = TRUE; //set window_quitting on all windows
1387 
1388  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1389 
1390  // if there are no pages destroy window
1391  if (priv->installed_pages == NULL)
1392  gtk_widget_destroy (GTK_WIDGET(window));
1393  }
1394  /* remove the preference callbacks from the main window */
1395  gnc_main_window_remove_prefs (window);
1396  g_timeout_add(250, gnc_main_window_timed_quit, nullptr);
1397  return TRUE;
1398  }
1399  return FALSE;
1400 }
1401 
1402 static gboolean
1403 gnc_main_window_delete_event (GtkWidget *window,
1404  GdkEvent *event,
1405  gpointer user_data)
1406 {
1407  static gboolean already_dead = FALSE;
1408 
1409  if (already_dead)
1410  return TRUE;
1411 
1412  if (gnc_list_length_cmp (active_windows, 1) > 0)
1413  {
1414  gint response;
1415  GtkWidget *dialog;
1416  gchar *message = _("This window is closing and will not be restored.");
1417 
1418  dialog = gtk_message_dialog_new (GTK_WINDOW (window),
1419  GTK_DIALOG_DESTROY_WITH_PARENT,
1420  GTK_MESSAGE_QUESTION,
1421  GTK_BUTTONS_NONE,
1422  "%s", _("Close Window?"));
1423  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
1424  "%s", message);
1425 
1426  gtk_dialog_add_buttons (GTK_DIALOG(dialog),
1427  _("_Cancel"), GTK_RESPONSE_CANCEL,
1428  _("_OK"), GTK_RESPONSE_YES,
1429  (gchar *)NULL);
1430  gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_YES);
1431  response = gnc_dialog_run (GTK_DIALOG(dialog), GNC_PREF_WARN_CLOSING_WINDOW_QUESTION);
1432  gtk_widget_destroy (dialog);
1433 
1434  if (response == GTK_RESPONSE_CANCEL)
1435  return TRUE;
1436  }
1437 
1438  if (!gnc_main_window_finish_pending(GNC_MAIN_WINDOW(window)))
1439  {
1440  /* Don't close the window. */
1441  return TRUE;
1442  }
1443 
1444  if (gnc_list_length_cmp (active_windows, 1) > 0)
1445  return FALSE;
1446 
1447  already_dead = gnc_main_window_quit(GNC_MAIN_WINDOW(window));
1448  return TRUE;
1449 }
1450 
1451 
1471 static void
1472 gnc_main_window_event_handler (QofInstance *entity, QofEventId event_type,
1473  gpointer user_data, gpointer event_data)
1474 {
1475  GncMainWindow *window;
1476  GncMainWindowPrivate *priv;
1477  GncPluginPage *page;
1478  GList *item, *next;
1479 
1480  /* hard failures */
1481  g_return_if_fail(GNC_IS_MAIN_WINDOW(user_data));
1482 
1483  /* soft failures */
1484  if (!QOF_CHECK_TYPE(entity, QOF_ID_BOOK))
1485  return;
1486  if (event_type != QOF_EVENT_DESTROY)
1487  return;
1488 
1489  ENTER("entity %p, event %d, window %p, event data %p",
1490  entity, event_type, user_data, event_data);
1491  window = GNC_MAIN_WINDOW(user_data);
1492  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1493 
1494  /* This is not a typical list iteration. We're removing while
1495  * we iterate, so we have to cache the 'next' pointer before
1496  * executing any code in the loop. */
1497  for (item = priv->installed_pages; item; item = next)
1498  {
1499  next = g_list_next(item);
1500  page = GNC_PLUGIN_PAGE(item->data);
1501  if (gnc_plugin_page_has_book (page, (QofBook *)entity))
1503  }
1504 
1505  if (GTK_IS_WIDGET(window) && window->window_quitting)
1506  gtk_widget_destroy (GTK_WIDGET(window));
1507 
1508  LEAVE(" ");
1509 }
1510 
1511 
1528 static gchar *
1529 gnc_main_window_generate_title (GncMainWindow *window)
1530 {
1531  GncMainWindowPrivate *priv;
1532  GncPluginPage *page;
1533  QofBook *book;
1534  gboolean immutable;
1535  gchar *filename = nullptr;
1536  const gchar *uri = nullptr;
1537  const gchar *dirty = "";
1538  const gchar *readonly_text = nullptr;
1539  gchar *readonly;
1540  gchar *title;
1541 
1542  if (gnc_current_session_exist())
1543  {
1544  uri = qof_session_get_url (gnc_get_current_session ());
1545  book = gnc_get_current_book();
1546  if (qof_book_session_not_saved (book))
1547  dirty = "*";
1548  if (qof_book_is_readonly(book))
1549  {
1550  /* Translators: This string is shown in the window title if this
1551  document is, well, read-only. */
1552  readonly_text = _("(read-only)");
1553  }
1554  }
1555  readonly = (readonly_text != nullptr)
1556  ? g_strdup_printf(" %s", readonly_text)
1557  : g_strdup("");
1558 
1559  if (!uri || g_strcmp0 (uri, "") == 0)
1560  filename = g_strdup(_("Unsaved Book"));
1561  else
1562  {
1563  if (gnc_uri_targets_local_fs (uri))
1564  {
1565  /* The filename is a true file.
1566  The Gnome HIG 2.0 recommends only the file name (no path) be used. (p15) */
1567  gchar *path = gnc_uri_get_path ( uri );
1568  filename = g_path_get_basename ( path );
1569  g_free ( path );
1570  }
1571  else
1572  {
1573  /* The filename is composed of database connection parameters.
1574  For this we will show access_method://username@database[:port] */
1575  filename = gnc_uri_normalize_uri (uri, FALSE);
1576  }
1577  }
1578 
1579  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1580  page = priv->current_page;
1581  if (page)
1582  {
1583  /* The Gnome HIG 2.0 recommends the application name not be used. (p16)
1584  but several developers prefer to use it anyway. */
1585  title = g_strdup_printf("%s%s%s - %s - GnuCash", dirty, filename, readonly,
1587  }
1588  else
1589  {
1590  title = g_strdup_printf("%s%s%s - GnuCash", dirty, filename, readonly);
1591  }
1592  /* Update the menus based upon whether this is an "immutable" page. */
1593  immutable = page &&
1594  g_object_get_data (G_OBJECT (page), PLUGIN_PAGE_IMMUTABLE);
1595  gnc_plugin_set_actions_enabled (G_ACTION_MAP(window),
1596  immutable_page_actions,
1597  !immutable);
1598  /* Trigger sensitivity updtates of other actions such as Save/Revert */
1599  g_signal_emit_by_name (window, "page_changed", page);
1600  g_free( filename );
1601  g_free(readonly);
1602 
1603  return title;
1604 }
1605 
1606 
1616 static void
1617 gnc_main_window_update_title (GncMainWindow *window)
1618 {
1619  gchar *title;
1620 
1621  title = gnc_main_window_generate_title(window);
1622  gtk_window_set_title(GTK_WINDOW(window), title);
1623  g_free(title);
1624 }
1625 
1626 static void
1627 gnc_main_window_update_all_titles (void)
1628 {
1629  g_list_foreach(active_windows,
1630  (GFunc)gnc_main_window_update_title,
1631  nullptr);
1632 }
1633 
1634 /* Callback function invoked when the user clicks on a GtkNotebook tab.
1635  *
1636  * This function is needed to make it possible to close a tab
1637  * when it's clicked using the middle mouse button;
1638  * there does not seem to be a way to do this with GtkNotebook natively.
1639  *
1640  * @param widget The event box in the tab, which was clicked.
1641  *
1642  * @param event The event parameter describing where on the screen
1643  * the mouse was pointing when clicked, type of click, modifiers,
1644  * etc.
1645  *
1646  * @param page This is the GncPluginPage corresponding to the tab.
1647  *
1648  * @return Returns TRUE if this was a middle-click, meaning Gnucash
1649  * handled the click.
1650  */
1651 static gboolean
1652 gnc_tab_clicked_cb(GtkWidget *widget, GdkEventButton *event, GncPluginPage *page) {
1653  if (event->type == GDK_BUTTON_PRESS && event->button == 2)
1654  {
1656  return TRUE;
1657  }
1658  return FALSE;
1659 }
1660 
1661 static void
1662 gnc_main_window_book_dirty_cb (QofBook *book,
1663  gboolean dirty,
1664  gpointer user_data)
1665 {
1666  gnc_main_window_update_all_titles();
1667 
1668  /* Auto-save feature */
1669  gnc_autosave_dirty_handler(book, dirty);
1670 }
1671 
1672 static void
1673 gnc_main_window_attach_to_book (QofSession *session)
1674 {
1675  QofBook *book;
1676 
1677  g_return_if_fail(session);
1678 
1679  book = qof_session_get_book(session);
1680  qof_book_set_dirty_cb(book, gnc_main_window_book_dirty_cb, nullptr);
1681  gnc_main_window_update_all_titles();
1682 #ifndef MAC_INTEGRATION
1683  gnc_main_window_update_all_menu_items();
1684 #endif
1685 }
1686 
1687 static guint gnc_statusbar_notification_messageid = 0;
1688 //#define STATUSBAR_NOTIFICATION_AUTOREMOVAL
1689 #ifdef STATUSBAR_NOTIFICATION_AUTOREMOVAL
1690 /* Removes the statusbar notification again that has been pushed to the
1691  * statusbar by generate_statusbar_lastmodified_message. */
1692 static gboolean statusbar_notification_off(gpointer user_data_unused)
1693 {
1694  GncMainWindow *mainwindow = GNC_MAIN_WINDOW (gnc_ui_get_main_window (nullptr));
1695  //g_warning("statusbar_notification_off\n");
1696  if (gnc_statusbar_notification_messageid == 0)
1697  return FALSE;
1698 
1699  if (mainwindow)
1700  {
1701  GtkWidget *statusbar = gnc_main_window_get_statusbar(GNC_WINDOW(mainwindow));
1702  gtk_statusbar_remove(GTK_STATUSBAR(statusbar), 0, gnc_statusbar_notification_messageid);
1703  gnc_statusbar_notification_messageid = 0;
1704  }
1705  else
1706  {
1707  g_warning("oops, no GncMainWindow obtained\n");
1708  }
1709  return FALSE; // should not be called again
1710 }
1711 #endif // STATUSBAR_NOTIFICATION_AUTOREMOVAL
1712 
1713 /* Creates a statusbar message stating the last modification time of the opened
1714  * data file. */
1715 static gchar *generate_statusbar_lastmodified_message()
1716 {
1717  gchar *message = nullptr;
1718  const gchar *uri = nullptr;
1719 
1720  if (gnc_current_session_exist())
1721  {
1722  uri = qof_session_get_url (gnc_get_current_session ());
1723  }
1724 
1725  if (!(uri && strlen (uri)))
1726  return nullptr;
1727  else
1728  {
1729  if (gnc_uri_targets_local_fs (uri))
1730  {
1731  /* The filename is a true file. */
1732  gchar *filepath = gnc_uri_get_path ( uri );
1733  gchar *filename = g_path_get_basename ( filepath );
1734  GFile *file = g_file_new_for_uri (uri);
1735  GFileInfo *info = g_file_query_info (file,
1736  G_FILE_ATTRIBUTE_TIME_MODIFIED,
1737  G_FILE_QUERY_INFO_NONE,
1738  NULL, NULL);
1739 
1740  if (info && g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED))
1741  {
1742  // Access the mtime information through stat(2)
1743  struct stat statbuf;
1744  int r = stat(filepath, &statbuf);
1745  if (r == 0)
1746  {
1747  /* Translators: This is the date and time that is shown in
1748  the status bar after opening a file: The date and time of
1749  last modification. The string is a format string using
1750  boost::date_time's format flags, see the boost docs for an
1751  explanation of the modifiers. */
1752  char *time_string = gnc_print_time64(statbuf.st_mtime,
1753  _("Last modified on %a, %b %d, %Y at %I:%M %p"));
1754  //g_warning("got time %ld, str=%s\n", mtime, time_string);
1755  /* Translators: This message appears in the status bar after opening the file. */
1756  message = g_strdup_printf(_("File %s opened. %s"),
1757  filename, time_string);
1758  free(time_string);
1759  }
1760  else
1761  {
1762  g_warning("Unable to read mtime for file %s\n", filepath);
1763  // message is still nullptr
1764  }
1765  }
1766  g_free(filename);
1767  g_free(filepath);
1768  g_object_unref (info);
1769  g_object_unref (file);
1770  }
1771  // If the URI is not a file but a database, we can maybe also show
1772  // something useful, but I have no idea how to obtain this information.
1773  }
1774  return message;
1775 }
1776 
1777 static void
1778 statusbar_notification_lastmodified()
1779 {
1780  // First look up the first GncMainWindow to set the statusbar there
1781  GList *iter;
1782  GtkWidget *widget = nullptr;
1783  for (iter = active_windows; iter && !(widget && GNC_IS_MAIN_WINDOW(widget));
1784  iter = g_list_next(iter))
1785  {
1786  widget = static_cast<GtkWidget*>(iter->data);
1787  }
1788  if (widget && GNC_IS_MAIN_WINDOW(widget))
1789  {
1790  // Ok, we found a mainwindow where we can set a statusbar message
1791  GncMainWindow *mainwindow = GNC_MAIN_WINDOW(widget);
1792  GtkWidget *statusbar = gnc_main_window_get_statusbar(GNC_WINDOW(mainwindow));
1793 
1794  gchar *msg = generate_statusbar_lastmodified_message();
1795  if (msg)
1796  {
1797  gnc_statusbar_notification_messageid = gtk_statusbar_push(GTK_STATUSBAR(statusbar), 0, msg);
1798  }
1799  g_free(msg);
1800 
1801 #ifdef STATUSBAR_NOTIFICATION_AUTOREMOVAL
1802  // Also register a timeout callback to remove that statusbar
1803  // notification again after 10 seconds
1804  g_timeout_add(10 * 1000, statusbar_notification_off, nullptr); // maybe not needed anyway?
1805 #endif
1806  }
1807  else
1808  {
1809  g_warning("uh oh, no GNC_IS_MAIN_WINDOW\n");
1810  }
1811 }
1812 
1813 
1817 {
1819  gchar *action_name;
1820 
1822  gchar *label;
1823 
1825  gboolean visible;
1826 
1828  gint index;
1829 };
1830 
1831 #ifndef MAC_INTEGRATION
1832 
1845 static void
1846 gnc_main_window_update_one_menu_action (GncMainWindow *window,
1847  struct menu_update *data)
1848 {
1849  GncMainWindowPrivate *priv;
1850  GncMenuModelSearch *gsm = g_new0 (GncMenuModelSearch, 1);
1851  GMenuItem *item;
1852  gint pos;
1853 
1854  ENTER("window %p, action %s, label %s, index %d, visible %d", window,
1855  data->action_name, data->label, data->index, data->visible);
1856 
1857  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1858 
1859  gsm->search_action_label = nullptr;
1860  gsm->search_action_name = "WindowsPlaceholder1"; // placeholder
1861  gsm->search_action_target = nullptr;
1862 
1863  if (!gnc_menubar_model_find_item (priv->menubar_model, gsm))
1864  {
1865  LEAVE("Could not find placeholder 'WindowsPlaceholder1' for windows entries");
1866  g_free (gsm);
1867  return;
1868  }
1869 
1870  pos = gsm->index + data->index + 1;
1871 
1872  if (!data->visible)
1873  {
1874  if (pos < g_menu_model_get_n_items (gsm->model))
1875  g_menu_remove (G_MENU(gsm->model), pos);
1876 
1877  g_free (gsm);
1878  LEAVE(" ");
1879  return;
1880  }
1881 
1882  item = g_menu_item_new (data->label, "mainwin.WindowAction");
1883  g_menu_item_set_attribute (item, G_MENU_ATTRIBUTE_TARGET, "i", data->index);
1884 
1885  if (pos < g_menu_model_get_n_items (gsm->model))
1886  g_menu_remove (G_MENU(gsm->model), pos);
1887  g_menu_insert_item (G_MENU(gsm->model), pos, item);
1888  g_object_unref (item);
1889 
1890  g_free (gsm);
1891  LEAVE(" ");
1892 }
1893 
1906 static void
1907 gnc_main_window_update_radio_button (GncMainWindow *window)
1908 {
1909  GAction *action;
1910  gsize index;
1911 
1912  ENTER("window %p", window);
1913 
1914  /* Show the new entry in all windows. */
1915  index = g_list_index (active_windows, window);
1916 
1917  if (index >= gnc_main_window_max_number)
1918  {
1919  LEAVE("window %" G_GSIZE_FORMAT ", only %d actions", index, gnc_main_window_max_number);
1920  return;
1921  }
1922 
1923  action = g_action_map_lookup_action (G_ACTION_MAP(window),
1924  "WindowAction");
1925 
1926  /* Block the signal so as not to affect window ordering (top to
1927  * bottom) on the screen */
1928  g_signal_handlers_block_by_func (G_OBJECT(action),
1929  (gpointer)gnc_main_window_cmd_window_raise,
1930  window);
1931 
1932  DEBUG("blocked signal on action %p, window %p", action, window);
1933  g_action_change_state (G_ACTION(action), g_variant_new_int32 (index));
1934 
1935  g_signal_handlers_unblock_by_func (G_OBJECT(action),
1936  (gpointer)gnc_main_window_cmd_window_raise,
1937  window);
1938  LEAVE(" ");
1939 }
1940 
1953 static void
1954 gnc_main_window_update_menu_item (GncMainWindow *window)
1955 {
1956  struct menu_update data;
1957  gchar **strings, *title, *expanded;
1958  gsize index;
1959 
1960  ENTER("window %p", window);
1961 
1962  index = g_list_index (active_windows, window);
1963 
1965  {
1966  LEAVE("skip window %" G_GSIZE_FORMAT " (only %d entries)", index, gnc_main_window_max_number);
1967  return;
1968  }
1969 
1970  /* Figure out the label name. Add the accelerator if possible. */
1971  title = gnc_main_window_generate_title (window);
1972  strings = g_strsplit (title, "_", 0);
1973  g_free (title);
1974  expanded = g_strjoinv ("__", strings);
1976  {
1977  data.label = g_strdup_printf ("_%" G_GSIZE_FORMAT " %s", (index + 1) % 10, expanded);
1978  g_free (expanded);
1979  }
1980  else
1981  {
1982  data.label = expanded;
1983  }
1984  g_strfreev (strings);
1985 
1986  data.visible = TRUE;
1987  data.action_name = g_strdup_printf ("Window%" G_GSIZE_FORMAT "Action", index);
1988  data.index = index;
1989 
1990  g_list_foreach (active_windows,
1991  (GFunc)gnc_main_window_update_one_menu_action,
1992  &data);
1993 
1994  g_free (data.action_name);
1995  g_free (data.label);
1996 
1997  LEAVE(" ");
1998 }
1999 #endif /* !MAC_INTEGRATION */
2000 
2009 #ifndef MAC_INTEGRATION
2010 static void
2011 gnc_main_window_update_all_menu_items (void)
2012 {
2013  struct menu_update data;
2014 
2015  ENTER("");
2016  /* First update the entries for all existing windows */
2017  g_list_foreach (active_windows,
2018  (GFunc)gnc_main_window_update_menu_item,
2019  nullptr);
2020 
2021  g_list_foreach (active_windows,
2022  (GFunc)gnc_main_window_update_radio_button,
2023  nullptr);
2024 
2025  /* Now hide any entries that aren't being used. */
2026  data.visible = FALSE;
2027  // need i to descend from gnc_main_window_max_number
2028  for (gsize i = gnc_main_window_max_number - 1; i > 0 && i >= g_list_length (active_windows); i--)
2029  {
2030  data.index = i;
2031  data.action_name = g_strdup_printf ("Window%dAction", data.index);
2032  data.label = g_strdup_printf ("mywin%" G_GSIZE_FORMAT, i % 10);
2033 
2034  g_list_foreach (active_windows,
2035  (GFunc)gnc_main_window_update_one_menu_action,
2036  &data);
2037 
2038  g_free (data.action_name);
2039  g_free (data.label);
2040  }
2041  LEAVE(" ");
2042 }
2043 #endif /* !MAC_INTEGRATION */
2044 
2056 static void
2057 gnc_main_window_update_tab_close_one_page (GncPluginPage *page,
2058  gpointer user_data)
2059 {
2060  auto new_value{static_cast<gboolean*>(user_data)};
2061  ENTER("page %p, visible %d", page, *new_value);
2062  auto close_button{static_cast<GtkWidget*>(g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON))};
2063  if (!close_button)
2064  {
2065  LEAVE("no close button");
2066  return;
2067  }
2068 
2069  if (*new_value)
2070  gtk_widget_show (close_button);
2071  else
2072  gtk_widget_hide (close_button);
2073  LEAVE(" ");
2074 }
2075 
2076 
2089 static void
2090 gnc_main_window_update_tab_close (gpointer prefs, gchar *pref, gpointer user_data)
2091 {
2092  gboolean new_value;
2093 
2094  ENTER(" ");
2095  new_value = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SHOW_CLOSE_BUTTON);
2097  gnc_main_window_update_tab_close_one_page,
2098  &new_value);
2099  LEAVE(" ");
2100 }
2101 
2102 
2111 static void
2112 gnc_main_window_update_tab_color_one_page (GncPluginPage *page,
2113  gpointer user_data)
2114 {
2115  const gchar *color_string;
2116 
2117  ENTER("page %p", page);
2118  color_string = gnc_plugin_page_get_page_color(page);
2119  main_window_update_page_color (page, color_string);
2120  LEAVE(" ");
2121 }
2122 
2123 
2134 static void
2135 gnc_main_window_update_tab_color (gpointer gsettings, gchar *pref, gpointer user_data)
2136 {
2137  ENTER(" ");
2138  g_return_if_fail(GNC_IS_MAIN_WINDOW(user_data));
2139  auto window{static_cast<GncMainWindow*>(user_data)};
2140  auto priv{GNC_MAIN_WINDOW_GET_PRIVATE(window)};
2141  if (g_strcmp0 (GNC_PREF_TAB_COLOR, pref) == 0)
2142  priv->show_color_tabs = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_COLOR);
2143  gnc_main_window_foreach_page (gnc_main_window_update_tab_color_one_page, window);
2144  LEAVE(" ");
2145 }
2146 
2147 
2151 typedef struct
2152 {
2153  gint tab_width;
2154  gboolean tabs_left_right;
2155 } TabWidth;
2156 
2157 static TabWidth *
2158 populate_tab_width_struct (void)
2159 {
2160  TabWidth *tw;
2161 
2162  tw = g_new0 (TabWidth, 1);
2163  tw->tab_width = gnc_prefs_get_float (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_WIDTH);
2164  tw->tabs_left_right = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT) ||
2165  gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT);
2166 
2167  return tw;
2168 }
2169 
2191 static void
2192 gnc_main_window_set_tab_ellipsize (GtkWidget *label, gint tab_width, gboolean tab_left_right)
2193 {
2194  const gchar *lab_text = gtk_label_get_text (GTK_LABEL(label));
2195 
2196  if (tab_width != 0)
2197  {
2198  gint text_length = g_utf8_strlen (lab_text, -1);
2199  if (text_length < tab_width)
2200  {
2201  if (tab_left_right) // tabs position is left or right
2202  gtk_label_set_width_chars (GTK_LABEL(label), tab_width);
2203  else // tabs position is top or bottom
2204  gtk_label_set_width_chars (GTK_LABEL(label), text_length);
2205 
2206  gtk_label_set_ellipsize (GTK_LABEL(label), PANGO_ELLIPSIZE_NONE);
2207  }
2208  else
2209  {
2210  gtk_label_set_width_chars (GTK_LABEL(label), tab_width);
2211  gtk_label_set_ellipsize (GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
2212  }
2213  }
2214  else
2215  {
2216  gtk_label_set_width_chars (GTK_LABEL(label), 15);
2217  gtk_label_set_ellipsize (GTK_LABEL(label), PANGO_ELLIPSIZE_NONE);
2218  }
2219 }
2220 
2221 
2232 static void
2233 gnc_main_window_update_tab_width_one_page (GncPluginPage *page,
2234  gpointer user_data)
2235 {
2236  auto tw{static_cast<TabWidth*>(user_data)};
2237 
2238  ENTER("page %p, tab width %d, tabs on left or right %d",
2239  page, tw->tab_width, tw->tabs_left_right);
2240 
2241  auto label{static_cast<GtkWidget *>(g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_TAB_LABEL))};
2242  if (!label)
2243  {
2244  LEAVE("no label");
2245  return;
2246  }
2247  gnc_main_window_set_tab_ellipsize (label, tw->tab_width, tw->tabs_left_right);
2248  LEAVE(" ");
2249 }
2250 
2251 
2264 static void
2265 gnc_main_window_update_tab_width (gpointer prefs, gchar *pref, gpointer user_data)
2266 {
2267  TabWidth *tw;
2268 
2269  ENTER(" ");
2270 
2271  tw = populate_tab_width_struct ();
2272 
2273  gnc_main_window_foreach_page (gnc_main_window_update_tab_width_one_page, tw);
2274  g_free (tw);
2275 
2276  LEAVE(" ");
2277 }
2278 
2279 
2280 /************************************************************
2281  * Tab Label Implementation *
2282  ************************************************************/
2283 static gboolean
2284 main_window_find_tab_items (GncMainWindow *window,
2285  GncPluginPage *page,
2286  GtkWidget **label_p,
2287  GtkWidget **entry_p)
2288 {
2289  GncMainWindowPrivate *priv;
2290  GtkWidget *tab_hbox, *widget, *tab_widget;
2291  GList *children, *tmp;
2292 
2293  ENTER("window %p, page %p, label_p %p, entry_p %p",
2294  window, page, label_p, entry_p);
2295  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2296  *label_p = *entry_p = nullptr;
2297 
2298  if (!page->notebook_page)
2299  {
2300  LEAVE("invalid notebook_page");
2301  return FALSE;
2302  }
2303 
2304  tab_widget = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
2305  page->notebook_page);
2306 
2307  // Walk through children to find the box containing label+entry
2308  tab_hbox = tab_widget;
2309  while (tab_hbox) {
2310  if (g_strcmp0(gtk_widget_get_name(tab_hbox), "tab-content") == 0) {
2311  break;
2312  }
2313  GList* _children = gtk_container_get_children(GTK_CONTAINER(tab_hbox));
2314  tab_hbox = _children ? GTK_WIDGET(_children->data) : nullptr;
2315  g_list_free(_children);
2316  }
2317 
2318  if (!GTK_IS_BOX(tab_hbox))
2319  {
2320  PWARN ("Unknown widget for tab label %p", tab_widget);
2321  return FALSE;
2322  }
2323 
2324  children = gtk_container_get_children(GTK_CONTAINER(tab_hbox));
2325  for (tmp = children; tmp; tmp = g_list_next(tmp))
2326  {
2327  widget = static_cast<GtkWidget*>(tmp->data);
2328  if (GTK_IS_LABEL(widget))
2329  {
2330  *label_p = widget;
2331  }
2332  else if (GTK_IS_ENTRY(widget))
2333  {
2334  *entry_p = widget;
2335  }
2336  }
2337  g_list_free(children);
2338 
2339  LEAVE("label %p, entry %p", *label_p, *entry_p);
2340  return (*label_p && *entry_p);
2341 }
2342 
2343 static gboolean
2344 main_window_find_tab_widget (GncMainWindow *window,
2345  GncPluginPage *page,
2346  GtkWidget **widget_p)
2347 {
2348  GncMainWindowPrivate *priv;
2349 
2350  ENTER("window %p, page %p, widget %p",
2351  window, page, widget_p);
2352  *widget_p = nullptr;
2353 
2354  if (!page->notebook_page)
2355  {
2356  LEAVE("invalid notebook_page");
2357  return FALSE;
2358  }
2359 
2360  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2361  *widget_p = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
2362  page->notebook_page);
2363 
2364  LEAVE("widget %p", *widget_p);
2365  return TRUE;
2366 }
2367 
2368 void
2370  const gchar *long_name_in)
2371 {
2372  GtkWidget *tab_widget;
2373 
2374  ENTER(" ");
2375 
2376  if ((long_name_in == nullptr) || (*long_name_in == '\0'))
2377  {
2378  LEAVE("no string");
2379  return;
2380  }
2381  gchar *long_name = g_strstrip (g_strdup (long_name_in));
2382  const gchar *old_long_name = gnc_plugin_page_get_page_long_name (page);
2383 
2384  /* Optimization, if the long_name hasn't changed, don't update X. */
2385  if (*long_name == '\0' || strcmp (long_name, old_long_name) == 0)
2386  {
2387  g_free (long_name);
2388  LEAVE("empty string or name unchanged");
2389  return;
2390  }
2391 
2392  gnc_plugin_page_set_page_long_name (page, long_name);
2393 
2394  GncMainWindow *window = GNC_MAIN_WINDOW(page->window);
2395  if (!window)
2396  {
2397  g_free (long_name);
2398  LEAVE("no window widget available");
2399  return;
2400  }
2401 
2402  /* Update the notebook tab tooltip */
2403  if (main_window_find_tab_widget (window, page, &tab_widget))
2404  gtk_widget_set_tooltip_text (tab_widget, long_name);
2405 
2406  g_free (long_name);
2407  LEAVE("");
2408 }
2409 
2410 void
2412  const gchar *name_in)
2413 {
2414  GncMainWindow *window;
2415  GncMainWindowPrivate *priv;
2416  GtkWidget *label, *entry;
2417  gchar *name;
2418  TabWidth *tw;
2419 
2420  ENTER(" ");
2421 
2422  if ((name_in == nullptr) || (*name_in == '\0'))
2423  {
2424  LEAVE("no string");
2425  return;
2426  }
2427  name = g_strstrip(g_strdup(name_in));
2428 
2429  /* Optimization, if the name hasn't changed, don't update X. */
2430  if (*name == '\0' || 0 == strcmp(name, gnc_plugin_page_get_page_name(page)))
2431  {
2432  g_free(name);
2433  LEAVE("empty string or name unchanged");
2434  return;
2435  }
2436 
2437  /* Update the plugin */
2438  gnc_plugin_page_set_page_name(page, name);
2439 
2440  /* Update the notebook tab */
2441  window = GNC_MAIN_WINDOW(page->window);
2442  if (!window)
2443  {
2444  g_free(name);
2445  LEAVE("no window widget available");
2446  return;
2447  }
2448 
2449  if (main_window_find_tab_items(window, page, &label, &entry))
2450  gtk_label_set_text(GTK_LABEL(label), name);
2451 
2452  /* Adjust the label width for new text */
2453  tw = populate_tab_width_struct ();
2454  gnc_main_window_update_tab_width_one_page (page, tw);
2455  g_free (tw);
2456 
2457  /* Update the notebook menu */
2458  if (page->notebook_page)
2459  {
2460  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2461  label = gtk_notebook_get_menu_label (GTK_NOTEBOOK(priv->notebook),
2462  page->notebook_page);
2463  gtk_label_set_text(GTK_LABEL(label), name);
2464  }
2465 
2466  /* Force an update of the window title */
2467  gnc_main_window_update_title(window);
2468  g_free(name);
2469  LEAVE("done");
2470 }
2471 
2472 
2473 void
2475  const gchar *color_in)
2476 {
2477  GncMainWindow *window;
2478  GncMainWindowPrivate *priv;
2479  GtkWidget *tab_widget;
2480  GdkRGBA tab_color;
2481  gchar *color_string = nullptr;
2482  gboolean want_color = FALSE;
2483 
2484  ENTER(" ");
2485  if (color_in)
2486  color_string = g_strstrip(g_strdup(color_in));
2487 
2488  if (color_string && *color_string != '\0')
2489  want_color = TRUE;
2490 
2491  /* Update the plugin */
2492  window = GNC_MAIN_WINDOW(page->window);
2493  if (want_color)
2494  gnc_plugin_page_set_page_color(page, color_string);
2495  else
2496  gnc_plugin_page_set_page_color(page, nullptr);
2497 
2498  /* Update the notebook tab */
2499  main_window_find_tab_widget (window, page, &tab_widget);
2500  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2501 
2502  if (want_color && gdk_rgba_parse(&tab_color, color_string) && priv->show_color_tabs)
2503  {
2504  GtkCssProvider *provider = gtk_css_provider_new();
2505  GtkStyleContext *stylectxt;
2506  gchar *col_str, *widget_css;
2507 
2508  if (!GTK_IS_EVENT_BOX (tab_widget))
2509  {
2510  GtkWidget *event_box = gtk_event_box_new ();
2511  g_object_ref (tab_widget);
2512  gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
2513  page->notebook_page, event_box);
2514  gtk_container_add (GTK_CONTAINER(event_box), tab_widget);
2515  g_object_unref (tab_widget);
2516  tab_widget = event_box;
2517  }
2518 
2519  stylectxt = gtk_widget_get_style_context (GTK_WIDGET (tab_widget));
2520  col_str = gdk_rgba_to_string (&tab_color);
2521  widget_css = g_strconcat ("*{\n background-color:", col_str, ";\n}\n", nullptr);
2522 
2523  gtk_css_provider_load_from_data (provider, widget_css, -1, nullptr);
2524  gtk_style_context_add_provider (stylectxt, GTK_STYLE_PROVIDER (provider),
2525  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
2526  g_object_unref (provider);
2527  g_free (col_str);
2528  g_free (widget_css);
2529  }
2530  else
2531  {
2532  if (GTK_IS_EVENT_BOX (tab_widget))
2533  {
2534  GtkWidget *tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
2535  g_object_ref (tab_hbox);
2536  gtk_container_remove (GTK_CONTAINER(tab_widget), tab_hbox);
2537  gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
2538  page->notebook_page, tab_hbox);
2539  g_object_unref (tab_hbox);
2540  }
2541  }
2542  g_free(color_string);
2543  LEAVE("done");
2544 }
2545 
2546 
2547 void
2549  gboolean read_only)
2550 {
2551  GncMainWindow *window;
2552  GtkWidget *tab_widget;
2553  GtkWidget *image = NULL;
2554  GList *children;
2555  gchar *image_name = NULL;
2556  const gchar *icon_name;
2557 
2558  ENTER(" ");
2559 
2560  g_return_if_fail (page && page->window);
2561 
2562  if (!GNC_IS_MAIN_WINDOW (page->window))
2563  return;
2564 
2565  window = GNC_MAIN_WINDOW(page->window);
2566 
2567  /* Get the notebook tab widget */
2568  main_window_find_tab_widget (window, page, &tab_widget);
2569 
2570  if (!tab_widget)
2571  {
2572  LEAVE("no tab widget");
2573  return;
2574  }
2575 
2576  if (GTK_IS_EVENT_BOX(tab_widget))
2577  tab_widget = gtk_bin_get_child (GTK_BIN(tab_widget));
2578 
2579  children = gtk_container_get_children (GTK_CONTAINER(tab_widget));
2580  /* For each, walk the list of container children to get image widget */
2581  for (GList *child = children; child; child = g_list_next (child))
2582  {
2583  GtkWidget *widget = static_cast<GtkWidget*>(child->data);
2584  if (GTK_IS_IMAGE(widget))
2585  image = widget;
2586  }
2587  g_list_free (children);
2588 
2589  if (!image)
2590  {
2591  LEAVE("no image to replace");
2592  return;
2593  }
2594 
2595  g_object_get (image, "icon-name", &image_name, NULL);
2596 
2597  if (read_only)
2598  icon_name = "changes-prevent-symbolic";
2599  else
2600  icon_name = GNC_PLUGIN_PAGE_GET_CLASS(page)->tab_icon;
2601 
2602  if (g_strcmp0 (icon_name, image_name) == 0)
2603  {
2604  LEAVE("page icon the same, no need to replace");
2605  g_free (image_name);
2606  return;
2607  }
2608  gtk_container_remove (GTK_CONTAINER(tab_widget), image);
2609  image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
2610  gtk_widget_show (image);
2611 
2612  gtk_container_add (GTK_CONTAINER(tab_widget), image);
2613  gtk_widget_set_margin_start (GTK_WIDGET(image), 5);
2614  gtk_box_reorder_child (GTK_BOX(tab_widget), image, 0);
2615 
2616  g_free (image_name);
2617  LEAVE("done");
2618 }
2619 
2620 
2621 static void
2622 gnc_main_window_tab_entry_activate (GtkWidget *entry,
2623  GncPluginPage *page)
2624 {
2625  GtkWidget *label, *entry2;
2626 
2627  g_return_if_fail(GTK_IS_ENTRY(entry));
2628  g_return_if_fail(GNC_IS_PLUGIN_PAGE(page));
2629 
2630  ENTER("");
2631  if (!main_window_find_tab_items(GNC_MAIN_WINDOW(page->window),
2632  page, &label, &entry2))
2633  {
2634  LEAVE("can't find required widgets");
2635  return;
2636  }
2637 
2638  main_window_update_page_name(page, gtk_entry_get_text(GTK_ENTRY(entry)));
2639 
2640  gtk_widget_hide(entry);
2641  gtk_widget_show(label);
2642  LEAVE("");
2643 }
2644 
2645 
2646 static gboolean
2647 gnc_main_window_tab_entry_editing_done (GtkWidget *entry,
2648  GncPluginPage *page)
2649 {
2650  ENTER("");
2651  gnc_main_window_tab_entry_activate(entry, page);
2652  LEAVE("");
2653  return FALSE;
2654 }
2655 
2656 static gboolean
2657 gnc_main_window_tab_entry_focus_out_event (GtkWidget *entry,
2658  GdkEvent *event,
2659  GncPluginPage *page)
2660 {
2661  ENTER("");
2662  gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
2663  LEAVE("");
2664  return FALSE;
2665 }
2666 
2667 static gboolean
2668 gnc_main_window_tab_entry_key_press_event (GtkWidget *entry,
2669  GdkEventKey *event,
2670  GncPluginPage *page)
2671 {
2672  if (event->keyval == GDK_KEY_Escape)
2673  {
2674  GtkWidget *label, *entry2;
2675 
2676  g_return_val_if_fail(GTK_IS_ENTRY(entry), FALSE);
2677  g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
2678 
2679  ENTER("");
2680  if (!main_window_find_tab_items(GNC_MAIN_WINDOW(page->window),
2681  page, &label, &entry2))
2682  {
2683  LEAVE("can't find required widgets");
2684  return FALSE;
2685  }
2686 
2687  gtk_entry_set_text(GTK_ENTRY(entry), gtk_label_get_text(GTK_LABEL(label)));
2688  gtk_widget_hide(entry);
2689  gtk_widget_show(label);
2690  LEAVE("");
2691  }
2692  return FALSE;
2693 }
2694 
2695 /************************************************************
2696  * Widget Implementation *
2697  ************************************************************/
2698 
2699 
2700 
2708 static void
2709 gnc_main_window_class_init (GncMainWindowClass *klass)
2710 {
2711  GObjectClass *object_class = G_OBJECT_CLASS (klass);
2712  GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS(klass);
2713 
2714  window_type = g_quark_from_static_string ("gnc-main-window");
2715 
2716  object_class->constructed = gnc_main_window_constructed;
2717  object_class->finalize = gnc_main_window_finalize;
2718 
2719  /* GtkWidget signals */
2720  gtkwidget_class->destroy = gnc_main_window_destroy;
2721 
2733  main_window_signals[PAGE_ADDED] =
2734  g_signal_new ("page_added",
2735  G_OBJECT_CLASS_TYPE (object_class),
2736  G_SIGNAL_RUN_FIRST,
2737  G_STRUCT_OFFSET (GncMainWindowClass, page_added),
2738  nullptr, nullptr,
2739  g_cclosure_marshal_VOID__OBJECT,
2740  G_TYPE_NONE, 1,
2741  G_TYPE_OBJECT);
2742 
2753  main_window_signals[PAGE_CHANGED] =
2754  g_signal_new ("page_changed",
2755  G_OBJECT_CLASS_TYPE (object_class),
2756  G_SIGNAL_RUN_FIRST,
2757  G_STRUCT_OFFSET (GncMainWindowClass, page_changed),
2758  nullptr, nullptr,
2759  g_cclosure_marshal_VOID__OBJECT,
2760  G_TYPE_NONE, 1,
2761  G_TYPE_OBJECT);
2762 
2771  main_window_signals[MENU_CHANGED] =
2772  g_signal_new ("menu_changed",
2773  G_OBJECT_CLASS_TYPE (object_class),
2774  G_SIGNAL_RUN_FIRST,
2775  G_STRUCT_OFFSET (GncMainWindowClass, menu_changed),
2776  nullptr, nullptr,
2777  g_cclosure_marshal_VOID__OBJECT,
2778  G_TYPE_NONE, 1,
2779  G_TYPE_OBJECT);
2780 
2781  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
2782  GNC_PREF_SHOW_CLOSE_BUTTON,
2783  (gpointer)gnc_main_window_update_tab_close,
2784  nullptr);
2785  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
2786  GNC_PREF_TAB_WIDTH,
2787  (gpointer)gnc_main_window_update_tab_width,
2788  nullptr);
2789 
2790  gnc_hook_add_dangler(HOOK_BOOK_SAVED,
2791  (GFunc)gnc_main_window_update_all_titles, nullptr, nullptr);
2792  gnc_hook_add_dangler(HOOK_BOOK_OPENED,
2793  (GFunc)gnc_main_window_attach_to_book, nullptr, nullptr);
2794 
2795 }
2796 
2797 
2803 static void
2804 gnc_main_window_init (GncMainWindow *window)
2805 {
2806  GncMainWindowPrivate *priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2807 
2808  // Set the name for this dialog so it can be easily manipulated with css
2809  gtk_widget_set_name (GTK_WIDGET(window), "gnc-id-main-window");
2810 
2811  priv->event_handler_id =
2812  qof_event_register_handler(gnc_main_window_event_handler, window);
2813 
2814  priv->restoring_pages = FALSE;
2815 
2816  priv->display_item_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, nullptr);
2817 
2818  priv->previous_plugin_page_name = nullptr;
2819  priv->previous_menu_qualifier = nullptr;
2820 
2821  priv->accel_group = gtk_accel_group_new ();
2822  gtk_window_add_accel_group (GTK_WINDOW(window), priv->accel_group);
2823 
2824  /* Get the show_color_tabs value preference */
2825  priv->show_color_tabs = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_COLOR);
2826 
2827  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
2828  GNC_PREF_TAB_COLOR,
2829  (gpointer)gnc_main_window_update_tab_color,
2830  window);
2831 
2832  gnc_main_window_setup_window (window);
2833 }
2834 
2841 static void
2842 gnc_main_window_constructed (GObject *obj)
2843 {
2845 
2846  G_OBJECT_CLASS (gnc_main_window_parent_class)->constructed (obj);
2847 }
2848 
2859 static void
2860 gnc_main_window_finalize (GObject *object)
2861 {
2862  g_return_if_fail (object != nullptr);
2863  g_return_if_fail (GNC_IS_MAIN_WINDOW (object));
2864 
2865  if (active_windows == nullptr)
2866  {
2867  /* Oops. User killed last window and we didn't catch it. */
2868  g_idle_add((GSourceFunc)gnc_shutdown, 0);
2869  }
2870 
2872  G_OBJECT_CLASS (gnc_main_window_parent_class)->finalize (object);
2873 }
2874 
2875 
2876 static void
2877 gnc_main_window_remove_prefs (GncMainWindow *window)
2878 {
2879  // remove the registered preference callbacks setup in this file.
2880  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2881  GNC_PREF_TAB_COLOR,
2882  (gpointer)gnc_main_window_update_tab_color,
2883  window);
2884 
2885  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2886  GNC_PREF_SHOW_CLOSE_BUTTON,
2887  (gpointer)gnc_main_window_update_tab_close,
2888  nullptr);
2889 
2890  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2891  GNC_PREF_TAB_WIDTH,
2892  (gpointer)gnc_main_window_update_tab_width,
2893  nullptr);
2894 
2895  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2896  GNC_PREF_TAB_POSITION_TOP,
2897  (gpointer)gnc_main_window_update_tab_position,
2898  window);
2899 
2900  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2901  GNC_PREF_TAB_POSITION_BOTTOM,
2902  (gpointer)gnc_main_window_update_tab_position,
2903  window);
2904 
2905  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2906  GNC_PREF_TAB_POSITION_LEFT,
2907  (gpointer)gnc_main_window_update_tab_position,
2908  window);
2909 
2910  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2911  GNC_PREF_TAB_POSITION_RIGHT,
2912  (gpointer)gnc_main_window_update_tab_position,
2913  window);
2914 
2915  // remove the registered negative color preference callback.
2916  if (gnc_prefs_get_reg_negative_color_pref_id() > 0 && window->window_quitting)
2917  {
2918  gnc_prefs_remove_cb_by_id (GNC_PREFS_GROUP_GENERAL,
2920  gnc_prefs_set_reg_negative_color_pref_id (0);
2921  }
2922 
2923  // remove the registered auto_raise_lists preference callback.
2924  if (gnc_prefs_get_reg_auto_raise_lists_id() > 0 && window->window_quitting)
2925  {
2926  gnc_prefs_remove_cb_by_id (GNC_PREFS_GROUP_GENERAL_REGISTER,
2928  gnc_prefs_set_reg_auto_raise_lists_id (0);
2929  }
2930 }
2931 
2932 
2933 static void
2934 gnc_main_window_destroy (GtkWidget *widget)
2935 {
2936  GncMainWindow *window;
2937  GncMainWindowPrivate *priv;
2938  GncPluginManager *manager;
2939  GList *plugins;
2940 
2941  g_return_if_fail (widget != nullptr);
2942  g_return_if_fail (GNC_IS_MAIN_WINDOW (widget));
2943 
2944  window = GNC_MAIN_WINDOW (widget);
2945 #ifdef MAC_INTEGRATION
2946  auto entry = g_list_find (active_windows, window);
2947  if (entry && (entry->next || entry->prev))
2948  gnc_quartz_set_menu (GNC_MAIN_WINDOW (entry->next ? entry->next->data : entry->prev->data));
2949 #endif
2950  active_windows = g_list_remove (active_windows, window);
2951 
2952  /* Do these things once */
2953  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2954  if (priv->event_handler_id > 0)
2955  {
2956 
2957  /* Close any pages in this window */
2958  while (priv->current_page)
2960 
2961  if (gnc_window_get_progressbar_window() == GNC_WINDOW(window))
2962  gnc_window_set_progressbar_window(nullptr);
2963 #ifndef MAC_INTEGRATION
2964  /* Update the "Windows" menu in all other windows */
2965  gnc_main_window_update_all_menu_items();
2966 #endif
2967  /* remove the preference callbacks from the main window */
2968  gnc_main_window_remove_prefs (window);
2969 
2971  priv->event_handler_id = 0;
2972 
2973  g_hash_table_destroy (priv->display_item_hash);
2974 
2975  /* GncPluginManager stuff */
2976  manager = gnc_plugin_manager_get ();
2977  plugins = gnc_plugin_manager_get_plugins (manager);
2978  g_list_foreach (plugins, gnc_main_window_remove_plugin, window);
2979  g_list_free (plugins);
2980  }
2981 
2982  GTK_WIDGET_CLASS (gnc_main_window_parent_class)->destroy (widget);
2983 }
2984 
2985 
2986 static gboolean
2987 gnc_main_window_key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
2988 {
2989  GncMainWindowPrivate *priv;
2990  GdkModifierType modifiers;
2991 
2992  g_return_val_if_fail (GNC_IS_MAIN_WINDOW(widget), FALSE);
2993 
2994  priv = GNC_MAIN_WINDOW_GET_PRIVATE(widget);
2995 
2996  modifiers = gtk_accelerator_get_default_mod_mask ();
2997 
2998  if ((event->state & modifiers) == (GDK_CONTROL_MASK | GDK_MOD1_MASK)) // Ctrl+Alt+
2999  {
3000  const gchar *account_key = C_ ("lower case key for short cut to 'Accounts'", "a");
3001  guint account_keyval = gdk_keyval_from_name (account_key);
3002 
3003  if ((account_keyval == event->keyval) || (account_keyval == gdk_keyval_to_lower (event->keyval)))
3004  {
3005  gint page = 0;
3006 
3007  for (GList *item = priv->installed_pages; item; item = g_list_next (item))
3008  {
3009  const gchar *pname = gnc_plugin_page_get_plugin_name (GNC_PLUGIN_PAGE(item->data));
3010 
3011  if (g_strcmp0 (pname, "GncPluginPageAccountTree") == 0)
3012  {
3013  gtk_notebook_set_current_page (GTK_NOTEBOOK(priv->notebook), page);
3014  return TRUE;
3015  }
3016  page++;
3017  }
3018  }
3019  else if ((GDK_KEY_Menu == event->keyval) || (GDK_KEY_space == event->keyval))
3020  {
3021  GList *menu = gtk_menu_get_for_attach_widget (GTK_WIDGET(priv->notebook));
3022 
3023  if (menu)
3024  {
3025  gtk_menu_popup_at_widget (GTK_MENU(menu->data),
3026  GTK_WIDGET(priv->notebook),
3027  GDK_GRAVITY_SOUTH,
3028  GDK_GRAVITY_SOUTH,
3029  NULL);
3030  return TRUE;
3031  }
3032  }
3033  }
3034  return FALSE;
3035 }
3036 
3037 
3038 /* Create a new gnc main window plugin.
3039  */
3040 GncMainWindow *
3042 {
3043  auto window{static_cast<GncMainWindow*>(g_object_new (GNC_TYPE_MAIN_WINDOW, nullptr))};
3044  gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
3045 
3046  auto old_window = gnc_ui_get_main_window (nullptr);
3047  if (old_window)
3048  {
3049  gint width, height;
3050  gtk_window_get_size (old_window, &width, &height);
3051  gtk_window_resize (GTK_WINDOW (window), width, height);
3052  if ((gdk_window_get_state((gtk_widget_get_window (GTK_WIDGET(old_window))))
3053  & GDK_WINDOW_STATE_MAXIMIZED) != 0)
3054  {
3055  gtk_window_maximize (GTK_WINDOW (window));
3056  }
3057  }
3058  active_windows = g_list_append (active_windows, window);
3059  gnc_main_window_update_title(window);
3060  window->window_quitting = FALSE;
3061  window->just_plugin_prefs = FALSE;
3062 #ifdef MAC_INTEGRATION
3063  gnc_quartz_set_menu(window);
3064 #else
3065  gnc_main_window_update_all_menu_items();
3066 #endif
3067  gnc_engine_add_commit_error_callback( gnc_main_window_engine_commit_error_callback, window );
3068 
3069  // set up a callback for notebook navigation
3070  g_signal_connect (G_OBJECT(window), "key-press-event",
3071  G_CALLBACK(gnc_main_window_key_press_event),
3072  NULL);
3073 
3074  return window;
3075 }
3076 
3077 /************************************************************
3078  * Utility Functions *
3079  ************************************************************/
3080 
3081 static void
3082 gnc_main_window_engine_commit_error_callback( gpointer data,
3083  QofBackendError errcode )
3084 {
3085  GncMainWindow* window = GNC_MAIN_WINDOW(data);
3086  GtkWidget* dialog;
3087  const gchar *reason = _("Unable to save to database.");
3088  if ( errcode == ERR_BACKEND_READONLY )
3089  reason = _("Unable to save to database: Book is marked read-only.");
3090  dialog = gtk_message_dialog_new( GTK_WINDOW(window),
3091  GTK_DIALOG_DESTROY_WITH_PARENT,
3092  GTK_MESSAGE_ERROR,
3093  GTK_BUTTONS_CLOSE,
3094  "%s",
3095  reason );
3096  gtk_dialog_run(GTK_DIALOG (dialog));
3097  gtk_widget_destroy(dialog);
3098 
3099 }
3100 
3118 static void
3119 gnc_main_window_connect (GncMainWindow *window,
3120  GncPluginPage *page,
3121  GtkWidget *tab_hbox,
3122  GtkWidget *menu_label)
3123 {
3124  GncMainWindowPrivate *priv;
3125  GtkNotebook *notebook;
3126  gint current_position = -1;
3127 
3128  page->window = GTK_WIDGET(window);
3129  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3130  notebook = GTK_NOTEBOOK (priv->notebook);
3131 
3132  if (!priv->restoring_pages
3133  && gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_OPEN_ADJACENT))
3134  current_position = g_list_index (priv->installed_pages, priv->current_page) + 1;
3135 
3136  priv->installed_pages = g_list_insert (priv->installed_pages, page, current_position);
3137  priv->usage_order = g_list_prepend (priv->usage_order, page);
3138  gtk_notebook_insert_page_menu (notebook, page->notebook_page,
3139  tab_hbox, menu_label, current_position);
3140  gtk_notebook_set_tab_reorderable (notebook, page->notebook_page, TRUE);
3141  gnc_plugin_page_inserted (page);
3142  if (!priv->restoring_pages)
3143  gtk_notebook_set_current_page (notebook, current_position);
3144 
3145  if (GNC_PLUGIN_PAGE_GET_CLASS(page)->window_changed)
3146  (GNC_PLUGIN_PAGE_GET_CLASS(page)->window_changed)(page, GTK_WIDGET(window));
3147  g_signal_emit (window, main_window_signals[PAGE_ADDED], 0, page);
3148 
3149  g_signal_connect(G_OBJECT(page->notebook_page), "popup-menu",
3150  G_CALLBACK(gnc_main_window_popup_menu_cb), page);
3151  g_signal_connect_after(G_OBJECT(page->notebook_page), "button-press-event",
3152  G_CALLBACK(gnc_main_window_button_press_cb), page);
3153 }
3154 
3155 
3169 static void
3170 gnc_main_window_disconnect (GncMainWindow *window,
3171  GncPluginPage *page)
3172 {
3173  GncMainWindowPrivate *priv;
3174  GtkNotebook *notebook;
3175  GncPluginPage *new_page;
3176  gint page_num;
3177 
3178  /* Disconnect the callbacks */
3179  g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
3180  (gpointer)gnc_main_window_popup_menu_cb, page);
3181  g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
3182  (gpointer)gnc_main_window_button_press_cb, page);
3183 
3184  // Remove the page_changed signal callback
3185  gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(page));
3186 
3187  /* Disconnect the page and summarybar from the window */
3188  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3189  if (priv->current_page == page)
3190  {
3191  gnc_plugin_page_unselected (page);
3192  priv->current_page = nullptr;
3193  }
3194 
3195  /* Remove it from the list of pages in the window */
3196  priv->installed_pages = g_list_remove (priv->installed_pages, page);
3197  priv->usage_order = g_list_remove (priv->usage_order, page);
3198 
3199  /* Switch to the last recently used page */
3200  notebook = GTK_NOTEBOOK (priv->notebook);
3201  if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_NEXT_RECENT))
3202  {
3203  new_page = static_cast<GncPluginPage*>(g_list_nth_data (priv->usage_order, 0));
3204  if (new_page)
3205  {
3206  page_num = gtk_notebook_page_num(notebook, new_page->notebook_page);
3207  gtk_notebook_set_current_page(notebook, page_num);
3208  /* This may have caused WebKit to schedule a timer interrupt which it
3209  sometimes forgets to cancel before deleting the object. See
3210  <https://bugs.webkit.org/show_bug.cgi?id=119003>. Get around this
3211  by flushing all events to get rid of the timer interrupt. */
3212  while (gtk_events_pending())
3213  gtk_main_iteration();
3214  }
3215  }
3216 
3217  /* Remove the page from the notebook */
3218  page_num = gtk_notebook_page_num(notebook, page->notebook_page);
3219  gtk_notebook_remove_page (notebook, page_num);
3220 
3221  if ( gtk_notebook_get_current_page(notebook) == -1)
3222  {
3223  /* Need to synthesize a page changed signal when the last
3224  * page is removed. The notebook doesn't generate a signal
3225  * for this, therefore the switch_page code in this file
3226  * never gets called to generate this signal. */
3227  gnc_main_window_switch_page(notebook, nullptr, -1, window);
3228  //g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, nullptr);
3229  }
3230 
3231  gnc_plugin_page_removed (page);
3232 
3233  gnc_window_set_status (GNC_WINDOW(window), page, nullptr);
3234 }
3235 
3236 
3237 /************************************************************
3238  * *
3239  ************************************************************/
3240 
3241 
3242 void
3244 {
3245  GncMainWindow *window;
3246  GncMainWindowPrivate *priv;
3247  GtkNotebook *notebook;
3248  gint page_num;
3249 
3250  window = GNC_MAIN_WINDOW (page->window);
3251  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3252  notebook = GTK_NOTEBOOK (priv->notebook);
3253  page_num = gtk_notebook_page_num(notebook, page->notebook_page);
3254  gtk_notebook_set_current_page (notebook, page_num);
3255  gtk_window_present(GTK_WINDOW(window));
3256 }
3257 
3258 
3259 /* Display a data plugin page in a window. If the page already
3260  * exists in any window, then that window will be brought to the
3261  * front and the notebook switch to display the specified page. If
3262  * the page is new then it will be added to the specified window. If
3263  * the window is nullptr, the new page will be added to the first
3264  * window.
3265  */
3266 void
3267 gnc_main_window_open_page (GncMainWindow *window,
3268  GncPluginPage *page)
3269 {
3270  GncMainWindowPrivate *priv;
3271  GtkWidget *tab_container, *tab_clickable_area;
3272  GtkWidget *label, *entry;
3273  const gchar *icon, *text, *color_string, *lab_text;
3274  GtkWidget *image;
3275  GList *tmp;
3276  TabWidth *tw;
3277 
3278  ENTER("window %p, page %p", window, page);
3279  if (window)
3280  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3281  g_return_if_fail (GNC_IS_PLUGIN_PAGE (page));
3282  g_return_if_fail (gnc_plugin_page_has_books(page));
3283 
3284  if (gnc_main_window_page_exists(page))
3285  {
3287  return;
3288  }
3289 
3290  /* Does the page want to be in a new window? */
3292  {
3293  /* See if there's a blank window. If so, use that. */
3294  for (tmp = active_windows; tmp; tmp = g_list_next(tmp))
3295  {
3296  window = GNC_MAIN_WINDOW(tmp->data);
3297  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3298  if (priv->installed_pages == nullptr)
3299  {
3300  break;
3301  }
3302  }
3303  if (tmp == nullptr)
3304  window = gnc_main_window_new ();
3305  gtk_widget_show(GTK_WIDGET(window));
3306  }
3307  else if ((window == nullptr) && active_windows)
3308  {
3309  window = static_cast<GncMainWindow*>(active_windows->data);
3310  }
3311 
3312  page->window = GTK_WIDGET(window);
3314  g_object_set_data (G_OBJECT (page->notebook_page),
3315  PLUGIN_PAGE_LABEL, page);
3316 
3317  /*
3318  * The page tab.
3319  * Component structure:
3320  *
3321  * tab_container (GtkBox)
3322  * ├── tab_clickable_area (GtkEventBox)
3323  * │ └── tab_content (GtkBox)
3324  * │ ├── image (GtkImage, optional)
3325  * │ ├── label (GtkLabel)
3326  * │ └── entry (GtkEntry, hidden)
3327  * └── close_button (GtkButton, if not immutable)
3328  */
3329  icon = GNC_PLUGIN_PAGE_GET_CLASS(page)->tab_icon;
3330  lab_text = gnc_plugin_page_get_page_name(page);
3331  label = gtk_label_new (lab_text);
3332  g_object_set_data (G_OBJECT (page), PLUGIN_PAGE_TAB_LABEL, label);
3333 
3334  tw = populate_tab_width_struct ();
3335  gnc_main_window_update_tab_width_one_page (page, tw);
3336  g_free (tw);
3337 
3338  gtk_widget_show (label);
3339 
3340  tab_container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
3341 
3343  if (text)
3344  {
3345  gtk_widget_set_tooltip_text(tab_container, text);
3346  }
3347 
3348  if (g_strcmp0 (gnc_plugin_page_get_plugin_name (page), "GncPluginPageAccountTree") == 0)
3349  gtk_widget_set_name (GTK_WIDGET(tab_container), "gnc-id-account-page-tab-box");
3350 
3351  gtk_box_set_homogeneous (GTK_BOX (tab_container), FALSE);
3352  gtk_widget_show (tab_container);
3353 
3354  // Create a custom clickable area for the tab to support middle-clicking
3355  tab_clickable_area = gtk_event_box_new();
3356  gtk_widget_show(tab_clickable_area);
3357  gtk_box_pack_start (GTK_BOX (tab_container), tab_clickable_area, TRUE, TRUE, 0);
3358 
3359  // Create a box for the tab's content
3360  // Give it a name so we can find it later (see main_window_find_tab_items)
3361  GtkWidget *tab_content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
3362  gtk_widget_set_name(tab_content, "tab-content");
3363  gtk_container_add(GTK_CONTAINER(tab_clickable_area), tab_content);
3364  gtk_widget_show(tab_content);
3365 
3366  if (icon != nullptr)
3367  {
3368  image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU);
3369  gtk_widget_show (image);
3370  gtk_box_pack_start (GTK_BOX (tab_content), image, FALSE, FALSE, 0);
3371  gtk_widget_set_margin_start (GTK_WIDGET(image), 5);
3372  gtk_box_pack_start (GTK_BOX (tab_content), label, TRUE, TRUE, 0);
3373  }
3374  else
3375  gtk_box_pack_start (GTK_BOX (tab_content), label, TRUE, TRUE, 0);
3376 
3377  entry = gtk_entry_new();
3378  gtk_widget_hide (entry);
3379  gtk_box_pack_start (GTK_BOX (tab_content), entry, TRUE, TRUE, 0);
3380  g_signal_connect(G_OBJECT(entry), "activate",
3381  G_CALLBACK(gnc_main_window_tab_entry_activate), page);
3382  g_signal_connect(G_OBJECT(entry), "focus-out-event",
3383  G_CALLBACK(gnc_main_window_tab_entry_focus_out_event),
3384  page);
3385  g_signal_connect(G_OBJECT(entry), "key-press-event",
3386  G_CALLBACK(gnc_main_window_tab_entry_key_press_event),
3387  page);
3388  g_signal_connect(G_OBJECT(entry), "editing-done",
3389  G_CALLBACK(gnc_main_window_tab_entry_editing_done),
3390  page);
3391 
3392  /* Add close button - Not for immutable pages */
3393  if (!g_object_get_data (G_OBJECT (page), PLUGIN_PAGE_IMMUTABLE))
3394  {
3395  GtkWidget *close_image, *close_button;
3396  GtkRequisition requisition;
3397 
3398  close_button = gtk_button_new();
3399  gtk_button_set_relief(GTK_BUTTON(close_button), GTK_RELIEF_NONE);
3400  close_image = gtk_image_new_from_icon_name ("window-close", GTK_ICON_SIZE_MENU);
3401  gtk_widget_show(close_image);
3402  gtk_widget_get_preferred_size (close_image, &requisition, nullptr);
3403  gtk_widget_set_size_request(close_button, requisition.width + 4,
3404  requisition.height + 2);
3405  gtk_container_add(GTK_CONTAINER(close_button), close_image);
3406  if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_SHOW_CLOSE_BUTTON))
3407  gtk_widget_show (close_button);
3408  else
3409  gtk_widget_hide (close_button);
3410 
3411  // Custom handler to close on middle-clicks
3412  g_signal_connect(G_OBJECT(tab_clickable_area), "button-press-event",
3413  G_CALLBACK(gnc_tab_clicked_cb), page);
3414 
3415  g_signal_connect_swapped (G_OBJECT (close_button), "clicked",
3416  G_CALLBACK(gnc_main_window_close_page), page);
3417 
3418  gtk_box_pack_start (GTK_BOX (tab_container), close_button, FALSE, FALSE, 0);
3419  gtk_widget_set_margin_end (GTK_WIDGET(close_button), 5);
3420  g_object_set_data (G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON, close_button);
3421  }
3422 
3423  /*
3424  * The popup menu
3425  */
3426  label = gtk_label_new (gnc_plugin_page_get_page_name(page));
3427 
3428  /*
3429  * Now install it all in the window.
3430  */
3431  gnc_main_window_connect(window, page, tab_container, label);
3432 
3433  color_string = gnc_plugin_page_get_page_color(page);
3434  main_window_update_page_color (page, color_string);
3435  LEAVE("");
3436 }
3437 
3438 
3439 /* Remove a data plugin page from a window and display the previous
3440  * page. If the page removed was the last page in the window, and
3441  * there is more than one window open, then the entire window will be
3442  * destroyed.
3443  */
3444 void
3446 {
3447  GncMainWindow *window;
3448  GncMainWindowPrivate *priv;
3449 
3450  if (!page || !page->notebook_page)
3451  return;
3452 
3453  if (!gnc_plugin_page_finish_pending(page))
3454  return;
3455 
3456  if (!GNC_IS_MAIN_WINDOW (page->window))
3457  return;
3458 
3459  window = GNC_MAIN_WINDOW (page->window);
3460  if (!window)
3461  {
3462  g_warning("Page is not in a window.");
3463  return;
3464  }
3465 
3466  gnc_main_window_disconnect(window, page);
3468  g_object_unref(page);
3469 
3470  /* If this isn't the last window, go ahead and destroy the window. */
3471  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3472  if (priv->installed_pages == nullptr)
3473  {
3474  if (window->window_quitting)
3475  {
3476  GncPluginManager *manager = gnc_plugin_manager_get ();
3477  GList *plugins = gnc_plugin_manager_get_plugins (manager);
3478 
3479  /* remove only the preference callbacks from the window plugins */
3480  window->just_plugin_prefs = TRUE;
3481  g_list_foreach (plugins, gnc_main_window_remove_plugin, window);
3482  window->just_plugin_prefs = FALSE;
3483  g_list_free (plugins);
3484 
3485  /* remove the preference callbacks from the main window */
3486  gnc_main_window_remove_prefs (window);
3487  }
3488  if (window && (gnc_list_length_cmp (active_windows, 1) > 0))
3489  gtk_widget_destroy (GTK_WIDGET(window));
3490  }
3491 }
3492 
3493 
3494 /* Retrieve a pointer to the page that is currently at the front of
3495  * the specified window. Any plugin that needs to manipulate its
3496  * menus based upon the currently selected menu page should connect
3497  * to the "page_changed" signal on a window. The callback function
3498  * from that signal can then call this function to obtain a pointer
3499  * to the current page.
3500  */
3501 GncPluginPage *
3502 gnc_main_window_get_current_page (GncMainWindow *window)
3503 {
3504  GncMainWindowPrivate *priv;
3505 
3506  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3507  return priv->current_page;
3508 }
3509 
3510 
3511 /* Manually add a set of actions to the specified window. Plugins
3512  * whose user interface is not hard coded (e.g. the menu-additions
3513  * plugin) must create their actions at run time, then use this
3514  * function to install them into the window.
3515  */
3516 void
3518  const gchar *group_name,
3519  GSimpleActionGroup *group)
3520 {
3521  g_return_if_fail (GNC_IS_MAIN_WINDOW(window));
3522  g_return_if_fail (group_name != nullptr);
3523  g_return_if_fail (G_IS_SIMPLE_ACTION_GROUP(group));
3524 
3525  gtk_widget_insert_action_group (GTK_WIDGET(window), group_name,
3526  G_ACTION_GROUP(group));
3527 }
3528 
3529 
3530 static void
3531 update_menu_model (GncMainWindow *window, const gchar *ui_filename,
3532  const gchar **ui_updates)
3533 {
3534  GncMainWindowPrivate *priv;
3535  GError *error = nullptr;
3536  gchar *res_name;
3537  GtkBuilder *builder = gtk_builder_new ();
3538  GMenuModel *menu_model_part;
3539  GncMenuModelSearch *gsm = g_new0 (GncMenuModelSearch, 1);
3540 
3541  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3542  g_return_if_fail (ui_filename != nullptr);
3543  g_return_if_fail (ui_updates != nullptr);
3544 
3545  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3546 
3547  gtk_builder_set_translation_domain (builder, PROJECT_NAME);
3548  res_name = g_strconcat (GNUCASH_RESOURCE_PREFIX "/", ui_filename, NULL);
3549 
3550  gtk_builder_add_from_resource (builder, res_name, &error);
3551  g_free (res_name);
3552 
3553  if (error)
3554  {
3555  g_critical ("Failed to load, Error %s", error->message);
3556  g_error_free (error);
3557  return;
3558  }
3559 
3560  for (gint i = 0; ui_updates[i]; i++)
3561  {
3562  menu_model_part = (GMenuModel *)gtk_builder_get_object (builder, ui_updates[i]);
3563 
3564  gsm->search_action_label = nullptr;
3565  gsm->search_action_name = ui_updates[i];
3566  gsm->search_action_target = nullptr;
3567 
3568  if (gnc_menubar_model_find_item (priv->menubar_model, gsm))
3569  g_menu_insert_section (G_MENU(gsm->model), gsm->index, NULL, G_MENU_MODEL(menu_model_part));
3570  else
3571  PERR("Could not find '%s' in menu model", ui_updates[i]);
3572  }
3573  g_free (gsm);
3574  g_object_unref (builder);
3575 }
3576 
3577 
3578 /* Add a set of actions to the specified window. This function
3579  * should not need to be called directly by plugin implementors.
3580  * Correctly assigning values to the GncPluginClass fields during
3581  * plugin initialization will cause this routine to be automatically
3582  * called.
3583  */
3584 void
3585 gnc_main_window_merge_actions (GncMainWindow *window,
3586  const gchar *group_name,
3587  GActionEntry *actions,
3588  guint n_actions,
3589  const gchar **ui_updates,
3590  const gchar *ui_filename,
3591  gpointer user_data)
3592 {
3594  GSimpleActionGroup *simple_action_group;
3595 
3596  g_return_if_fail (GNC_IS_MAIN_WINDOW(window));
3597  g_return_if_fail (group_name != nullptr);
3598  g_return_if_fail (actions != nullptr);
3599  g_return_if_fail (n_actions > 0);
3600 
3601  data = g_new0 (GncMainWindowActionData, 1);
3602  data->window = window;
3603  data->data = user_data;
3604 
3605  simple_action_group = g_simple_action_group_new ();
3606 
3607  g_action_map_add_action_entries (G_ACTION_MAP(simple_action_group),
3608  actions,
3609  n_actions,
3610  data);
3611 
3612  gtk_widget_insert_action_group (GTK_WIDGET(window), group_name,
3613  G_ACTION_GROUP(simple_action_group));
3614 
3615  if (ui_filename)
3616  update_menu_model (window, ui_filename, ui_updates);
3617 }
3618 
3619 
3620 /* Remove a set of actions from the specified window. This function
3621  * should not need to be called directly by plugin implementors. It
3622  * will automatically be called when a plugin is removed from a
3623  * window.
3624  */
3625 void
3626 gnc_main_window_unmerge_actions (GncMainWindow *window,
3627  const gchar *group_name)
3628 {
3629  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3630  g_return_if_fail (group_name != nullptr);
3631 
3632  gtk_widget_insert_action_group (GTK_WIDGET(window), group_name, nullptr);
3633 }
3634 
3635 GAction *
3636 gnc_main_window_find_action (GncMainWindow *window, const gchar *action_name)
3637 {
3638  GAction *action = nullptr;
3639 
3640  g_return_val_if_fail (GNC_IS_MAIN_WINDOW(window), nullptr);
3641  g_return_val_if_fail (action_name != nullptr, nullptr);
3642 
3643  action = g_action_map_lookup_action (G_ACTION_MAP(window),
3644  action_name);
3645 
3646  return action;
3647 }
3648 
3649 GAction *
3651  const gchar *group_name,
3652  const gchar *action_name)
3653 {
3654  GAction *action = nullptr;
3655 
3656  g_return_val_if_fail (GNC_IS_MAIN_WINDOW(window), nullptr);
3657  g_return_val_if_fail (group_name != nullptr, nullptr);
3658  g_return_val_if_fail (action_name != nullptr, nullptr);
3659 
3660  auto action_group = gtk_widget_get_action_group (GTK_WIDGET(window), group_name);
3661 
3662  if (action_group)
3663  action = g_action_map_lookup_action (G_ACTION_MAP(action_group), action_name);
3664 
3665  return action;
3666 }
3667 
3668 
3669 /* Retrieve a specific set of user interface actions from a window.
3670  * This function can be used to get an group of action to be
3671  * manipulated when the front page of a window has changed.
3672  */
3673 GSimpleActionGroup *
3674 gnc_main_window_get_action_group (GncMainWindow *window,
3675  const gchar *group_name)
3676 {
3677  g_return_val_if_fail (GNC_IS_MAIN_WINDOW(window), nullptr);
3678  g_return_val_if_fail (group_name != nullptr, nullptr);
3679 
3680  auto action_group = gtk_widget_get_action_group (GTK_WIDGET(window), group_name);
3681  return (GSimpleActionGroup*)action_group;
3682 }
3683 
3684 GtkWidget *
3685 gnc_main_window_toolbar_find_tool_item (GncMainWindow *window, const gchar *action_name)
3686 {
3687  GncMainWindowPrivate *priv;
3688 
3689  g_return_val_if_fail (GNC_IS_MAIN_WINDOW(window), nullptr);
3690  g_return_val_if_fail (action_name != nullptr, nullptr);
3691 
3692  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3693 
3694  return gnc_find_toolbar_item (priv->toolbar, action_name);
3695 }
3696 
3697 GtkWidget *
3698 gnc_main_window_menu_find_menu_item (GncMainWindow *window, const gchar *action_name)
3699 {
3700  GncMainWindowPrivate *priv;
3701  GtkWidget *menu_item;
3702 
3703  g_return_val_if_fail (GNC_IS_MAIN_WINDOW(window), nullptr);
3704  g_return_val_if_fail (action_name != nullptr, nullptr);
3705 
3706  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3707 
3708  menu_item = GTK_WIDGET(g_hash_table_lookup (priv->display_item_hash, action_name));
3709 
3710  if (!menu_item)
3711  {
3713 
3714  g_hash_table_insert (priv->display_item_hash, g_strdup (action_name), menu_item);
3715  }
3716  return menu_item;
3717 }
3718 
3719 
3720 void
3722 {
3723  GncMainWindowPrivate *priv;
3724 
3725  g_return_if_fail (GNC_IS_MAIN_WINDOW(window));
3726 
3727  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3728 
3730 }
3731 
3732 
3733 gboolean
3735  const gchar *action_name,
3736  const gchar *label,
3737  const gchar *tooltip)
3738 {
3739  GncMainWindowPrivate *priv;
3740  gboolean found = false;
3741 
3742  g_return_val_if_fail (GNC_IS_MAIN_WINDOW(window), false);
3743  g_return_val_if_fail (action_name != nullptr, false);
3744  g_return_val_if_fail (label != nullptr, false);
3745 
3746  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3747 
3749  nullptr, _(label), nullptr, _(tooltip));
3750 
3751  // add tooltip redirect call backs
3753  priv->menubar_model,
3754  priv->statusbar);
3755 
3756  return found;
3757 }
3758 
3759 void
3761  const gchar **action_names,
3762  gboolean vis)
3763 {
3764  GncMainWindowPrivate *priv;
3765 
3766  g_return_if_fail (GNC_IS_MAIN_WINDOW(window));
3767 
3768  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3769 
3770  for (gint i = 0; action_names[i]; i++)
3771  {
3772  GtkWidget *tool_item = gnc_find_toolbar_item (priv->toolbar, action_names[i]);
3773  GtkWidget *menu_item = gnc_main_window_menu_find_menu_item (window, action_names[i]);
3774 
3775  if (menu_item)
3776  {
3777  PINFO("Found menu_item %p with action name '%s', seting vis to '%s'",
3778  menu_item, action_names[i], vis ? "true" : "false");
3779  gtk_widget_set_visible (menu_item, vis);
3780  }
3781  else
3782  PINFO("Did not find menu_item with action name '%s' to set vis '%s'",
3783  action_names[i], vis ? "true" : "false");
3784 
3785  if (tool_item)
3786  {
3787  PINFO("Found tool_item %p with action name '%s', seting vis to '%s'",
3788  tool_item, action_names[i], vis ? "true" : "false");
3789  gtk_widget_set_visible (tool_item, vis);
3790  }
3791  else
3792  PINFO("Did not find tool_item with action name '%s' to set vis '%s'",
3793  action_names[i], vis ? "true" : "false");
3794  }
3795 }
3796 
3797 
3798 void
3799 gnc_main_window_init_short_names (GncMainWindow *window,
3800  GncToolBarShortNames *toolbar_labels)
3801 {
3802  GncMainWindowPrivate *priv;
3803 
3804  g_return_if_fail (GNC_IS_MAIN_WINDOW(window));
3805  g_return_if_fail (toolbar_labels != nullptr);
3806 
3807  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3808 
3809  gnc_plugin_init_short_names (priv->toolbar, toolbar_labels);
3810 }
3811 
3812 
3813 static void
3814 gnc_main_window_update_toolbar (GncMainWindow *window, GncPluginPage *page,
3815  const gchar *toolbar_qualifier)
3816 {
3817  GncMainWindowPrivate *priv;
3818  GtkBuilder *builder;
3819  GAction *action;
3820 
3821  g_return_if_fail (GNC_IS_MAIN_WINDOW(window));
3822  g_return_if_fail (GNC_IS_PLUGIN_PAGE(page));
3823 
3824  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3825 
3826  builder = gnc_plugin_page_get_builder (page);
3827 
3828  if (builder)
3829  {
3830  gchar *toolbar_name;
3831  gtk_container_remove (GTK_CONTAINER(priv->menu_dock), priv->toolbar);
3832 
3833  if (toolbar_qualifier)
3834  toolbar_name = g_strconcat ("mainwin-toolbar-", toolbar_qualifier, nullptr);
3835  else
3836  toolbar_name = g_strdup ("mainwin-toolbar");
3837 
3838  priv->toolbar = (GtkWidget *)gtk_builder_get_object (builder, toolbar_name);
3839 
3840  if (!priv->toolbar)
3841  priv->toolbar = (GtkWidget *)gtk_builder_get_object (builder, "mainwin-toolbar");
3842 
3843  g_object_set (priv->toolbar, "toolbar-style", GTK_TOOLBAR_BOTH, NULL);
3844  gtk_container_add (GTK_CONTAINER(priv->menu_dock), priv->toolbar);
3845  g_free (toolbar_name);
3846  }
3847 
3848  action = gnc_main_window_find_action (window, "ViewToolbarAction");
3849 
3850  // set visibility of toolbar
3851  if (action)
3852  {
3853  GVariant *state = g_action_get_state (G_ACTION(action));
3854  gtk_widget_set_visible (priv->toolbar, g_variant_get_boolean (state));
3855  g_variant_unref (state);
3856  }
3857  // add tooltip redirect call backs
3859 }
3860 
3861 
3862 void
3864  GncPluginPage *page,
3865  const gchar **ui_updates)
3866 {
3867  GncMainWindowPrivate *priv;
3868  const gchar *plugin_page_actions_group_name;
3869  GtkBuilder *builder;
3870  const gchar *menu_qualifier;
3871 
3872  GMenuModel *menu_model_part;
3873 #ifdef MAC_INTEGRATION
3874  auto theApp{static_cast<GtkosxApplication *>(g_object_new(GTKOSX_TYPE_APPLICATION, nullptr))};
3875 #endif
3876  g_return_if_fail (GNC_IS_MAIN_WINDOW(window));
3877  g_return_if_fail (page != nullptr);
3878  g_return_if_fail (ui_updates != nullptr);
3879 
3880  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3881 
3882  builder = gnc_plugin_page_get_builder (page);
3883 
3884  if (!builder)
3885  return;
3886 
3887  menu_qualifier = gnc_plugin_page_get_menu_qualifier (page);
3888 
3889  plugin_page_actions_group_name = gnc_plugin_page_get_simple_action_group_name (page);
3890 
3891  if (!plugin_page_actions_group_name)
3892  return;
3893 
3894  gtk_widget_insert_action_group (GTK_WIDGET(window), gnc_plugin_page_get_simple_action_group_name (page),
3895  G_ACTION_GROUP(gnc_plugin_page_get_action_group (page)));
3896 
3897  if ((g_strcmp0 (priv->previous_plugin_page_name,
3898  plugin_page_actions_group_name) == 0) &&
3899  (g_strcmp0 (priv->previous_menu_qualifier,
3900  menu_qualifier) == 0))
3901  return;
3902 
3903  priv->previous_plugin_page_name = plugin_page_actions_group_name;
3904  priv->previous_menu_qualifier = menu_qualifier;
3905 
3906  gnc_main_window_update_toolbar (window, page, menu_qualifier);
3907 
3908  // reset hash table and remove added menu items
3909  g_hash_table_remove_all (priv->display_item_hash);
3911  GNC_MENU_ATTRIBUTE_TEMPORARY);
3912 
3913  GncMenuModelSearch *gsm = g_new0 (GncMenuModelSearch, 1);
3914  for (gint i = 0; ui_updates[i]; i++)
3915  {
3916  gchar *menu_name;
3917 
3918  if (menu_qualifier)
3919  menu_name = g_strconcat (ui_updates[i], "-", menu_qualifier, nullptr);
3920  else
3921  menu_name = g_strdup (ui_updates[i]);
3922 
3923  menu_model_part = (GMenuModel *)gtk_builder_get_object (builder, menu_name);
3924 
3925  if (!menu_model_part)
3926  menu_model_part = (GMenuModel *)gtk_builder_get_object (builder, ui_updates[i]);
3927 
3928  gsm->search_action_label = nullptr;
3929  gsm->search_action_name = ui_updates[i];
3930  gsm->search_action_target = nullptr;
3931 
3932  if (gnc_menubar_model_find_item (priv->menubar_model, gsm))
3933  g_menu_insert_section (G_MENU(gsm->model), gsm->index,
3934  nullptr, G_MENU_MODEL(menu_model_part));
3935  else
3936  PERR("Could not find '%s' in menu model", ui_updates[i]);
3937 
3938  g_free (menu_name);
3939  }
3940 
3941  // add tooltip redirect call backs
3943 
3944  // need to add the accelerator keys
3946 #ifdef MAC_INTEGRATION
3947  gtkosx_application_sync_menubar (theApp);
3948  g_object_unref (theApp);
3949 #endif
3950  // need to signal menu has been changed
3951  g_signal_emit_by_name (window, "menu_changed", page);
3952 
3953  g_free (gsm);
3954 }
3955 
3956 
3957 static void
3958 gnc_main_window_update_tab_position (gpointer prefs, gchar *pref, gpointer user_data)
3959 {
3960  GncMainWindow *window;
3961  GtkPositionType position = GTK_POS_TOP;
3962  gint item = 0;
3963  GncMainWindowPrivate *priv;
3964  GAction *action;
3965 
3966  g_return_if_fail (GNC_IS_MAIN_WINDOW(user_data));
3967 
3968  window = GNC_MAIN_WINDOW(user_data);
3969 
3970  ENTER ("window %p", window);
3971 
3972  /* Ignore notification of the preference that is being set to false when
3973  * the choice of tab position changes. */
3974  if (pref && !gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, pref))
3975  return;
3976 
3977  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_BOTTOM))
3978  {
3979  position = GTK_POS_BOTTOM;
3980  item = 1;
3981  }
3982  else if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT))
3983  {
3984  position = GTK_POS_LEFT;
3985  item = 2;
3986  }
3987  else if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT))
3988  {
3989  position = GTK_POS_RIGHT;
3990  item = 3;
3991  }
3992 
3993  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3994  gtk_notebook_set_tab_pos (GTK_NOTEBOOK(priv->notebook), position);
3995 
3996  action = g_action_map_lookup_action (G_ACTION_MAP(window),
3997  "ViewTabPositionAction");
3998 
3999  g_signal_handlers_block_by_func (G_OBJECT(action),
4000  (gpointer)gnc_main_window_cmd_view_tab_position,
4001  window);
4002  g_action_change_state (G_ACTION(action), g_variant_new_int32 (item));
4003  g_signal_handlers_unblock_by_func (G_OBJECT(action),
4004  (gpointer)gnc_main_window_cmd_view_tab_position,
4005  window);
4006 
4007  gnc_main_window_update_tab_width (nullptr, (char*)GNC_PREF_TAB_WIDTH, nullptr);
4008 
4009  LEAVE ("");
4010 }
4011 
4012 /*
4013  * Based on code from Epiphany (src/ephy-window.c)
4014  */
4015 static void
4016 gnc_main_window_update_edit_actions_sensitivity (GncMainWindow *window, gboolean hide)
4017 {
4018  GncMainWindowPrivate *priv;
4019  GncPluginPage *page;
4020  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
4021  GAction *action;
4022  gboolean can_copy = false, can_cut = false, can_paste = false;
4023 
4024  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4025  page = priv->current_page;
4026 
4027  if (page && GNC_PLUGIN_PAGE_GET_CLASS(page)->update_edit_menu_actions)
4028  {
4029  (GNC_PLUGIN_PAGE_GET_CLASS(page)->update_edit_menu_actions)(page, hide);
4030  return;
4031  }
4032 
4033  if (GTK_IS_EDITABLE (widget))
4034  {
4035  gboolean has_selection;
4036 
4037  has_selection = gtk_editable_get_selection_bounds
4038  (GTK_EDITABLE (widget), nullptr, nullptr);
4039 
4040  can_copy = has_selection;
4041  can_cut = has_selection;
4042  can_paste = TRUE;
4043  }
4044  else if (GTK_IS_TEXT_VIEW (widget))
4045  {
4046  gboolean has_selection;
4047  GtkTextBuffer *text_buffer;
4048 
4049  text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
4050  has_selection = gtk_text_buffer_get_selection_bounds
4051  (text_buffer, nullptr, nullptr);
4052 
4053  can_copy = has_selection;
4054  can_cut = has_selection;
4055  can_paste = TRUE;
4056  }
4057  else
4058  {
4059 #ifdef ORIGINAL_EPIPHANY_CODE
4060  /* For now we assume all actions are possible */
4061  can_copy = can_cut = can_paste = true;
4062 #else
4063  /* If its not a GtkEditable, we don't know what to do
4064  * with it. */
4065  can_copy = can_cut = can_paste = false;
4066 #endif
4067  }
4068  action = gnc_main_window_find_action (window, "EditCopyAction");
4069  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), can_copy);
4070 
4071  action = gnc_main_window_find_action (window, "EditCutAction");
4072  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), can_cut);
4073 
4074  action = gnc_main_window_find_action (window, "EditPasteAction");
4075  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), can_paste);
4076 }
4077 
4078 static void
4079 gnc_main_window_enable_edit_actions_sensitivity (GncMainWindow *window)
4080 {
4081  GAction *action;
4082 
4083  action = gnc_main_window_find_action (window, "EditCopyAction");
4084  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), true);
4085 
4086  action = gnc_main_window_find_action (window, "EditCutAction");
4087  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), true);
4088 
4089  action = gnc_main_window_find_action (window, "EditPasteAction");
4090  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), true);
4091 
4092 }
4093 
4094 static void
4095 gnc_main_window_edit_menu_show_cb (GtkWidget *menu,
4096  GncMainWindow *window)
4097 {
4098  gnc_main_window_update_edit_actions_sensitivity (window, FALSE);
4099 }
4100 
4101 static void
4102 gnc_main_window_edit_menu_hide_cb (GtkWidget *menu,
4103  GncMainWindow *window)
4104 {
4105  gnc_main_window_enable_edit_actions_sensitivity (window);
4106 }
4107 
4108 static void
4109 gnc_main_window_init_menu_updaters (GncMainWindow *window)
4110 {
4111  GtkWidget *edit_menu_item, *edit_menu;
4112 
4113  edit_menu_item = gnc_main_window_menu_find_menu_item (window, "EditAction");
4114 
4115  edit_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM(edit_menu_item));
4116 
4117  g_signal_connect (edit_menu, "show",
4118  G_CALLBACK(gnc_main_window_edit_menu_show_cb), window);
4119  g_signal_connect (edit_menu, "hide",
4120  G_CALLBACK(gnc_main_window_edit_menu_hide_cb), window);
4121 }
4122 
4123 /* This is used to prevent the tab having focus */
4124 static gboolean
4125 gnc_main_window_page_focus_in (GtkWidget *widget, GdkEvent *event,
4126  gpointer user_data)
4127 {
4128  auto window{static_cast<GncMainWindow *>(user_data)};
4130 
4131  g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, page);
4132  return FALSE;
4133 }
4134 
4135 static GAction *
4136 gnc_main_window_get_redirect (GncMainWindow *window, const gchar *action_name)
4137 {
4138  GncMainWindowPrivate *priv;
4139  GAction *action = nullptr;
4140  const gchar *group_name;
4141 
4142  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), nullptr);
4143  g_return_val_if_fail (action_name != nullptr, nullptr);
4144 
4145  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4146 
4148 
4149  PINFO("action anme is '%s', group_name is '%s'", action_name, group_name);
4150 
4151  if (group_name)
4152  {
4153  action = gnc_main_window_find_action_in_group (window, group_name, action_name);
4154 
4155  if (!action)
4157  }
4158 
4159  PINFO("Redirect action is %p for action anme '%s' and group_name '%s'",
4160  action, action_name, group_name);
4161  return action;
4162 }
4163 
4164 static void
4165 main_window_realize_cb (GtkWidget *widget, gpointer user_data)
4166 {
4167  GncMainWindow *window = (GncMainWindow*)user_data;
4168  GncMainWindowPrivate *priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4169 
4170  gnc_add_accelerator_keys_for_menu (GTK_WIDGET(priv->menubar), priv->menubar_model, priv->accel_group);
4171 
4172  /* need to signal menu has been changed, this will call the
4173  business function 'bind_extra_toolbuttons_visibility' */
4174  g_signal_emit_by_name (window, "menu_changed", nullptr);
4175 }
4176 
4177 static void
4178 gnc_main_window_setup_window (GncMainWindow *window)
4179 {
4180  GncMainWindowPrivate *priv;
4181  GtkWidget *main_vbox;
4182  GtkBuilder *builder;
4183  GncPluginManager *manager;
4184  GList *plugins;
4185  GError *error = nullptr;
4186  GAction *action;
4187 
4188  ENTER(" ");
4189 
4190  /* Catch window manager delete signal */
4191  g_signal_connect (G_OBJECT (window), "delete-event",
4192  G_CALLBACK (gnc_main_window_delete_event), window);
4193 
4194  /* Create widgets and add them to the window */
4195  main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
4196  gtk_box_set_homogeneous (GTK_BOX (main_vbox), FALSE);
4197  gtk_widget_show (main_vbox);
4198  gtk_container_add (GTK_CONTAINER (window), main_vbox);
4199 
4200  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4201  priv->menu_dock = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
4202  gtk_box_set_homogeneous (GTK_BOX (priv->menu_dock), FALSE);
4203  gtk_widget_show (priv->menu_dock);
4204  gtk_box_pack_start (GTK_BOX (main_vbox), priv->menu_dock,
4205  FALSE, TRUE, 0);
4206 
4207  priv->notebook = gtk_notebook_new ();
4208  g_object_set(G_OBJECT(priv->notebook),
4209  "scrollable", TRUE,
4210  "enable-popup", TRUE,
4211  (char *)nullptr);
4212  gtk_widget_show (priv->notebook);
4213  g_signal_connect (G_OBJECT (priv->notebook), "switch-page",
4214  G_CALLBACK (gnc_main_window_switch_page), window);
4215  g_signal_connect (G_OBJECT (priv->notebook), "page-reordered",
4216  G_CALLBACK (gnc_main_window_page_reordered), window);
4217  g_signal_connect (G_OBJECT (priv->notebook), "focus-in-event",
4218  G_CALLBACK (gnc_main_window_page_focus_in), window);
4219  gtk_box_pack_start (GTK_BOX (main_vbox), priv->notebook,
4220  TRUE, TRUE, 0);
4221 
4222  priv->statusbar = gtk_statusbar_new ();
4223  gtk_widget_show (priv->statusbar);
4224  gtk_box_pack_start (GTK_BOX (main_vbox), priv->statusbar,
4225  FALSE, TRUE, 0);
4226 
4227  priv->progressbar = gtk_progress_bar_new ();
4228  gtk_progress_bar_set_show_text (GTK_PROGRESS_BAR(priv->progressbar), TRUE);
4229  gtk_progress_bar_set_text(GTK_PROGRESS_BAR(priv->progressbar), " ");
4230  gtk_widget_show (priv->progressbar);
4231  gtk_box_pack_start (GTK_BOX (priv->statusbar), priv->progressbar,
4232  FALSE, TRUE, 0);
4233  gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(priv->progressbar),
4234  0.01);
4235 
4236  builder = gtk_builder_new ();
4237  gtk_builder_set_translation_domain (builder, PROJECT_NAME);
4238  gtk_builder_add_from_resource (builder, GNUCASH_RESOURCE_PREFIX "/gnc-main-window.ui", &error);
4239 
4240  if (error)
4241  {
4242  g_critical ("Failed to load, Error %s", error->message);
4243  g_error_free (error);
4244  return;
4245  }
4246 
4247  g_action_map_add_action_entries (G_ACTION_MAP(window),
4248  gnc_menu_actions,
4249  gnc_menu_n_actions,
4250  window);
4251 
4252  priv->menubar_model = (GMenuModel *)gtk_builder_get_object (builder, "mainwin-menu");
4253  priv->menubar = gtk_menu_bar_new_from_model (priv->menubar_model);
4254  gtk_container_add (GTK_CONTAINER(priv->menu_dock), priv->menubar);
4255  gtk_widget_show (GTK_WIDGET(priv->menubar));
4256 
4257  priv->toolbar = (GtkWidget *)gtk_builder_get_object (builder, "mainwin-toolbar");
4258  g_object_set (priv->toolbar, "toolbar-style", GTK_TOOLBAR_BOTH, NULL);
4259  gtk_container_add (GTK_CONTAINER(priv->menu_dock), GTK_WIDGET(priv->toolbar));
4260  gtk_widget_show (GTK_WIDGET(priv->toolbar));
4261 
4262  g_object_unref (builder);
4263 
4264  gnc_plugin_set_actions_enabled (G_ACTION_MAP(window),
4265  initially_insensitive_actions,
4266  FALSE);
4267  gnc_plugin_set_actions_enabled (G_ACTION_MAP(window),
4268  always_insensitive_actions,
4269  FALSE);
4270 
4271  gnc_main_window_set_vis_of_items_by_action (window, always_hidden_actions,
4272  false);
4273 
4274  gtk_widget_insert_action_group (GTK_WIDGET(window), "mainwin",
4275  G_ACTION_GROUP(window));
4276 
4277  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
4278  GNC_PREF_TAB_POSITION_TOP,
4279  (gpointer)gnc_main_window_update_tab_position,
4280  window);
4281  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
4282  GNC_PREF_TAB_POSITION_BOTTOM,
4283  (gpointer)gnc_main_window_update_tab_position,
4284  window);
4285  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
4286  GNC_PREF_TAB_POSITION_LEFT,
4287  (gpointer)gnc_main_window_update_tab_position,
4288  window);
4289  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
4290  GNC_PREF_TAB_POSITION_RIGHT,
4291  (gpointer)gnc_main_window_update_tab_position,
4292  window);
4293  gnc_main_window_update_tab_position (nullptr, nullptr, window);
4294 
4295  gnc_main_window_init_menu_updaters (window);
4296 
4297  /* Disable the Transaction menu */
4298  action = gnc_main_window_find_action (window, "TransactionAction");
4299  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), false);
4300  /* Disable the Schedule menu */
4301  action = gnc_main_window_find_action (window, "ScheduledAction");
4302  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), false);
4303 
4304  /* Now update the "eXtensions" menu */
4305  if (!gnc_prefs_is_extra_enabled())
4306  {
4307  action = gnc_main_window_find_action (window, "ExtensionsAction");
4308  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), false);
4309  }
4310 
4311  /* GncPluginManager stuff */
4312  manager = gnc_plugin_manager_get ();
4313  plugins = gnc_plugin_manager_get_plugins (manager);
4314  g_list_foreach (plugins, gnc_main_window_add_plugin, window);
4315  g_list_free (plugins);
4316 
4317  g_signal_connect (G_OBJECT (manager), "plugin-added",
4318  G_CALLBACK (gnc_main_window_plugin_added), window);
4319  g_signal_connect (G_OBJECT (manager), "plugin-removed",
4320  G_CALLBACK (gnc_main_window_plugin_removed), window);
4321 
4322  // need to add the accelerator keys this way, mainly for --nofile
4323  g_signal_connect (G_OBJECT(window), "realize",
4324  G_CALLBACK(main_window_realize_cb), window);
4325 
4326  LEAVE(" ");
4327 }
4328 
4329 #ifdef MAC_INTEGRATION
4330 /* Event handlers for the shutdown process. Gnc_quartz_shutdown is
4331  * connected to NSApplicationWillTerminate, the last chance to do
4332  * anything before quitting. The problem is that it's launched from a
4333  * CFRunLoop, not a g_main_loop, and if we call anything that would
4334  * affect the main_loop we get an assert that we're in a subidiary
4335  * loop.
4336  */
4337 static void
4338 gnc_quartz_shutdown (GtkosxApplication *theApp, gpointer data)
4339 {
4340  /* Do Nothing. It's too late. */
4341 }
4342 /* Should quit responds to NSApplicationBlockTermination; returning TRUE means
4343  * "don't terminate", FALSE means "do terminate". gnc_main_window_quit() queues
4344  * a timer that starts an orderly shutdown in 250ms and if we tell macOS it's OK
4345  * to quit GnuCash gets terminated instead of doing its orderly shutdown,
4346  * leaving the book locked.
4347  */
4348 static gboolean
4349 gnc_quartz_should_quit (GtkosxApplication *theApp, GncMainWindow *window)
4350 {
4352  gnc_main_window_quit (window);
4353  return TRUE;
4354 }
4355 /* Enable GtkMenuItem accelerators */
4356 static gboolean
4357 can_activate_cb(GtkWidget *widget, guint signal_id, gpointer data)
4358 {
4359  //return gtk_widget_is_sensitive (widget);
4360  return TRUE;
4361 }
4362 
4363 static void
4364 gnc_quartz_set_menu (GncMainWindow* window)
4365 {
4366  GncMainWindowPrivate *priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4367  auto theApp{static_cast<GtkosxApplication *>(g_object_new(GTKOSX_TYPE_APPLICATION, nullptr))};
4368  GtkWidget *item = nullptr;
4369  GClosure *quit_closure;
4370 
4371  gtk_widget_hide (priv->menubar);
4372  gtk_widget_set_no_show_all (priv->menubar, true);
4373 
4374  gtkosx_application_set_menu_bar (theApp, GTK_MENU_SHELL(priv->menubar));
4375 
4376  // File Quit
4377  item = gnc_main_window_menu_find_menu_item (window, "FileQuitAction");
4378  if (item)
4379  gtk_widget_hide (GTK_WIDGET(item));
4380 
4381  quit_closure = g_cclosure_new (G_CALLBACK (gnc_quartz_should_quit),
4382  window, NULL);
4383  gtk_accel_group_connect (priv->accel_group, 'q', GDK_META_MASK,
4384  GTK_ACCEL_MASK, quit_closure);
4385 
4386 
4387  // Help About
4388  item = gnc_main_window_menu_find_menu_item (window, "HelpAboutAction");
4389  if (item)
4390  {
4391  gtk_widget_hide (item);
4392  gtkosx_application_insert_app_menu_item (theApp, GTK_WIDGET(item), 0);
4393  }
4394 
4395  // Edit Preferences
4396  item = gnc_main_window_menu_find_menu_item (window, "EditPreferencesAction");
4397  if (item)
4398  {
4399  gtk_widget_hide (GTK_WIDGET(item));
4400  gtkosx_application_insert_app_menu_item (theApp, GTK_WIDGET(item), 2);
4401  }
4402 
4403  // Help Menu
4404  item = gnc_main_window_menu_find_menu_item (window, "HelpAction");
4405  if (item)
4406  gtkosx_application_set_help_menu (theApp, GTK_MENU_ITEM(item));
4407  // Windows Menu
4408  item = gnc_main_window_menu_find_menu_item (window, "WindowsAction");
4409  if (item)
4410  gtkosx_application_set_window_menu (theApp, GTK_MENU_ITEM(item));
4411 
4412  g_signal_connect (theApp, "NSApplicationBlockTermination",
4413  G_CALLBACK(gnc_quartz_should_quit), window);
4414 
4415  g_signal_connect (priv->menubar, "can-activate-accel",
4416  G_CALLBACK (can_activate_cb), nullptr);
4417 
4418  gtkosx_application_set_use_quartz_accelerators (theApp, FALSE);
4419  g_object_unref (theApp);
4420 }
4421 #endif //MAC_INTEGRATION
4422 
4423 /* Callbacks */
4424 
4437 static gboolean
4438 gnc_main_window_show_summarybar (GncMainWindow *window, GAction *action)
4439 {
4440  GVariant *state;
4441  gboolean visible;
4442 
4443  if (action == nullptr)
4444  action = g_action_map_lookup_action (G_ACTION_MAP(window),
4445  "ViewSummaryAction");
4446  if (action == nullptr)
4447  return TRUE;
4448 
4449  state = g_action_get_state (G_ACTION(action));
4450 
4451  visible = g_variant_get_boolean (state);
4452 
4453  g_variant_unref (state);
4454 
4455  return visible;
4456 }
4457 
4467 static void
4468 gnc_main_window_switch_page (GtkNotebook *notebook,
4469  gpointer *notebook_page,
4470  gint pos,
4471  GncMainWindow *window)
4472 {
4473  GncMainWindowPrivate *priv;
4474  GtkWidget *child;
4475  GncPluginPage *page;
4476  gboolean visible;
4477 
4478  ENTER("Notebook %p, page, %p, index %d, window %p",
4479  notebook, notebook_page, pos, window);
4480  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
4481 
4482  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4483  if (priv->current_page != nullptr)
4484  {
4485  page = priv->current_page;
4486  gnc_plugin_page_unselected (page);
4487  }
4488 
4489  child = gtk_notebook_get_nth_page (notebook, pos);
4490  if (child)
4491  {
4492  page = static_cast<GncPluginPage*>(g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL));
4493  }
4494  else
4495  {
4496  page = nullptr;
4497  }
4498 
4499  priv->current_page = page;
4500 
4501  if (page != nullptr)
4502  {
4503  /* Update the user interface (e.g. menus and toolbars */
4505  visible = gnc_main_window_show_summarybar (window, nullptr);
4507 
4508  /* Allow page specific actions */
4509  gnc_plugin_page_selected (page);
4510  gnc_window_update_status (GNC_WINDOW(window), page);
4511 
4512  /* Update the page reference info */
4513  priv->usage_order = g_list_remove (priv->usage_order, page);
4514  priv->usage_order = g_list_prepend (priv->usage_order, page);
4515  }
4516 
4517  gnc_plugin_set_actions_enabled (G_ACTION_MAP(window),
4518  multiple_page_actions,
4519  g_list_length (priv->installed_pages) > 1);
4520 
4521  gnc_main_window_update_title(window);
4522 #ifndef MAC_INTEGRATION
4523  gnc_main_window_update_menu_item(window);
4524 #endif
4525  g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, page);
4526  LEAVE(" ");
4527 }
4528 
4535 static void
4536 gnc_main_window_page_reordered (GtkNotebook *notebook,
4537  GtkWidget *child,
4538  guint pos,
4539  GncMainWindow *window)
4540 {
4541  GncMainWindowPrivate *priv;
4542  GncPluginPage *page;
4543  GList *old_link;
4544 
4545  ENTER("Notebook %p, child %p, index %d, window %p",
4546  notebook, child, pos, window);
4547  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
4548 
4549  if (!child) return;
4550 
4551  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4552 
4553  page = static_cast<GncPluginPage*>(g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL));
4554  if (!page) return;
4555 
4556  old_link = g_list_find (priv->installed_pages, page);
4557  if (!old_link) return;
4558 
4559  priv->installed_pages = g_list_delete_link (priv->installed_pages,
4560  old_link);
4561  priv->installed_pages = g_list_insert (priv->installed_pages,
4562  page, pos);
4563 
4564  LEAVE(" ");
4565 }
4566 
4567 static void
4568 gnc_main_window_plugin_added (GncPlugin *manager,
4569  GncPlugin *plugin,
4570  GncMainWindow *window)
4571 {
4572  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
4573  g_return_if_fail (GNC_IS_PLUGIN (plugin));
4574 
4575  gnc_plugin_add_to_window (plugin, window, window_type);
4576 }
4577 
4578 static void
4579 gnc_main_window_plugin_removed (GncPlugin *manager,
4580  GncPlugin *plugin,
4581  GncMainWindow *window)
4582 {
4583  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
4584  g_return_if_fail (GNC_IS_PLUGIN (plugin));
4585 
4586  gnc_plugin_remove_from_window (plugin, window, window_type);
4587 }
4588 
4589 
4590 /* Command callbacks */
4591 static void
4592 gnc_main_window_cmd_redirect (GSimpleAction *simple,
4593  GVariant *parameter,
4594  gpointer user_data)
4595 {
4596  GncMainWindow *window = (GncMainWindow*)user_data;
4597  GAction *redirect_action;
4598 
4599  PINFO("Redirect action_is %p, name is '%s'", simple, g_action_get_name (G_ACTION(simple)));
4600 
4601  redirect_action = gnc_main_window_get_redirect (window, g_action_get_name (G_ACTION(simple)));
4602 
4603  if (redirect_action)
4604  {
4605  PINFO("Found action %p", redirect_action);
4606  g_action_activate (redirect_action, nullptr);
4607  return;
4608  }
4609 }
4610 
4611 static void
4612 gnc_main_window_cmd_page_setup (GSimpleAction *simple,
4613  GVariant *parameter,
4614  gpointer user_data)
4615 {
4616  GncMainWindow *window = (GncMainWindow*)user_data;
4617  GtkWindow *gtk_window;
4618 
4619  g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
4620 
4621  gtk_window = gnc_window_get_gtk_window(GNC_WINDOW(window));
4622  gnc_ui_page_setup(gtk_window);
4623 }
4624 
4625 gboolean
4627 {
4628  QofBook *book = gnc_get_current_book ();
4629  gboolean use_split_action_for_num_before =
4631  gint use_read_only_threshold_before =
4633  gboolean use_split_action_for_num_after;
4634  gint use_read_only_threshold_after;
4635  gboolean return_val = FALSE;
4636  GList *results = nullptr, *iter;
4637 
4638  if (!options) return return_val;
4639 
4640  results = gnc_option_db_commit (options);
4641  for (iter = results; iter; iter = iter->next)
4642  {
4643  GtkWidget *dialog = gtk_message_dialog_new(gnc_ui_get_main_window (nullptr),
4644  (GtkDialogFlags)0,
4645  GTK_MESSAGE_ERROR,
4646  GTK_BUTTONS_OK,
4647  "%s",
4648  (char*)iter->data);
4649  gtk_dialog_run(GTK_DIALOG(dialog));
4650  gtk_widget_destroy(dialog);
4651  g_free (iter->data);
4652  }
4653  g_list_free (results);
4654  qof_book_begin_edit (book);
4655  qof_book_save_options (book, gnc_option_db_save, options, TRUE);
4656  use_split_action_for_num_after =
4658 
4659  // mark cached value as invalid so we get new value
4660  book->cached_num_days_autoreadonly_isvalid = FALSE;
4661  use_read_only_threshold_after = qof_book_get_num_days_autoreadonly (book);
4662 
4663  if (use_split_action_for_num_before != use_split_action_for_num_after)
4664  {
4666  use_split_action_for_num_after);
4667  return_val = TRUE;
4668  }
4669  if (use_read_only_threshold_before != use_read_only_threshold_after)
4670  return_val = TRUE;
4671 
4672  qof_book_commit_edit (book);
4673  return return_val;
4674 }
4675 
4676 static void
4677 gnc_book_options_dialog_apply_cb(GncOptionsDialog * optionwin,
4678  gpointer user_data)
4679 {
4680  auto options{static_cast<GncOptionDB *>(user_data)};
4681 
4682  if (!options) return;
4683 
4685  gnc_gui_refresh_all ();
4686 }
4687 
4688 static void
4689 gnc_book_options_dialog_close_cb(GncOptionsDialog * optionwin,
4690  gpointer user_data)
4691 {
4692  auto options{static_cast<GncOptionDB *>(user_data)};
4693 
4694  delete optionwin;
4695  gnc_option_db_destroy(options);
4696 }
4697 
4701 void
4703 {
4704  gnc_suspend_gui_refresh ();
4705  if (num_action)
4706  {
4707  /* Set a feature flag in the book for use of the split action field as number.
4708  * This will prevent older GnuCash versions that don't support this feature
4709  * from opening this file. */
4710  gnc_features_set_used (gnc_get_current_book(),
4711  GNC_FEATURE_NUM_FIELD_SOURCE);
4712  }
4713  gnc_book_option_num_field_source_change (num_action);
4714  gnc_resume_gui_refresh ();
4715 }
4716 
4717 static gboolean
4718 show_handler (const char *class_name, gint component_id,
4719  gpointer user_data, gpointer iter_data)
4720 {
4721  auto optwin{static_cast<GncOptionsDialog*>(user_data)};
4722 
4723  if (!optwin)
4724  return(FALSE);
4725 
4726  auto widget = optwin->get_widget();
4727  gtk_window_present(GTK_WINDOW(widget));
4728  return(TRUE);
4729 }
4730 
4731 GtkWidget *
4732 gnc_book_options_dialog_cb (gboolean modal, gchar *title, GtkWindow* parent)
4733 {
4734  auto book = gnc_get_current_book ();
4735 
4736  auto options = gnc_option_db_new();
4737  gnc_option_db_book_options(options);
4738  qof_book_load_options (book, gnc_option_db_load, options);
4739  gnc_option_db_clean (options);
4740 
4741  /* Only allow one Book Options dialog if called from file->properties
4742  menu */
4743  if (gnc_forall_gui_components(DIALOG_BOOK_OPTIONS_CM_CLASS,
4744  show_handler, nullptr))
4745  {
4746  return nullptr;
4747  }
4748  auto optionwin = new GncOptionsDialog (modal,
4749  (title ? title : _( "Book Options")),
4750  DIALOG_BOOK_OPTIONS_CM_CLASS, parent);
4751  optionwin->build_contents(options);
4752  optionwin->set_book_help_cb();
4753  optionwin->set_apply_cb(gnc_book_options_dialog_apply_cb,
4754  (gpointer)options);
4755  optionwin->set_close_cb ( gnc_book_options_dialog_close_cb,
4756  (gpointer)options);
4757  if (modal)
4759  return optionwin->get_widget();
4760 }
4761 
4762 static void
4763 gnc_main_window_cmd_file_properties (GSimpleAction *simple,
4764  GVariant *parameter,
4765  gpointer user_data)
4766 {
4767  GncMainWindow *window = (GncMainWindow*)user_data;
4768  gnc_book_options_dialog_cb (FALSE, nullptr, GTK_WINDOW (window));
4769 }
4770 
4771 static void
4772 gnc_main_window_cmd_file_close (GSimpleAction *simple,
4773  GVariant *parameter,
4774  gpointer user_data)
4775 {
4776  GncMainWindow *window = (GncMainWindow*)user_data;
4777  GncMainWindowPrivate *priv;
4778  GncPluginPage *page;
4779 
4780  g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
4781 
4782  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4783  page = priv->current_page;
4785 }
4786 
4787 static void
4788 gnc_main_window_cmd_file_quit (GSimpleAction *simple,
4789  GVariant *parameter,
4790  gpointer user_data)
4791 {
4792  GncMainWindow *window = (GncMainWindow*)user_data;
4794  return;
4795 
4796  gnc_main_window_quit(window);
4797 }
4798 
4799 static void
4800 gnc_main_window_cmd_edit_cut (GSimpleAction *simple,
4801  GVariant *parameter,
4802  gpointer user_data)
4803 {
4804  GncMainWindow *window = (GncMainWindow*)user_data;
4805  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW(window));
4806  GAction *redirect_action;
4807 
4808  PINFO("Copy action_is %p, name is '%s'", simple, g_action_get_name (G_ACTION(simple)));
4809 
4810  redirect_action = gnc_main_window_get_redirect (window, g_action_get_name (G_ACTION(simple)));
4811 
4812  if (redirect_action)
4813  {
4814  PINFO("Found action %p", redirect_action);
4815  g_action_activate (redirect_action, nullptr);
4816  return;
4817  }
4818 
4819  if (GTK_IS_EDITABLE(widget))
4820  {
4821  gtk_editable_cut_clipboard (GTK_EDITABLE(widget));
4822  }
4823  else if (GTK_IS_TEXT_VIEW(widget))
4824  {
4825  GtkTextBuffer *text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
4826  GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(widget),
4827  GDK_SELECTION_CLIPBOARD);
4828  gboolean editable = gtk_text_view_get_editable (GTK_TEXT_VIEW(widget));
4829 
4830  if (clipboard)
4831  gtk_text_buffer_cut_clipboard (text_buffer, clipboard, editable);
4832  }
4833 }
4834 
4835 static void
4836 gnc_main_window_cmd_edit_copy (GSimpleAction *simple,
4837  GVariant *parameter,
4838  gpointer user_data)
4839 {
4840  GncMainWindow *window = (GncMainWindow*)user_data;
4841  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW(window));
4842  GAction *redirect_action;
4843 
4844  PINFO("Copy action_is %p, name is '%s'", simple, g_action_get_name (G_ACTION(simple)));
4845 
4846  redirect_action = gnc_main_window_get_redirect (window, g_action_get_name (G_ACTION(simple)));
4847 
4848  if (redirect_action)
4849  {
4850  PINFO("Found action %p", redirect_action);
4851  g_action_activate (redirect_action, nullptr);
4852  return;
4853  }
4854 
4855  if (GTK_IS_EDITABLE(widget))
4856  {
4857  gtk_editable_copy_clipboard (GTK_EDITABLE(widget));
4858  }
4859  else if (GTK_IS_TEXT_VIEW(widget))
4860  {
4861  GtkTextBuffer *text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
4862  GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET(widget),
4863  GDK_SELECTION_CLIPBOARD);
4864  if (clipboard)
4865  gtk_text_buffer_copy_clipboard (text_buffer, clipboard);
4866  }
4867 }
4868 
4869 static void
4870 gnc_main_window_cmd_edit_paste (GSimpleAction *simple,
4871  GVariant *parameter,
4872  gpointer user_data)
4873 {
4874  GncMainWindow *window = (GncMainWindow*)user_data;
4875  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW(window));
4876  GAction *redirect_action;
4877 
4878  PINFO("Paste action_is %p, name is '%s'", simple, g_action_get_name (G_ACTION(simple)));
4879 
4880  redirect_action = gnc_main_window_get_redirect (window, g_action_get_name (G_ACTION(simple)));
4881 
4882  if (redirect_action)
4883  {
4884  PINFO("Found action %p", redirect_action);
4885  g_action_activate (redirect_action, nullptr);
4886  return;
4887  }
4888 
4889  if (GTK_IS_EDITABLE(widget))
4890  {
4891  gtk_editable_paste_clipboard (GTK_EDITABLE(widget));
4892  }
4893  else if (GTK_IS_TEXT_VIEW(widget))
4894  {
4895  auto clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_CLIPBOARD);
4896 
4897  if (clipboard)
4898  {
4899  auto text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
4900  auto editable = gtk_text_view_get_editable (GTK_TEXT_VIEW(widget));
4901  gtk_text_buffer_paste_clipboard (text_buffer, clipboard, nullptr,
4902  editable);
4903  }
4904  }
4905 }
4906 
4907 static void
4908 gnc_main_window_cmd_edit_preferences (GSimpleAction *simple,
4909  GVariant *parameter,
4910  gpointer user_data)
4911 {
4912  GncMainWindow *window = (GncMainWindow*)user_data;
4913  gnc_preferences_dialog (GTK_WINDOW(window));
4914 }
4915 
4916 static void
4917 gnc_main_window_cmd_view_refresh (GSimpleAction *simple,
4918  GVariant *parameter,
4919  gpointer user_data)
4920 {
4921 }
4922 
4923 static void
4924 gnc_main_window_cmd_actions_reset_warnings (GSimpleAction *simple,
4925  GVariant *parameter,
4926  gpointer user_data)
4927 {
4928  GncMainWindow *window = (GncMainWindow*)user_data;
4929  gnc_reset_warnings_dialog(GTK_WINDOW(window));
4930 }
4931 
4932 static void
4933 gnc_main_window_cmd_actions_rename_page (GSimpleAction *simple,
4934  GVariant *parameter,
4935  gpointer user_data)
4936 {
4937  GncMainWindow *window = (GncMainWindow*)user_data;
4938  GncMainWindowPrivate *priv;
4939  GncPluginPage *page;
4940  GtkWidget *label, *entry;
4941 
4942  ENTER(" ");
4943  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4944  page = priv->current_page;
4945  if (!page)
4946  {
4947  LEAVE("No current page");
4948  return;
4949  }
4950 
4951  if (!main_window_find_tab_items(window, page, &label, &entry))
4952  {
4953  LEAVE("can't find required widgets");
4954  return;
4955  }
4956 
4957  gtk_entry_set_text(GTK_ENTRY(entry), gtk_label_get_text(GTK_LABEL(label)));
4958  gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
4959  gtk_widget_hide(label);
4960  gtk_widget_show(entry);
4961  gtk_widget_grab_focus(entry);
4962  LEAVE("opened for editing");
4963 }
4964 
4965 static void
4966 gnc_main_window_cmd_view_toolbar (GSimpleAction *simple,
4967  GVariant *parameter,
4968  gpointer user_data)
4969 {
4970  GncMainWindow *window = (GncMainWindow*)user_data;
4971  GncMainWindowPrivate *priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4972  GVariant *state = g_action_get_state (G_ACTION(simple));
4973 
4974  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4975 
4976  g_action_change_state (G_ACTION(simple), g_variant_new_boolean (!g_variant_get_boolean (state)));
4977 
4978  if (!g_variant_get_boolean (state))
4979  gtk_widget_show (priv->toolbar);
4980  else
4981  gtk_widget_hide (priv->toolbar);
4982 
4983  g_variant_unref (state);
4984 }
4985 
4986 static void
4987 gnc_main_window_cmd_view_summary (GSimpleAction *simple,
4988  GVariant *parameter,
4989  gpointer user_data)
4990 {
4991  GncMainWindow *window = (GncMainWindow*)user_data;
4992  GncMainWindowPrivate *priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4993  GList *item;
4994  gboolean visible;
4995 
4996  visible = gnc_main_window_show_summarybar (window, G_ACTION(simple));
4997 
4998  g_action_change_state (G_ACTION(simple), g_variant_new_boolean (!visible));
4999 
5000  for (item = priv->installed_pages; item; item = g_list_next (item))
5001  {
5002  gnc_plugin_page_show_summarybar (static_cast<GncPluginPage*>(item->data),
5003  !visible);
5004  }
5005 }
5006 
5007 static void
5008 gnc_main_window_cmd_view_statusbar (GSimpleAction *simple,
5009  GVariant *parameter,
5010  gpointer user_data)
5011 {
5012  GncMainWindow *window = (GncMainWindow*)user_data;
5013  GncMainWindowPrivate *priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
5014  GVariant *state = g_action_get_state (G_ACTION(simple));
5015 
5016  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
5017 
5018  g_action_change_state (G_ACTION(simple), g_variant_new_boolean (!g_variant_get_boolean (state)));
5019 
5020  if (!g_variant_get_boolean (state))
5021  gtk_widget_show (priv->statusbar);
5022  else
5023  gtk_widget_hide (priv->statusbar);
5024 
5025  g_variant_unref (state);
5026 }
5027 
5028 static void
5029 gnc_main_window_cmd_window_new (GSimpleAction *simple,
5030  GVariant *paramter,
5031  gpointer user_data)
5032 {
5033  GncMainWindow *new_window;
5034 
5035  /* Create the new window */
5036  ENTER(" ");
5037  new_window = gnc_main_window_new ();
5038  gtk_widget_show(GTK_WIDGET(new_window));
5039  LEAVE(" ");
5040 }
5041 
5042 static void
5043 gnc_main_window_cmd_window_move_page (GSimpleAction *simple,
5044  GVariant *paramter,
5045  gpointer user_data)
5046 {
5047  GncMainWindow *window = (GncMainWindow*)user_data;
5048  GncMainWindowPrivate *priv;
5049  GncMainWindow *new_window;
5050  GncPluginPage *page;
5051  GtkNotebook *notebook;
5052  GtkWidget *tab_widget, *menu_widget;
5053 
5054  ENTER("action %p, window %p", simple, window);
5055 
5056  /* Setup */
5057  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
5058  page = priv->current_page;
5059  if (!page)
5060  {
5061  LEAVE("invalid page");
5062  return;
5063  }
5064  if (!page->notebook_page)
5065  {
5066  LEAVE("invalid notebook_page");
5067  return;
5068  }
5069 
5070 #ifndef MAC_INTEGRATION
5071  if (gnc_list_length_cmp (active_windows, gnc_main_window_max_number) == 0)
5072  gnc_info_dialog (GTK_WINDOW(window), "%s",
5073  _("The maximum number of window menu entries reached, no more entries will be added."));
5074 #endif /* !MAC_INTEGRATION */
5075 
5076  notebook = GTK_NOTEBOOK (priv->notebook);
5077  tab_widget = gtk_notebook_get_tab_label (notebook, page->notebook_page);
5078  menu_widget = gtk_notebook_get_menu_label (notebook, page->notebook_page);
5079 
5080  // Remove the page_changed signal callback
5081  gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(page));
5082 
5083  /* Ref the page components, then remove it from its old window */
5084  g_object_ref(page);
5085  g_object_ref(tab_widget);
5086  g_object_ref(menu_widget);
5087  g_object_ref(page->notebook_page);
5088  gnc_main_window_disconnect(window, page);
5089 
5090  /* Create the new window */
5091  new_window = gnc_main_window_new ();
5092  gtk_widget_show(GTK_WIDGET(new_window));
5093 
5094  /* Now add the page to the new window */
5095  gnc_main_window_connect (new_window, page, tab_widget, menu_widget);
5096 
5097  /* Unref the page components now that we're done */
5098  g_object_unref(page->notebook_page);
5099  g_object_unref(menu_widget);
5100  g_object_unref(tab_widget);
5101  g_object_unref(page);
5102 
5103  /* just a little debugging. :-) */
5104  DEBUG("Moved page %p from window %p to new window %p",
5105  page, window, new_window);
5106  DEBUG("Old window current is %p, new window current is %p",
5107  priv->current_page, priv->current_page);
5108 
5109  LEAVE("page moved");
5110 }
5111 
5112 static void
5113 gnc_main_window_cmd_view_tab_position (GSimpleAction *simple,
5114  GVariant *parameter,
5115  gpointer user_data)
5116 {
5117  gint item = g_variant_get_int32 (parameter);
5118 
5119  g_action_change_state (G_ACTION(simple), parameter);
5120 
5121  if (item < 0 || item > 3)
5122  return;
5123 
5124  if (item != 0 && gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_TOP))
5125  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_TOP, FALSE);
5126 
5127  if (item != 1 && gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_BOTTOM))
5128  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_BOTTOM, FALSE);
5129 
5130  if (item != 2 && gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT))
5131  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT, FALSE);
5132 
5133  if (item != 3 && gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT))
5134  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT, FALSE);
5135 
5136  switch (item)
5137  {
5138  case 0:
5139  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_TOP, TRUE);
5140  break;
5141 
5142  case 1:
5143  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_BOTTOM, TRUE);
5144  break;
5145 
5146  case 2:
5147  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT, TRUE);
5148  break;
5149 
5150  case 3:
5151  gnc_prefs_set_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT, TRUE);
5152  break;
5153  }
5154 
5155 }
5156 
5157 #ifndef MAC_INTEGRATION
5158 static void
5159 gnc_main_window_cmd_window_raise (GSimpleAction *simple,
5160  GVariant *parameter,
5161  gpointer user_data)
5162 {
5163  GncMainWindow *window = (GncMainWindow*)user_data;
5164  GncMainWindow *new_window;
5165  gint item;
5166 
5167  g_return_if_fail (G_IS_SIMPLE_ACTION(simple));
5168  g_return_if_fail (GNC_IS_MAIN_WINDOW(window));
5169 
5170  item = g_variant_get_int32 (parameter);
5171 
5172  ENTER("action %p, window %p, item %d", simple, window, item);
5173 
5174  g_action_change_state (G_ACTION(simple), parameter);
5175 
5176  new_window = static_cast<GncMainWindow*>(g_list_nth_data (active_windows, item));
5177  gtk_window_present (GTK_WINDOW(new_window));
5178 
5179  /* revert the change in the radio group
5180  * impossible while handling "changed" (G_SIGNAL_NO_RECURSE) */
5181  g_idle_add ((GSourceFunc)gnc_main_window_update_radio_button, window);
5182  LEAVE(" ");
5183 }
5184 #endif /* !MAC_INTEGRATION */
5185 
5186 static void
5187 gnc_main_window_cmd_help_tutorial (GSimpleAction *simple,
5188  GVariant *paramter,
5189  gpointer user_data)
5190 {
5191  GncMainWindow *window = (GncMainWindow*)user_data;
5192  gnc_gnome_help (GTK_WINDOW(window), DF_GUIDE, NULL);
5193 }
5194 
5195 static void
5196 gnc_main_window_cmd_help_contents (GSimpleAction *simple,
5197  GVariant *paramter,
5198  gpointer user_data)
5199 {
5200  GncMainWindow *window = (GncMainWindow*)user_data;
5201  gnc_gnome_help (GTK_WINDOW(window), DF_MANUAL, NULL);
5202 }
5203 
5213 static gchar *
5214 get_file (const gchar *partial)
5215 {
5216  gchar *filename, *text = nullptr;
5217  gsize length;
5218 
5219  filename = gnc_filepath_locate_doc_file(partial);
5220  if (filename && g_file_get_contents(filename, &text, &length, nullptr))
5221  {
5222  if (length)
5223  {
5224  g_free(filename);
5225  return text;
5226  }
5227  g_free(text);
5228  }
5229  g_free (filename);
5230  return nullptr;
5231 }
5232 
5233 
5243 static gchar **
5244 get_file_strsplit (const gchar *partial)
5245 {
5246  gchar *text, **lines;
5247 
5248  text = get_file(partial);
5249  if (!text)
5250  return nullptr;
5251 
5252  lines = g_strsplit_set(text, "\r\n", -1);
5253  g_free(text);
5254  return lines;
5255 }
5262 static gboolean
5263 url_signal_cb (GtkAboutDialog *dialog, gchar *uri, gpointer data)
5264 {
5265  gnc_launch_doclink (GTK_WINDOW(dialog), uri);
5266  return TRUE;
5267 }
5268 
5269 static gboolean
5270 link_button_cb (GtkLinkButton *button, gpointer user_data)
5271 {
5272  const gchar *uri = gtk_link_button_get_uri (button);
5273  gchar *escaped_uri = g_uri_escape_string (uri, ":/.\\", true);
5274  gnc_launch_doclink (GTK_WINDOW(user_data), escaped_uri);
5275  g_free (escaped_uri);
5276  return TRUE;
5277 }
5278 
5279 static void
5280 add_about_paths (GtkDialog *dialog)
5281 {
5282  GtkWidget *page_vbox = gnc_get_dialog_widget_from_id (dialog, "page_vbox");
5283  GtkWidget *grid;
5284  gint i = 0;
5285 
5286  if (!page_vbox)
5287  {
5288  PWARN("Unable to find AboutDialog 'page_vbox' Widget");
5289  return;
5290  }
5291 
5292  grid = gtk_grid_new ();
5293 
5294  for (const auto& ep : gnc_list_all_paths ())
5295  {
5296  gchar *env_name = g_strconcat (ep.env_name, ":", NULL);
5297  GtkWidget *label = gtk_label_new (env_name);
5298  const gchar *uri = gnc_uri_create_uri ("file", NULL, 0, NULL, NULL, ep.env_path);
5299  gchar *display_uri = gnc_doclink_get_unescaped_just_uri (uri);
5300  GtkWidget *widget = gtk_link_button_new_with_label (uri, display_uri);
5301 
5302  gtk_grid_attach (GTK_GRID(grid), label, 0, i, 1, 1);
5303  gtk_widget_set_halign (label, GTK_ALIGN_END);
5304  gtk_grid_attach (GTK_GRID(grid), widget, 1, i, 1, 1);
5305  gtk_widget_set_halign (widget, GTK_ALIGN_START);
5306  gtk_widget_set_margin_top (widget, 0);
5307  gtk_widget_set_margin_bottom (widget, 0);
5308 
5309  if (ep.modifiable)
5310  {
5311  GtkWidget *mod_lab = gtk_label_new (_("(user modifiable)"));
5312  gtk_grid_attach (GTK_GRID(grid), mod_lab, 2, i, 1, 1);
5313  gtk_widget_show (mod_lab);
5314  }
5315  g_signal_connect (widget, "activate-link",
5316  G_CALLBACK(link_button_cb), dialog);
5317  i++;
5318 
5319  g_free (display_uri);
5320  g_free (env_name);
5321  }
5322  gtk_container_add_with_properties (GTK_CONTAINER(page_vbox), grid,
5323  "position", 1, NULL);
5324  gtk_widget_show_all (grid);
5325 }
5326 
5329 static void
5330 gnc_main_window_cmd_help_about (GSimpleAction *simple,
5331  GVariant *paramter,
5332  gpointer user_data)
5333 {
5334  GncMainWindow *window = (GncMainWindow*)user_data;
5335  /* Translators: %s will be replaced with the current year */
5336  gchar *copyright = g_strdup_printf(_("Copyright © 1997-%s The GnuCash contributors."),
5337  GNC_VCS_REV_YEAR);
5338  gchar **authors = get_file_strsplit("AUTHORS");
5339  gchar **documenters = get_file_strsplit("DOCUMENTERS");
5340  gchar *license = get_file("LICENSE");
5341  GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
5342  GdkPixbuf *logo = gtk_icon_theme_load_icon (icon_theme,
5343  GNC_ICON_APP,
5344  128,
5345  GTK_ICON_LOOKUP_USE_BUILTIN,
5346  nullptr);
5347  gchar *version = g_strdup_printf ("%s: %s\n%s: %s\nFinance::Quote: %s",
5348  _("Version"), gnc_version(),
5349  _("Build ID"), gnc_build_id(),
5352  : "-");
5353  GtkDialog *dialog = GTK_DIALOG (gtk_about_dialog_new ());
5354  g_object_set (G_OBJECT (dialog),
5355  "authors", authors,
5356  "documenters", documenters,
5357  "comments", _("Accounting for personal and small business finance."),
5358  "copyright", copyright,
5359  "license", license,
5360  "logo", logo,
5361  "name", "GnuCash",
5362  /* Translators: the following string will be shown in Help->About->Credits
5363  Enter your name or that of your team and an email contact for feedback.
5364  The string can have multiple rows, so you can also add a list of
5365  contributors. */
5366  "translator-credits", _("translator-credits"),
5367  "version", version,
5368  "website", PACKAGE_URL,
5369  "website-label", _("Visit the GnuCash website."),
5370  nullptr);
5371 
5372  g_free(version);
5373  g_free(copyright);
5374  if (license)
5375  g_free(license);
5376  if (documenters)
5377  g_strfreev(documenters);
5378  if (authors)
5379  g_strfreev(authors);
5380  g_object_unref (logo);
5381  g_signal_connect (dialog, "activate-link",
5382  G_CALLBACK (url_signal_cb), nullptr);
5383 
5384  // Add environment paths
5385  add_about_paths (dialog);
5386 
5387  /* Set dialog to resize. */
5388  gtk_window_set_resizable(GTK_WINDOW (dialog), TRUE);
5389 
5390  gtk_window_set_transient_for (GTK_WINDOW (dialog),
5391  GTK_WINDOW (window));
5392  gtk_dialog_run (dialog);
5393  gtk_widget_destroy (GTK_WIDGET (dialog));
5394 }
5395 
5396 
5397 /************************************************************
5398  * *
5399  ************************************************************/
5400 
5401 void
5403 {
5404  GList *window_iter;
5405 #ifdef MAC_INTEGRATION
5406  auto theApp{static_cast<GtkosxApplication *>(g_object_new(GTKOSX_TYPE_APPLICATION, nullptr))};
5407 #endif
5408  for (window_iter = active_windows; window_iter != nullptr; window_iter = window_iter->next)
5409  {
5410  gtk_widget_show(GTK_WIDGET(window_iter->data));
5411  }
5412 #ifdef MAC_INTEGRATION
5413  g_signal_connect(theApp, "NSApplicationWillTerminate",
5414  G_CALLBACK(gnc_quartz_shutdown), nullptr);
5415  gtkosx_application_ready(theApp);
5416  g_object_unref (theApp);
5417 #endif
5418 }
5419 
5420 GtkWindow *
5421 gnc_ui_get_gtk_window (GtkWidget *widget)
5422 {
5423  GtkWidget *toplevel;
5424 
5425  if (!widget)
5426  return nullptr;
5427 
5428  toplevel = gtk_widget_get_toplevel (widget);
5429  if (toplevel && GTK_IS_WINDOW (toplevel))
5430  return GTK_WINDOW (toplevel);
5431  else
5432  return nullptr;
5433 }
5434 
5435 GtkWindow *
5436 gnc_ui_get_main_window (GtkWidget *widget)
5437 {
5438  GList *window;
5439 
5440  GtkWindow *toplevel = gnc_ui_get_gtk_window (widget);
5441  while (toplevel && !GNC_IS_MAIN_WINDOW (toplevel))
5442  toplevel = gtk_window_get_transient_for(toplevel);
5443 
5444  if (toplevel)
5445  return toplevel;
5446 
5447  for (window = active_windows; window; window = window->next)
5448  if (gtk_window_is_active (GTK_WINDOW (window->data)))
5449  return static_cast<GtkWindow*>(window->data);
5450 
5451  for (window = active_windows; window; window = window->next)
5452  if (gtk_widget_get_mapped (GTK_WIDGET(window->data)))
5453  return static_cast<GtkWindow*>(window->data);
5454 
5455  return nullptr;
5456 }
5457 
5458 
5464 static GtkWindow *
5465 gnc_main_window_get_gtk_window (GncWindow *window)
5466 {
5467  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), nullptr);
5468  return GTK_WINDOW(window);
5469 }
5470 
5471 
5477 static GtkWidget *
5478 gnc_main_window_get_statusbar (GncWindow *window_in)
5479 {
5480  GncMainWindowPrivate *priv;
5481  GncMainWindow *window;
5482 
5483  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), nullptr);
5484 
5485  window = GNC_MAIN_WINDOW(window_in);
5486  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
5487  return priv->statusbar;
5488 }
5489 
5490 
5496 static GtkWidget *
5497 gnc_main_window_get_progressbar (GncWindow *window_in)
5498 {
5499  GncMainWindowPrivate *priv;
5500  GncMainWindow *window;
5501 
5502  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), nullptr);
5503 
5504  window = GNC_MAIN_WINDOW(window_in);
5505  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
5506  return priv->progressbar;
5507 }
5508 
5509 
5515 static GtkWidget *
5516 gnc_main_window_get_menubar (GncWindow *window)
5517 {
5518  GncMainWindowPrivate *priv;
5519 
5520  g_return_val_if_fail (GNC_IS_MAIN_WINDOW(window), nullptr);
5521 
5522  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
5523 
5524  return priv->menubar;
5525 }
5526 
5532 static GtkWidget *
5533 gnc_main_window_get_toolbar (GncWindow *window)
5534 {
5535  GncMainWindowPrivate *priv;
5536 
5537  g_return_val_if_fail (GNC_IS_MAIN_WINDOW(window), nullptr);
5538 
5539  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
5540 
5541  return priv->toolbar;
5542 }
5543 
5549 static GMenuModel *
5550 gnc_main_window_get_menubar_model (GncWindow *window)
5551 {
5552  GncMainWindowPrivate *priv;
5553 
5554  g_return_val_if_fail (GNC_IS_MAIN_WINDOW(window), nullptr);
5555 
5556  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
5557 
5558  return priv->menubar_model;
5559 }
5560 
5566 static GtkAccelGroup *
5567 gnc_main_window_get_accel_group (GncWindow *window)
5568 {
5569  GncMainWindowPrivate *priv;
5570 
5571  g_return_val_if_fail (GNC_IS_MAIN_WINDOW(window), nullptr);
5572 
5573  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
5574 
5575  return priv->accel_group;
5576 }
5577 
5582 static void
5583 gnc_window_main_window_init (GncWindowInterface *iface)
5584 {
5585  iface->get_gtk_window = gnc_main_window_get_gtk_window;
5586  iface->get_statusbar = gnc_main_window_get_statusbar;
5587  iface->get_progressbar = gnc_main_window_get_progressbar;
5588  iface->get_menubar = gnc_main_window_get_menubar;
5589  iface->get_toolbar = gnc_main_window_get_toolbar;
5590  iface->get_menubar_model = gnc_main_window_get_menubar_model;
5591  iface->get_accel_group = gnc_main_window_get_accel_group;
5592 }
5593 
5594 
5595 /* Set the window where all progressbar updates should occur. This
5596  * is a wrapper around the gnc_window_set_progressbar_window()
5597  * function.
5598  */
5599 void
5601 {
5602  GncWindow *gncwin;
5603  gncwin = GNC_WINDOW(window);
5604  gnc_window_set_progressbar_window(gncwin);
5605 }
5606 
5607 
5620 static void
5621 do_popup_menu (GncPluginPage *page, GdkEventButton *event)
5622 {
5623  GtkBuilder *builder;
5624  GMenuModel *menu_model;
5625  GtkWidget *menu;
5626  const gchar *menu_qualifier;
5627  gchar *popup_menu_name;
5628  GncWindow* gnc_window;
5629  GtkWidget *statusbar;
5630 
5631  g_return_if_fail (GNC_IS_PLUGIN_PAGE(page));
5632 
5633  ENTER("page %p, event %p", page, event);
5634 
5635  gnc_window = GNC_WINDOW(GNC_PLUGIN_PAGE(page)->window);
5636 
5637  statusbar = gnc_window_get_statusbar (gnc_window);
5638 
5639  builder = gnc_plugin_page_get_builder (page);
5640 
5641  menu_qualifier = gnc_plugin_page_get_menu_popup_qualifier (page);
5642 
5643  if (!menu_qualifier)
5644  menu_qualifier = gnc_plugin_page_get_menu_qualifier (page);
5645 
5646  if (builder == nullptr)
5647  {
5648  LEAVE("no builder");
5649  return;
5650  }
5651 
5652  if (menu_qualifier)
5653  popup_menu_name = g_strconcat ("mainwin-popup-", menu_qualifier, nullptr);
5654  else
5655  popup_menu_name = g_strdup ("mainwin-popup");
5656 
5657  menu_model = (GMenuModel *)gtk_builder_get_object (builder, popup_menu_name);
5658 
5659  if (!menu_model)
5660  menu_model = (GMenuModel *)gtk_builder_get_object (builder, "mainwin-popup");
5661 
5662  menu = gtk_menu_new_from_model (menu_model);
5663 
5664  if (!menu)
5665  {
5666  LEAVE("no menu");
5667  return;
5668  }
5669 
5670  // add tooltip redirect call backs
5671  gnc_plugin_add_menu_tooltip_callbacks (menu, menu_model, statusbar);
5672 
5673  gtk_menu_attach_to_widget (GTK_MENU(menu), GTK_WIDGET(page->window), nullptr);
5674  gtk_menu_popup_at_pointer (GTK_MENU(menu), (GdkEvent *) event);
5675 
5676  g_free (popup_menu_name);
5677 
5678  LEAVE(" ");
5679 }
5680 
5681 
5695 gboolean
5697  GncPluginPage *page)
5698 {
5699  ENTER("widget %p, page %p", widget, page);
5700  do_popup_menu(page, nullptr);
5701  LEAVE(" ");
5702  return TRUE;
5703 }
5704 
5705 
5706 /* Callback function invoked when the user clicks in the content of
5707  * any Gnucash window. If this was a "right-click" then Gnucash will
5708  * popup the contextual menu.
5709  */
5710 gboolean
5711 gnc_main_window_button_press_cb (GtkWidget *whatever,
5712  GdkEventButton *event,
5713  GncPluginPage *page)
5714 {
5715  g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
5716 
5717  ENTER("widget %p, event %p, page %p", whatever, event, page);
5718  /* Ignore double-clicks and triple-clicks */
5719  if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
5720  {
5721  do_popup_menu(page, event);
5722  LEAVE("menu shown");
5723  return TRUE;
5724  }
5725 
5726  LEAVE("other click");
5727  return FALSE;
5728 }
5729 
5730 void
5732  gboolean sensitive)
5733 {
5734  for (auto tmp = active_windows; tmp; tmp = g_list_next(tmp))
5735  {
5736  auto action{gnc_main_window_find_action (static_cast<GncMainWindow*>(tmp->data), action_name)};
5737  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), sensitive);
5738  }
5739 }
5740 
5741 GMenuModel *
5742 gnc_main_window_get_menu_model (GncMainWindow *window)
5743 {
5744  GncMainWindowPrivate *priv;
5745 
5746  g_return_val_if_fail (GNC_IS_MAIN_WINDOW(window), nullptr);
5747 
5748  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
5749 
5750  return priv->menubar_model;
5751 }
5752 
5753 gboolean
5754 gnc_main_window_just_plugin_prefs (GncMainWindow* window)
5755 {
5756  return window->just_plugin_prefs;
5757 }
5758 
Holds all of the options for a book, report, or stylesheet, organized by GncOptionSections.
void gnc_preferences_dialog(GtkWindow *parent)
This function creates the preferences dialog and presents it to the user.
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.
void gnc_menubar_model_remove_items_with_attrib(GMenuModel *menu_model, const gchar *attrib)
Remove GMenuModel entries based on having an attribute value equal to attrib, it does not matter what...
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.
void gnc_option_db_clean(GncOptionDB *odb)
Reset all ui_items to the option value.
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_main_window_update_menu_and_toolbar(GncMainWindow *window, GncPluginPage *page, const gchar **ui_updates)
Update the main window menu with the placeholders listed in ui_updates and load the page specific too...
void qof_book_load_options(QofBook *book, GncOptionLoad load_cb, GncOptionDB *odb)
Load a GncOptionsDB from KVP data.
Definition: qofbook.cpp:1316
GtkApplicationWindow gtk_application_window
The parent object for a main window.
void gnc_plugin_page_destroy_widget(GncPluginPage *plugin_page)
Destroy the display widget that corresponds to this plugin.
GtkAccelGroup * accel_group
The accelerator group for the window.
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:426
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
GtkWindow * gnc_ui_get_main_window(GtkWidget *widget)
Get a pointer to the final GncMainWindow widget is rooted in.
gint index
Index number in active windows list.
utility functions for the GnuCash UI
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
void gnc_main_window_set_vis_of_items_by_action(GncMainWindow *window, const gchar **action_names, gboolean vis)
Show or hide menu and toolbar items based on a NULL terminated list of action names.
functions to query various version related strings that were set at build time.
gtk helper routines.
void gnc_plugin_page_merge_actions(GncPluginPage *page)
Add the actions for a content page to the specified window.
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition: qofbackend.h:57
void gnc_main_window_menu_add_accelerator_keys(GncMainWindow *window)
Scan the main window menu and add accelerator keys to main window accelerator group.
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:420
GMenuModel * gnc_main_window_get_menu_model(GncMainWindow *window)
Return the GMenuModel for the main window menu bar.
gboolean gnc_menubar_model_find_item(GMenuModel *menu_model, GncMenuModelSearch *gsm)
Find a GtkMenu item from the action name.
This data structure allows the passing of the tab width and whether the tab layout is on the left or ...
void gnc_main_window_init_short_names(GncMainWindow *window, GncToolBarShortNames *toolbar_labels)
Update the labels of the toolbar items with short names.
#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:968
GMenuModel * menubar_model
The menubar_model.
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...
GSimpleActionGroup * gnc_plugin_page_get_action_group(GncPluginPage *page)
Retrieve the GSimpleActionGroup object associated with this page.
GtkWidget * toolbar
The toolbar.
void gnc_plugin_add_toolbar_tooltip_callbacks(GtkWidget *toolbar, GtkWidget *statusbar)
This function adds the tooltip callbacks to make the tooltips appear in the status bar...
Definition: gnc-plugin.c:299
GKeyFile helper routines.
GtkBuilder * gnc_plugin_page_get_builder(GncPluginPage *page)
Retrieve the GtkBuilder object associated with this page.
gboolean restoring_pages
Set when restoring plugin pages.
gint pos[2]
Array for window position.
Plugin management functions for the GnuCash UI.
GtkWidget * gnc_main_window_menu_find_menu_item(GncMainWindow *window, const gchar *action_name)
Find the menu item with the given action name for the window specified.
void gnc_plugin_add_to_window(GncPlugin *plugin, GncMainWindow *window, GQuark type)
Add the specified plugin from the specified window.
Definition: gnc-plugin.c:133
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.
gboolean window_quitting
Set to TRUE when quitting from this window.
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.
C public interface for the Options Database.
void gnc_plugin_page_set_page_long_name(GncPluginPage *page, const char *name)
Set the long name of this page.
void gnc_main_window_merge_actions(GncMainWindow *window, const gchar *group_name, GActionEntry *actions, guint n_actions, const gchar **ui_updates, const gchar *ui_filename, gpointer user_data)
Add a set of actions to the specified window.
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.cpp: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:71
void gnc_main_window_foreach_page(GncMainWindowPageFunc fn, gpointer user_data)
Iterator function to walk all pages in all windows, calling the specified function for each page...
gboolean gnc_main_window_update_menu_for_action(GncMainWindow *window, const gchar *action_name, const gchar *label, const gchar *tooltip)
Find the GMenuModel item given the action name for the window specified.
void gnc_main_window_unmerge_actions(GncMainWindow *window, const gchar *group_name)
Remove a set of actions from the specified window.
const gchar * gnc_plugin_page_get_menu_qualifier(GncPluginPage *page)
Retrieve the menu qualifier for this page.
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
void gnc_option_db_save(GncOptionDB *odb, QofBook *book, gboolean clear_options)
Save the GncOptionDB contents into a book&#39;s options store.
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.
const gchar * gnc_plugin_page_get_menu_popup_qualifier(GncPluginPage *page)
Retrieve the menu popup qualifier for this page.
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.
void gnc_plugin_add_menu_tooltip_callbacks(GtkWidget *menubar, GMenuModel *menubar_model, GtkWidget *statusbar)
This function adds the tooltip callbacks to make the tooltips appear in the status bar...
Definition: gnc-plugin.c:268
G_DEFINE_TYPE_WITH_CODE(GncMainWindow, gnc_main_window, GTK_TYPE_APPLICATION_WINDOW, G_IMPLEMENT_INTERFACE(GNC_TYPE_WINDOW, gnc_window_main_window_init)) static guint main_window_signals[LAST_SIGNAL]
A holding place for all the signals generated by the main window code.
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
void main_window_update_page_long_name(GncPluginPage *page, const gchar *long_name_in)
Update the long name of the page in the main window.
#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
void gnc_gobject_tracking_remember(GObject *object)
Tell gnucash to remember this object in the database.
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:1322
gboolean visible
Whether or not the GAction should be visible.
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.
GAction * gnc_main_window_find_action_in_group(GncMainWindow *window, const gchar *group_name, const gchar *action_name)
Find the GAction in a specific action group for window.
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:574
#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:383
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...
GAction * gnc_main_window_find_action(GncMainWindow *window, const gchar *action_name)
Find the GAction in the main window.
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.
void gnc_option_db_load(GncOptionDB *odb, QofBook *book)
Load a book&#39;s options into the GncOptionDB.
gchar * action_name
The name of the GAction to be updated.
void gnc_option_db_destroy(GncOptionDB *odb)
Destruct and release a GncOptionDB.
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:369
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.
The instance data structure for a main window object.
GtkWidget * gnc_menubar_model_find_menu_item(GMenuModel *menu_model, GtkWidget *menu, const gchar *action_name)
Find a GtkMenu item from the action name.
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_...
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
Definition: qofevent.cpp:103
const gchar * gnc_plugin_page_get_simple_action_group_name(GncPluginPage *page)
Retrieve the simple action group name associated with this plugin page.
GncPluginManager * gnc_plugin_manager_get(void)
Retrieve a pointer to the plugin manager.
void gnc_plugin_set_actions_enabled(GActionMap *action_map, const gchar **action_names, gboolean enable)
This function sets the sensitivity of a GAction in a specific group.
Definition: gnc-plugin.c:250
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:175
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.
GtkWidget * gnc_find_toolbar_item(GtkWidget *toolbar, const gchar *action_name)
Search the toolbar for the tool item based on the action name.
Dialog for handling user preferences.
GtkWidget * menubar
The menubar.
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:375
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 just_plugin_prefs
Just remove preferences only from plugins.
void gnc_add_accelerator_keys_for_menu(GtkWidget *menu, GMenuModel *model, GtkAccelGroup *accel_group)
Add accelerator keys for menu item widgets.
void gnc_plugin_init_short_names(GtkWidget *toolbar, GncToolBarShortNames *toolbar_labels)
Add "short" labels to existing actions.
Definition: gnc-plugin.c:229
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 GAction.
This data structure is used to describe the requested state of a GAction, and is used to pass data am...
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:497
A structure for defining alternate action names for use in the toolbar.
GncMainWindow * gnc_main_window_new(void)
Create a new gnc main window plugin.
void gnc_plugin_page_set_page_color(GncPluginPage *page, const char *color)
Set the color of this page.
GList * gnc_option_db_commit(GncOptionDB *odb)
Write all changed ui_item values to their options.
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.
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.
#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 time
Definition: gnc-date.cpp:261
Utility functions for convert uri in separate components and back.
GncPluginPage * current_page
The currently selected page.
GSimpleActionGroup * gnc_main_window_get_action_group(GncMainWindow *window, const gchar *group_name)
Retrieve a specific set of user interface actions from a window.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
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.
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.
void gnc_option_db_book_options(GncOptionDB *odb)
Register the standard option set for a QofBook.
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_main_window_manual_merge_actions(GncMainWindow *window, const gchar *group_name, GSimpleActionGroup *group)
Manually add a set of actions to 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.
gboolean gnc_menubar_model_update_item(GMenuModel *menu_model, const gchar *action_name, const gchar *target, const gchar *label, const gchar *accel_name, const gchar *tooltip)
Update the GMenuModel item based on the action name by copying existing item, removing it and inserti...
GAction * gnc_plugin_page_get_action(GncPluginPage *page, const gchar *name)
Retrieve a GAction object associated with this page.
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.
GtkWidget * gnc_main_window_toolbar_find_tool_item(GncMainWindow *window, const gchar *action_name)
Find the toolbar item with the given action name for the window specified.
GtkWidget * gnc_plugin_page_create_widget(GncPluginPage *plugin_page)
Create the display widget that corresponds to this plugin.
constexpr auto gnc_main_window_max_number
Max number of windows allowed.
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.
GncOptionDB * gnc_option_db_new(void)
Create an empty option database.