GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-main-window.c
Go to the documentation of this file.
1 /*
2  * gnc-main-window.c -- GtkWindow which represents the
3  * GnuCash main window.
4  *
5  * Copyright (C) 2003 Jan Arne Petersen <[email protected]>
6  * Copyright (C) 2003,2005,2006 David Hampton <[email protected]>
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 [email protected]
24  */
25 
35 #include "config.h"
36 
37 #include <glib/gi18n.h>
38 #include <gdk/gdkkeysyms.h>
39 
40 #include "gnc-plugin.h"
41 #include "gnc-plugin-manager.h"
42 #include "gnc-main-window.h"
43 
44 #include "dialog-options.h"
45 #include "dialog-preferences.h"
46 #include "dialog-reset-warnings.h"
47 #include "dialog-transfer.h"
48 #include "dialog-utils.h"
49 #include "file-utils.h"
50 #include "gnc-component-manager.h"
51 #include "gnc-engine.h"
52 #include "gnc-file.h"
53 #include "gnc-filepath-utils.h"
54 #include "gnc-gkeyfile-utils.h"
55 #include "gnc-gnome-utils.h"
56 #include "gnc-gobject-utils.h"
57 #include "gnc-gui-query.h"
58 #include "gnc-hooks.h"
59 #include "gnc-session.h"
60 #include "gnc-state.h"
61 #include "gnc-ui.h"
62 #include "gnc-ui-util.h"
63 #include "gnc-uri-utils.h"
64 #include "core-utils/gnc-version.h"
65 #include "gnc-window.h"
66 #include "gnc-prefs.h"
67 #include "option-util.h"
68 // +JSLED
69 //#include "gnc-html.h"
70 #include "gnc-autosave.h"
71 #include "print-session.h"
72 #ifdef MAC_INTEGRATION
73 #include <gtkmacintegration/gtkosxapplication.h>
74 #endif
75 #ifdef HAVE_SYS_STAT_H
76 # include <sys/types.h>
77 # include <sys/stat.h> // for stat(2)
78 #endif
79 
81 enum
82 {
83  PAGE_ADDED,
84  PAGE_CHANGED,
85  LAST_SIGNAL
86 };
87 
90 #define PLUGIN_PAGE_LABEL "plugin-page"
91 
92 #define PLUGIN_PAGE_CLOSE_BUTTON "close-button"
93 #define PLUGIN_PAGE_TAB_LABEL "label"
94 
95 #define GNC_PREF_SHOW_CLOSE_BUTTON "tab-close-buttons"
96 #define GNC_PREF_TAB_NEXT_RECENT "tab-next-recent"
97 #define GNC_PREF_TAB_POSITION_TOP "tab-position-top"
98 #define GNC_PREF_TAB_POSITION_BOTTOM "tab-position-bottom"
99 #define GNC_PREF_TAB_POSITION_LEFT "tab-position-left"
100 #define GNC_PREF_TAB_POSITION_RIGHT "tab-position-right"
101 #define GNC_PREF_TAB_WIDTH "tab-width"
102 #define GNC_PREF_TAB_COLOR "show-account-color-tabs"
103 #define GNC_PREF_SAVE_CLOSE_EXPIRES "save-on-close-expires"
104 #define GNC_PREF_SAVE_CLOSE_WAIT_TIME "save-on-close-wait-time"
105 
106 #define GNC_MAIN_WINDOW_NAME "GncMainWindow"
107 
108 
109 /* Static Globals *******************************************************/
110 
112 static QofLogModule log_module = GNC_MOD_GUI;
114 static GObjectClass *parent_class = NULL;
116 static GQuark window_type = 0;
119 static GList *active_windows = NULL;
122 static guint secs_to_save = 0;
123 #define MSG_AUTO_SAVE _("Changes will be saved automatically in %u seconds")
124 
125 /* Declarations *********************************************************/
126 static void gnc_main_window_class_init (GncMainWindowClass *klass);
127 static void gnc_main_window_init (GncMainWindow *window, GncMainWindowClass *klass);
128 static void gnc_main_window_finalize (GObject *object);
129 static void gnc_main_window_destroy (GtkObject *object);
130 
131 static void gnc_main_window_setup_window (GncMainWindow *window);
132 static void gnc_window_main_window_init (GncWindowIface *iface);
133 #ifndef MAC_INTEGRATION
134 static void gnc_main_window_update_all_menu_items (void);
135 #endif
136 
137 /* Callbacks */
138 static void gnc_main_window_add_widget (GtkUIManager *merge, GtkWidget *widget, GncMainWindow *window);
139 static void gnc_main_window_switch_page (GtkNotebook *notebook, gpointer *notebook_page, gint pos, GncMainWindow *window);
140 static void gnc_main_window_page_reordered (GtkNotebook *notebook, GtkWidget *child, guint pos, GncMainWindow *window);
141 static void gnc_main_window_plugin_added (GncPlugin *manager, GncPlugin *plugin, GncMainWindow *window);
142 static void gnc_main_window_plugin_removed (GncPlugin *manager, GncPlugin *plugin, GncMainWindow *window);
143 static void gnc_main_window_engine_commit_error_callback( gpointer data, QofBackendError errcode );
144 
145 /* Command callbacks */
146 static void gnc_main_window_cmd_page_setup (GtkAction *action, GncMainWindow *window);
147 static void gnc_main_window_cmd_file_properties (GtkAction *action, GncMainWindow *window);
148 static void gnc_main_window_cmd_file_close (GtkAction *action, GncMainWindow *window);
149 static void gnc_main_window_cmd_file_quit (GtkAction *action, GncMainWindow *window);
150 static void gnc_main_window_cmd_edit_cut (GtkAction *action, GncMainWindow *window);
151 static void gnc_main_window_cmd_edit_copy (GtkAction *action, GncMainWindow *window);
152 static void gnc_main_window_cmd_edit_paste (GtkAction *action, GncMainWindow *window);
153 static void gnc_main_window_cmd_edit_preferences (GtkAction *action, GncMainWindow *window);
154 static void gnc_main_window_cmd_view_refresh (GtkAction *action, GncMainWindow *window);
155 static void gnc_main_window_cmd_view_toolbar (GtkAction *action, GncMainWindow *window);
156 static void gnc_main_window_cmd_view_summary (GtkAction *action, GncMainWindow *window);
157 static void gnc_main_window_cmd_view_statusbar (GtkAction *action, GncMainWindow *window);
158 static void gnc_main_window_cmd_actions_reset_warnings (GtkAction *action, GncMainWindow *window);
159 static void gnc_main_window_cmd_actions_rename_page (GtkAction *action, GncMainWindow *window);
160 static void gnc_main_window_cmd_window_new (GtkAction *action, GncMainWindow *window);
161 static void gnc_main_window_cmd_window_move_page (GtkAction *action, GncMainWindow *window);
162 #ifndef MAC_INTEGRATION
163 static void gnc_main_window_cmd_window_raise (GtkAction *action, GtkRadioAction *current, GncMainWindow *window);
164 #endif
165 static void gnc_main_window_cmd_help_tutorial (GtkAction *action, GncMainWindow *window);
166 static void gnc_main_window_cmd_help_contents (GtkAction *action, GncMainWindow *window);
167 static void gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window);
168 
169 static void do_popup_menu(GncPluginPage *page, GdkEventButton *event);
170 static gboolean gnc_main_window_popup_menu_cb (GtkWidget *widget, GncPluginPage *page);
171 static GtkWidget *gnc_main_window_get_statusbar (GncWindow *window_in);
172 static void statusbar_notification_lastmodified(void);
173 
174 #ifdef MAC_INTEGRATION
175 static void gnc_quartz_shutdown(GtkosxApplication *theApp, gpointer data);
176 static gboolean gnc_quartz_should_quit(GtkosxApplication *theApp, GncMainWindow *window);
177 static void gnc_quartz_set_menu(GncMainWindow* window);
178 #endif
179 
182 typedef struct GncMainWindowPrivate
183 {
188  GtkWidget *menu_dock;
191  GtkWidget *toolbar;
193  GtkWidget *notebook;
195  gboolean show_color_tabs;
199  GtkWidget *statusbar;
203  GtkWidget *progressbar;
208  GtkWidget *about_dialog;
209 
213  GtkActionGroup *action_group;
214 
218  GList *usage_order;
223 
228  GHashTable *merged_actions_table;
230 
231 #define GNC_MAIN_WINDOW_GET_PRIVATE(o) \
232  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_MAIN_WINDOW, GncMainWindowPrivate))
233 
236 typedef struct
237 {
240  guint merge_id;
243  GtkActionGroup *action_group;
245 
248 static guint main_window_signals[LAST_SIGNAL] = { 0 };
249 
250 
255 static GtkActionEntry gnc_menu_actions [] =
256 {
257  /* Toplevel */
258 
259  { "FileAction", NULL, N_("_File"), NULL, NULL, NULL, },
260  { "EditAction", NULL, N_("_Edit"), NULL, NULL, NULL },
261  { "ViewAction", NULL, N_("_View"), NULL, NULL, NULL },
262  { "ActionsAction", NULL, N_("_Actions"), NULL, NULL, NULL },
263  { "TransactionAction", NULL, N_("Tra_nsaction"), NULL, NULL, NULL },
264  { "ReportsAction", NULL, N_("_Reports"), NULL, NULL, NULL },
265  { "ToolsAction", NULL, N_("_Tools"), NULL, NULL, NULL },
266  { "ExtensionsAction", NULL, N_("E_xtensions"), NULL, NULL, NULL },
267  { "WindowsAction", NULL, N_("_Windows"), NULL, NULL, NULL },
268  { "HelpAction", NULL, N_("_Help"), NULL, NULL, NULL },
269 
270  /* File menu */
271 
272  { "FileImportAction", NULL, N_("_Import"), NULL, NULL, NULL },
273  { "FileExportAction", NULL, N_("_Export"), NULL, NULL, NULL },
274  {
275  "FilePrintAction", GTK_STOCK_PRINT, N_("_Print..."), "<control>p",
276  N_("Print the currently active page"), NULL
277  },
278 #ifndef GTK_STOCK_PAGE_SETUP
279 # define GTK_STOCK_PAGE_SETUP NULL
280 #endif
281  {
282  "FilePageSetupAction", GTK_STOCK_PAGE_SETUP, N_("Pa_ge Setup..."), "<control><shift>p",
283  N_("Specify the page size and orientation for printing"),
284  G_CALLBACK (gnc_main_window_cmd_page_setup)
285  },
286  {
287  "FilePropertiesAction", GTK_STOCK_PROPERTIES, N_("Proper_ties"), "<Alt>Return",
288  N_("Edit the properties of the current file"),
289  G_CALLBACK (gnc_main_window_cmd_file_properties)
290  },
291  {
292  "FileCloseAction", GTK_STOCK_CLOSE, N_("_Close"), NULL,
293  N_("Close the currently active page"),
294  G_CALLBACK (gnc_main_window_cmd_file_close)
295  },
296  {
297  "FileQuitAction", GTK_STOCK_QUIT, N_("_Quit"), NULL,
298  N_("Quit this application"),
299  G_CALLBACK (gnc_main_window_cmd_file_quit)
300  },
301 
302  /* Edit menu */
303 
304  {
305  "EditCutAction", GTK_STOCK_CUT, N_("Cu_t"), NULL,
306  N_("Cut the current selection and copy it to clipboard"),
307  G_CALLBACK (gnc_main_window_cmd_edit_cut)
308  },
309  {
310  "EditCopyAction", GTK_STOCK_COPY, N_("_Copy"), NULL,
311  N_("Copy the current selection to clipboard"),
312  G_CALLBACK (gnc_main_window_cmd_edit_copy)
313  },
314  {
315  "EditPasteAction", GTK_STOCK_PASTE, N_("_Paste"), NULL,
316  N_("Paste the clipboard content at the cursor position"),
317  G_CALLBACK (gnc_main_window_cmd_edit_paste)
318  },
319  {
320  "EditPreferencesAction", GTK_STOCK_PREFERENCES, N_("Pr_eferences"), NULL,
321  N_("Edit the global preferences of GnuCash"),
322  G_CALLBACK (gnc_main_window_cmd_edit_preferences)
323  },
324 
325  /* View menu */
326 
327  {
328  "ViewSortByAction", NULL, N_("_Sort By..."), NULL,
329  N_("Select sorting criteria for this page view"), NULL
330  },
331  {
332  "ViewFilterByAction", NULL, N_("_Filter By..."), NULL,
333  N_("Select the account types that should be displayed."), NULL
334  },
335  {
336  "ViewRefreshAction", GTK_STOCK_REFRESH, N_("_Refresh"), "<control>r",
337  N_("Refresh this window"),
338  G_CALLBACK (gnc_main_window_cmd_view_refresh)
339  },
340 
341  /* Actions menu */
342 
343  { "ScrubMenuAction", NULL, N_("_Check & Repair"), NULL, NULL, NULL },
344  {
345  "ActionsForgetWarningsAction", NULL, N_("Reset _Warnings..."), NULL,
346  N_("Reset the state of all warning messages so they will be shown again."),
347  G_CALLBACK (gnc_main_window_cmd_actions_reset_warnings)
348  },
349  {
350  "ActionsRenamePageAction", NULL, N_("Re_name Page"), NULL,
351  N_("Rename this page."),
352  G_CALLBACK (gnc_main_window_cmd_actions_rename_page)
353  },
354 
355  /* Windows menu */
356 
357  {
358  "WindowNewAction", NULL, N_("_New Window"), NULL,
359  N_("Open a new top-level GnuCash window."),
360  G_CALLBACK (gnc_main_window_cmd_window_new)
361  },
362  {
363  "WindowMovePageAction", NULL, N_("New Window with _Page"), NULL,
364  N_("Move the current page to a new top-level GnuCash window."),
365  G_CALLBACK (gnc_main_window_cmd_window_move_page)
366  },
367 
368  /* Help menu */
369 
370  {
371  "HelpTutorialAction", GTK_STOCK_HELP, N_("Tutorial and Concepts _Guide"), NULL,
372  N_("Open the GnuCash Tutorial"),
373  G_CALLBACK (gnc_main_window_cmd_help_tutorial)
374  },
375  {
376  "HelpContentsAction", GTK_STOCK_HELP, N_("_Contents"), "F1",
377  N_("Open the GnuCash Help"),
378  G_CALLBACK (gnc_main_window_cmd_help_contents)
379  },
380  {
381  "HelpAboutAction", GTK_STOCK_ABOUT, N_("_About"), NULL,
382  N_("About GnuCash"),
383  G_CALLBACK (gnc_main_window_cmd_help_about)
384  },
385 };
387 static guint gnc_menu_n_actions = G_N_ELEMENTS (gnc_menu_actions);
388 
391 static GtkToggleActionEntry toggle_actions [] =
392 {
393  {
394  "ViewToolbarAction", NULL, N_("_Toolbar"), NULL,
395  N_("Show/hide the toolbar on this window"),
396  G_CALLBACK (gnc_main_window_cmd_view_toolbar), TRUE
397  },
398  {
399  "ViewSummaryAction", NULL, N_("Su_mmary Bar"), NULL,
400  N_("Show/hide the summary bar on this window"),
401  G_CALLBACK (gnc_main_window_cmd_view_summary), TRUE
402  },
403  {
404  "ViewStatusbarAction", NULL, N_("Stat_us Bar"), NULL,
405  N_("Show/hide the status bar on this window"),
406  G_CALLBACK (gnc_main_window_cmd_view_statusbar), TRUE
407  },
408 };
410 static guint n_toggle_actions = G_N_ELEMENTS (toggle_actions);
411 
412 #ifndef MAC_INTEGRATION
413 
415 static GtkRadioActionEntry radio_entries [] =
416 {
417  { "Window0Action", NULL, N_("Window _1"), NULL, NULL, 0 },
418  { "Window1Action", NULL, N_("Window _2"), NULL, NULL, 1 },
419  { "Window2Action", NULL, N_("Window _3"), NULL, NULL, 2 },
420  { "Window3Action", NULL, N_("Window _4"), NULL, NULL, 3 },
421  { "Window4Action", NULL, N_("Window _5"), NULL, NULL, 4 },
422  { "Window5Action", NULL, N_("Window _6"), NULL, NULL, 5 },
423  { "Window6Action", NULL, N_("Window _7"), NULL, NULL, 6 },
424  { "Window7Action", NULL, N_("Window _8"), NULL, NULL, 7 },
425  { "Window8Action", NULL, N_("Window _9"), NULL, NULL, 8 },
426  { "Window9Action", NULL, N_("Window _0"), NULL, NULL, 9 },
427 };
428 
430 static guint n_radio_entries = G_N_ELEMENTS (radio_entries);
431 #endif
432 
436 static const gchar *gnc_menu_important_actions[] =
437 {
438  "FileCloseAction",
439  NULL,
440 };
441 
442 
447 static const gchar *always_insensitive_actions[] =
448 {
449  "FilePrintAction",
450  NULL
451 };
452 
453 
457 static const gchar *initially_insensitive_actions[] =
458 {
459  "FileCloseAction",
460  NULL
461 };
462 
463 
468 static const gchar *always_hidden_actions[] =
469 {
470  "ViewSortByAction",
471  "ViewFilterByAction",
472  NULL
473 };
474 
475 
478 static const gchar *immutable_page_actions[] =
479 {
480  "FileCloseAction",
481  NULL
482 };
483 
484 
487 static const gchar *multiple_page_actions[] =
488 {
489  "WindowMovePageAction",
490  NULL
491 };
492 
493 
494 /************************************************************
495  * *
496  ************************************************************/
497 #define WINDOW_COUNT "WindowCount"
498 #define WINDOW_STRING "Window %d"
499 #define WINDOW_GEOMETRY "WindowGeometry"
500 #define WINDOW_POSITION "WindowPosition"
501 #define WINDOW_MAXIMIZED "WindowMaximized"
502 #define TOOLBAR_VISIBLE "ToolbarVisible"
503 #define STATUSBAR_VISIBLE "StatusbarVisible"
504 #define SUMMARYBAR_VISIBLE "SummarybarVisible"
505 #define WINDOW_FIRSTPAGE "FirstPage"
506 #define WINDOW_PAGECOUNT "PageCount"
507 #define WINDOW_PAGEORDER "PageOrder"
508 #define PAGE_TYPE "PageType"
509 #define PAGE_NAME "PageName"
510 #define PAGE_STRING "Page %d"
511 
512 typedef struct
513 {
514  GKeyFile *key_file;
515  const gchar *group_name;
516  gint window_num;
517  gint page_num;
518  gint page_offset;
520 
521 
522 /* Iterator function to walk all pages in all windows, calling the
523  * specified function for each page. */
524 void
525 gnc_main_window_foreach_page (GncMainWindowPageFunc fn, gpointer user_data)
526 {
527  GncMainWindowPrivate *priv;
528  GncMainWindow *window;
529  GncPluginPage *page;
530  GList *w, *p;
531 
532  ENTER(" ");
533  for (w = active_windows; w; w = g_list_next(w))
534  {
535  window = w->data;
536  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
537  for (p = priv->installed_pages; p; p = g_list_next(p))
538  {
539  page = p->data;
540  fn(page, user_data);
541  }
542  }
543  LEAVE(" ");
544 }
545 
546 
558 static void
559 gnc_main_window_restore_page (GncMainWindow *window,
560  GncMainWindowSaveData *data)
561 {
562  GncMainWindowPrivate *priv;
563  GncPluginPage *page;
564  gchar *page_group, *page_type = NULL, *name = NULL;
565  const gchar *class_type;
566  GError *error = NULL;
567 
568  ENTER("window %p, data %p (key file %p, window %d, page start %d, page num %d)",
569  window, data, data->key_file, data->window_num, data->page_offset,
570  data->page_num);
571 
572  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
573  page_group = g_strdup_printf(PAGE_STRING,
574  data->page_offset + data->page_num);
575  page_type = g_key_file_get_string(data->key_file, page_group,
576  PAGE_TYPE, &error);
577  if (error)
578  {
579  g_warning("error reading group %s key %s: %s",
580  page_group, PAGE_TYPE, error->message);
581  goto cleanup;
582  }
583 
584  /* See if the page already exists. */
585  page = g_list_nth_data(priv->installed_pages, data->page_num);
586  if (page)
587  {
588  class_type = GNC_PLUGIN_PAGE_GET_CLASS(page)->plugin_name;
589  if (strcmp(page_type, class_type) != 0)
590  {
591  g_warning("error: page types don't match: state %s, existing page %s",
592  page_type, class_type);
593  goto cleanup;
594  }
595  }
596  else
597  {
598  /* create and install the page */
599  page = gnc_plugin_page_recreate_page(GTK_WIDGET(window), page_type,
600  data->key_file, page_group);
601  if (page)
602  {
603  /* Does the page still need to be installed into the window? */
604  if (page->window == NULL)
605  {
607  gnc_main_window_open_page(window, page);
608  }
609 
610  /* Restore the page name */
611  name = g_key_file_get_string(data->key_file, page_group,
612  PAGE_NAME, &error);
613  if (error)
614  {
615  g_warning("error reading group %s key %s: %s",
616  page_group, PAGE_NAME, error->message);
617  /* Fall through and still show the page. */
618  }
619  else
620  {
621  DEBUG("updating page name for %p to %s.", page, name);
622  main_window_update_page_name(page, name);
623  g_free(name);
624  }
625  }
626  }
627 
628  LEAVE("ok");
629 cleanup:
630  if (error)
631  g_error_free(error);
632  if (page_type)
633  g_free(page_type);
634  g_free(page_group);
635 }
636 
637 
646 static void
647 gnc_main_window_restore_window (GncMainWindow *window, GncMainWindowSaveData *data)
648 {
649  GncMainWindowPrivate *priv;
650  GtkAction *action;
651  gint *pos, *geom, *order;
652  gsize length;
653  gboolean max, visible, desired_visibility;
654  gchar *window_group;
655  gint page_start, page_count, i;
656  GError *error = NULL;
657 
658  /* Setup */
659  ENTER("window %p, data %p (key file %p, window %d)",
660  window, data, data->key_file, data->window_num);
661  window_group = g_strdup_printf(WINDOW_STRING, data->window_num + 1);
662 
663  /* Deal with the uncommon case that the state file defines a window
664  * but no pages. An example to get in such a situation can be found
665  * here: https://bugzilla.gnome.org/show_bug.cgi?id=436479#c3
666  * If this happens on the first window, we will open an account hierarchy
667  * to avoid confusing the user by presenting a completely empty window.
668  * If it happens on a later window, we'll just skip restoring that window.
669  */
670  if (!g_key_file_has_group (data->key_file, window_group) ||
671  !g_key_file_has_key (data->key_file, window_group, WINDOW_PAGECOUNT, &error))
672  {
673  if (window)
674  {
676  PINFO ("saved state had an empty first main window\n"
677  "an account hierarchy page was added automatically to avoid confusion");
678  }
679  else
680  PINFO ("saved state had an empty main window, skipping restore");
681 
682  goto cleanup;
683  }
684 
685 
686  /* Get this window's notebook info */
687  page_count = g_key_file_get_integer(data->key_file,
688  window_group, WINDOW_PAGECOUNT, &error);
689  if (error)
690  {
691  g_warning("error reading group %s key %s: %s",
692  window_group, WINDOW_PAGECOUNT, error->message);
693  goto cleanup;
694  }
695  if (page_count == 0)
696  {
697  /* Should never happen, but has during alpha testing. Having this
698  * check doesn't hurt anything. */
699  goto cleanup;
700  }
701  page_start = g_key_file_get_integer(data->key_file,
702  window_group, WINDOW_FIRSTPAGE, &error);
703  if (error)
704  {
705  g_warning("error reading group %s key %s: %s",
706  window_group, WINDOW_FIRSTPAGE, error->message);
707  goto cleanup;
708  }
709 
710  /* Build a window if we don't already have one */
711  if (window == NULL)
712  {
713  DEBUG("Window %d doesn't exist. Creating new window.", data->window_num);
714  DEBUG("active_windows %p.", active_windows);
715  if (active_windows)
716  DEBUG("first window %p.", active_windows->data);
717  window = gnc_main_window_new();
718  gtk_widget_show(GTK_WIDGET(window));
719  }
720 
721  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
722 
723  /* Get the window coordinates, etc. */
724  geom = g_key_file_get_integer_list(data->key_file, window_group,
725  WINDOW_GEOMETRY, &length, &error);
726  if (error)
727  {
728  g_warning("error reading group %s key %s: %s",
729  window_group, WINDOW_GEOMETRY, error->message);
730  g_error_free(error);
731  error = NULL;
732  }
733  else if (length != 2)
734  {
735  g_warning("invalid number of values for group %s key %s",
736  window_group, WINDOW_GEOMETRY);
737  }
738  else
739  {
740  gtk_window_resize(GTK_WINDOW(window), geom[0], geom[1]);
741  DEBUG("window (%p) size %dx%d", window, geom[0], geom[1]);
742  }
743  /* keep the geometry for a test whether the windows position
744  is offscreen */
745 
746  pos = g_key_file_get_integer_list(data->key_file, window_group,
747  WINDOW_POSITION, &length, &error);
748  if (error)
749  {
750  g_warning("error reading group %s key %s: %s",
751  window_group, WINDOW_POSITION, error->message);
752  g_error_free(error);
753  error = NULL;
754  }
755  else if (length != 2)
756  {
757  g_warning("invalid number of values for group %s key %s",
758  window_group, WINDOW_POSITION);
759  }
760  else if ((pos[0] + (geom ? geom[0] : 0) < 0) ||
761  (pos[0] > gdk_screen_width()) ||
762  (pos[1] + (geom ? geom[1] : 0) < 0) ||
763  (pos[1] > gdk_screen_height()))
764  {
765 // g_debug("position %dx%d, size%dx%d is offscreen; will not move",
766 // pos[0], pos[1], geom[0], geom[1]);
767  }
768  else
769  {
770  gtk_window_move(GTK_WINDOW(window), pos[0], pos[1]);
771  DEBUG("window (%p) position %dx%d", window, pos[0], pos[1]);
772  }
773  if (geom)
774  {
775  g_free(geom);
776  }
777  if (pos)
778  {
779  g_free(pos);
780  }
781 
782  max = g_key_file_get_boolean(data->key_file, window_group,
783  WINDOW_MAXIMIZED, &error);
784  if (error)
785  {
786  g_warning("error reading group %s key %s: %s",
787  window_group, WINDOW_MAXIMIZED, error->message);
788  g_error_free(error);
789  error = NULL;
790  }
791  else if (max)
792  {
793  gtk_window_maximize(GTK_WINDOW(window));
794  }
795 
796  /* Common view menu items */
797  action = gnc_main_window_find_action(window, "ViewToolbarAction");
798  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
799  desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
800  TOOLBAR_VISIBLE, &error);
801  if (error)
802  {
803  g_warning("error reading group %s key %s: %s",
804  window_group, TOOLBAR_VISIBLE, error->message);
805  g_error_free(error);
806  error = NULL;
807  }
808  else if (visible != desired_visibility)
809  {
810  gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
811  }
812 
813  action = gnc_main_window_find_action(window, "ViewSummaryAction");
814  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
815  desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
816  SUMMARYBAR_VISIBLE, &error);
817  if (error)
818  {
819  g_warning("error reading group %s key %s: %s",
820  window_group, TOOLBAR_VISIBLE, error->message);
821  g_error_free(error);
822  error = NULL;
823  }
824  else if (visible != desired_visibility)
825  {
826  gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
827  }
828 
829  action = gnc_main_window_find_action(window, "ViewStatusbarAction");
830  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
831  desired_visibility = g_key_file_get_boolean(data->key_file, window_group,
832  STATUSBAR_VISIBLE, &error);
833  if (error)
834  {
835  g_warning("error reading group %s key %s: %s",
836  window_group, TOOLBAR_VISIBLE, error->message);
837  g_error_free(error);
838  error = NULL;
839  }
840  else if (visible != desired_visibility)
841  {
842  gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), desired_visibility);
843  }
844 
845  /* Now populate the window with pages. */
846  for (i = 0; i < page_count; i++)
847  {
848  data->page_offset = page_start;
849  data->page_num = i;
850  gnc_main_window_restore_page(window, data);
851 
852  /* give the page a chance to display */
853  while (gtk_events_pending ())
854  gtk_main_iteration ();
855  }
856 
857  /* Restore page ordering within the notebook. Use +1 notation so the
858  * numbers in the page order match the page sections, at least for
859  * the one window case. */
860  order = g_key_file_get_integer_list(data->key_file, window_group,
861  WINDOW_PAGEORDER, &length, &error);
862  if (error)
863  {
864  g_warning("error reading group %s key %s: %s",
865  window_group, WINDOW_PAGEORDER, error->message);
866  g_error_free(error);
867  error = NULL;
868  }
869  else if (length != page_count)
870  {
871  g_warning("%s key %s length %" G_GSIZE_FORMAT " differs from window page count %d",
872  window_group, WINDOW_PAGEORDER, length, page_count);
873  }
874  else
875  {
876  /* Dump any list that might exist */
877  g_list_free(priv->usage_order);
878  priv->usage_order = NULL;
879  /* Now rebuild the list from the key file. */
880  for (i = 0; i < length; i++)
881  {
882  gpointer page = g_list_nth_data(priv->installed_pages, order[i] - 1);
883  if (page)
884  {
885  priv->usage_order = g_list_append(priv->usage_order, page);
886  }
887  }
888  gtk_notebook_set_current_page (GTK_NOTEBOOK(priv->notebook),
889  order[0] - 1);
890  }
891  if (order)
892  {
893  g_free(order);
894  }
895 
896  LEAVE("window %p", window);
897 cleanup:
898  if (error)
899  g_error_free(error);
900  g_free(window_group);
901 }
902 
903 void
904 gnc_main_window_restore_all_windows(const GKeyFile *keyfile)
905 {
906  gint i, window_count;
907  GError *error = NULL;
909  GncMainWindow *window;
910 
911  /* We use the same struct for reading and for writing, so we cast
912  away the const. */
913  data.key_file = (GKeyFile *) keyfile;
914  window_count = g_key_file_get_integer(data.key_file, STATE_FILE_TOP,
915  WINDOW_COUNT, &error);
916  if (error)
917  {
918  g_warning("error reading group %s key %s: %s",
919  STATE_FILE_TOP, WINDOW_COUNT, error->message);
920  g_error_free(error);
921  LEAVE("can't read count");
922  return;
923  }
924 
925  /* Restore all state information on the open windows. Window
926  numbers in state file are 1-based. GList indices are 0-based. */
927  gnc_set_busy_cursor (NULL, TRUE);
928  for (i = 0; i < window_count; i++)
929  {
930  data.window_num = i;
931  window = g_list_nth_data(active_windows, i);
932  gnc_main_window_restore_window(window, &data);
933  }
934  gnc_unset_busy_cursor (NULL);
935 
936  statusbar_notification_lastmodified();
937 }
938 
939 void
941 {
942  GtkAction *action;
943 
944  /* The default state should be to have an Account Tree page open
945  * in the window. */
946  DEBUG("no saved state file");
947  if (!window)
948  window = g_list_nth_data(active_windows, 0);
949  action = gnc_main_window_find_action(window, "ViewAccountTreeAction");
950  gtk_action_activate(action);
951 }
952 
962 static void
963 gnc_main_window_save_page (GncPluginPage *page, GncMainWindowSaveData *data)
964 {
965  gchar *page_group;
966  const gchar *plugin_name, *page_name;
967 
968  ENTER("page %p, data %p (key file %p, window %d, page %d)",
969  page, data, data->key_file, data->window_num, data->page_num);
970  plugin_name = gnc_plugin_page_get_plugin_name(page);
971  page_name = gnc_plugin_page_get_page_name(page);
972  if (!plugin_name || !page_name)
973  {
974  LEAVE("not saving invalid page");
975  return;
976  }
977  page_group = g_strdup_printf(PAGE_STRING, data->page_num++);
978  g_key_file_set_string(data->key_file, page_group, PAGE_TYPE, plugin_name);
979  g_key_file_set_string(data->key_file, page_group, PAGE_NAME, page_name);
980 
981  gnc_plugin_page_save_page(page, data->key_file, page_group);
982  g_free(page_group);
983  LEAVE(" ");
984 }
985 
986 
995 static void
996 gnc_main_window_save_window (GncMainWindow *window, GncMainWindowSaveData *data)
997 {
998  GncMainWindowPrivate *priv;
999  GtkAction *action;
1000  gint i, num_pages, coords[4], *order;
1001  gboolean maximized, visible;
1002  gchar *window_group;
1003 
1004  /* Setup */
1005  ENTER("window %p, data %p (key file %p, window %d)",
1006  window, data, data->key_file, data->window_num);
1007  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1008 
1009  /* Check for bogus window structures. */
1010  num_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook));
1011  if (0 == num_pages)
1012  {
1013  LEAVE("empty window %p", window);
1014  return;
1015  }
1016 
1017  /* Save this window's notebook info */
1018  window_group = g_strdup_printf(WINDOW_STRING, data->window_num++);
1019  g_key_file_set_integer(data->key_file, window_group,
1020  WINDOW_PAGECOUNT, num_pages);
1021  g_key_file_set_integer(data->key_file, window_group,
1022  WINDOW_FIRSTPAGE, data->page_num);
1023 
1024  /* Save page ordering within the notebook. Use +1 notation so the
1025  * numbers in the page order match the page sections, at least for
1026  * the one window case. */
1027  order = g_malloc(sizeof(gint) * num_pages);
1028  for (i = 0; i < num_pages; i++)
1029  {
1030  gpointer page = g_list_nth_data(priv->usage_order, i);
1031  order[i] = g_list_index(priv->installed_pages, page) + 1;
1032  }
1033  g_key_file_set_integer_list(data->key_file, window_group,
1034  WINDOW_PAGEORDER, order, num_pages);
1035  g_free(order);
1036 
1037  /* Save the window coordinates, etc. */
1038  gtk_window_get_position(GTK_WINDOW(window), &coords[0], &coords[1]);
1039  gtk_window_get_size(GTK_WINDOW(window), &coords[2], &coords[3]);
1040  maximized = (gdk_window_get_state((GTK_WIDGET(window))->window)
1041  & GDK_WINDOW_STATE_MAXIMIZED) != 0;
1042  g_key_file_set_integer_list(data->key_file, window_group,
1043  WINDOW_POSITION, &coords[0], 2);
1044  g_key_file_set_integer_list(data->key_file, window_group,
1045  WINDOW_GEOMETRY, &coords[2], 2);
1046  g_key_file_set_boolean(data->key_file, window_group,
1047  WINDOW_MAXIMIZED, maximized);
1048  DEBUG("window (%p) position %dx%d, size %dx%d, %s", window, coords[0], coords[1],
1049  coords[2], coords[3],
1050  maximized ? "maximized" : "not maximized");
1051 
1052  /* Common view menu items */
1053  action = gnc_main_window_find_action(window, "ViewToolbarAction");
1054  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
1055  g_key_file_set_boolean(data->key_file, window_group,
1056  TOOLBAR_VISIBLE, visible);
1057  action = gnc_main_window_find_action(window, "ViewSummaryAction");
1058  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
1059  g_key_file_set_boolean(data->key_file, window_group,
1060  SUMMARYBAR_VISIBLE, visible);
1061  action = gnc_main_window_find_action(window, "ViewStatusbarAction");
1062  visible = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
1063  g_key_file_set_boolean(data->key_file, window_group,
1064  STATUSBAR_VISIBLE, visible);
1065 
1066  /* Save individual pages in this window */
1067  g_list_foreach(priv->installed_pages, (GFunc)gnc_main_window_save_page, data);
1068 
1069  g_free(window_group);
1070  LEAVE("window %p", window);
1071 }
1072 
1073 void
1075 {
1076  GncMainWindowSaveData data;
1077 
1078  /* Set up the iterator data structures */
1079  data.key_file = keyfile;
1080  data.window_num = 1;
1081  data.page_num = 1;
1082 
1083  g_key_file_set_integer(data.key_file,
1084  STATE_FILE_TOP, WINDOW_COUNT,
1085  g_list_length(active_windows));
1086  /* Dump all state information on the open windows */
1087  g_list_foreach(active_windows, (GFunc)gnc_main_window_save_window, &data);
1088 }
1089 
1090 
1091 gboolean
1093 {
1094  GncMainWindowPrivate *priv;
1095  GList *item;
1096 
1097  g_return_val_if_fail(GNC_IS_MAIN_WINDOW(window), TRUE);
1098 
1099  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1100  for (item = priv->installed_pages; item; item = g_list_next(item))
1101  {
1102  if (!gnc_plugin_page_finish_pending(item->data))
1103  {
1104  return FALSE;
1105  }
1106  }
1107  return TRUE;
1108 }
1109 
1110 
1111 gboolean
1113 {
1114  const GList *windows, *item;
1115 
1116  windows = gnc_gobject_tracking_get_list(GNC_MAIN_WINDOW_NAME);
1117  for (item = windows; item; item = g_list_next(item))
1118  {
1119  if (!gnc_main_window_finish_pending(item->data))
1120  {
1121  return FALSE;
1122  }
1123  }
1124  return TRUE;
1125 }
1126 
1127 
1138 static gboolean
1139 gnc_main_window_page_exists (GncPluginPage *page)
1140 {
1141  GncMainWindow *window;
1142  GncMainWindowPrivate *priv;
1143  GList *walker;
1144 
1145  for (walker = active_windows; walker; walker = g_list_next(walker))
1146  {
1147  window = walker->data;
1148  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1149  if (g_list_find(priv->installed_pages, page))
1150  {
1151  return TRUE;
1152  }
1153  }
1154  return FALSE;
1155 }
1156 
1157 static gboolean auto_save_countdown (GtkWidget *dialog)
1158 {
1159  GtkWidget *label;
1160  gchar *timeoutstr = NULL;
1161 
1162  /* Stop count down if user closed the dialog since the last time we were called */
1163  if (!GTK_IS_DIALOG (dialog))
1164  return FALSE; /* remove timer */
1165 
1166  /* Stop count down if count down text can't be updated */
1167  label = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), "count-down-label"));
1168  if (!GTK_IS_LABEL (label))
1169  return FALSE; /* remove timer */
1170 
1171  secs_to_save--;
1172  DEBUG ("Counting down: %d seconds", secs_to_save);
1173 
1174  timeoutstr = g_strdup_printf (MSG_AUTO_SAVE, secs_to_save);
1175  gtk_label_set_text (GTK_LABEL (label), timeoutstr);
1176  g_free (timeoutstr);
1177 
1178  /* Count down reached 0. Save and close dialog */
1179  if (!secs_to_save)
1180  {
1181  gtk_dialog_response (GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);
1182  return FALSE; /* remove timer */
1183  }
1184 
1185  /* Run another cycle */
1186  return TRUE;
1187 }
1188 
1189 
1199 static gboolean
1200 gnc_main_window_prompt_for_save (GtkWidget *window)
1201 {
1202  QofSession *session;
1203  QofBook *book;
1204  GtkWidget *dialog, *msg_area, *label;
1205  gint response;
1206  const gchar *filename, *tmp;
1207  const gchar *title = _("Save changes to file %s before closing?");
1208  /* This should be the same message as in gnc-file.c */
1209  const gchar *message_hours =
1210  _("If you don't save, changes from the past %d hours and %d minutes will be discarded.");
1211  const gchar *message_days =
1212  _("If you don't save, changes from the past %d days and %d hours will be discarded.");
1213  time64 oldest_change;
1214  gint minutes, hours, days;
1215 
1216  session = gnc_get_current_session();
1217  book = qof_session_get_book(session);
1218  filename = qof_session_get_url(session);
1219  if (filename == NULL)
1220  filename = _("<unknown>");
1221  if ((tmp = strrchr(filename, '/')) != NULL)
1222  filename = tmp + 1;
1223 
1224  /* Remove any pending auto-save timeouts */
1225  gnc_autosave_remove_timer(book);
1226 
1227  dialog = gtk_message_dialog_new(GTK_WINDOW(window),
1228  GTK_DIALOG_MODAL,
1229  GTK_MESSAGE_WARNING,
1230  GTK_BUTTONS_NONE,
1231  title,
1232  filename);
1233  oldest_change = qof_book_get_session_dirty_time(book);
1234  minutes = (gnc_time (NULL) - oldest_change) / 60 + 1;
1235  hours = minutes / 60;
1236  minutes = minutes % 60;
1237  days = hours / 24;
1238  hours = hours % 24;
1239  if (days > 0)
1240  {
1241  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1242  message_days, days, hours);
1243  }
1244  else if (hours > 0)
1245  {
1246  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1247  message_hours, hours, minutes);
1248  }
1249  else
1250  {
1251  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1252  ngettext("If you don't save, changes from the past %d minute will be discarded.",
1253  "If you don't save, changes from the past %d minutes will be discarded.",
1254  minutes), minutes);
1255  }
1256  gtk_dialog_add_buttons(GTK_DIALOG(dialog),
1257  _("Close _Without Saving"), GTK_RESPONSE_CLOSE,
1258  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1259  GTK_STOCK_SAVE, GTK_RESPONSE_APPLY,
1260  NULL);
1261  gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_APPLY);
1262 
1263  /* If requested by the user, add a timeout to the question to save automatically
1264  * if the user doesn't answer after a chosen number of seconds.
1265  */
1266  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_CLOSE_EXPIRES))
1267  {
1268  gchar *timeoutstr = NULL;
1269 
1270  secs_to_save = gnc_prefs_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_CLOSE_WAIT_TIME);
1271  timeoutstr = g_strdup_printf (MSG_AUTO_SAVE, secs_to_save);
1272  label = GTK_WIDGET(gtk_label_new (timeoutstr));
1273  g_free (timeoutstr);
1274  gtk_widget_show (label);
1275 
1276  msg_area = gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG(dialog));
1277  gtk_box_pack_end (GTK_BOX(msg_area), label, TRUE, TRUE, 0);
1278  g_object_set (G_OBJECT (label), "xalign", 0.0, NULL);
1279 
1280  g_object_set_data (G_OBJECT (dialog), "count-down-label", label);
1281  g_timeout_add_seconds (1, (GSourceFunc)auto_save_countdown, dialog);
1282  }
1283 
1284  response = gtk_dialog_run (GTK_DIALOG (dialog));
1285  gtk_widget_destroy(dialog);
1286 
1287  switch (response)
1288  {
1289  case GTK_RESPONSE_APPLY:
1290  gnc_file_save();
1291  return FALSE;
1292 
1293  case GTK_RESPONSE_CLOSE:
1295  return FALSE;
1296 
1297  default:
1298  return TRUE;
1299  }
1300 }
1301 
1302 
1303 static void
1304 gnc_main_window_add_plugin (gpointer plugin,
1305  gpointer window)
1306 {
1307  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
1308  g_return_if_fail (GNC_IS_PLUGIN (plugin));
1309 
1310  ENTER(" ");
1311  gnc_plugin_add_to_window (GNC_PLUGIN (plugin),
1312  GNC_MAIN_WINDOW (window),
1313  window_type);
1314  LEAVE(" ");
1315 }
1316 
1317 static void
1318 gnc_main_window_remove_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_remove_from_window (GNC_PLUGIN (plugin),
1326  GNC_MAIN_WINDOW (window),
1327  window_type);
1328  LEAVE(" ");
1329 }
1330 
1331 
1332 static gboolean
1333 gnc_main_window_timed_quit (gpointer dummy)
1334 {
1335  if (gnc_file_save_in_progress())
1336  return TRUE;
1337 
1338  gnc_shutdown (0);
1339  return FALSE;
1340 }
1341 
1342 static gboolean
1343 gnc_main_window_quit(GncMainWindow *window)
1344 {
1345  QofSession *session;
1346  gboolean needs_save, do_shutdown;
1347 
1348  session = gnc_get_current_session();
1349  needs_save = qof_book_session_not_saved(qof_session_get_book(session)) &&
1350  !gnc_file_save_in_progress();
1351  do_shutdown = !needs_save ||
1352  (needs_save && !gnc_main_window_prompt_for_save(GTK_WIDGET(window)));
1353 
1354  if (do_shutdown)
1355  {
1356  g_timeout_add(250, gnc_main_window_timed_quit, NULL);
1357  return TRUE;
1358  }
1359  return FALSE;
1360 }
1361 
1362 static gboolean
1363 gnc_main_window_delete_event (GtkWidget *window,
1364  GdkEvent *event,
1365  gpointer user_data)
1366 {
1367  static gboolean already_dead = FALSE;
1368 
1369  if (already_dead)
1370  return TRUE;
1371 
1372  if (!gnc_main_window_finish_pending(GNC_MAIN_WINDOW(window)))
1373  {
1374  /* Don't close the window. */
1375  return TRUE;
1376  }
1377 
1378  if (g_list_length(active_windows) > 1)
1379  return FALSE;
1380 
1381  already_dead = gnc_main_window_quit(GNC_MAIN_WINDOW(window));
1382  return TRUE;
1383 }
1384 
1385 
1405 static void
1406 gnc_main_window_event_handler (QofInstance *entity, QofEventId event_type,
1407  gpointer user_data, gpointer event_data)
1408 {
1409  GncMainWindow *window;
1410  GncMainWindowPrivate *priv;
1411  GncPluginPage *page;
1412  GList *item, *next;
1413 
1414  /* hard failures */
1415  g_return_if_fail(GNC_IS_MAIN_WINDOW(user_data));
1416 
1417  /* soft failures */
1418  if (!QOF_CHECK_TYPE(entity, QOF_ID_BOOK))
1419  return;
1420  if (event_type != QOF_EVENT_DESTROY)
1421  return;
1422 
1423  ENTER("entity %p, event %d, window %p, event data %p",
1424  entity, event_type, user_data, event_data);
1425  window = GNC_MAIN_WINDOW(user_data);
1426  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1427 
1428  /* This is not a typical list iteration. We're removing while
1429  * we iterate, so we have to cache the 'next' pointer before
1430  * executing any code in the loop. */
1431  for (item = priv->installed_pages; item; item = next)
1432  {
1433  next = g_list_next(item);
1434  page = GNC_PLUGIN_PAGE(item->data);
1435  if (gnc_plugin_page_has_book (page, (QofBook *)entity))
1437  }
1438  LEAVE(" ");
1439 }
1440 
1441 
1458 static gchar *
1459 gnc_main_window_generate_title (GncMainWindow *window)
1460 {
1461  GncMainWindowPrivate *priv;
1462  GncPluginPage *page;
1463  QofBook *book;
1464  gboolean immutable;
1465  gchar *filename = NULL;
1466  const gchar *book_id = NULL;
1467  const gchar *dirty = "";
1468  const gchar *readonly_text = NULL;
1469  gchar *readonly;
1470  gchar *title;
1471  GtkAction* action;
1472 
1473  /* The save action is sensitive if the book is dirty */
1474  action = gnc_main_window_find_action (window, "FileSaveAction");
1475  if (action != NULL)
1476  {
1477  gtk_action_set_sensitive(action, FALSE);
1478  }
1479  if (gnc_current_session_exist())
1480  {
1481  book_id = qof_session_get_url (gnc_get_current_session ());
1482  book = gnc_get_current_book();
1483  if (qof_book_session_not_saved (book))
1484  {
1485  dirty = "*";
1486  if (action != NULL)
1487  {
1488  gtk_action_set_sensitive(action, TRUE);
1489  }
1490  }
1491  if (qof_book_is_readonly(book))
1492  {
1493  /* Translators: This string is shown in the window title if this
1494  document is, well, read-only. */
1495  readonly_text = _("(read-only)");
1496  }
1497  }
1498  readonly = (readonly_text != NULL)
1499  ? g_strdup_printf(" %s", readonly_text)
1500  : g_strdup("");
1501 
1502  if (!book_id)
1503  filename = g_strdup(_("Unsaved Book"));
1504  else
1505  {
1506  if ( gnc_uri_is_file_uri ( book_id ) )
1507  {
1508  /* The filename is a true file.
1509  * The Gnome HIG 2.0 recommends only the file name (no path) be used. (p15) */
1510  gchar *path = gnc_uri_get_path ( book_id );
1511  filename = g_path_get_basename ( path );
1512  g_free ( path );
1513  }
1514  else
1515  {
1516  /* The filename is composed of database connection parameters.
1517  * For this we will show access_method://username@database[:port] */
1518  filename = gnc_uri_normalize_uri (book_id, FALSE);
1519  }
1520  }
1521 
1522  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1523  page = priv->current_page;
1524  if (page)
1525  {
1526  /* The Gnome HIG 2.0 recommends the application name not be used. (p16)
1527  * but several developers prefer to use it anyway. */
1528  title = g_strdup_printf("%s%s%s - %s - GnuCash", dirty, filename, readonly,
1530  }
1531  else
1532  {
1533  title = g_strdup_printf("%s%s%s - GnuCash", dirty, filename, readonly);
1534  }
1535  /* Update the menus based upon whether this is an "immutable" page. */
1536  immutable = page &&
1537  g_object_get_data (G_OBJECT (page), PLUGIN_PAGE_IMMUTABLE);
1539  immutable_page_actions,
1540  "sensitive", !immutable);
1541  g_free( filename );
1542  g_free(readonly);
1543 
1544  return title;
1545 }
1546 
1547 
1557 static void
1558 gnc_main_window_update_title (GncMainWindow *window)
1559 {
1560  gchar *title;
1561 
1562  title = gnc_main_window_generate_title(window);
1563  gtk_window_set_title(GTK_WINDOW(window), title);
1564  g_free(title);
1565 }
1566 
1567 static void
1568 gnc_main_window_update_all_titles (void)
1569 {
1570  g_list_foreach(active_windows,
1571  (GFunc)gnc_main_window_update_title,
1572  NULL);
1573 }
1574 
1575 static void
1576 gnc_main_window_book_dirty_cb (QofBook *book,
1577  gboolean dirty,
1578  gpointer user_data)
1579 {
1580  gnc_main_window_update_all_titles();
1581 
1582  /* Auto-save feature */
1583  gnc_autosave_dirty_handler(book, dirty);
1584 }
1585 
1586 static void
1587 gnc_main_window_attach_to_book (QofSession *session)
1588 {
1589  QofBook *book;
1590 
1591  g_return_if_fail(session);
1592 
1593  book = qof_session_get_book(session);
1594  qof_book_set_dirty_cb(book, gnc_main_window_book_dirty_cb, NULL);
1595  gnc_main_window_update_all_titles();
1596 #ifndef MAC_INTEGRATION
1597  gnc_main_window_update_all_menu_items();
1598 #endif
1599 }
1600 
1601 static guint gnc_statusbar_notification_messageid = 0;
1602 //#define STATUSBAR_NOTIFICATION_AUTOREMOVAL
1603 #ifdef STATUSBAR_NOTIFICATION_AUTOREMOVAL
1604 /* Removes the statusbar notification again that has been pushed to the
1605  * statusbar by generate_statusbar_lastmodified_message. */
1606 static gboolean statusbar_notification_off(gpointer user_data_unused)
1607 {
1608  GtkWidget *widget = gnc_ui_get_toplevel();
1609  //g_warning("statusbar_notification_off\n");
1610  if (gnc_statusbar_notification_messageid == 0)
1611  return FALSE;
1612 
1613  if (widget && GNC_IS_MAIN_WINDOW(widget))
1614  {
1615  GncMainWindow *mainwindow = GNC_MAIN_WINDOW(widget);
1616  GtkWidget *statusbar = gnc_main_window_get_statusbar(GNC_WINDOW(mainwindow));
1617  gtk_statusbar_remove(GTK_STATUSBAR(statusbar), 0, gnc_statusbar_notification_messageid);
1618  gnc_statusbar_notification_messageid = 0;
1619  }
1620  else
1621  {
1622  g_warning("oops, no GncMainWindow obtained\n");
1623  }
1624  return FALSE; // should not be called again
1625 }
1626 #endif // STATUSBAR_NOTIFICATION_AUTOREMOVAL
1627 
1628 /* Creates a statusbar message stating the last modification time of the opened
1629  * data file. */
1630 static gchar *generate_statusbar_lastmodified_message()
1631 {
1632  gchar *message = NULL;
1633  const gchar *book_id = NULL;
1634 
1635  if (gnc_current_session_exist())
1636  {
1637  book_id = qof_session_get_url (gnc_get_current_session ());
1638  }
1639 
1640  if (!book_id)
1641  return NULL;
1642  else
1643  {
1644  if ( gnc_uri_is_file_uri ( book_id ) )
1645  {
1646 #ifdef HAVE_SYS_STAT_H
1647  /* The filename is a true file. */
1648  gchar *filepath = gnc_uri_get_path ( book_id );
1649  gchar *filename = g_path_get_basename ( filepath );
1650  {
1651  // Access the mtime information through stat(2)
1652  struct stat statbuf;
1653  int r = stat(filepath, &statbuf);
1654  if (r == 0)
1655  {
1656  // File mtime could be accessed ok
1657  gint64 mtime = statbuf.st_mtime;
1658  GDateTime *gdt = gnc_g_date_time_new_from_unix_local (mtime);
1659  gchar *dummy_strftime_has_ampm = g_date_time_format (gdt, "%P");
1660  /* Translators: This is the date and time that is shown in
1661  the status bar after opening a file: The date and time of
1662  last modification. The string is the format string for
1663  glib's function g_date_time_format(), see there for an
1664  explanation of the modifiers. First string is for a locale
1665  that has the a.m. or p.m. string in its locale, second
1666  string is for locales that do not have that string. */
1667  gchar *time_string =
1668  g_date_time_format (gdt, (strlen(dummy_strftime_has_ampm) > 0)
1669  ? _("Last modified on %a, %b %e, %Y at %I:%M%P")
1670  : _("Last modified on %a, %b %e, %Y at %H:%M"));
1671 
1672  g_date_time_unref (gdt);
1673 
1674  //g_warning("got time %ld, str=%s\n", mtime, time_string);
1675  /* Translators: This message appears in the status bar after opening the file. */
1676  message = g_strdup_printf(_("File %s opened. %s"),
1677  filename, time_string);
1678  g_free(time_string);
1679  g_free(dummy_strftime_has_ampm);
1680  }
1681  else
1682  {
1683  g_warning("Unable to read mtime for file %s\n", filepath);
1684  // message is still NULL
1685  }
1686  }
1687  g_free(filename);
1688  g_free(filepath);
1689 #else
1690  return NULL;
1691 #endif
1692  }
1693  // If the URI is not a file but a database, we can maybe also show
1694  // something useful, but I have no idea how to obtain this information.
1695  }
1696  return message;
1697 }
1698 
1699 static void
1700 statusbar_notification_lastmodified()
1701 {
1702  // First look up the first GncMainWindow to set the statusbar there
1703  GList *iter;
1704  GtkWidget *widget = NULL;
1705  for (iter = active_windows; iter && !(widget && GNC_IS_MAIN_WINDOW(widget));
1706  iter = g_list_next(iter))
1707  {
1708  widget = iter->data;
1709  }
1710  if (widget && GNC_IS_MAIN_WINDOW(widget))
1711  {
1712  // Ok, we found a mainwindow where we can set a statusbar message
1713  GncMainWindow *mainwindow = GNC_MAIN_WINDOW(widget);
1714  GtkWidget *statusbar = gnc_main_window_get_statusbar(GNC_WINDOW(mainwindow));
1715 
1716  gchar *msg = generate_statusbar_lastmodified_message();
1717  if (msg)
1718  {
1719  gnc_statusbar_notification_messageid = gtk_statusbar_push(GTK_STATUSBAR(statusbar), 0, msg);
1720  }
1721  g_free(msg);
1722 
1723 #ifdef STATUSBAR_NOTIFICATION_AUTOREMOVAL
1724  // Also register a timeout callback to remove that statusbar
1725  // notification again after 10 seconds
1726  g_timeout_add(10 * 1000, statusbar_notification_off, NULL); // maybe not needed anyway?
1727 #endif
1728  }
1729  else
1730  {
1731  g_warning("uh oh, no GNC_IS_MAIN_WINDOW\n");
1732  }
1733 }
1734 
1735 
1740 {
1742  gchar *action_name;
1743 
1745  gchar *label;
1746 
1748  gboolean visible;
1749 };
1750 
1751 #ifndef MAC_INTEGRATION
1752 
1765 static void
1766 gnc_main_window_update_one_menu_action (GncMainWindow *window,
1767  struct menu_update *data)
1768 {
1769  GncMainWindowPrivate *priv;
1770  GtkAction* action;
1771 
1772  ENTER("window %p, action %s, label %s, visible %d", window,
1773  data->action_name, data->label, data->visible);
1774  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1775  action = gtk_action_group_get_action(priv->action_group, data->action_name);
1776  if (action)
1777  g_object_set(G_OBJECT(action),
1778  "label", data->label,
1779  "visible", data->visible,
1780  (char *)NULL);
1781  LEAVE(" ");
1782 }
1783 
1796 static void
1797 gnc_main_window_update_radio_button (GncMainWindow *window)
1798 {
1799  GncMainWindowPrivate *priv;
1800  GtkAction *action, *first_action;
1801  GSList *action_list;
1802  gchar *action_name;
1803  gint index;
1804 
1805  ENTER("window %p", window);
1806 
1807  /* Show the new entry in all windows. */
1808  index = g_list_index(active_windows, window);
1809  if (index >= n_radio_entries)
1810  {
1811  LEAVE("window %d, only %d actions", index, n_radio_entries);
1812  return;
1813  }
1814 
1815  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
1816  action_name = g_strdup_printf("Window%dAction", index);
1817  action = gtk_action_group_get_action(priv->action_group, action_name);
1818 
1819  /* Block the signal so as not to affect window ordering (top to
1820  * bottom) on the screen */
1821  action_list = gtk_radio_action_get_group(GTK_RADIO_ACTION(action));
1822  if (action_list)
1823  {
1824  first_action = g_slist_last(action_list)->data;
1825  g_signal_handlers_block_by_func(G_OBJECT(first_action),
1826  G_CALLBACK(gnc_main_window_cmd_window_raise),
1827  window);
1828  DEBUG("blocked signal on %p, set %p active, window %p", first_action,
1829  action, window);
1830  gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
1831  g_signal_handlers_unblock_by_func(G_OBJECT(first_action),
1832  G_CALLBACK(gnc_main_window_cmd_window_raise),
1833  window);
1834  }
1835  g_free(action_name);
1836  LEAVE(" ");
1837 }
1838 
1852 static void
1853 gnc_main_window_update_menu_item (GncMainWindow *window)
1854 {
1855  struct menu_update data;
1856  gchar **strings, *title, *expanded;
1857  gint index;
1858 
1859  ENTER("window %p", window);
1860  index = g_list_index(active_windows, window);
1861  if (index > n_radio_entries)
1862  {
1863  LEAVE("skip window %d (only %d entries)", index, n_radio_entries);
1864  return;
1865  }
1866 
1867  /* Figure out the label name. Add the accelerator if possible. */
1868  title = gnc_main_window_generate_title(window);
1869  strings = g_strsplit(title, "_", 0);
1870  g_free(title);
1871  expanded = g_strjoinv("__", strings);
1872  if (index < 10)
1873  {
1874  data.label = g_strdup_printf("_%d %s", (index + 1) % 10, expanded);
1875  g_free(expanded);
1876  }
1877  else
1878  {
1879  data.label = expanded;
1880  }
1881  g_strfreev(strings);
1882 
1883  data.visible = TRUE;
1884  data.action_name = g_strdup_printf("Window%dAction", index);
1885  g_list_foreach(active_windows,
1886  (GFunc)gnc_main_window_update_one_menu_action,
1887  &data);
1888  g_free(data.action_name);
1889  g_free(data.label);
1890 
1891  LEAVE(" ");
1892 }
1893 #endif /* !MAC_INTEGRATION */
1894 
1903 #ifndef MAC_INTEGRATION
1904 static void
1905 gnc_main_window_update_all_menu_items (void)
1906 {
1907  struct menu_update data;
1908  gchar *label;
1909  gint i;
1910 
1911  ENTER("");
1912  /* First update the entries for all existing windows */
1913  g_list_foreach(active_windows,
1914  (GFunc)gnc_main_window_update_menu_item,
1915  NULL);
1916  g_list_foreach(active_windows,
1917  (GFunc)gnc_main_window_update_radio_button,
1918  NULL);
1919 
1920  /* Now hide any entries that aren't being used. */
1921  data.visible = FALSE;
1922  for (i = g_list_length(active_windows); i < n_radio_entries; i++)
1923  {
1924  data.action_name = g_strdup_printf("Window%dAction", i);
1925  label = g_strdup_printf("Window _%d", (i - 1) % 10);
1926  data.label = gettext(label);
1927 
1928  g_list_foreach(active_windows,
1929  (GFunc)gnc_main_window_update_one_menu_action,
1930  &data);
1931 
1932  g_free(data.action_name);
1933  g_free(label);
1934  }
1935  LEAVE(" ");
1936 }
1937 #endif /* !MAC_INTEGRATION */
1938 
1950 static void
1951 gnc_main_window_update_tab_close_one_page (GncPluginPage *page,
1952  gpointer user_data)
1953 {
1954  gboolean *new_value = user_data;
1955  GtkWidget * close_button;
1956 
1957  ENTER("page %p, visible %d", page, *new_value);
1958  close_button = g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON);
1959  if (!close_button)
1960  {
1961  LEAVE("no close button");
1962  return;
1963  }
1964 
1965  if (*new_value)
1966  gtk_widget_show (close_button);
1967  else
1968  gtk_widget_hide (close_button);
1969  LEAVE(" ");
1970 }
1971 
1972 
1985 static void
1986 gnc_main_window_update_tab_close (gpointer prefs, gchar *pref, gpointer user_data)
1987 {
1988  gboolean new_value;
1989 
1990  ENTER(" ");
1991  new_value = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SHOW_CLOSE_BUTTON);
1992  gnc_main_window_foreach_page(
1993  gnc_main_window_update_tab_close_one_page,
1994  &new_value);
1995  LEAVE(" ");
1996 }
1997 
1998 
2007 static void
2008 gnc_main_window_update_tab_color_one_page (GncPluginPage *page,
2009  gpointer user_data)
2010 {
2011  const gchar *color_string;
2012 
2013  ENTER("page %p", page);
2014  color_string = gnc_plugin_page_get_page_color(page);
2015  main_window_update_page_color (page, color_string);
2016  LEAVE(" ");
2017 }
2018 
2019 
2030 static void
2031 gnc_main_window_update_tab_color (gpointer gsettings, gchar *pref, gpointer user_data)
2032 {
2033  GncMainWindowPrivate *priv;
2034  GncMainWindow *window;
2035 
2036  ENTER(" ");
2037  g_return_if_fail(GNC_IS_MAIN_WINDOW(user_data));
2038  window = user_data;
2039  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2040  if (g_strcmp0 (GNC_PREF_TAB_COLOR, pref) == 0)
2041  priv->show_color_tabs = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_COLOR);
2042  gnc_main_window_foreach_page (gnc_main_window_update_tab_color_one_page, window);
2043  LEAVE(" ");
2044 }
2045 
2046 
2059 static void
2060 gnc_main_window_update_tab_width_one_page (GncPluginPage *page,
2061  gpointer user_data)
2062 {
2063  gint *new_value = user_data;
2064  GtkWidget *label;
2065 
2066  ENTER("page %p, visible %d", page, *new_value);
2067  label = g_object_get_data(G_OBJECT (page), PLUGIN_PAGE_TAB_LABEL);
2068  if (!label)
2069  {
2070  LEAVE("no label");
2071  return;
2072  }
2073 
2074  if (*new_value != 0)
2075  {
2076  gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
2077  gtk_label_set_max_width_chars(GTK_LABEL(label), *new_value);
2078  }
2079  else
2080  {
2081  gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_NONE);
2082  gtk_label_set_max_width_chars(GTK_LABEL(label), 100);
2083  }
2084  LEAVE(" ");
2085 }
2086 
2087 
2100 static void
2101 gnc_main_window_update_tab_width (gpointer prefs, gchar *pref, gpointer user_data)
2102 {
2103  gint new_value;
2104 
2105  ENTER(" ");
2106  new_value = gnc_prefs_get_float (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_WIDTH);
2107  gnc_main_window_foreach_page(
2108  gnc_main_window_update_tab_width_one_page,
2109  &new_value);
2110  LEAVE(" ");
2111 }
2112 
2113 
2114 /************************************************************
2115  * Tab Label Implementation *
2116  ************************************************************/
2117 static gboolean
2118 main_window_find_tab_items (GncMainWindow *window,
2119  GncPluginPage *page,
2120  GtkWidget **label_p,
2121  GtkWidget **entry_p)
2122 {
2123  GncMainWindowPrivate *priv;
2124  GtkWidget *tab_hbox, *widget, *tab_widget;
2125  GList *children, *tmp;
2126 
2127  ENTER("window %p, page %p, label_p %p, entry_p %p",
2128  window, page, label_p, entry_p);
2129  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2130  *label_p = *entry_p = NULL;
2131 
2132  if (!page->notebook_page)
2133  {
2134  LEAVE("invalid notebook_page");
2135  return FALSE;
2136  }
2137 
2138  tab_widget = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
2139  page->notebook_page);
2140  if (GTK_IS_EVENT_BOX (tab_widget))
2141  tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
2142  else if (GTK_IS_HBOX (tab_widget))
2143  tab_hbox = tab_widget;
2144  else
2145  {
2146  PWARN ("Unknown widget for tab label %p", tab_widget);
2147  return FALSE;
2148  }
2149 
2150  children = gtk_container_get_children(GTK_CONTAINER(tab_hbox));
2151  for (tmp = children; tmp; tmp = g_list_next(tmp))
2152  {
2153  widget = tmp->data;
2154  if (GTK_IS_LABEL(widget))
2155  {
2156  *label_p = widget;
2157  }
2158  else if (GTK_IS_ENTRY(widget))
2159  {
2160  *entry_p = widget;
2161  }
2162  }
2163  g_list_free(children);
2164 
2165  LEAVE("label %p, entry %p", *label_p, *entry_p);
2166  return (*label_p && *entry_p);
2167 }
2168 
2169 static gboolean
2170 main_window_find_tab_widget (GncMainWindow *window,
2171  GncPluginPage *page,
2172  GtkWidget **widget_p)
2173 {
2174  GncMainWindowPrivate *priv;
2175 
2176  ENTER("window %p, page %p, widget %p",
2177  window, page, widget_p);
2178  *widget_p = NULL;
2179 
2180  if (!page->notebook_page)
2181  {
2182  LEAVE("invalid notebook_page");
2183  return FALSE;
2184  }
2185 
2186  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2187  *widget_p = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
2188  page->notebook_page);
2189 
2190  LEAVE("widget %p", *widget_p);
2191  return TRUE;
2192 }
2193 
2194 void
2196  const gchar *name_in)
2197 {
2198  GncMainWindow *window;
2199  GncMainWindowPrivate *priv;
2200  GtkWidget *label, *entry;
2201  gchar *name, *old_page_name, *old_page_long_name;
2202 
2203  ENTER(" ");
2204 
2205  if ((name_in == NULL) || (*name_in == '\0'))
2206  {
2207  LEAVE("no string");
2208  return;
2209  }
2210  name = g_strstrip(g_strdup(name_in));
2211 
2212  /* Optimization, if the name hasn't changed, don't update X. */
2213  if (*name == '\0' || 0 == strcmp(name, gnc_plugin_page_get_page_name(page)))
2214  {
2215  g_free(name);
2216  LEAVE("empty string or name unchanged");
2217  return;
2218  }
2219 
2220  old_page_name = g_strdup( gnc_plugin_page_get_page_name(page));
2221  old_page_long_name = g_strdup( gnc_plugin_page_get_page_long_name(page));
2222 
2223  /* Update the plugin */
2224  gnc_plugin_page_set_page_name(page, name);
2225 
2226  /* Update the notebook tab */
2227  window = GNC_MAIN_WINDOW(page->window);
2228  if (!window)
2229  {
2230  g_free(old_page_name);
2231  g_free(old_page_long_name);
2232  g_free(name);
2233  LEAVE("no window widget available");
2234  return;
2235  }
2236 
2237  if (main_window_find_tab_items(window, page, &label, &entry))
2238  gtk_label_set_text(GTK_LABEL(label), name);
2239 
2240  /* Update Tooltip on notebook Tab */
2241  if (old_page_long_name && old_page_name
2242  && g_strrstr(old_page_long_name, old_page_name) != NULL)
2243  {
2244  gchar *new_page_long_name;
2245  gint string_position;
2246  GtkWidget *tab_widget;
2247 
2248  string_position = strlen(old_page_long_name) - strlen(old_page_name);
2249  new_page_long_name = g_strconcat(g_strndup(old_page_long_name, string_position), name, NULL);
2250 
2251  gnc_plugin_page_set_page_long_name(page, new_page_long_name);
2252 
2253  if (main_window_find_tab_widget(window, page, &tab_widget))
2254  gtk_widget_set_tooltip_text(tab_widget, new_page_long_name);
2255 
2256  g_free(new_page_long_name);
2257  }
2258 
2259  /* Update the notebook menu */
2260  if (page->notebook_page)
2261  {
2262  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2263  label = gtk_notebook_get_menu_label (GTK_NOTEBOOK(priv->notebook),
2264  page->notebook_page);
2265  gtk_label_set_text(GTK_LABEL(label), name);
2266  }
2267 
2268  /* Force an update of the window title */
2269  gnc_main_window_update_title(window);
2270  g_free(old_page_long_name);
2271  g_free(old_page_name);
2272  g_free(name);
2273  LEAVE("done");
2274 }
2275 
2276 
2277 void
2279  const gchar *color_in)
2280 {
2281  GncMainWindow *window;
2282  GncMainWindowPrivate *priv;
2283  GtkWidget *tab_widget;
2284  GdkColor tab_color;
2285  gchar *color_string = NULL;
2286  gboolean want_color = FALSE;
2287 
2288  ENTER(" ");
2289  if (color_in)
2290  color_string = g_strstrip(g_strdup(color_in));
2291 
2292  if (color_string && *color_string != '\0')
2293  want_color = TRUE;
2294 
2295  /* Update the plugin */
2296  window = GNC_MAIN_WINDOW(page->window);
2297  if (want_color)
2298  gnc_plugin_page_set_page_color(page, color_string);
2299  else
2300  gnc_plugin_page_set_page_color(page, NULL);
2301 
2302  /* Update the notebook tab */
2303  main_window_find_tab_widget (window, page, &tab_widget);
2304  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2305 
2306  if (want_color && gdk_color_parse(color_string, &tab_color) && priv->show_color_tabs)
2307  {
2308  if (!GTK_IS_EVENT_BOX (tab_widget))
2309  {
2310  GtkWidget *event_box = gtk_event_box_new ();
2311  g_object_ref (tab_widget);
2312  gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
2313  page->notebook_page, event_box);
2314  gtk_container_add (GTK_CONTAINER(event_box), tab_widget);
2315  g_object_unref (tab_widget);
2316  tab_widget = event_box;
2317  }
2318  gtk_widget_modify_bg(tab_widget, GTK_STATE_NORMAL, &tab_color);
2319  gtk_widget_modify_bg(tab_widget, GTK_STATE_ACTIVE, &tab_color);
2320  }
2321  else
2322  {
2323  if (GTK_IS_EVENT_BOX (tab_widget))
2324  {
2325  GtkWidget *tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
2326  g_object_ref (tab_hbox);
2327  gtk_container_remove (GTK_CONTAINER(tab_widget), tab_hbox);
2328  gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
2329  page->notebook_page, tab_hbox);
2330  g_object_unref (tab_hbox);
2331  }
2332  }
2333  g_free(color_string);
2334  LEAVE("done");
2335 }
2336 
2337 
2338 static void
2339 gnc_main_window_tab_entry_activate (GtkWidget *entry,
2340  GncPluginPage *page)
2341 {
2342  GtkWidget *label, *entry2;
2343 
2344  g_return_if_fail(GTK_IS_ENTRY(entry));
2345  g_return_if_fail(GNC_IS_PLUGIN_PAGE(page));
2346 
2347  ENTER("");
2348  if (!main_window_find_tab_items(GNC_MAIN_WINDOW(page->window),
2349  page, &label, &entry2))
2350  {
2351  LEAVE("can't find required widgets");
2352  return;
2353  }
2354 
2355  main_window_update_page_name(page, gtk_entry_get_text(GTK_ENTRY(entry)));
2356 
2357  gtk_widget_hide(entry);
2358  gtk_widget_show(label);
2359  LEAVE("");
2360 }
2361 
2362 
2363 static gboolean
2364 gnc_main_window_tab_entry_editing_done (GtkWidget *entry,
2365  GncPluginPage *page)
2366 {
2367  ENTER("");
2368  gnc_main_window_tab_entry_activate(entry, page);
2369  LEAVE("");
2370  return FALSE;
2371 }
2372 
2373 static gboolean
2374 gnc_main_window_tab_entry_focus_out_event (GtkWidget *entry,
2375  GdkEvent *event,
2376  GncPluginPage *page)
2377 {
2378  ENTER("");
2379  gtk_cell_editable_editing_done(GTK_CELL_EDITABLE(entry));
2380  LEAVE("");
2381  return FALSE;
2382 }
2383 
2384 static gboolean
2385 gnc_main_window_tab_entry_key_press_event (GtkWidget *entry,
2386  GdkEventKey *event,
2387  GncPluginPage *page)
2388 {
2389  if (event->keyval == GDK_KEY_Escape)
2390  {
2391  GtkWidget *label, *entry2;
2392 
2393  g_return_val_if_fail(GTK_IS_ENTRY(entry), FALSE);
2394  g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
2395 
2396  ENTER("");
2397  if (!main_window_find_tab_items(GNC_MAIN_WINDOW(page->window),
2398  page, &label, &entry2))
2399  {
2400  LEAVE("can't find required widgets");
2401  return FALSE;
2402  }
2403 
2404  gtk_entry_set_text(GTK_ENTRY(entry), gtk_label_get_text(GTK_LABEL(label)));
2405  gtk_widget_hide(entry);
2406  gtk_widget_show(label);
2407  LEAVE("");
2408  }
2409  return FALSE;
2410 }
2411 
2412 /************************************************************
2413  * Widget Implementation *
2414  ************************************************************/
2415 
2416 /* Get the type of a gnc main window.
2417  */
2418 GType
2420 {
2421  static GType gnc_main_window_type = 0;
2422 
2423  if (gnc_main_window_type == 0)
2424  {
2425  static const GTypeInfo our_info =
2426  {
2427  sizeof (GncMainWindowClass),
2428  NULL,
2429  NULL,
2430  (GClassInitFunc) gnc_main_window_class_init,
2431  NULL,
2432  NULL,
2433  sizeof (GncMainWindow),
2434  0,
2435  (GInstanceInitFunc) gnc_main_window_init
2436  };
2437 
2438  static const GInterfaceInfo plugin_info =
2439  {
2440  (GInterfaceInitFunc) gnc_window_main_window_init,
2441  NULL,
2442  NULL
2443  };
2444 
2445  gnc_main_window_type = g_type_register_static (GTK_TYPE_WINDOW,
2446  GNC_MAIN_WINDOW_NAME,
2447  &our_info, 0);
2448  g_type_add_interface_static (gnc_main_window_type,
2449  GNC_TYPE_WINDOW,
2450  &plugin_info);
2451  }
2452 
2453  return gnc_main_window_type;
2454 }
2455 
2456 
2464 static void
2465 gnc_main_window_class_init (GncMainWindowClass *klass)
2466 {
2467  GObjectClass *object_class = G_OBJECT_CLASS (klass);
2468  GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS(klass);
2469 
2470  parent_class = g_type_class_peek_parent (klass);
2471 
2472  window_type = g_quark_from_static_string ("gnc-main-window");
2473 
2474  object_class->finalize = gnc_main_window_finalize;
2475 
2476  /* GtkObject signals */
2477  gtkobject_class->destroy = gnc_main_window_destroy;
2478 
2479  g_type_class_add_private(klass, sizeof(GncMainWindowPrivate));
2480 
2492  main_window_signals[PAGE_ADDED] =
2493  g_signal_new ("page_added",
2494  G_OBJECT_CLASS_TYPE (object_class),
2495  G_SIGNAL_RUN_FIRST,
2496  G_STRUCT_OFFSET (GncMainWindowClass, page_added),
2497  NULL, NULL,
2498  g_cclosure_marshal_VOID__OBJECT,
2499  G_TYPE_NONE, 1,
2500  G_TYPE_OBJECT);
2501 
2512  main_window_signals[PAGE_CHANGED] =
2513  g_signal_new ("page_changed",
2514  G_OBJECT_CLASS_TYPE (object_class),
2515  G_SIGNAL_RUN_FIRST,
2516  G_STRUCT_OFFSET (GncMainWindowClass, page_changed),
2517  NULL, NULL,
2518  g_cclosure_marshal_VOID__OBJECT,
2519  G_TYPE_NONE, 1,
2520  G_TYPE_OBJECT);
2521 
2522  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
2523  GNC_PREF_SHOW_CLOSE_BUTTON,
2524  gnc_main_window_update_tab_close,
2525  NULL);
2526  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
2527  GNC_PREF_TAB_WIDTH,
2528  gnc_main_window_update_tab_width,
2529  NULL);
2530 
2531  gnc_hook_add_dangler(HOOK_BOOK_SAVED,
2532  (GFunc)gnc_main_window_update_all_titles, NULL);
2533  gnc_hook_add_dangler(HOOK_BOOK_OPENED,
2534  (GFunc)gnc_main_window_attach_to_book, NULL);
2535 
2536 }
2537 
2538 
2547 static void
2548 gnc_main_window_init (GncMainWindow *window,
2549  GncMainWindowClass *klass)
2550 {
2551  GncMainWindowPrivate *priv;
2552 
2553  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2554  priv->merged_actions_table =
2555  g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
2556 
2557  priv->event_handler_id =
2558  qof_event_register_handler(gnc_main_window_event_handler, window);
2559 
2560  /* Get the show_color_tabs value preference */
2561  priv->show_color_tabs = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_COLOR);
2562  priv->about_dialog = NULL;
2563 
2564  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
2565  GNC_PREF_TAB_COLOR,
2566  gnc_main_window_update_tab_color,
2567  window);
2568 
2569  gnc_main_window_setup_window (window);
2570  gnc_gobject_tracking_remember(G_OBJECT(window),
2571  G_OBJECT_CLASS(klass));
2572 }
2573 
2574 
2585 static void
2586 gnc_main_window_finalize (GObject *object)
2587 {
2588  g_return_if_fail (object != NULL);
2589  g_return_if_fail (GNC_IS_MAIN_WINDOW (object));
2590 
2591  if (active_windows == NULL)
2592  {
2593  /* Oops. User killed last window and we didn't catch it. */
2594  g_idle_add((GSourceFunc)gnc_shutdown, 0);
2595  }
2596 
2598  G_OBJECT_CLASS (parent_class)->finalize (object);
2599 }
2600 
2601 
2602 static void
2603 gnc_main_window_destroy (GtkObject *object)
2604 {
2605  GncMainWindow *window;
2606  GncMainWindowPrivate *priv;
2607  GncPluginManager *manager;
2608  GList *plugins;
2609 
2610  g_return_if_fail (object != NULL);
2611  g_return_if_fail (GNC_IS_MAIN_WINDOW (object));
2612 
2613  window = GNC_MAIN_WINDOW (object);
2614 
2615  active_windows = g_list_remove (active_windows, window);
2616 
2617  /* Do these things once */
2618  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2619  if (priv->merged_actions_table)
2620  {
2621 
2622  /* Close any pages in this window */
2623  while (priv->current_page)
2625 
2626  if (gnc_window_get_progressbar_window() == GNC_WINDOW(window))
2627  gnc_window_set_progressbar_window(NULL);
2628 #ifndef MAC_INTEGRATION
2629  /* Update the "Windows" menu in all other windows */
2630  gnc_main_window_update_all_menu_items();
2631 #endif
2632  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
2633  GNC_PREF_TAB_COLOR,
2634  gnc_main_window_update_tab_color,
2635  window);
2636 
2638  priv->event_handler_id = 0;
2639 
2640  g_hash_table_destroy (priv->merged_actions_table);
2641  priv->merged_actions_table = NULL;
2642 
2643  /* GncPluginManager stuff */
2644  manager = gnc_plugin_manager_get ();
2645  plugins = gnc_plugin_manager_get_plugins (manager);
2646  g_list_foreach (plugins, gnc_main_window_remove_plugin, window);
2647  g_list_free (plugins);
2648  }
2649  if (priv->about_dialog)
2650  g_object_unref (priv->about_dialog);
2651  GTK_OBJECT_CLASS (parent_class)->destroy (object);
2652 }
2653 
2654 
2655 /* Create a new gnc main window plugin.
2656  */
2657 GncMainWindow *
2659 {
2660  GncMainWindow *window;
2661  GtkWidget *old_window;
2662 
2663  window = g_object_new (GNC_TYPE_MAIN_WINDOW, NULL);
2664  gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
2665 
2666  old_window = gnc_ui_get_toplevel();
2667  if (old_window)
2668  {
2669  gint width, height;
2670  gtk_window_get_size (GTK_WINDOW (old_window), &width, &height);
2671  gtk_window_resize (GTK_WINDOW (window), width, height);
2672  if ((gdk_window_get_state((GTK_WIDGET(old_window))->window)
2673  & GDK_WINDOW_STATE_MAXIMIZED) != 0)
2674  {
2675  gtk_window_maximize (GTK_WINDOW (window));
2676  }
2677  }
2678  active_windows = g_list_append (active_windows, window);
2679  gnc_main_window_update_title(window);
2680 #ifdef MAC_INTEGRATION
2681  gnc_quartz_set_menu(window);
2682 #else
2683  gnc_main_window_update_all_menu_items();
2684 #endif
2685  gnc_engine_add_commit_error_callback( gnc_main_window_engine_commit_error_callback, window );
2686 
2687  return window;
2688 }
2689 
2690 /************************************************************
2691  * Utility Functions *
2692  ************************************************************/
2693 
2694 static void
2695 gnc_main_window_engine_commit_error_callback( gpointer data,
2696  QofBackendError errcode )
2697 {
2698  GncMainWindow* window = GNC_MAIN_WINDOW(data);
2699  GtkWidget* dialog;
2700  const gchar *reason = _("Unable to save to database.");
2701  if ( errcode == ERR_BACKEND_READONLY )
2702  reason = _("Unable to save to database: Book is marked read-only.");
2703  dialog = gtk_message_dialog_new( GTK_WINDOW(window),
2704  GTK_DIALOG_DESTROY_WITH_PARENT,
2705  GTK_MESSAGE_ERROR,
2706  GTK_BUTTONS_CLOSE,
2707  "%s",
2708  reason );
2709  gtk_dialog_run(GTK_DIALOG (dialog));
2710  gtk_widget_destroy(dialog);
2711 
2712 }
2713 
2731 static void
2732 gnc_main_window_connect (GncMainWindow *window,
2733  GncPluginPage *page,
2734  GtkWidget *tab_hbox,
2735  GtkWidget *menu_label)
2736 {
2737  GncMainWindowPrivate *priv;
2738  GtkNotebook *notebook;
2739 
2740  page->window = GTK_WIDGET(window);
2741  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2742  notebook = GTK_NOTEBOOK (priv->notebook);
2743  priv->installed_pages = g_list_append (priv->installed_pages, page);
2744  priv->usage_order = g_list_prepend (priv->usage_order, page);
2745  gtk_notebook_append_page_menu (notebook, page->notebook_page,
2746  tab_hbox, menu_label);
2747  gtk_notebook_set_tab_reorderable (notebook, page->notebook_page, TRUE);
2748  gnc_plugin_page_inserted (page);
2749  gtk_notebook_set_current_page (notebook, -1);
2750  if (GNC_PLUGIN_PAGE_GET_CLASS(page)->window_changed)
2751  (GNC_PLUGIN_PAGE_GET_CLASS(page)->window_changed)(page, GTK_WIDGET(window));
2752  g_signal_emit (window, main_window_signals[PAGE_ADDED], 0, page);
2753 
2754  g_signal_connect(G_OBJECT(page->notebook_page), "popup-menu",
2755  G_CALLBACK(gnc_main_window_popup_menu_cb), page);
2756  g_signal_connect_after(G_OBJECT(page->notebook_page), "button-press-event",
2757  G_CALLBACK(gnc_main_window_button_press_cb), page);
2758 }
2759 
2760 
2774 static void
2775 gnc_main_window_disconnect (GncMainWindow *window,
2776  GncPluginPage *page)
2777 {
2778  GncMainWindowPrivate *priv;
2779  GtkNotebook *notebook;
2780  GncPluginPage *new_page;
2781  gint page_num;
2782 
2783  /* Disconnect the callbacks */
2784  g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
2785  G_CALLBACK(gnc_main_window_popup_menu_cb), page);
2786  g_signal_handlers_disconnect_by_func(G_OBJECT(page->notebook_page),
2787  G_CALLBACK(gnc_main_window_button_press_cb), page);
2788 
2789  /* Disconnect the page and summarybar from the window */
2790  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2791  if (priv->current_page == page)
2792  {
2793  gnc_plugin_page_unmerge_actions (page, window->ui_merge);
2794  gnc_plugin_page_unselected (page);
2795  priv->current_page = NULL;
2796  }
2797 
2798  /* Remove it from the list of pages in the window */
2799  priv->installed_pages = g_list_remove (priv->installed_pages, page);
2800  priv->usage_order = g_list_remove (priv->usage_order, page);
2801 
2802  /* Switch to the last recently used page */
2803  notebook = GTK_NOTEBOOK (priv->notebook);
2804  if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_NEXT_RECENT))
2805  {
2806  new_page = g_list_nth_data (priv->usage_order, 0);
2807  if (new_page)
2808  {
2809  page_num = gtk_notebook_page_num(notebook, new_page->notebook_page);
2810  gtk_notebook_set_current_page(notebook, page_num);
2811  /* This may have caused WebKit to schedule a timer interrupt which it
2812  sometimes forgets to cancel before deleting the object. See
2813  <https://bugs.webkit.org/show_bug.cgi?id=119003>. Get around this
2814  by flushing all events to get rid of the timer interrupt. */
2815  while (gtk_events_pending())
2816  gtk_main_iteration();
2817  }
2818  }
2819 
2820  /* Remove the page from the notebook */
2821  page_num = gtk_notebook_page_num(notebook, page->notebook_page);
2822  gtk_notebook_remove_page (notebook, page_num);
2823 
2824  if ( gtk_notebook_get_current_page(notebook) == -1)
2825  {
2826  /* Need to synthesize a page changed signal when the last
2827  * page is removed. The notebook doesn't generate a signal
2828  * for this, therefore the switch_page code in this file
2829  * never gets called to generate this signal. */
2830  gnc_main_window_switch_page(notebook, NULL, -1, window);
2831  //g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, NULL);
2832  }
2833 
2834  gnc_plugin_page_removed (page);
2835 
2836  gtk_ui_manager_ensure_update (window->ui_merge);
2837  gnc_window_set_status (GNC_WINDOW(window), page, NULL);
2838 }
2839 
2840 
2841 /************************************************************
2842  * *
2843  ************************************************************/
2844 
2845 
2846 void
2848 {
2849  GncMainWindow *window;
2850  GncMainWindowPrivate *priv;
2851  GtkNotebook *notebook;
2852  gint page_num;
2853 
2854  window = GNC_MAIN_WINDOW (page->window);
2855  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2856  notebook = GTK_NOTEBOOK (priv->notebook);
2857  page_num = gtk_notebook_page_num(notebook, page->notebook_page);
2858  gtk_notebook_set_current_page (notebook, page_num);
2859  gtk_window_present(GTK_WINDOW(window));
2860 }
2861 
2862 
2863 /* Display a data plugin page in a window. If the page already
2864  * exists in any window, then that window will be brought to the
2865  * front and the notebook switch to display the specified page. If
2866  * the page is new then it will be added to the specified window. If
2867  * the window is NULL, the new page will be added to the first
2868  * window.
2869  */
2870 void
2872  GncPluginPage *page)
2873 {
2874  GncMainWindowPrivate *priv;
2875  GtkWidget *tab_hbox;
2876  GtkWidget *label, *entry;
2877  const gchar *icon, *text, *color_string;
2878  GtkWidget *image;
2879  GList *tmp;
2880  gint width;
2881  GdkColor tab_color;
2882 
2883  ENTER("window %p, page %p", window, page);
2884  if (window)
2885  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
2886  g_return_if_fail (GNC_IS_PLUGIN_PAGE (page));
2887  g_return_if_fail (gnc_plugin_page_has_books(page));
2888 
2889  if (gnc_main_window_page_exists(page))
2890  {
2892  return;
2893  }
2894 
2895  /* Does the page want to be in a new window? */
2897  {
2898  /* See if there's a blank window. If so, use that. */
2899  for (tmp = active_windows; tmp; tmp = g_list_next(tmp))
2900  {
2901  window = GNC_MAIN_WINDOW(tmp->data);
2902  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2903  if (priv->installed_pages == NULL)
2904  {
2905  break;
2906  }
2907  }
2908  if (tmp == NULL)
2909  window = gnc_main_window_new ();
2910  gtk_widget_show(GTK_WIDGET(window));
2911  }
2912  else if ((window == NULL) && active_windows)
2913  {
2914  window = active_windows->data;
2915  }
2916 
2917  page->window = GTK_WIDGET(window);
2918  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
2920  g_object_set_data (G_OBJECT (page->notebook_page),
2921  PLUGIN_PAGE_LABEL, page);
2922 
2923  /*
2924  * The page tab.
2925  */
2926  width = gnc_prefs_get_float(GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_WIDTH);
2927  icon = GNC_PLUGIN_PAGE_GET_CLASS(page)->tab_icon;
2928  label = gtk_label_new (gnc_plugin_page_get_page_name(page));
2929  if (width != 0)
2930  {
2931  gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
2932  gtk_label_set_max_width_chars(GTK_LABEL(label), width);
2933  }
2934  gtk_widget_show (label);
2935 
2936  tab_hbox = gtk_hbox_new (FALSE, 6);
2937  gtk_widget_show (tab_hbox);
2938 
2939  if (icon != NULL)
2940  {
2941  image = gtk_image_new_from_stock (icon, GTK_ICON_SIZE_MENU);
2942  gtk_widget_show (image);
2943  gtk_box_pack_start (GTK_BOX (tab_hbox), image, FALSE, FALSE, 0);
2944  gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0);
2945  }
2946  else
2947  gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0);
2948 
2950  if (text)
2951  {
2952  gtk_widget_set_tooltip_text(tab_hbox, text);
2953  }
2954 
2955  entry = gtk_entry_new();
2956  gtk_widget_hide (entry);
2957  gtk_box_pack_start (GTK_BOX (tab_hbox), entry, TRUE, TRUE, 0);
2958  g_signal_connect(G_OBJECT(entry), "activate",
2959  G_CALLBACK(gnc_main_window_tab_entry_activate), page);
2960  g_signal_connect(G_OBJECT(entry), "focus-out-event",
2961  G_CALLBACK(gnc_main_window_tab_entry_focus_out_event),
2962  page);
2963  g_signal_connect(G_OBJECT(entry), "key-press-event",
2964  G_CALLBACK(gnc_main_window_tab_entry_key_press_event),
2965  page);
2966  g_signal_connect(G_OBJECT(entry), "editing-done",
2967  G_CALLBACK(gnc_main_window_tab_entry_editing_done),
2968  page);
2969 
2970  /* Add close button - Not for immutable pages */
2971  if (!g_object_get_data (G_OBJECT (page), PLUGIN_PAGE_IMMUTABLE))
2972  {
2973  GtkWidget *close_image, *close_button;
2974  GtkRequisition requisition;
2975 
2976  close_button = gtk_button_new();
2977  gtk_button_set_relief(GTK_BUTTON(close_button), GTK_RELIEF_NONE);
2978  close_image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
2979  gtk_widget_show(close_image);
2980  gtk_widget_size_request(close_image, &requisition);
2981  gtk_widget_set_size_request(close_button, requisition.width + 4,
2982  requisition.height + 2);
2983  gtk_button_set_alignment(GTK_BUTTON(close_button), 0.5, 0.5);
2984  gtk_container_add(GTK_CONTAINER(close_button), close_image);
2985  if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_SHOW_CLOSE_BUTTON))
2986  gtk_widget_show (close_button);
2987  else
2988  gtk_widget_hide (close_button);
2989 
2990  g_signal_connect_swapped (G_OBJECT (close_button), "clicked",
2991  G_CALLBACK(gnc_main_window_close_page), page);
2992 
2993  gtk_box_pack_start (GTK_BOX (tab_hbox), close_button, FALSE, FALSE, 0);
2994 
2995  g_object_set_data (G_OBJECT (page), PLUGIN_PAGE_CLOSE_BUTTON, close_button);
2996  }
2997 
2998  /*
2999  * The popup menu
3000  */
3001  label = gtk_label_new (gnc_plugin_page_get_page_name(page));
3002 
3003  /*
3004  * Now install it all in the window.
3005  */
3006  gnc_main_window_connect(window, page, tab_hbox, label);
3007 
3008  color_string = gnc_plugin_page_get_page_color(page);
3009  main_window_update_page_color (page, color_string);
3010  LEAVE("");
3011 }
3012 
3013 
3014 /* Remove a data plugin page from a window and display the previous
3015  * page. If the page removed was the last page in the window, and
3016  * there is more than one window open, then the entire window will be
3017  * destroyed.
3018  */
3019 void
3021 {
3022  GncMainWindow *window;
3023  GncMainWindowPrivate *priv;
3024 
3025  if (!page || !page->notebook_page)
3026  return;
3027 
3028  if (!gnc_plugin_page_finish_pending(page))
3029  return;
3030 
3031  if (!GNC_IS_MAIN_WINDOW (page->window))
3032  return;
3033 
3034  window = GNC_MAIN_WINDOW (page->window);
3035  if (!window)
3036  {
3037  g_warning("Page is not in a window.");
3038  return;
3039  }
3040 
3041  gnc_main_window_disconnect(window, page);
3043  g_object_unref(page);
3044 
3045  /* If this isn't the last window, go ahead and destroy the window. */
3046  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3047  if (priv->installed_pages == NULL)
3048  {
3049  if (g_list_length(active_windows) > 1)
3050  {
3051  gtk_widget_destroy(GTK_WIDGET(window));
3052  }
3053  }
3054 }
3055 
3056 
3057 /* Retrieve a pointer to the page that is currently at the front of
3058  * the specified window. Any plugin that needs to manipulate its
3059  * menus based upon the currently selected menu page should connect
3060  * to the "page_changed" signal on a window. The callback function
3061  * from that signal can then call this function to obtain a pointer
3062  * to the current page.
3063  */
3064 GncPluginPage *
3066 {
3067  GncMainWindowPrivate *priv;
3068 
3069  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3070  return priv->current_page;
3071 }
3072 
3073 
3074 /* Manually add a set of actions to the specified window. Plugins
3075  * whose user interface is not hard coded (e.g. the menu-additions
3076  * plugin) must create their actions at run time, then use this
3077  * function to install them into the window.
3078  */
3079 void
3081  const gchar *group_name,
3082  GtkActionGroup *group,
3083  guint merge_id)
3084 {
3085  GncMainWindowPrivate *priv;
3086  MergedActionEntry *entry;
3087 
3088  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3089  g_return_if_fail (group_name != NULL);
3090  g_return_if_fail (GTK_IS_ACTION_GROUP(group));
3091  g_return_if_fail (merge_id > 0);
3092 
3093  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3094  entry = g_new0 (MergedActionEntry, 1);
3095  entry->action_group = group;
3096  entry->merge_id = merge_id;
3097  gtk_ui_manager_ensure_update (window->ui_merge);
3098  g_hash_table_insert (priv->merged_actions_table, g_strdup (group_name), entry);
3099 }
3100 
3101 
3102 /* Add a set of actions to the specified window. This function
3103  * should not need to be called directly by plugin implementors.
3104  * Correctly assigning values to the GncPluginClass fields during
3105  * plugin initialization will cause this routine to be automatically
3106  * called.
3107  */
3108 void
3110  const gchar *group_name,
3111  GtkActionEntry *actions,
3112  guint n_actions,
3113  GtkToggleActionEntry *toggle_actions,
3114  guint n_toggle_actions,
3115  const gchar *filename,
3116  gpointer user_data)
3117 {
3118  GncMainWindowPrivate *priv;
3120  MergedActionEntry *entry;
3121  GError *error = NULL;
3122  gchar *pathname;
3123 
3124  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3125  g_return_if_fail (group_name != NULL);
3126  g_return_if_fail (actions != NULL);
3127  g_return_if_fail (n_actions > 0);
3128  g_return_if_fail (filename != NULL);
3129 
3130  pathname = gnc_filepath_locate_ui_file (filename);
3131  if (pathname == NULL)
3132  return;
3133 
3134  data = g_new0 (GncMainWindowActionData, 1);
3135  data->window = window;
3136  data->data = user_data;
3137 
3138  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3139  entry = g_new0 (MergedActionEntry, 1);
3140  entry->action_group = gtk_action_group_new (group_name);
3142  gtk_action_group_add_actions (entry->action_group, actions, n_actions, data);
3143  if (toggle_actions != NULL && n_toggle_actions > 0)
3144  {
3145  gtk_action_group_add_toggle_actions (entry->action_group,
3146  toggle_actions, n_toggle_actions,
3147  data);
3148  }
3149  gtk_ui_manager_insert_action_group (window->ui_merge, entry->action_group, 0);
3150  entry->merge_id = gtk_ui_manager_add_ui_from_file (window->ui_merge, pathname, &error);
3151  g_assert(entry->merge_id || error);
3152  if (entry->merge_id)
3153  {
3154  gtk_ui_manager_ensure_update (window->ui_merge);
3155  g_hash_table_insert (priv->merged_actions_table, g_strdup (group_name), entry);
3156  }
3157  else
3158  {
3159  g_critical("Failed to load ui file.\n Filename %s\n Error %s",
3160  filename, error->message);
3161  g_error_free(error);
3162  g_free(entry);
3163  }
3164  g_free(pathname);
3165 }
3166 
3167 
3168 /* Remove a set of actions from the specified window. This function
3169  * should not need to be called directly by plugin implementors. It
3170  * will automatically be called when a plugin is removed from a
3171  * window.
3172  */
3173 void
3175  const gchar *group_name)
3176 {
3177  GncMainWindowPrivate *priv;
3178  MergedActionEntry *entry;
3179 
3180  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3181  g_return_if_fail (group_name != NULL);
3182 
3183  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3184  if (priv->merged_actions_table == NULL)
3185  return;
3186  entry = g_hash_table_lookup (priv->merged_actions_table, group_name);
3187 
3188  if (entry == NULL)
3189  return;
3190 
3191  gtk_ui_manager_remove_action_group (window->ui_merge, entry->action_group);
3192  gtk_ui_manager_remove_ui (window->ui_merge, entry->merge_id);
3193  gtk_ui_manager_ensure_update (window->ui_merge);
3194 
3195  g_hash_table_remove (priv->merged_actions_table, group_name);
3196 }
3197 
3198 
3199 /* Force a full update of the user interface for the specified
3200  * window. This can be an expensive function, but is needed because
3201  * the gtk ui manager doesn't always seem to update properly when
3202  * actions are changed.
3203  */
3204 void
3206 {
3207  GtkActionGroup *force;
3208 
3209  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3210 
3211  /* Unfortunately gtk_ui_manager_ensure_update doesn't work
3212  * here. Force a full update by adding and removing an empty
3213  * action group.
3214  */
3215  force = gtk_action_group_new("force_update");
3216  gtk_ui_manager_insert_action_group (window->ui_merge, force, 0);
3217  gtk_ui_manager_ensure_update (window->ui_merge);
3218  gtk_ui_manager_remove_action_group (window->ui_merge, force);
3219  g_object_unref(force);
3220 }
3221 
3222 
3223 GtkAction *
3224 gnc_main_window_find_action (GncMainWindow *window, const gchar *name)
3225 {
3226  GtkAction *action = NULL;
3227  const GList *groups, *tmp;
3228 
3229  groups = gtk_ui_manager_get_action_groups(window->ui_merge);
3230  for (tmp = groups; tmp; tmp = g_list_next(tmp))
3231  {
3232  action = gtk_action_group_get_action(GTK_ACTION_GROUP(tmp->data), name);
3233  if (action)
3234  break;
3235  }
3236  return action;
3237 }
3238 
3239 
3240 /* Retrieve a specific set of user interface actions from a window.
3241  * This function can be used to get an group of action to be
3242  * manipulated when the front page of a window has changed.
3243  */
3244 GtkActionGroup *
3246  const gchar *group_name)
3247 {
3248  GncMainWindowPrivate *priv;
3249  MergedActionEntry *entry;
3250 
3251  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), NULL);
3252  g_return_val_if_fail (group_name != NULL, NULL);
3253 
3254  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3255  if (priv->merged_actions_table == NULL)
3256  return NULL;
3257  entry = g_hash_table_lookup (priv->merged_actions_table, group_name);
3258 
3259  if (entry == NULL)
3260  return NULL;
3261 
3262  return entry->action_group;
3263 }
3264 
3265 static void
3266 gnc_main_window_update_tab_position (gpointer prefs, gchar *pref, gpointer user_data)
3267 {
3268  GncMainWindow *window;
3269  GtkPositionType position = GTK_POS_TOP;
3270  GncMainWindowPrivate *priv;
3271 
3272  window = GNC_MAIN_WINDOW(user_data);
3273 
3274  ENTER ("window %p", window);
3275  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_BOTTOM))
3276  position = GTK_POS_BOTTOM;
3277  else if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_LEFT))
3278  position = GTK_POS_LEFT;
3279  else if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_TAB_POSITION_RIGHT))
3280  position = GTK_POS_RIGHT;
3281 
3282  priv = GNC_MAIN_WINDOW_GET_PRIVATE (window);
3283  gtk_notebook_set_tab_pos (GTK_NOTEBOOK (priv->notebook), position);
3284 
3285  LEAVE ("");
3286 }
3287 
3288 /*
3289  * Based on code from Epiphany (src/ephy-window.c)
3290  */
3291 static void
3292 gnc_main_window_update_edit_actions_sensitivity (GncMainWindow *window, gboolean hide)
3293 {
3294  GncMainWindowPrivate *priv;
3295  GncPluginPage *page;
3296  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
3297  GtkAction *action;
3298  gboolean can_copy = FALSE, can_cut = FALSE, can_paste = FALSE;
3299 
3300  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3301  page = priv->current_page;
3302  if (page && GNC_PLUGIN_PAGE_GET_CLASS(page)->update_edit_menu_actions)
3303  {
3304  (GNC_PLUGIN_PAGE_GET_CLASS(page)->update_edit_menu_actions)(page, hide);
3305  return;
3306  }
3307 
3308  if (GTK_IS_EDITABLE (widget))
3309  {
3310  gboolean has_selection;
3311 
3312  has_selection = gtk_editable_get_selection_bounds
3313  (GTK_EDITABLE (widget), NULL, NULL);
3314 
3315  can_copy = has_selection;
3316  can_cut = has_selection;
3317  can_paste = TRUE;
3318  }
3319  else if (GTK_IS_TEXT_VIEW (widget))
3320  {
3321  gboolean has_selection;
3322  GtkTextBuffer *text_buffer;
3323 
3324  text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
3325  has_selection = gtk_text_buffer_get_selection_bounds
3326  (text_buffer, NULL, NULL);
3327 
3328  can_copy = has_selection;
3329  can_cut = has_selection;
3330  can_paste = TRUE;
3331  }
3332  else
3333  {
3334 #ifdef ORIGINAL_EPIPHANY_CODE
3335  /* For now we assume all actions are possible */
3336  can_copy = can_cut = can_paste = TRUE;
3337 #else
3338  /* If its not a GtkEditable, we don't know what to do
3339  * with it. */
3340  can_copy = can_cut = can_paste = FALSE;
3341 #endif
3342  }
3343 
3344  action = gnc_main_window_find_action (window, "EditCopyAction");
3345  gtk_action_set_sensitive (action, can_copy);
3346  gtk_action_set_visible (action, !hide || can_copy);
3347  action = gnc_main_window_find_action (window, "EditCutAction");
3348  gtk_action_set_sensitive (action, can_cut);
3349  gtk_action_set_visible (action, !hide || can_cut);
3350  action = gnc_main_window_find_action (window, "EditPasteAction");
3351  gtk_action_set_sensitive (action, can_paste);
3352  gtk_action_set_visible (action, !hide || can_paste);
3353 }
3354 
3355 static void
3356 gnc_main_window_enable_edit_actions_sensitivity (GncMainWindow *window)
3357 {
3358  GtkAction *action;
3359 
3360  action = gnc_main_window_find_action (window, "EditCopyAction");
3361  gtk_action_set_sensitive (action, TRUE);
3362  gtk_action_set_visible (action, TRUE);
3363  action = gnc_main_window_find_action (window, "EditCutAction");
3364  gtk_action_set_sensitive (action, TRUE);
3365  gtk_action_set_visible (action, TRUE);
3366  action = gnc_main_window_find_action (window, "EditPasteAction");
3367  gtk_action_set_sensitive (action, TRUE);
3368  gtk_action_set_visible (action, TRUE);
3369 }
3370 
3371 static void
3372 gnc_main_window_edit_menu_show_cb (GtkWidget *menu,
3373  GncMainWindow *window)
3374 {
3375  gnc_main_window_update_edit_actions_sensitivity (window, FALSE);
3376 }
3377 
3378 static void
3379 gnc_main_window_edit_menu_hide_cb (GtkWidget *menu,
3380  GncMainWindow *window)
3381 {
3382  gnc_main_window_enable_edit_actions_sensitivity (window);
3383 }
3384 
3385 static void
3386 gnc_main_window_init_menu_updaters (GncMainWindow *window)
3387 {
3388  GtkWidget *edit_menu_item, *edit_menu;
3389 
3390  edit_menu_item = gtk_ui_manager_get_widget
3391  (window->ui_merge, "/menubar/Edit");
3392  edit_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (edit_menu_item));
3393 
3394  g_signal_connect (edit_menu, "show",
3395  G_CALLBACK (gnc_main_window_edit_menu_show_cb), window);
3396  g_signal_connect (edit_menu, "hide",
3397  G_CALLBACK (gnc_main_window_edit_menu_hide_cb), window);
3398 }
3399 
3400 /* CS: This callback functions will set the statusbar text to the
3401  * "tooltip" property of the currently selected GtkAction.
3402  *
3403  * This code is directly copied from gtk+/test/testmerge.c.
3404  * Thanks to (L)GPL! */
3405 typedef struct _ActionStatus ActionStatus;
3407 {
3408  GtkAction *action;
3409  GtkWidget *statusbar;
3410 };
3411 
3412 static void
3413 action_status_destroy (gpointer data)
3414 {
3415  ActionStatus *action_status = data;
3416 
3417  g_object_unref (action_status->action);
3418  g_object_unref (action_status->statusbar);
3419 
3420  g_free (action_status);
3421 }
3422 
3423 static void
3424 set_tip (GtkWidget *widget)
3425 {
3426  ActionStatus *data;
3427  gchar *tooltip;
3428 
3429  data = g_object_get_data (G_OBJECT (widget), "action-status");
3430 
3431  if (data)
3432  {
3433  g_object_get (data->action, "tooltip", &tooltip, NULL);
3434 
3435  gtk_statusbar_push (GTK_STATUSBAR (data->statusbar), 0,
3436  tooltip ? tooltip : "");
3437 
3438  g_free (tooltip);
3439  }
3440 }
3441 
3442 static void
3443 unset_tip (GtkWidget *widget)
3444 {
3445  ActionStatus *data;
3446 
3447  data = g_object_get_data (G_OBJECT (widget), "action-status");
3448 
3449  if (data)
3450  gtk_statusbar_pop (GTK_STATUSBAR (data->statusbar), 0);
3451 }
3452 
3453 static void
3454 connect_proxy (GtkUIManager *merge,
3455  GtkAction *action,
3456  GtkWidget *proxy,
3457  GtkWidget *statusbar)
3458 {
3459  if (GTK_IS_MENU_ITEM (proxy))
3460  {
3461  ActionStatus *data;
3462 
3463  data = g_object_get_data (G_OBJECT (proxy), "action-status");
3464  if (data)
3465  {
3466  g_object_unref (data->action);
3467  g_object_unref (data->statusbar);
3468 
3469  data->action = g_object_ref (action);
3470  data->statusbar = g_object_ref (statusbar);
3471  }
3472  else
3473  {
3474  data = g_new0 (ActionStatus, 1);
3475 
3476  data->action = g_object_ref (action);
3477  data->statusbar = g_object_ref (statusbar);
3478 
3479  g_object_set_data_full (G_OBJECT (proxy), "action-status",
3480  data, action_status_destroy);
3481 
3482  g_signal_connect (proxy, "select", G_CALLBACK (set_tip), NULL);
3483  g_signal_connect (proxy, "deselect", G_CALLBACK (unset_tip), NULL);
3484  }
3485  }
3486 }
3487 /* CS: end copied code from gtk+/test/testmerge.c */
3488 
3489 static void
3490 gnc_main_window_window_menu (GncMainWindow *window)
3491 {
3492  guint merge_id;
3493 #ifdef MAC_INTEGRATION
3494  gchar *filename = gnc_filepath_locate_ui_file("gnc-windows-menu-ui-quartz.xml");
3495 #else
3496  gchar *filename = gnc_filepath_locate_ui_file("gnc-windows-menu-ui.xml");
3497  GncMainWindowPrivate *priv;
3498 #endif
3499  GError *error = NULL;
3500  g_assert(filename);
3501  merge_id = gtk_ui_manager_add_ui_from_file(window->ui_merge, filename,
3502  &error);
3503  g_free(filename);
3504  g_assert(merge_id);
3505 #ifndef MAC_INTEGRATION
3506  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3507  gtk_action_group_add_radio_actions (priv->action_group,
3508  radio_entries, n_radio_entries,
3509  0,
3510  G_CALLBACK(gnc_main_window_cmd_window_raise),
3511  window);
3512 #endif
3513 };
3514 
3515 static void
3516 gnc_main_window_setup_window (GncMainWindow *window)
3517 {
3518  GncMainWindowPrivate *priv;
3519  GtkWidget *main_vbox;
3520  guint merge_id;
3521  GncPluginManager *manager;
3522  GList *plugins;
3523  GError *error = NULL;
3524  gchar *filename;
3525 
3526  ENTER(" ");
3527 
3528  /* Catch window manager delete signal */
3529  g_signal_connect (G_OBJECT (window), "delete-event",
3530  G_CALLBACK (gnc_main_window_delete_event), window);
3531 
3532  /* Create widgets and add them to the window */
3533  main_vbox = gtk_vbox_new (FALSE, 0);
3534  gtk_widget_show (main_vbox);
3535  gtk_container_add (GTK_CONTAINER (window), main_vbox);
3536 
3537  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3538  priv->menu_dock = gtk_vbox_new (FALSE, 0);
3539  gtk_widget_show (priv->menu_dock);
3540  gtk_box_pack_start (GTK_BOX (main_vbox), priv->menu_dock,
3541  FALSE, TRUE, 0);
3542 
3543  priv->notebook = gtk_notebook_new ();
3544  g_object_set(G_OBJECT(priv->notebook),
3545  "scrollable", TRUE,
3546  "enable-popup", TRUE,
3547  (char *)NULL);
3548  gtk_widget_show (priv->notebook);
3549  g_signal_connect (G_OBJECT (priv->notebook), "switch-page",
3550  G_CALLBACK (gnc_main_window_switch_page), window);
3551  g_signal_connect (G_OBJECT (priv->notebook), "page-reordered",
3552  G_CALLBACK (gnc_main_window_page_reordered), window);
3553  gtk_box_pack_start (GTK_BOX (main_vbox), priv->notebook,
3554  TRUE, TRUE, 0);
3555 
3556  priv->statusbar = gtk_statusbar_new ();
3557  gtk_widget_show (priv->statusbar);
3558  gtk_box_pack_start (GTK_BOX (main_vbox), priv->statusbar,
3559  FALSE, TRUE, 0);
3560  gtk_statusbar_set_has_resize_grip( GTK_STATUSBAR(priv->statusbar), TRUE );
3561 
3562  priv->progressbar = gtk_progress_bar_new ();
3563  gtk_progress_bar_set_text(GTK_PROGRESS_BAR(priv->progressbar), " ");
3564  gtk_widget_show (priv->progressbar);
3565  gtk_box_pack_start (GTK_BOX (priv->statusbar), priv->progressbar,
3566  FALSE, TRUE, 0);
3567  gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(priv->progressbar),
3568  0.01);
3569 
3570  window->ui_merge = gtk_ui_manager_new ();
3571 
3572  /* Create menu and toolbar information */
3573  priv->action_group = gtk_action_group_new ("MainWindowActions");
3575  gtk_action_group_add_actions (priv->action_group, gnc_menu_actions,
3576  gnc_menu_n_actions, window);
3577  gtk_action_group_add_toggle_actions (priv->action_group,
3578  toggle_actions, n_toggle_actions,
3579  window);
3581  initially_insensitive_actions,
3582  "sensitive", FALSE);
3584  always_insensitive_actions,
3585  "sensitive", FALSE);
3587  always_hidden_actions,
3588  "visible", FALSE);
3590  gnc_menu_important_actions);
3591  gtk_ui_manager_insert_action_group (window->ui_merge, priv->action_group, 0);
3592 
3593  g_signal_connect (G_OBJECT (window->ui_merge), "add_widget",
3594  G_CALLBACK (gnc_main_window_add_widget), window);
3595  /* Use the "connect-proxy" signal for tooltip display in the
3596  status bar */
3597  g_signal_connect (G_OBJECT (window->ui_merge), "connect-proxy",
3598  G_CALLBACK (connect_proxy), priv->statusbar);
3599 
3600  filename = gnc_filepath_locate_ui_file("gnc-main-window-ui.xml");
3601 
3602  /* Can't do much without a ui. */
3603  g_assert (filename);
3604 
3605  merge_id = gtk_ui_manager_add_ui_from_file (window->ui_merge,
3606  filename, &error);
3607  g_assert(merge_id || error);
3608  if (merge_id)
3609  {
3610  gtk_window_add_accel_group (GTK_WINDOW (window),
3611  gtk_ui_manager_get_accel_group(window->ui_merge));
3612  gtk_ui_manager_ensure_update (window->ui_merge);
3613  }
3614  else
3615  {
3616  g_critical("Failed to load ui file.\n Filename %s\n Error %s",
3617  filename, error->message);
3618  g_error_free(error);
3619  g_assert(merge_id != 0);
3620  }
3621  g_free(filename);
3622  gnc_main_window_window_menu(window);
3623  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
3624  GNC_PREF_TAB_POSITION_TOP,
3625  gnc_main_window_update_tab_position,
3626  window);
3627  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
3628  GNC_PREF_TAB_POSITION_BOTTOM,
3629  gnc_main_window_update_tab_position,
3630  window);
3631  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
3632  GNC_PREF_TAB_POSITION_LEFT,
3633  gnc_main_window_update_tab_position,
3634  window);
3635  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
3636  GNC_PREF_TAB_POSITION_RIGHT,
3637  gnc_main_window_update_tab_position,
3638  window);
3639  gnc_main_window_update_tab_position(NULL, NULL, window);
3640 
3641  gnc_main_window_init_menu_updaters(window);
3642 
3643  /* Testing */
3644  /* Now update the "eXtensions" menu */
3645  if (!gnc_prefs_is_extra_enabled())
3646  {
3647  GtkAction* action;
3648 
3649  action = gtk_action_group_get_action(priv->action_group,
3650  "ExtensionsAction");
3651  gtk_action_set_visible(action, FALSE);
3652  }
3653 
3654  /* GncPluginManager stuff */
3655  manager = gnc_plugin_manager_get ();
3656  plugins = gnc_plugin_manager_get_plugins (manager);
3657  g_list_foreach (plugins, gnc_main_window_add_plugin, window);
3658  g_list_free (plugins);
3659 
3660  g_signal_connect (G_OBJECT (manager), "plugin-added",
3661  G_CALLBACK (gnc_main_window_plugin_added), window);
3662  g_signal_connect (G_OBJECT (manager), "plugin-removed",
3663  G_CALLBACK (gnc_main_window_plugin_removed), window);
3664 
3665  LEAVE(" ");
3666 }
3667 
3668 #ifdef MAC_INTEGRATION
3669 /* Event handlers for the shutdown process. Gnc_quartz_shutdown is
3670  * connected to NSApplicationWillTerminate, the last chance to do
3671  * anything before quitting. The problem is that it's launched from a
3672  * CFRunLoop, not a g_main_loop, and if we call anything that would
3673  * affect the main_loop we get an assert that we're in a subidiary
3674  * loop.
3675  */
3676 static void
3677 gnc_quartz_shutdown (GtkosxApplication *theApp, gpointer data)
3678 {
3679  /* Do Nothing. It's too late. */
3680 }
3681 /* Should quit responds to NSApplicationBlockTermination; returning
3682  * TRUE means "don't terminate", FALSE means "do terminate". If we
3683  * decide that it's OK to terminate, then we queue a gnc_shutdown for
3684  * the next idle time (because we're not running in the main loop) and
3685  * then tell the OS not to terminate. That gives the gnc_shutdown an
3686  * opportunity to shut down.
3687  */
3688 static gboolean
3689 gnc_quartz_should_quit (GtkosxApplication *theApp, GncMainWindow *window)
3690 {
3691  QofSession *session;
3692  gboolean needs_save;
3693 
3695  gnc_file_save_in_progress())
3696  {
3697  return TRUE;
3698  }
3699  session = gnc_get_current_session();
3700  needs_save = qof_book_session_not_saved(qof_session_get_book(session)) &&
3701  !gnc_file_save_in_progress();
3702  if (needs_save && gnc_main_window_prompt_for_save(GTK_WIDGET(window)))
3703  return TRUE;
3704 
3705  g_timeout_add(250, gnc_main_window_timed_quit, NULL);
3706  return TRUE;
3707 }
3708 
3709 static void
3710 gnc_quartz_set_menu(GncMainWindow* window)
3711 {
3712  GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
3713  GtkWidget *menu;
3714  GtkWidget *item;
3715 
3716  menu = gtk_ui_manager_get_widget (window->ui_merge, "/menubar");
3717  if (GTK_IS_MENU_ITEM (menu))
3718  menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
3719  gtk_widget_hide(menu);
3720  gtkosx_application_set_menu_bar (theApp, GTK_MENU_SHELL (menu));
3721 
3722  item = gtk_ui_manager_get_widget (window->ui_merge,
3723  "/menubar/File/FileQuit");
3724  if (GTK_IS_MENU_ITEM (item))
3725  gtk_widget_hide (GTK_WIDGET (item));
3726 
3727  item = gtk_ui_manager_get_widget (window->ui_merge,
3728  "/menubar/Help/HelpAbout");
3729  if (GTK_IS_MENU_ITEM (item))
3730  {
3731  gtkosx_application_insert_app_menu_item (theApp, GTK_WIDGET (item), 0);
3732  }
3733 
3734  item = gtk_ui_manager_get_widget (window->ui_merge,
3735  "/menubar/Edit/EditPreferences");
3736  if (GTK_IS_MENU_ITEM (item))
3737  {
3738  gtkosx_application_insert_app_menu_item (theApp,
3739  gtk_separator_menu_item_new (), 1);
3740  gtkosx_application_insert_app_menu_item (theApp, GTK_WIDGET (item), 2);
3741  }
3742 
3743  item = gtk_ui_manager_get_widget (window->ui_merge,
3744  "/menubar/Help");
3745  gtkosx_application_set_help_menu(theApp, GTK_MENU_ITEM(item));
3746  item = gtk_ui_manager_get_widget (window->ui_merge,
3747  "/menubar/Windows");
3748  gtkosx_application_set_window_menu(theApp, GTK_MENU_ITEM(item));
3749  g_signal_connect(theApp, "NSApplicationBlockTermination",
3750  G_CALLBACK(gnc_quartz_should_quit), window);
3751  gtkosx_application_set_use_quartz_accelerators (theApp, FALSE);
3752  g_object_unref (theApp);
3753 
3754 }
3755 #endif //MAC_INTEGRATION
3756 
3757 /* Callbacks */
3758 static void
3759 gnc_main_window_add_widget (GtkUIManager *merge,
3760  GtkWidget *widget,
3761  GncMainWindow *window)
3762 {
3763  GncMainWindowPrivate *priv;
3764 
3765  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3766  if (GTK_IS_TOOLBAR (widget))
3767  {
3768  priv->toolbar = widget;
3769  }
3770 
3771  gtk_box_pack_start (GTK_BOX (priv->menu_dock), widget, FALSE, FALSE, 0);
3772  gtk_widget_show (widget);
3773 }
3774 
3787 static gboolean
3788 gnc_main_window_show_summarybar (GncMainWindow *window, GtkAction *action)
3789 {
3790  GncMainWindowPrivate *priv;
3791 
3792  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3793  if (action == NULL)
3794  action = gtk_action_group_get_action(priv->action_group,
3795  "ViewSummaryAction");
3796  if (action == NULL)
3797  return TRUE;
3798  return gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
3799 }
3800 
3810 static void
3811 gnc_main_window_switch_page (GtkNotebook *notebook,
3812  gpointer *notebook_page,
3813  gint pos,
3814  GncMainWindow *window)
3815 {
3816  GncMainWindowPrivate *priv;
3817  GtkWidget *child;
3818  GncPluginPage *page;
3819  gboolean immutable, visible;
3820 
3821  ENTER("Notebook %p, page, %p, index %d, window %p",
3822  notebook, notebook_page, pos, window);
3823  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3824 
3825  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3826  if (priv->current_page != NULL)
3827  {
3828  page = priv->current_page;
3829  gnc_plugin_page_unmerge_actions (page, window->ui_merge);
3830  gnc_plugin_page_unselected (page);
3831  }
3832 
3833  child = gtk_notebook_get_nth_page (notebook, pos);
3834  if (child)
3835  {
3836  page = g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL);
3837  }
3838  else
3839  {
3840  page = NULL;
3841  }
3842 
3843  priv->current_page = page;
3844 
3845  if (page != NULL)
3846  {
3847  /* Update the user interface (e.g. menus and toolbars */
3848  gnc_plugin_page_merge_actions (page, window->ui_merge);
3849  visible = gnc_main_window_show_summarybar(window, NULL);
3850  gnc_plugin_page_show_summarybar (page, visible);
3851 
3852  /* Allow page specific actions */
3853  gnc_plugin_page_selected (page);
3854  gnc_window_update_status (GNC_WINDOW(window), page);
3855 
3856  /* Update the page reference info */
3857  priv->usage_order = g_list_remove (priv->usage_order, page);
3858  priv->usage_order = g_list_prepend (priv->usage_order, page);
3859  }
3860 
3862  multiple_page_actions,
3863  "sensitive",
3864  g_list_length(priv->installed_pages) > 1);
3865 
3866  gnc_main_window_update_title(window);
3867 #ifndef MAC_INTEGRATION
3868  gnc_main_window_update_menu_item(window);
3869 #endif
3870  g_signal_emit (window, main_window_signals[PAGE_CHANGED], 0, page);
3871  LEAVE(" ");
3872 }
3873 
3880 static void
3881 gnc_main_window_page_reordered (GtkNotebook *notebook,
3882  GtkWidget *child,
3883  guint pos,
3884  GncMainWindow *window)
3885 {
3886  GncMainWindowPrivate *priv;
3887  GncPluginPage *page;
3888  GList *old_link;
3889 
3890  ENTER("Notebook %p, child %p, index %d, window %p",
3891  notebook, child, pos, window);
3892  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3893 
3894  if (!child) return;
3895 
3896  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
3897 
3898  page = g_object_get_data (G_OBJECT (child), PLUGIN_PAGE_LABEL);
3899  if (!page) return;
3900 
3901  old_link = g_list_find (priv->installed_pages, page);
3902  if (!old_link) return;
3903 
3904  priv->installed_pages = g_list_delete_link (priv->installed_pages,
3905  old_link);
3906  priv->installed_pages = g_list_insert (priv->installed_pages,
3907  page, pos);
3908 
3909  LEAVE(" ");
3910 }
3911 
3912 static void
3913 gnc_main_window_plugin_added (GncPlugin *manager,
3914  GncPlugin *plugin,
3915  GncMainWindow *window)
3916 {
3917  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3918  g_return_if_fail (GNC_IS_PLUGIN (plugin));
3919 
3920  gnc_plugin_add_to_window (plugin, window, window_type);
3921 }
3922 
3923 static void
3924 gnc_main_window_plugin_removed (GncPlugin *manager,
3925  GncPlugin *plugin,
3926  GncMainWindow *window)
3927 {
3928  g_return_if_fail (GNC_IS_MAIN_WINDOW (window));
3929  g_return_if_fail (GNC_IS_PLUGIN (plugin));
3930 
3931  gnc_plugin_remove_from_window (plugin, window, window_type);
3932 }
3933 
3934 
3935 /* Command callbacks */
3936 static void
3937 gnc_main_window_cmd_page_setup (GtkAction *action,
3938  GncMainWindow *window)
3939 {
3940  GtkWindow *gtk_window;
3941 
3942  g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
3943 
3944  gtk_window = gnc_window_get_gtk_window(GNC_WINDOW(window));
3945  gnc_ui_page_setup(gtk_window);
3946 }
3947 
3948 static void
3949 gnc_book_options_dialog_apply_cb(GNCOptionWin * optionwin,
3950  gpointer user_data)
3951 {
3952  GNCOptionDB * options = user_data;
3953  gboolean use_split_action_for_num_before =
3954  qof_book_use_split_action_for_num_field (gnc_get_current_book ());
3955  gboolean use_split_action_for_num_after;
3956  QofBook *book = gnc_get_current_book ();
3957 
3958  if (!options) return;
3959 
3960  gnc_option_db_commit (options);
3961  qof_book_begin_edit (book);
3962  qof_book_save_options (book, gnc_option_db_save_to_kvp, options, TRUE);
3963  use_split_action_for_num_after =
3964  qof_book_use_split_action_for_num_field (gnc_get_current_book ());
3965  if (use_split_action_for_num_before != use_split_action_for_num_after)
3966  gnc_book_option_num_field_source_change_cb (use_split_action_for_num_after);
3967  qof_book_commit_edit (book);
3968 }
3969 
3970 static void
3971 gnc_book_options_dialog_close_cb(GNCOptionWin * optionwin,
3972  gpointer user_data)
3973 {
3974  GNCOptionDB * options = user_data;
3975 
3976  gnc_options_dialog_destroy(optionwin);
3977  gnc_option_db_destroy(options);
3978 }
3979 
3980 GtkWidget *
3981 gnc_book_options_dialog_cb (gboolean modal, gchar *title)
3982 {
3983  QofBook *book = gnc_get_current_book ();
3984  GNCOptionDB *options;
3985  GNCOptionWin *optionwin;
3986 
3987  options = gnc_option_db_new_for_type (QOF_ID_BOOK);
3988  qof_book_load_options (book, gnc_option_db_load_from_kvp, options);
3989  gnc_option_db_clean (options);
3990 
3991  optionwin = gnc_options_dialog_new_modal (modal,
3992  (title ? title : _( "Book Options")));
3993  gnc_options_dialog_build_contents (optionwin, options);
3994 
3996 
3997  gnc_options_dialog_set_apply_cb (optionwin,
3998  gnc_book_options_dialog_apply_cb,
3999  (gpointer)options);
4000  gnc_options_dialog_set_close_cb (optionwin,
4001  gnc_book_options_dialog_close_cb,
4002  (gpointer)options);
4003  if (modal)
4005  return gnc_options_dialog_widget (optionwin);
4006 }
4007 
4008 static void
4009 gnc_main_window_cmd_file_properties (GtkAction *action, GncMainWindow *window)
4010 {
4011  gnc_book_options_dialog_cb (FALSE, NULL);
4012 }
4013 
4014 static void
4015 gnc_main_window_cmd_file_close (GtkAction *action, GncMainWindow *window)
4016 {
4017  GncMainWindowPrivate *priv;
4018  GncPluginPage *page;
4019 
4020  g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
4021 
4022  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4023  page = priv->current_page;
4025 }
4026 
4027 static void
4028 gnc_main_window_cmd_file_quit (GtkAction *action, GncMainWindow *window)
4029 {
4031  return;
4032 
4033  gnc_main_window_quit(window);
4034 }
4035 
4036 static void
4037 gnc_main_window_cmd_edit_cut (GtkAction *action, GncMainWindow *window)
4038 {
4039  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
4040  GtkTextBuffer *text_buffer;
4041  GtkClipboard *clipboard;
4042  gboolean editable;
4043 
4044  if (GTK_IS_EDITABLE (widget))
4045  {
4046  gtk_editable_cut_clipboard (GTK_EDITABLE (widget));
4047  }
4048  else if (GTK_IS_TEXT_VIEW (widget))
4049  {
4050  text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
4051  clipboard = gtk_widget_get_clipboard (GTK_WIDGET(text_buffer),
4052  GDK_SELECTION_CLIPBOARD);
4053  editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (widget));
4054  gtk_text_buffer_cut_clipboard (text_buffer, clipboard, editable);
4055  }
4056 }
4057 
4058 static void
4059 gnc_main_window_cmd_edit_copy (GtkAction *action, GncMainWindow *window)
4060 {
4061  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
4062  GtkTextBuffer *text_buffer;
4063  GtkClipboard *clipboard;
4064 
4065  if (GTK_IS_EDITABLE (widget))
4066  {
4067  gtk_editable_copy_clipboard (GTK_EDITABLE (widget));
4068  }
4069  else if (GTK_IS_TEXT_VIEW (widget))
4070  {
4071  text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
4072  clipboard = gtk_widget_get_clipboard (GTK_WIDGET(text_buffer),
4073  GDK_SELECTION_CLIPBOARD);
4074  gtk_text_buffer_copy_clipboard (text_buffer, clipboard);
4075  }
4076 }
4077 
4078 static void
4079 gnc_main_window_cmd_edit_paste (GtkAction *action, GncMainWindow *window)
4080 {
4081  GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (window));
4082  GtkTextBuffer *text_buffer;
4083  GtkClipboard *clipboard;
4084 
4085  if (GTK_IS_EDITABLE (widget))
4086  {
4087  gtk_editable_paste_clipboard (GTK_EDITABLE (widget));
4088  }
4089  else if (GTK_IS_TEXT_VIEW (widget))
4090  {
4091  text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(widget));
4092  clipboard = gtk_widget_get_clipboard (GTK_WIDGET(text_buffer),
4093  GDK_SELECTION_CLIPBOARD);
4094  gtk_text_buffer_paste_clipboard (text_buffer, clipboard, NULL, FALSE);
4095  }
4096 }
4097 
4098 static void
4099 gnc_main_window_cmd_edit_preferences (GtkAction *action, GncMainWindow *window)
4100 {
4102 }
4103 
4104 static void
4105 gnc_main_window_cmd_view_refresh (GtkAction *action, GncMainWindow *window)
4106 {
4107 }
4108 
4109 static void
4110 gnc_main_window_cmd_actions_reset_warnings (GtkAction *action, GncMainWindow *window)
4111 {
4112  gnc_reset_warnings_dialog(GTK_WINDOW(window));
4113 }
4114 
4115 static void
4116 gnc_main_window_cmd_actions_rename_page (GtkAction *action, GncMainWindow *window)
4117 {
4118  GncMainWindowPrivate *priv;
4119  GncPluginPage *page;
4120  GtkWidget *label, *entry;
4121 
4122  ENTER(" ");
4123  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4124  page = priv->current_page;
4125  if (!page)
4126  {
4127  LEAVE("No current page");
4128  return;
4129  }
4130 
4131  if (!main_window_find_tab_items(window, page, &label, &entry))
4132  {
4133  LEAVE("can't find required widgets");
4134  return;
4135  }
4136 
4137  gtk_entry_set_text(GTK_ENTRY(entry), gtk_label_get_text(GTK_LABEL(label)));
4138  gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
4139  gtk_widget_hide(label);
4140  gtk_widget_show(entry);
4141  gtk_widget_grab_focus(entry);
4142  LEAVE("opened for editing");
4143 }
4144 
4145 static void
4146 gnc_main_window_cmd_view_toolbar (GtkAction *action, GncMainWindow *window)
4147 {
4148  GncMainWindowPrivate *priv;
4149 
4150  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4151  if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)))
4152  {
4153  gtk_widget_show (priv->toolbar);
4154  }
4155  else
4156  {
4157  gtk_widget_hide (priv->toolbar);
4158  }
4159 }
4160 
4161 static void
4162 gnc_main_window_cmd_view_summary (GtkAction *action, GncMainWindow *window)
4163 {
4164  GncMainWindowPrivate *priv;
4165  GList *item;
4166  gboolean visible;
4167 
4168  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4169  visible = gnc_main_window_show_summarybar(window, action);
4170  for (item = priv->installed_pages; item; item = g_list_next(item))
4171  {
4172  gnc_plugin_page_show_summarybar(item->data, visible);
4173  }
4174 }
4175 
4176 static void
4177 gnc_main_window_cmd_view_statusbar (GtkAction *action, GncMainWindow *window)
4178 {
4179  GncMainWindowPrivate *priv;
4180 
4181  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4182  if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)))
4183  {
4184  gtk_widget_show (priv->statusbar);
4185  }
4186  else
4187  {
4188  gtk_widget_hide (priv->statusbar);
4189  }
4190 }
4191 
4192 static void
4193 gnc_main_window_cmd_window_new (GtkAction *action, GncMainWindow *window)
4194 {
4195  GncMainWindow *new_window;
4196 
4197  /* Create the new window */
4198  ENTER(" ");
4199  new_window = gnc_main_window_new ();
4200  gtk_widget_show(GTK_WIDGET(new_window));
4201  LEAVE(" ");
4202 }
4203 
4204 static void
4205 gnc_main_window_cmd_window_move_page (GtkAction *action, GncMainWindow *window)
4206 {
4207  GncMainWindowPrivate *priv;
4208  GncMainWindow *new_window;
4209  GncPluginPage *page;
4210  GtkNotebook *notebook;
4211  GtkWidget *tab_widget, *menu_widget;
4212 
4213  ENTER("action %p,window %p", action, window);
4214 
4215  /* Setup */
4216  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4217  page = priv->current_page;
4218  if (!page)
4219  {
4220  LEAVE("invalid page");
4221  return;
4222  }
4223  if (!page->notebook_page)
4224  {
4225  LEAVE("invalid notebook_page");
4226  return;
4227  }
4228 
4229  notebook = GTK_NOTEBOOK (priv->notebook);
4230  tab_widget = gtk_notebook_get_tab_label (notebook, page->notebook_page);
4231  menu_widget = gtk_notebook_get_menu_label (notebook, page->notebook_page);
4232 
4233  /* Ref the page components, then remove it from its old window */
4234  g_object_ref(page);
4235  g_object_ref(tab_widget);
4236  g_object_ref(menu_widget);
4237  g_object_ref(page->notebook_page);
4238  gnc_main_window_disconnect(window, page);
4239 
4240  /* Create the new window */
4241  new_window = gnc_main_window_new ();
4242  gtk_widget_show(GTK_WIDGET(new_window));
4243 
4244  /* Now add the page to the new window */
4245  gnc_main_window_connect (new_window, page, tab_widget, menu_widget);
4246 
4247  /* Unref the page components now that we're done */
4248  g_object_unref(page->notebook_page);
4249  g_object_unref(menu_widget);
4250  g_object_unref(tab_widget);
4251  g_object_unref(page);
4252 
4253  /* just a little debugging. :-) */
4254  DEBUG("Moved page %p from window %p to new window %p",
4255  page, window, new_window);
4256  DEBUG("Old window current is %p, new window current is %p",
4257  priv->current_page, priv->current_page);
4258 
4259  LEAVE("page moved");
4260 }
4261 
4262 #ifndef MAC_INTEGRATION
4263 static void
4264 gnc_main_window_cmd_window_raise (GtkAction *action,
4265  GtkRadioAction *current,
4266  GncMainWindow *old_window)
4267 {
4268  GncMainWindow *new_window;
4269  gint value;
4270 
4271  g_return_if_fail(GTK_IS_ACTION(action));
4272  g_return_if_fail(GTK_IS_RADIO_ACTION(current));
4273  g_return_if_fail(GNC_IS_MAIN_WINDOW(old_window));
4274 
4275  ENTER("action %p, current %p, window %p", action, current, old_window);
4276  value = gtk_radio_action_get_current_value(current);
4277  new_window = g_list_nth_data(active_windows, value);
4278  gtk_window_present(GTK_WINDOW(new_window));
4279  /* revert the change in the radio group
4280  * impossible while handling "changed" (G_SIGNAL_NO_RECURSE) */
4281  g_idle_add((GSourceFunc)gnc_main_window_update_radio_button, old_window);
4282  LEAVE(" ");
4283 }
4284 #endif /* !MAC_INTEGRATION */
4285 
4286 static void
4287 gnc_main_window_cmd_help_tutorial (GtkAction *action, GncMainWindow *window)
4288 {
4289  gnc_gnome_help (HF_GUIDE, NULL);
4290 }
4291 
4292 static void
4293 gnc_main_window_cmd_help_contents (GtkAction *action, GncMainWindow *window)
4294 {
4295  gnc_gnome_help (HF_HELP, NULL);
4296 }
4297 
4307 static gchar *
4308 get_file (const gchar *partial)
4309 {
4310  gchar *filename, *text = NULL;
4311  gsize length;
4312 
4313  filename = gnc_filepath_locate_doc_file(partial);
4314  if (filename && g_file_get_contents(filename, &text, &length, NULL))
4315  {
4316  if (length)
4317  {
4318  g_free(filename);
4319  return text;
4320  }
4321  g_free(text);
4322  }
4323  g_free (filename);
4324  return NULL;
4325 }
4326 
4327 
4337 static gchar **
4338 get_file_strsplit (const gchar *partial)
4339 {
4340  gchar *text, **lines;
4341 
4342  text = get_file(partial);
4343  if (!text)
4344  return NULL;
4345 
4346  lines = g_strsplit_set(text, "\r\n", -1);
4347  g_free(text);
4348  return lines;
4349 }
4356 static gboolean
4357 url_signal_cb (GtkAboutDialog *dialog, gchar *uri, gpointer data)
4358 {
4359  gnc_launch_assoc (uri);
4360  return TRUE;
4361 }
4362 
4369 static void
4370 gnc_main_window_cmd_help_about (GtkAction *action, GncMainWindow *window)
4371 {
4372  GncMainWindowPrivate *priv;
4373 
4374  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4375 
4376  if (priv->about_dialog == NULL)
4377  {
4378  const gchar *fixed_message = _("The GnuCash personal finance manager. "
4379  "The GNU way to manage your money!");
4380  const gchar *copyright = _("© 1997-2014 Contributors");
4381  gchar **authors = get_file_strsplit("AUTHORS");
4382  gchar **documenters = get_file_strsplit("DOCUMENTERS");
4383  gchar *license = get_file("LICENSE");
4384  gchar *message;
4385  GdkPixbuf *logo = gnc_gnome_get_gdkpixbuf ("gnucash-icon-48x48.png");
4386 
4387 #ifdef GNUCASH_SCM
4388  /* Development version */
4389  /* Translators: 1st %s is a fixed message, which is translated independently;
4390  2nd %s is the scm type (svn/svk/git/bzr);
4391  3rd %s is the scm revision number;
4392  4th %s is the build date */
4393  message = g_strdup_printf(_("%s\nThis copy was built from %s rev %s on %s."),
4394  fixed_message, GNUCASH_SCM, GNUCASH_SCM_REV,
4395  GNUCASH_BUILD_DATE);
4396 #else
4397  /* Translators: 1st %s is a fixed message, which is translated independently;
4398  2nd %s is the scm (svn/svk/git/bzr) revision number;
4399  3rd %s is the build date */
4400  message = g_strdup_printf(_("%s\nThis copy was built from rev %s on %s."),
4401  fixed_message, GNUCASH_SCM_REV,
4402  GNUCASH_BUILD_DATE);
4403 #endif
4404  priv->about_dialog = gtk_about_dialog_new ();
4405  g_object_set (priv->about_dialog,
4406  "authors", authors,
4407  "documenters", documenters,
4408  "comments", message,
4409  "copyright", copyright,
4410  "license", license,
4411  "logo", logo,
4412  "name", "GnuCash",
4413  /* Translators: the following string will be shown in Help->About->Credits
4414  * Enter your name or that of your team and an email contact for feedback.
4415  * The string can have multiple rows, so you can also add a list of
4416  * contributors. */
4417  "translator-credits", _("translator_credits"),
4418  "version", VERSION,
4419  "website", "http://www.gnucash.org",
4420  NULL);
4421 
4422  g_free(message);
4423  if (license) g_free(license);
4424  if (documenters) g_strfreev(documenters);
4425  if (authors) g_strfreev(authors);
4426  g_object_unref (logo);
4427  g_signal_connect (priv->about_dialog, "activate-link",
4428  G_CALLBACK (url_signal_cb), NULL);
4429  g_signal_connect (priv->about_dialog, "response",
4430  G_CALLBACK (gtk_widget_hide), NULL);
4431  gtk_window_set_transient_for (GTK_WINDOW (priv->about_dialog),
4432  GTK_WINDOW (window));
4433  }
4434  gtk_dialog_run (GTK_DIALOG (priv->about_dialog));
4435 }
4436 
4437 
4438 /************************************************************
4439  * *
4440  ************************************************************/
4441 
4442 void
4444 {
4445  GList *window_iter;
4446 #ifdef MAC_INTEGRATION
4447  GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
4448 #endif
4449  for (window_iter = active_windows; window_iter != NULL; window_iter = window_iter->next)
4450  {
4451  gtk_widget_show(GTK_WIDGET(window_iter->data));
4452  }
4453 #ifdef MAC_INTEGRATION
4454  g_signal_connect(theApp, "NSApplicationWillTerminate",
4455  G_CALLBACK(gnc_quartz_shutdown), NULL);
4456  gtkosx_application_ready(theApp);
4457  g_object_unref (theApp);
4458 #endif
4459 }
4460 
4465 GtkWidget *
4467 {
4468  GList *window;
4469 
4470  for (window = active_windows; window; window = window->next)
4471  if (gtk_window_is_active (GTK_WINDOW (window->data)))
4472  return window->data;
4473 
4474  return NULL;
4475 }
4476 
4477 
4483 static GtkWindow *
4484 gnc_main_window_get_gtk_window (GncWindow *window)
4485 {
4486  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window), NULL);
4487  return GTK_WINDOW(window);
4488 }
4489 
4490 
4496 static GtkWidget *
4497 gnc_main_window_get_statusbar (GncWindow *window_in)
4498 {
4499  GncMainWindowPrivate *priv;
4500  GncMainWindow *window;
4501 
4502  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), NULL);
4503 
4504  window = GNC_MAIN_WINDOW(window_in);
4505  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4506  return priv->statusbar;
4507 }
4508 
4509 
4515 static GtkWidget *
4516 gnc_main_window_get_progressbar (GncWindow *window_in)
4517 {
4518  GncMainWindowPrivate *priv;
4519  GncMainWindow *window;
4520 
4521  g_return_val_if_fail (GNC_IS_MAIN_WINDOW (window_in), NULL);
4522 
4523  window = GNC_MAIN_WINDOW(window_in);
4524  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4525  return priv->progressbar;
4526 }
4527 
4528 
4529 static void
4530 gnc_main_window_all_ui_set_sensitive (GncWindow *unused, gboolean sensitive)
4531 {
4532  GncMainWindow *window;
4533  GncMainWindowPrivate *priv;
4534  GList *groupp, *groups, *winp, *tmp;
4535  GtkWidget *close_button;
4536 
4537  for (winp = active_windows; winp; winp = g_list_next(winp))
4538  {
4539  window = winp->data;
4540  priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
4541 
4542  groups = gtk_ui_manager_get_action_groups(window->ui_merge);
4543  for (groupp = groups; groupp; groupp = g_list_next(groupp))
4544  {
4545  gtk_action_group_set_sensitive(GTK_ACTION_GROUP(groupp->data), sensitive);
4546  }
4547 
4548  for (tmp = priv->installed_pages; tmp; tmp = g_list_next(tmp))
4549  {
4550  close_button = g_object_get_data(tmp->data, PLUGIN_PAGE_CLOSE_BUTTON);
4551  if (!close_button)
4552  continue;
4553  gtk_widget_set_sensitive (close_button, sensitive);
4554  }
4555  }
4556 }
4557 
4558 
4563 static void
4564 gnc_window_main_window_init (GncWindowIface *iface)
4565 {
4566  iface->get_gtk_window = gnc_main_window_get_gtk_window;
4567  iface->get_statusbar = gnc_main_window_get_statusbar;
4568  iface->get_progressbar = gnc_main_window_get_progressbar;
4569  iface->ui_set_sensitive = gnc_main_window_all_ui_set_sensitive;
4570 }
4571 
4572 
4573 /* Set the window where all progressbar updates should occur. This
4574  * is a wrapper around the gnc_window_set_progressbar_window()
4575  * function.
4576  */
4577 void
4579 {
4580  GncWindow *gncwin;
4581  gncwin = GNC_WINDOW(window);
4582  gnc_window_set_progressbar_window(gncwin);
4583 }
4584 
4585 
4598 static void
4599 do_popup_menu(GncPluginPage *page, GdkEventButton *event)
4600 {
4601  GtkUIManager *ui_merge;
4602  GtkWidget *menu;
4603  int button, event_time;
4604 
4605  g_return_if_fail(GNC_IS_PLUGIN_PAGE(page));
4606 
4607  ENTER("page %p, event %p", page, event);
4608  ui_merge = gnc_plugin_page_get_ui_merge(page);
4609  if (ui_merge == NULL)
4610  {
4611  LEAVE("no ui merge");
4612  return;
4613  }
4614 
4615  menu = gtk_ui_manager_get_widget(ui_merge, "/MainPopup");
4616  if (!menu)
4617  {
4618  LEAVE("no menu");
4619  return;
4620  }
4621 
4622  if (event)
4623  {
4624  button = event->button;
4625  event_time = event->time;
4626  }
4627  else
4628  {
4629  button = 0;
4630  event_time = gtk_get_current_event_time ();
4631  }
4632 
4633  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button, event_time);
4634  LEAVE(" ");
4635 }
4636 
4637 
4651 static gboolean
4652 gnc_main_window_popup_menu_cb (GtkWidget *widget,
4653  GncPluginPage *page)
4654 {
4655  ENTER("widget %p, page %p", widget, page);
4656  do_popup_menu(page, NULL);
4657  LEAVE(" ");
4658  return TRUE;
4659 }
4660 
4661 
4662 /* Callback function invoked when the user clicks in the content of
4663  * any Gnucash window. If this was a "right-click" then Gnucash will
4664  * popup the contextual menu.
4665  */
4666 gboolean
4667 gnc_main_window_button_press_cb (GtkWidget *whatever,
4668  GdkEventButton *event,
4669  GncPluginPage *page)
4670 {
4671  g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
4672 
4673  ENTER("widget %p, event %p, page %p", whatever, event, page);
4674  /* Ignore double-clicks and triple-clicks */
4675  if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
4676  {
4677  do_popup_menu(page, event);
4678  LEAVE("menu shown");
4679  return TRUE;
4680  }
4681 
4682  LEAVE("other click");
4683  return FALSE;
4684 }
4685 
4686 
4687 /* CS: Code copied from gtk/gtkactiongroup.c */
4688 static gchar *
4689 dgettext_swapped (const gchar *msgid,
4690  const gchar *domainname)
4691 {
4692  /* CS: Pass this through dgettext if and only if msgid is
4693  nonempty. */
4694  return (msgid && *msgid) ? dgettext (domainname, msgid) : (gchar*) msgid;
4695 }
4696 
4697 /*
4698  * This is copied into GnuCash from Gtk in order to fix problems when
4699  * empty msgids were passed through gettext().
4700  *
4701  * See http://bugzilla.gnome.org/show_bug.cgi?id=326200 . If that bug
4702  * is fixed in the gtk that we can rely open, then
4703  * gnc_gtk_action_group_set_translation_domain can be replaced by
4704  * gtk_action_group_set_translation_domain again.
4705  */
4706 void
4707 gnc_gtk_action_group_set_translation_domain (GtkActionGroup *action_group,
4708  const gchar *domain)
4709 {
4710  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
4711 
4712  gtk_action_group_set_translate_func (action_group,
4713  (GtkTranslateFunc)dgettext_swapped,
4714  g_strdup (domain),
4715  g_free);
4716 }
4717 /* CS: End of code copied from gtk/gtkactiongroup.c */
4718 
4719 void
4721  gboolean sensitive)
4722 {
4723  GList *tmp;
4724  GtkAction *action;
4725 
4726  for (tmp = active_windows; tmp; tmp = g_list_next(tmp))
4727  {
4728  action = gnc_main_window_find_action (tmp->data, action_name);
4729  gtk_action_set_sensitive (action, sensitive);
4730  }
4731 }
4732 
4734 {
4735  g_assert(window);
4736  return window->ui_merge;
4737 }
4738 
struct GncMainWindowPrivate GncMainWindowPrivate
gboolean gnc_plugin_page_finish_pending(GncPluginPage *page)
GtkActionGroup * action_group
Functions to load, save and get gui state.
gboolean gnc_plugin_page_has_books(GncPluginPage *page)
gboolean gnc_plugin_page_get_use_new_window(GncPluginPage *page)
void gnc_main_window_restore_all_windows(const GKeyFile *keyfile)
GtkWidget * gnc_book_options_dialog_cb(gboolean modal, gchar *title)
void gnc_plugin_page_destroy_widget(GncPluginPage *plugin_page)
void qof_book_set_dirty_cb(QofBook *book, QofBookDirtyCB cb, gpointer user_data)
const GList * gnc_gobject_tracking_get_list(const gchar *name)
gboolean gnc_main_window_button_press_cb(GtkWidget *whatever, GdkEventButton *event, GncPluginPage *page)
gulong gnc_prefs_register_cb(const char *group, const gchar *pref_name, gpointer func, gpointer user_data)
Definition: gnc-prefs.c:128
gboolean gnc_uri_is_file_uri(const gchar *uri)
Definition: gnc-uri-utils.c:71
utility functions for the GnuCash UI
#define PINFO(format, args...)
Definition: qoflog.h:249
void gnc_gobject_tracking_remember(GObject *object, GObjectClass *klass)
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition: qofbackend.h:59
const gchar * gnc_plugin_page_get_page_long_name(GncPluginPage *page)
void gnc_gobject_tracking_forget(GObject *object)
time64 qof_book_get_session_dirty_time(const QofBook *book)
#define DEBUG(format, args...)
Definition: qoflog.h:255
const gchar * gnc_plugin_page_get_page_name(GncPluginPage *page)
Functions that are supported by all types of windows.
gboolean qof_book_use_split_action_for_num_field(const QofBook *book)
void gnc_plugin_page_merge_actions(GncPluginPage *page, GtkUIManager *ui_merge)
GKeyFile helper routines.
Plugin management functions for the GnuCash UI.
GDateTime * gnc_g_date_time_new_from_unix_local(time64 time)
void gnc_plugin_add_to_window(GncPlugin *plugin, GncMainWindow *window, GQuark type)
Definition: gnc-plugin.c:157
void gnc_book_option_num_field_source_change_cb(gboolean num_action)
Definition: gnc-ui-util.c:253
GtkWidget * window
gchar * gnc_uri_get_path(const gchar *uri)
const gchar * gnc_plugin_page_get_page_color(GncPluginPage *page)
void gnc_plugin_page_set_page_long_name(GncPluginPage *page, const char *name)
gchar * gnc_filepath_locate_ui_file(const gchar *name)
void gnc_engine_add_commit_error_callback(EngineCommitErrorCallback cb, gpointer data)
Definition: gnc-engine.c:197
void gnc_shutdown(int exit_status)
void gnc_ui_page_setup(GtkWindow *parent)
Definition: print-session.c:79
void gnc_main_window_unmerge_actions(GncMainWindow *window, const gchar *group_name)
#define ENTER(format, args...)
Definition: qoflog.h:261
void gnc_main_window_show_all_windows(void)
void gnc_main_window_merge_actions(GncMainWindow *window, const gchar *group_name, GtkActionEntry *actions, guint n_actions, GtkToggleActionEntry *toggle_actions, guint n_toggle_actions, const gchar *filename, gpointer user_data)
void gnc_main_window_display_page(GncPluginPage *page)
void gnc_main_window_save_all_windows(GKeyFile *keyfile)
GncPluginPage * gnc_main_window_get_current_page(GncMainWindow *window)
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
gint gnc_prefs_get_int(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:206
Functions for adding content to a window.
gint qof_event_register_handler(QofEventHandler handler, gpointer handler_data)
Register a handler for events.
#define PWARN(format, args...)
Definition: qoflog.h:243
void gnc_plugin_page_unmerge_actions(GncPluginPage *page, GtkUIManager *ui_merge)
GtkWidget * gnc_plugin_page_create_widget(GncPluginPage *plugin_page)
gboolean visible
void gnc_main_window_all_action_set_sensitive(const gchar *action_name, gboolean sensitive)
QofBook * qof_session_get_book(const QofSession *session)
#define QOF_CHECK_TYPE(obj, type)
Definition: qofid.h:124
void main_window_update_page_color(GncPluginPage *page, const gchar *color_in)
gchar * gnc_uri_normalize_uri(const gchar *uri, gboolean allow_password)
gchar * gnc_filepath_locate_doc_file(const gchar *name)
gint QofEventId
Definition: qofevent.h:45
Gobject helper routines.
void qof_book_mark_session_saved(QofBook *book)
void gnc_plugin_page_set_use_new_window(GncPluginPage *page, gboolean use_new)
GType gnc_main_window_get_type(void)
gchar * action_name
GtkUIManager * ui_merge
gboolean gnc_plugin_page_has_book(GncPluginPage *page, QofBook *book)
void gnc_options_dialog_set_book_options_help_cb(GNCOptionWin *win)
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
GncPluginManager * gnc_plugin_manager_get(void)
Gnome specific utility functions.
void gnc_plugin_remove_from_window(GncPlugin *plugin, GncMainWindow *window, GQuark type)
Definition: gnc-plugin.c:209
void gnc_plugin_page_save_page(GncPluginPage *page, GKeyFile *key_file, const gchar *group_name)
#define PLUGIN_PAGE_LABEL
Dialog for handling user preferences.
gboolean qof_book_session_not_saved(const QofBook *book)
void gnc_gnome_help(const char *file_name, const char *anchor)
All type declarations for the whole Gnucash engine.
struct GncMainWindow GncMainWindow
gboolean gnc_main_window_finish_pending(GncMainWindow *window)
Generic api to store and retrieve preferences.
Utility functions for file access.
void gnc_preferences_dialog(void)
GtkActionGroup * gnc_main_window_get_action_group(GncMainWindow *window, const gchar *group_name)
void gnc_launch_assoc(const char *uri)
gboolean qof_book_is_readonly(const QofBook *book)
GncMainWindow * gnc_main_window_new(void)
void gnc_plugin_page_set_page_color(GncPluginPage *page, const char *color)
void gnc_plugin_update_actions(GtkActionGroup *action_group, const gchar **action_names, const gchar *property_name, gboolean value)
Definition: gnc-plugin.c:313
void gnc_main_window_manual_merge_actions(GncMainWindow *window, const gchar *group_name, GtkActionGroup *group, guint merge_id)
void gnc_options_dialog_set_new_book_option_values(GNCOptionDB *odb)
GdkPixbuf * gnc_gnome_get_gdkpixbuf(const char *name)
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:196
GHashTable * merged_actions_table
void gnc_main_window_close_page(GncPluginPage *page)
Functions for adding plugins to a GnuCash window.
GtkActionGroup * action_group
#define LEAVE(format, args...)
Definition: qoflog.h:271
GList * gnc_plugin_manager_get_plugins(GncPluginManager *manager)
void gnc_plugin_page_set_page_name(GncPluginPage *page, const char *name)
time64 gnc_time(time64 *tbuf)
get the current local time
Utility functions for convert uri in separate components and back.
GncPluginPage * current_page
GtkWidget * gnc_ui_get_toplevel(void)
gint64 time64
Definition: gnc-date.h:83
GtkUIManager * gnc_plugin_page_get_ui_merge(GncPluginPage *page)
void gnc_main_window_restore_default_state(GncMainWindow *window)
File path resolution utility functions.
gboolean gnc_main_window_all_finish_pending(void)
void gnc_gtk_action_group_set_translation_domain(GtkActionGroup *action_group, const gchar *domain)
void gnc_plugin_set_important_actions(GtkActionGroup *action_group, const gchar **name)
Definition: gnc-plugin.c:289
void gnc_main_window_actions_updated(GncMainWindow *window)
void main_window_update_page_name(GncPluginPage *page, const gchar *name_in)
GtkWidget * notebook_page
GncPluginPage * gnc_plugin_page_recreate_page(GtkWidget *window, const gchar *page_type, GKeyFile *key_file, const gchar *page_group)
void gnc_plugin_page_show_summarybar(GncPluginPage *page, gboolean visible)
GtkUIManager * gnc_main_window_get_uimanager(GncMainWindow *window)
GtkAction * gnc_main_window_find_action(GncMainWindow *window, const gchar *name)
const gchar * QofLogModule
Definition: qofid.h:89
void gnc_main_window_set_progressbar_window(GncMainWindow *window)
void gnc_prefs_remove_cb_by_func(const gchar *group, const gchar *pref_name, gpointer func, gpointer user_data)
Definition: gnc-prefs.c:148
gdouble gnc_prefs_get_float(const gchar *group, const gchar *pref_name)
Definition: gnc-prefs.c:227
const gchar * gnc_plugin_page_get_plugin_name(GncPluginPage *plugin_page)