GnuCash  5.6-150-g038405b370+
gnc-plugin-page-account-tree.cpp
1 /*
2  * gnc-plugin-page-account-tree.c --
3  *
4  * Copyright (C) 2003 Jan Arne Petersen <jpetersen@uni-bonn.de>
5  * Copyright (C) 2003,2005,2006 David Hampton <hampton@employees.org>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, contact:
19  *
20  * Free Software Foundation Voice: +1-617-542-5942
21  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
22  * Boston, MA 02110-1301, USA gnu@gnu.org
23  */
24 
35 #include <config.h>
36 
37 #include <gtk/gtk.h>
38 #include <glib/gi18n.h>
41 
42 #include "Account.hpp"
43 #include "Scrub.h"
44 #include "Scrub3.h"
45 #include "ScrubBusiness.h"
46 #include "Transaction.h"
47 #include "dialog-account.h"
48 #include "dialog-transfer.h"
49 #include "dialog-utils.h"
50 #include "assistant-hierarchy.h"
51 #include "assistant-stock-transaction.h"
52 #include "gnc-account-sel.h"
53 #include "gnc-component-manager.h"
54 #include "gnc-engine.h"
55 #include "gnc-gnome-utils.h"
56 #include "gnc-gobject-utils.h"
57 #include "gnc-icons.h"
59 #include "gnc-prefs.h"
60 #include "gnc-session.h"
61 #include "gnc-split-reg.h"
62 #include "gnc-state.h"
63 #include "gnc-tree-view-account.h"
65 #include "gnc-ui.h"
66 #include "gnc-ui-util.h"
67 #include "gnc-window.h"
68 #include "dialog-lot-viewer.h"
69 #include "window-reconcile.h"
70 #include "window-autoclear.h"
71 #include "window-main-summarybar.h"
73 #include "dialog-find-account.h"
74 #include <gnc-glib-utils.h>
75 
76 /* This static indicates the debugging module that this .o belongs to. */
77 static QofLogModule log_module = GNC_MOD_GUI;
78 
79 
80 /********************************************************************
81  * delete_account_helper
82  * See if this account has any splits present. Set the user data
83  * and return the same value to stop walking the account tree if
84  * appropriate.
85  ********************************************************************/
86 typedef struct _delete_helper
87 {
88  gboolean has_splits;
89  gboolean has_ro_splits;
91 
92 
93 #define PLUGIN_PAGE_ACCT_TREE_CM_CLASS "plugin-page-acct-tree"
94 #define STATE_SECTION "Account Hierarchy"
95 
96 #define DELETE_DIALOG_FILTER "filter"
97 #define DELETE_DIALOG_ACCOUNT "account"
98 #define DELETE_DIALOG_TRANS_MAS "trans_mas"
99 #define DELETE_DIALOG_SA_MAS "sa_mas"
100 #define DELETE_DIALOG_SA_TRANS_MAS "sa_trans_mas"
101 #define DELETE_DIALOG_SA_TRANS "sa_trans"
102 #define DELETE_DIALOG_SA_SPLITS "sa_has_split"
103 #define DELETE_DIALOG_OK_BUTTON "deletebutton"
104 
105 enum
106 {
107  ACCOUNT_SELECTED,
108  LAST_SIGNAL
109 };
110 
112 {
113  GtkWidget *widget;
114  GtkTreeView *tree_view;
115  gint component_id;
118 
119 #define GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(o) \
120  ((GncPluginPageAccountTreePrivate*)gnc_plugin_page_account_tree_get_instance_private((GncPluginPageAccountTree*)o))
121 
122 /************************************************************
123  * Prototypes *
124  ************************************************************/
125 /* Plugin Actions */
126 static void gnc_plugin_page_account_tree_finalize (GObject *object);
127 static void gnc_plugin_page_account_tree_selected (GObject *object, gpointer user_data);
128 
129 static gboolean gnc_plugin_page_account_tree_focus_widget (GncPluginPage *plugin_page);
130 static GtkWidget *gnc_plugin_page_account_tree_create_widget (GncPluginPage *plugin_page);
131 static void gnc_plugin_page_account_tree_destroy_widget (GncPluginPage *plugin_page);
132 static void gnc_plugin_page_account_tree_save_page (GncPluginPage *plugin_page, GKeyFile *file, const gchar *group);
133 static GncPluginPage *gnc_plugin_page_account_tree_recreate_page (GtkWidget *window, GKeyFile *file, const gchar *group);
134 
135 /* Callbacks */
136 static void gnc_plugin_page_account_tree_summarybar_position_changed(gpointer prefs, gchar* pref, gpointer user_data);
137 static gboolean gnc_plugin_page_account_tree_button_press_cb (GtkWidget *widget, GdkEventButton *event, GncPluginPage *page);
138 static void gnc_plugin_page_account_tree_double_click_cb (GtkTreeView *treeview,
139  GtkTreePath *path,
140  GtkTreeViewColumn *col,
142 
143 static void gnc_plugin_page_account_tree_selection_changed_cb (GtkTreeSelection *selection,
145 static void accounting_period_changed_cb(gpointer prefs, gchar *pref, gpointer user_data);
146 
147 extern "C" {
148 void gppat_populate_trans_mas_list(GtkToggleButton *sa_mrb, GtkWidget *dialog);
149 void gppat_set_insensitive_iff_rb_active(GtkWidget *widget, GtkToggleButton *b);
150 }
151 
152 /* Command callbacks */
153 static void gnc_plugin_page_account_tree_cmd_new_account (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
154 static void gnc_plugin_page_account_tree_cmd_file_new_hierarchy (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
155 static void gnc_plugin_page_account_tree_cmd_open_account (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
156 static void gnc_plugin_page_account_tree_cmd_open_subaccounts (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
157 static void gnc_plugin_page_account_tree_cmd_edit_account (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
158 static void gnc_plugin_page_account_tree_cmd_find_account (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
159 static void gnc_plugin_page_account_tree_cmd_find_account_popup (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
160 static void gnc_plugin_page_account_tree_cmd_delete_account (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
161 static void gnc_plugin_page_account_tree_cmd_renumber_accounts (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
162 static void gnc_plugin_page_account_tree_cmd_view_filter_by (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
163 static void gnc_plugin_page_account_tree_cmd_reconcile (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
164 static void gnc_plugin_page_account_tree_cmd_refresh (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
165 static void gnc_plugin_page_account_tree_cmd_autoclear (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
166 static void gnc_plugin_page_account_tree_cmd_transfer (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
167 static void gnc_plugin_page_account_tree_cmd_stock_split (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
168 static void gnc_plugin_page_account_tree_cmd_stock_assistant (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
169 static void gnc_plugin_page_account_tree_cmd_edit_tax_options (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
170 static void gnc_plugin_page_account_tree_cmd_lots (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
171 static void gnc_plugin_page_account_tree_cmd_scrub (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
172 static void gnc_plugin_page_account_tree_cmd_scrub_sub (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
173 static void gnc_plugin_page_account_tree_cmd_scrub_all (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
174 static void gnc_plugin_page_account_tree_cmd_cascade_account_properties (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
175 
176 /* Account Deletion Actions. */
177 static int confirm_delete_account (GSimpleAction *simple,
179  Account* sta, Account* saa,
180  delete_helper_t delete_res);
181 static void do_delete_account (Account* account, Account* saa, Account* sta,
182  Account* ta);
183 
184 
185 
186 static guint plugin_page_signals[LAST_SIGNAL] = { 0 };
187 
188 
189 static GActionEntry gnc_plugin_page_account_tree_actions [] =
190 {
191  { "FileNewAccountAction", gnc_plugin_page_account_tree_cmd_new_account, NULL, NULL, NULL },
192  { "FileAddAccountHierarchyAssistantAction", gnc_plugin_page_account_tree_cmd_file_new_hierarchy, NULL, NULL, NULL },
193  { "EditOpenAccountAction", gnc_plugin_page_account_tree_cmd_open_account, NULL, NULL, NULL },
194  { "EditOpenSubaccountsAction", gnc_plugin_page_account_tree_cmd_open_subaccounts, NULL, NULL, NULL },
195  { "EditEditAccountAction", gnc_plugin_page_account_tree_cmd_edit_account, NULL, NULL, NULL },
196  { "EditDeleteAccountAction", gnc_plugin_page_account_tree_cmd_delete_account, NULL, NULL, NULL },
197  { "EditCascadeAccountAction", gnc_plugin_page_account_tree_cmd_cascade_account_properties, NULL, NULL, NULL },
198  { "EditFindAccountAction", gnc_plugin_page_account_tree_cmd_find_account, NULL, NULL, NULL },
199  { "EditFindAccountPopupAction", gnc_plugin_page_account_tree_cmd_find_account_popup, NULL, NULL, NULL },
200  { "EditRenumberSubaccountsAction", gnc_plugin_page_account_tree_cmd_renumber_accounts, NULL, NULL, NULL },
201  { "EditTaxOptionsAction", gnc_plugin_page_account_tree_cmd_edit_tax_options, NULL, NULL, NULL },
202  { "ViewFilterByAction", gnc_plugin_page_account_tree_cmd_view_filter_by, NULL, NULL, NULL },
203  { "ViewRefreshAction", gnc_plugin_page_account_tree_cmd_refresh, NULL, NULL, NULL },
204  { "ActionsReconcileAction", gnc_plugin_page_account_tree_cmd_reconcile, NULL, NULL, NULL },
205  { "ActionsAutoClearAction", gnc_plugin_page_account_tree_cmd_autoclear, NULL, NULL, NULL },
206  { "ActionsTransferAction", gnc_plugin_page_account_tree_cmd_transfer, NULL, NULL, NULL },
207  { "ActionsStockSplitAction", gnc_plugin_page_account_tree_cmd_stock_split, NULL, NULL, NULL },
208  { "ActionsStockAssistantAction", gnc_plugin_page_account_tree_cmd_stock_assistant, NULL, NULL, NULL },
209  { "ActionsLotsAction", gnc_plugin_page_account_tree_cmd_lots, NULL, NULL, NULL },
210  { "ScrubAction", gnc_plugin_page_account_tree_cmd_scrub, NULL, NULL, NULL },
211  { "ScrubSubAction", gnc_plugin_page_account_tree_cmd_scrub_sub, NULL, NULL, NULL },
212  { "ScrubAllAction", gnc_plugin_page_account_tree_cmd_scrub_all, NULL, NULL, NULL },
213 };
215 static guint gnc_plugin_page_account_tree_n_actions = G_N_ELEMENTS(gnc_plugin_page_account_tree_actions);
216 
218 static const gchar *gnc_plugin_load_ui_items [] =
219 {
220  "FilePlaceholder3",
221  "EditPlaceholder1",
222  "EditPlaceholder2",
223  "EditPlaceholder3",
224  "EditPlaceholder5",
225  "ViewPlaceholder1",
226  "ViewPlaceholder4",
227  "ActionsPlaceholder4",
228  "ActionsPlaceholder5",
229  "ActionsPlaceholder6",
230  NULL,
231 };
232 
233 
234 
235 
238 static const gchar *actions_requiring_account_rw[] =
239 {
240  "EditEditAccountAction",
241  "EditDeleteAccountAction",
242  "ActionsReconcileAction",
243  "ActionsAutoClearAction",
244  NULL
245 };
246 
249 static const gchar *actions_requiring_subaccounts_rw[] =
250 {
251  "EditRenumberSubaccountsAction",
252  "EditCascadeAccountAction",
253  NULL
254 };
255 
258 static const gchar *actions_requiring_account_always[] =
259 {
260  "EditOpenAccountAction",
261  "EditOpenSubaccountsAction",
262  "ActionsLotsAction",
263  NULL
264 };
265 
266 static const gchar* actions_requiring_priced_account[] =
267 {
268  "ActionsStockAssistantAction",
269  NULL
270 };
271 
272 /* This is the list of actions which are switched inactive in a read-only book. */
273 static const gchar* readonly_inactive_actions[] =
274 {
275  "FileNewAccountAction",
276  "FileAddAccountHierarchyAssistantAction",
277  "EditEditAccountAction",
278  "EditDeleteAccountAction",
279  "ActionsTransferAction",
280  "ActionsReconcileAction",
281  "ActionsAutoClearAction",
282  "ActionsStockSplitAction",
283  "ScrubAction",
284  "ScrubSubAction",
285  "ScrubAllAction",
286  NULL
287 };
288 
290 static GncToolBarShortNames toolbar_labels[] =
291 {
292  { "EditOpenAccountAction", N_("Open") },
293  { "EditEditAccountAction", N_("Edit") },
294  { "FileNewAccountAction", N_("New") },
295  { "EditDeleteAccountAction", N_("Delete") },
296  { NULL, NULL },
297 };
298 
301 {
302  ENTER(" ");
303  auto plugin_page = GNC_PLUGIN_PAGE_ACCOUNT_TREE
304  (g_object_new (GNC_TYPE_PLUGIN_PAGE_ACCOUNT_TREE, nullptr));
305 
306  LEAVE("new account tree page %p", plugin_page);
307  return GNC_PLUGIN_PAGE (plugin_page);
308 }
309 
310 G_DEFINE_TYPE_WITH_PRIVATE(GncPluginPageAccountTree, gnc_plugin_page_account_tree, GNC_TYPE_PLUGIN_PAGE)
311 
312 static gboolean show_abort_verify = TRUE;
313 
314 static void
315 prepare_scrubbing ()
316 {
317  gnc_suspend_gui_refresh ();
318  gnc_set_abort_scrub (FALSE);
319 }
320 
321 static void
322 finish_scrubbing (GncWindow *window, gulong handler_id)
323 {
324  g_signal_handler_disconnect (G_OBJECT(window), handler_id);
325  show_abort_verify = TRUE;
326  gnc_resume_gui_refresh ();
327 }
328 
329 static const char*
330 check_repair_abort_YN = N_("'Check & Repair' is currently running, do you want to abort it?");
331 
332 static gboolean
333 gnc_plugin_page_account_finish_pending (GncPluginPage* page)
334 {
335  if (gnc_get_ongoing_scrub ())
336  {
337  if (show_abort_verify)
338  {
339  gboolean ret = gnc_verify_dialog (GTK_WINDOW(gnc_plugin_page_get_window
340  (GNC_PLUGIN_PAGE(page))), FALSE,
341  "%s", _(check_repair_abort_YN));
342 
343  show_abort_verify = FALSE;
344 
345  if (ret)
346  gnc_set_abort_scrub (TRUE);
347 
348  return ret; // verify response
349  }
350  else
351  {
352  if (gnc_get_abort_scrub ())
353  return TRUE; // close
354  else
355  return FALSE; // no close
356  }
357  }
358  else
359  return TRUE; // normal close
360 }
361 
362 static void
363 gnc_plugin_page_account_tree_class_init (GncPluginPageAccountTreeClass *klass)
364 {
365  GObjectClass *object_class = G_OBJECT_CLASS (klass);
366  GncPluginPageClass *gnc_plugin_class = GNC_PLUGIN_PAGE_CLASS(klass);
367 
368  object_class->finalize = gnc_plugin_page_account_tree_finalize;
369 
370  gnc_plugin_class->tab_icon = GNC_ICON_ACCOUNT;
371  gnc_plugin_class->plugin_name = GNC_PLUGIN_PAGE_ACCOUNT_TREE_NAME;
372  gnc_plugin_class->create_widget = gnc_plugin_page_account_tree_create_widget;
373  gnc_plugin_class->destroy_widget = gnc_plugin_page_account_tree_destroy_widget;
374  gnc_plugin_class->save_page = gnc_plugin_page_account_tree_save_page;
375  gnc_plugin_class->recreate_page = gnc_plugin_page_account_tree_recreate_page;
376  gnc_plugin_class->focus_page_function = gnc_plugin_page_account_tree_focus_widget;
377  gnc_plugin_class->finish_pending = gnc_plugin_page_account_finish_pending;
378 
379  plugin_page_signals[ACCOUNT_SELECTED] =
380  g_signal_new ("account_selected",
381  G_OBJECT_CLASS_TYPE (object_class),
382  G_SIGNAL_RUN_FIRST,
383  G_STRUCT_OFFSET (GncPluginPageAccountTreeClass, account_selected),
384  NULL, NULL,
385  g_cclosure_marshal_VOID__POINTER,
386  G_TYPE_NONE, 1,
387  G_TYPE_POINTER);
388 }
389 
390 static void
391 gnc_plugin_page_account_tree_init (GncPluginPageAccountTree *plugin_page)
392 {
393  GSimpleActionGroup *simple_action_group = NULL;
395  GncPluginPage *parent;
396  const GList *page_list;
397 
398  ENTER("page %p", plugin_page);
399  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(plugin_page);
400 
401  /* Init parent declared variables */
402  parent = GNC_PLUGIN_PAGE(plugin_page);
403  g_object_set (G_OBJECT(plugin_page),
404  "page-name", _("Accounts"),
405  "ui-description", "gnc-plugin-page-account-tree.ui",
406  NULL);
407  g_signal_connect (G_OBJECT (plugin_page), "selected",
408  G_CALLBACK (gnc_plugin_page_account_tree_selected), plugin_page);
409 
410  /* change me when the system supports multiple books */
411  gnc_plugin_page_add_book (parent, gnc_get_current_book());
412 
413  /* Is this the first accounts page? */
414  page_list =
415  gnc_gobject_tracking_get_list (GNC_PLUGIN_PAGE_ACCOUNT_TREE_NAME);
416  if (!page_list || plugin_page == page_list->data)
417  {
418  g_object_set_data (G_OBJECT(plugin_page), PLUGIN_PAGE_IMMUTABLE,
419  GINT_TO_POINTER(1));
420  }
421 
422  /* Create menu and toolbar information */
423  simple_action_group = gnc_plugin_page_create_action_group (parent, "GncPluginPageAccountTreeActions");
424  g_action_map_add_action_entries (G_ACTION_MAP(simple_action_group),
425  gnc_plugin_page_account_tree_actions,
426  gnc_plugin_page_account_tree_n_actions,
427  plugin_page);
428 
429  /* Visible types */
430  priv->fd.visible_types = -1; /* Start with all types */
431  priv->fd.show_hidden = FALSE;
432  priv->fd.show_unused = TRUE;
433  priv->fd.show_zero_total = TRUE;
434  priv->fd.filter_override = g_hash_table_new (g_direct_hash, g_direct_equal);
435 
436  LEAVE("page %p, priv %p, action group %p",
437  plugin_page, priv, simple_action_group);
438 }
439 
440 static void
441 gnc_plugin_page_account_tree_finalize (GObject *object)
442 {
445 
446  ENTER("object %p", object);
447  page = GNC_PLUGIN_PAGE_ACCOUNT_TREE (object);
448  g_return_if_fail (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE (page));
449  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
450  g_return_if_fail (priv != NULL);
451 
452  G_OBJECT_CLASS (gnc_plugin_page_account_tree_parent_class)->finalize (object);
453  LEAVE(" ");
454 }
455 
456 void
457 gnc_plugin_page_account_tree_open (Account *account, GtkWindow *win)
458 {
461  GncPluginPage *plugin_page = NULL;
462  const GList *page_list;
463  GtkWidget *window;
464 
465  /* Find Accounts page */
466  page_list = gnc_gobject_tracking_get_list(GNC_PLUGIN_PAGE_ACCOUNT_TREE_NAME);
467 
468  // If we have a window, look for account page in that window
469  if (gnc_list_length_cmp (page_list, 0))
470  {
471  if (win != NULL)
472  {
473  for ( ; page_list; page_list = g_list_next(page_list))
474  {
475  plugin_page = GNC_PLUGIN_PAGE(page_list->data);
476  if (GTK_WINDOW(plugin_page->window) == win)
477  break;
478  }
479  }
480  else // if no window, open first account page in list
481  plugin_page = GNC_PLUGIN_PAGE(page_list->data);
482  }
483  else // we have no account pages, create one
484  plugin_page = gnc_plugin_page_account_tree_new ();
485 
486  g_return_if_fail(plugin_page);
487  window = plugin_page->window;
488 
489  gnc_main_window_open_page (GNC_MAIN_WINDOW(window), plugin_page);
490 
491  page = GNC_PLUGIN_PAGE_ACCOUNT_TREE (plugin_page);
492  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
493 
494  if (account != NULL)
495  {
496  Account *root_account = gnc_get_current_root_account ();
497  Account *parent_account = NULL;
498  Account *temp_account = account;
499 
500  g_hash_table_insert (priv->fd.filter_override, account, account);
501 
502  // make sure we override all the parent accounts to root
503  while (parent_account != root_account)
504  {
505  parent_account = gnc_account_get_parent (temp_account);
506 
507  g_hash_table_insert (priv->fd.filter_override, parent_account, parent_account);
508  temp_account = parent_account;
509  }
510  gnc_tree_view_account_refilter (GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
511  gnc_tree_view_account_set_selected_account (GNC_TREE_VIEW_ACCOUNT(priv->tree_view), account);
512  }
513 }
514 
515 Account *
517 {
519  Account *account;
520 
521  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
522  ENTER("page %p (tree view %p)", page, priv->tree_view);
523  account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
524  if (account == NULL)
525  {
526  LEAVE("no account");
527  return NULL;
528  }
529 
530  LEAVE("account %p", account);
531  return account;
532 }
533 
538 static gboolean
539 gnc_plugin_page_account_tree_focus_widget (GncPluginPage *account_plugin_page)
540 {
541  if (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(account_plugin_page))
542  {
543  GncPluginPageAccountTreePrivate *priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(account_plugin_page);
544  GtkTreeView *view = GTK_TREE_VIEW(priv->tree_view);
545 
546  /* Disable the Transaction Menu */
547  GAction *action = gnc_main_window_find_action (GNC_MAIN_WINDOW(account_plugin_page->window), "TransactionAction");
548  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
549  /* Disable the Schedule menu */
550  action = gnc_main_window_find_action (GNC_MAIN_WINDOW(account_plugin_page->window), "ScheduledAction");
551  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
552 
553  gnc_main_window_update_menu_and_toolbar (GNC_MAIN_WINDOW(account_plugin_page->window),
554  account_plugin_page,
555  gnc_plugin_load_ui_items);
556 
557  // setup any short toolbar names
558  gnc_main_window_init_short_names (GNC_MAIN_WINDOW(account_plugin_page->window), toolbar_labels);
559 
560  /* Disable the FilePrintAction */
561  action = gnc_main_window_find_action (GNC_MAIN_WINDOW(account_plugin_page->window), "FilePrintAction");
562  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
563 
564  if (!gtk_widget_is_focus (GTK_WIDGET(view)))
565  gtk_widget_grab_focus (GTK_WIDGET(view));
566  }
567  return FALSE;
568 }
569 
570 /* Virtual Functions */
571 
572 static void
573 gnc_plugin_page_account_refresh_cb (GHashTable *changes, gpointer user_data)
574 {
575  /* We're only looking for forced updates here. */
576  if (!changes)
577  gnc_plugin_page_account_tree_cmd_refresh(NULL, NULL, user_data);
578 }
579 
580 static void
581 gnc_plugin_page_account_tree_close_cb (gpointer user_data)
582 {
583  GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(user_data);
584  gnc_main_window_close_page(plugin_page);
585 }
586 
587 static void
588 gnc_plugin_page_account_editing_started_cd (gpointer various, GncPluginPageRegister *page)
589 {
590  GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(page);
591  GAction *action = gnc_main_window_find_action_in_group (GNC_MAIN_WINDOW(plugin_page->window),
592  "GncPluginPageAccountTreeActions",
593  "EditDeleteAccountAction");
594  if (action != NULL)
595  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), FALSE);
596 }
597 
598 static void
599 gnc_plugin_page_account_editing_finished_cb (gpointer various, GncPluginPageRegister *page)
600 {
601  GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(page);
602  GAction *action = gnc_main_window_find_action_in_group (GNC_MAIN_WINDOW(plugin_page->window),
603  "GncPluginPageAccountTreeActions",
604  "EditDeleteAccountAction");
605  if (action != NULL)
606  g_simple_action_set_enabled (G_SIMPLE_ACTION(action), TRUE);
607 }
608 
609 static GtkWidget *
610 gnc_plugin_page_account_tree_create_widget (GncPluginPage *plugin_page)
611 {
614  GtkTreeSelection *selection;
615  GtkTreeView *tree_view;
616  GtkWidget *scrolled_window;
617  GtkTreeViewColumn *col;
618 
619  ENTER("page %p", plugin_page);
620  page = GNC_PLUGIN_PAGE_ACCOUNT_TREE (plugin_page);
621  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
622  if (priv->widget != NULL)
623  {
624  LEAVE("widget = %p", priv->widget);
625  return priv->widget;
626  }
627 
628  priv->widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
629  gtk_box_set_homogeneous (GTK_BOX (priv->widget), FALSE);
630  gtk_widget_show (priv->widget);
631 
632  // Set the name for this widget so it can be easily manipulated with css
633  gtk_widget_set_name (GTK_WIDGET(priv->widget), "gnc-id-account-page");
634 
635  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
636  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
637  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
638  gtk_widget_show (scrolled_window);
639  gtk_box_pack_start (GTK_BOX (priv->widget), scrolled_window,
640  TRUE, TRUE, 0);
641 
642  tree_view = gnc_tree_view_account_new(FALSE);
644  GNC_TREE_VIEW(tree_view), "description");
645  g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
647  GNC_TREE_VIEW(tree_view), "total");
648  g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
649  gnc_tree_view_configure_columns(GNC_TREE_VIEW(tree_view));
650  g_object_set(G_OBJECT(tree_view),
651  "state-section", STATE_SECTION,
652  "show-column-menu", TRUE,
653  NULL);
654 
655  /* No name handler; then the user can't click on the name of the
656  account to open its register. */
657  gnc_tree_view_account_set_code_edited(GNC_TREE_VIEW_ACCOUNT(tree_view),
658  gnc_tree_view_account_code_edited_cb);
659  gnc_tree_view_account_set_description_edited(GNC_TREE_VIEW_ACCOUNT(tree_view),
660  gnc_tree_view_account_description_edited_cb);
661  gnc_tree_view_account_set_notes_edited(GNC_TREE_VIEW_ACCOUNT(tree_view),
662  gnc_tree_view_account_notes_edited_cb);
663 
664  // Setup some callbacks so menu actions can be disabled/enabled
665  gnc_tree_view_account_set_editing_started_cb(GNC_TREE_VIEW_ACCOUNT(tree_view),
666  (GFunc)gnc_plugin_page_account_editing_started_cd, page);
667  gnc_tree_view_account_set_editing_finished_cb(GNC_TREE_VIEW_ACCOUNT(tree_view),
668  (GFunc)gnc_plugin_page_account_editing_finished_cb, page);
669 
670  priv->tree_view = tree_view;
671  selection = gtk_tree_view_get_selection(tree_view);
672  g_signal_connect (G_OBJECT (selection), "changed",
673  G_CALLBACK (gnc_plugin_page_account_tree_selection_changed_cb), page);
674  g_signal_connect (G_OBJECT (tree_view), "button-press-event",
675  G_CALLBACK (gnc_plugin_page_account_tree_button_press_cb), page);
676  g_signal_connect (G_OBJECT (tree_view), "row-activated",
677  G_CALLBACK (gnc_plugin_page_account_tree_double_click_cb), page);
678 
679  gtk_tree_view_set_headers_visible(tree_view, TRUE);
680  gnc_plugin_page_account_tree_selection_changed_cb (NULL, page);
681  gtk_widget_show (GTK_WIDGET (tree_view));
682  gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET(tree_view));
683 
684  priv->fd.tree_view = GNC_TREE_VIEW_ACCOUNT(priv->tree_view);
686  GNC_TREE_VIEW_ACCOUNT(tree_view),
688 
689  priv->component_id =
690  gnc_register_gui_component(PLUGIN_PAGE_ACCT_TREE_CM_CLASS,
691  gnc_plugin_page_account_refresh_cb,
692  gnc_plugin_page_account_tree_close_cb,
693  page);
694  gnc_gui_component_set_session (priv->component_id,
695  gnc_get_current_session());
696 
697  plugin_page->summarybar = gnc_main_window_summary_new();
698  gtk_box_pack_start (GTK_BOX (priv->widget), plugin_page->summarybar,
699  FALSE, FALSE, 0);
700  gtk_widget_show(plugin_page->summarybar);
701  gnc_plugin_page_account_tree_summarybar_position_changed(NULL, NULL, page);
702  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
703  GNC_PREF_SUMMARYBAR_POSITION_TOP,
704  (gpointer)gnc_plugin_page_account_tree_summarybar_position_changed,
705  page);
706  gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
707  GNC_PREF_SUMMARYBAR_POSITION_BOTTOM,
708  (gpointer)gnc_plugin_page_account_tree_summarybar_position_changed,
709  page);
710 
711  gnc_prefs_register_cb(GNC_PREFS_GROUP_ACCT_SUMMARY, GNC_PREF_START_CHOICE_ABS,
712  (gpointer)accounting_period_changed_cb, page);
713  gnc_prefs_register_cb(GNC_PREFS_GROUP_ACCT_SUMMARY, GNC_PREF_START_DATE,
714  (gpointer)accounting_period_changed_cb, page);
715  gnc_prefs_register_cb(GNC_PREFS_GROUP_ACCT_SUMMARY, GNC_PREF_START_PERIOD,
716  (gpointer)accounting_period_changed_cb, page);
717  gnc_prefs_register_cb(GNC_PREFS_GROUP_ACCT_SUMMARY, GNC_PREF_END_CHOICE_ABS,
718  (gpointer)accounting_period_changed_cb, page);
719  gnc_prefs_register_cb(GNC_PREFS_GROUP_ACCT_SUMMARY, GNC_PREF_END_DATE,
720  (gpointer)accounting_period_changed_cb, page);
721  gnc_prefs_register_cb(GNC_PREFS_GROUP_ACCT_SUMMARY, GNC_PREF_END_PERIOD,
722  (gpointer)accounting_period_changed_cb, page);
723 
724  g_signal_connect (G_OBJECT(plugin_page), "inserted",
725  G_CALLBACK(gnc_plugin_page_inserted_cb),
726  NULL);
727 
728  // Read account filter state information from account section
729  gnc_tree_view_account_restore_filter (GNC_TREE_VIEW_ACCOUNT(priv->tree_view), &priv->fd,
730  gnc_state_get_current(), gnc_tree_view_get_state_section (GNC_TREE_VIEW(priv->tree_view)));
731 
732  LEAVE("widget = %p", priv->widget);
733  return priv->widget;
734 }
735 
736 static void
737 gnc_plugin_page_account_tree_destroy_widget (GncPluginPage *plugin_page)
738 {
741 
742  ENTER("page %p", plugin_page);
743  page = GNC_PLUGIN_PAGE_ACCOUNT_TREE (plugin_page);
744  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
745 
746  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
747  GNC_PREF_SUMMARYBAR_POSITION_TOP,
748  (gpointer)gnc_plugin_page_account_tree_summarybar_position_changed,
749  page);
750  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
751  GNC_PREF_SUMMARYBAR_POSITION_BOTTOM,
752  (gpointer)gnc_plugin_page_account_tree_summarybar_position_changed,
753  page);
754 
755  // Save account filter state information to account section
756  gnc_tree_view_account_save_filter (GNC_TREE_VIEW_ACCOUNT(priv->tree_view), &priv->fd,
757  gnc_state_get_current(), gnc_tree_view_get_state_section (GNC_TREE_VIEW(priv->tree_view)));
758 
759  // Destroy the filter override hash table
760  g_hash_table_destroy(priv->fd.filter_override);
761 
762  // Remove the page_changed signal callback
763  gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(plugin_page));
764 
765  // Remove the page focus idle function if present
766  g_idle_remove_by_data (plugin_page);
767 
768  if (priv->widget)
769  {
770  g_object_unref(G_OBJECT(priv->widget));
771  priv->widget = NULL;
772  }
773 
774  if (priv->component_id)
775  {
776  gnc_unregister_gui_component(priv->component_id);
777  priv->component_id = 0;
778  }
779 
780  LEAVE("widget destroyed");
781 }
782 
783 static void
784 update_inactive_actions (GncPluginPage *plugin_page)
785 {
787  GSimpleActionGroup *simple_action_group = NULL;
788  Account *account = NULL;
789  gboolean allow_write = !qof_book_is_readonly (gnc_get_current_book());
790  gboolean has_account = FALSE;
791  gboolean subaccounts = FALSE;
792 
793  g_return_if_fail (plugin_page && GNC_IS_PLUGIN_PAGE(plugin_page));
794 
795  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE (plugin_page);
796 
797  if (gtk_tree_view_get_selection (priv->tree_view))
798  {
799  account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
800  has_account = (account != NULL);
801  subaccounts = (account && gnc_account_n_children (account) != 0);
802  /* Check here for placeholder accounts, etc. */
803  }
804 
805  /* Get the action group */
806  simple_action_group = gnc_plugin_page_get_action_group (plugin_page);
807  g_return_if_fail (G_IS_SIMPLE_ACTION_GROUP (simple_action_group));
808 
809  /* Set the action's sensitivity */
810  gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), readonly_inactive_actions,
811  allow_write);
812  gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), actions_requiring_account_rw,
813  allow_write && has_account);
814  gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), actions_requiring_account_always,
815  has_account);
816  gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), actions_requiring_subaccounts_rw,
817  allow_write && subaccounts);
818  gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), actions_requiring_priced_account,
819  account && xaccAccountIsPriced (account));
820 
821  g_signal_emit (plugin_page, plugin_page_signals[ACCOUNT_SELECTED], 0, account);
822 }
823 
828 static void
829 gnc_plugin_page_account_tree_selected (GObject *object, gpointer user_data)
830 {
831  GncPluginPage *plugin_page = GNC_PLUGIN_PAGE (object);
832  g_return_if_fail (GNC_IS_PLUGIN_PAGE (plugin_page));
833  update_inactive_actions(plugin_page);
834 }
835 
845 static void
846 gnc_plugin_page_account_tree_save_page (GncPluginPage *plugin_page,
847  GKeyFile *key_file,
848  const gchar *group_name)
849 {
850  GncPluginPageAccountTree *account_page;
852 
853  g_return_if_fail (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(plugin_page));
854  g_return_if_fail (key_file != NULL);
855  g_return_if_fail (group_name != NULL);
856 
857  ENTER("page %p, key_file %p, group_name %s", plugin_page, key_file,
858  group_name);
859 
860  account_page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(plugin_page);
861  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(account_page);
862 
863  gnc_tree_view_account_save(GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
864  &priv->fd, key_file, group_name);
865  LEAVE(" ");
866 }
867 
868 
869 
879 static GncPluginPage *
880 gnc_plugin_page_account_tree_recreate_page (GtkWidget *window,
881  GKeyFile *key_file,
882  const gchar *group_name)
883 {
884  GncPluginPageAccountTree *account_page;
886  GncPluginPage *page;
887 
888  g_return_val_if_fail(key_file, NULL);
889  g_return_val_if_fail(group_name, NULL);
890  ENTER("key_file %p, group_name %s", key_file, group_name);
891 
892  /* Create the new page. */
894  account_page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(page);
895  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(account_page);
896 
897  /* Install it now so we can then manipulate the created widget */
898  gnc_main_window_open_page(GNC_MAIN_WINDOW(window), page);
899 
900  gnc_tree_view_account_restore(GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
901  &priv->fd, key_file, group_name);
902  LEAVE(" ");
903  return page;
904 }
905 
906 
907 /* Callbacks */
908 
909 static void
910 gnc_plugin_page_account_tree_summarybar_position_changed (gpointer prefs,
911  gchar* pref,
912  gpointer user_data)
913 {
914  GncPluginPage *plugin_page;
917  GtkPositionType position = GTK_POS_BOTTOM;
918 
919  g_return_if_fail(user_data != NULL);
920 
921  plugin_page = GNC_PLUGIN_PAGE(user_data);
922  page = GNC_PLUGIN_PAGE_ACCOUNT_TREE (user_data);
923  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
924 
925  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SUMMARYBAR_POSITION_TOP))
926  position = GTK_POS_TOP;
927 
928  gtk_box_reorder_child(GTK_BOX(priv->widget),
929  plugin_page->summarybar,
930  (position == GTK_POS_TOP ? 0 : -1) );
931 }
932 
940 static gboolean
941 gnc_plugin_page_account_tree_button_press_cb (GtkWidget *widget,
942  GdkEventButton *event,
943  GncPluginPage *page)
944 {
945 
946  g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
947 
948  ENTER("widget %p, event %p, page %p", widget, event, page);
949  gnc_main_window_button_press_cb(widget, event, page);
950  LEAVE(" ");
951 
952  /* Always return FALSE. This will let the tree view callback run as
953  * well which will select the item under the cursor. By the time
954  * the user sees the menu both callbacks will have run and the menu
955  * actions will operate on the just-selected account. */
956  return FALSE;
957 }
958 
959 static void
960 gppat_open_account_common (GncPluginPageAccountTree *page,
961  Account *account,
962  gboolean include_subs)
963 {
964  GtkWidget *window;
965  GncPluginPage *new_page;
966 
967  if (account == NULL)
968  return;
969 
970  window = GNC_PLUGIN_PAGE (page)->window;
971  new_page = gnc_plugin_page_register_new (account, include_subs);
972  gnc_main_window_open_page (GNC_MAIN_WINDOW(window), new_page);
973 }
974 
975 static void
976 gnc_plugin_page_account_tree_double_click_cb (GtkTreeView *treeview,
977  GtkTreePath *path,
978  GtkTreeViewColumn *col,
980 {
981  GtkTreeModel *model;
982  GtkTreeIter iter;
983 
984  g_return_if_fail (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE (page));
985  g_return_if_fail (treeview);
986 
987  model = gtk_tree_view_get_model(treeview);
988  if (gtk_tree_model_get_iter(model, &iter, path))
989  {
990  Account *account = gnc_tree_view_account_get_account_from_path (GNC_TREE_VIEW_ACCOUNT(treeview), path);
991  if (xaccAccountGetPlaceholder (account))
992  {
993  /* This is a placeholder account. Only only show/hide
994  * subaccount list if there is one.
995  */
996  if (gtk_tree_model_iter_has_child(model, &iter))
997  {
998  /* There are children,
999  * just expand or collapse the row. */
1000  if (gtk_tree_view_row_expanded(treeview, path))
1001  gtk_tree_view_collapse_row(treeview, path);
1002  else
1003  gtk_tree_view_expand_row(treeview, path, FALSE);
1004  }
1005  }
1006  else
1007  {
1008  /* No placeholder account, so open its register */
1009  gppat_open_account_common (page, account, FALSE);
1010  }
1011  }
1012 }
1013 
1014 static void
1015 gnc_plugin_page_account_tree_selection_changed_cb (GtkTreeSelection *selection,
1017 {
1018  GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(page);
1019  update_inactive_actions (plugin_page);
1020 }
1021 
1022 static void
1023 accounting_period_changed_cb (gpointer prefs, gchar *pref, gpointer user_data)
1024 {
1025  gnc_plugin_page_account_tree_cmd_refresh (NULL, NULL, user_data);
1026 }
1027 
1028 /* Command callbacks */
1029 static void
1030 gnc_plugin_page_account_tree_cmd_new_account (GSimpleAction *simple,
1031  GVariant *paramter,
1032  gpointer user_data)
1033 {
1034  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1036  GtkWindow *parent = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE (page)));
1037  gnc_ui_new_account_window (parent, gnc_get_current_book(),
1038  account);
1039 }
1040 
1041 static void
1042 gnc_plugin_page_account_tree_cmd_file_new_hierarchy (GSimpleAction *simple,
1043  GVariant *paramter,
1044  gpointer user_data)
1045 {
1046  gnc_ui_hierarchy_assistant(FALSE);
1047 }
1048 
1049 static void
1050 gnc_plugin_page_account_tree_cmd_open_account (GSimpleAction *simple,
1051  GVariant *paramter,
1052  gpointer user_data)
1053 {
1054  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1055  Account *account;
1056 
1057  g_return_if_fail (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE (page));
1059  gppat_open_account_common (page, account, FALSE);
1060 }
1061 
1062 static void
1063 gnc_plugin_page_account_tree_cmd_open_subaccounts (GSimpleAction *simple,
1064  GVariant *paramter,
1065  gpointer user_data)
1066 {
1067  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1068  Account *account;
1069 
1070  g_return_if_fail (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE (page));
1072  gppat_open_account_common (page, account, TRUE);
1073 }
1074 
1075 static void
1076 gnc_plugin_page_account_tree_cmd_edit_account (GSimpleAction *simple,
1077  GVariant *paramter,
1078  gpointer user_data)
1079 {
1080  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1081  Account *account;
1082  GtkWindow *parent = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE (page)));
1083  ENTER("action %p, page %p", simple, page);
1084 
1086  g_return_if_fail (account != NULL);
1087 
1088  gnc_ui_edit_account_window (parent, account);
1089  LEAVE(" ");
1090 }
1091 
1092 static void
1093 gnc_plugin_page_account_tree_cmd_find_account (GSimpleAction *simple,
1094  GVariant *paramter,
1095  gpointer user_data)
1096 {
1097  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1098  GtkWidget *window;
1099 
1100  ENTER("action %p, page %p", simple, page);
1101 
1102  window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1103 
1104  gnc_find_account_dialog (window, NULL);
1105  LEAVE(" ");
1106 }
1107 
1108 static void
1109 gnc_plugin_page_account_tree_cmd_find_account_popup (GSimpleAction *simple,
1110  GVariant *paramter,
1111  gpointer user_data)
1112 {
1113  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1114  Account *account = NULL;
1115  GtkWidget *window;
1116 
1117  ENTER("action %p, page %p", simple, page);
1118 
1120 
1121  window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1122 
1123  gnc_find_account_dialog (window, account);
1124  LEAVE(" ");
1125 }
1126 
1127 static void
1128 gnc_plugin_page_account_tree_cmd_cascade_account_properties (GSimpleAction *simple,
1129  GVariant *paramter,
1130  gpointer user_data)
1131 {
1132  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1133  Account *account = NULL;
1134  GtkWidget *window;
1135 
1136  ENTER("action %p, page %p", simple, page);
1137 
1139 
1140  window = gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page));
1141 
1142  if (account != NULL)
1143  gnc_account_cascade_properties_dialog (window, account);
1144 
1145  LEAVE(" ");
1146 }
1147 
1148 static gpointer
1149 delete_account_helper (Account * account, gpointer data)
1150 {
1151  auto helper_res = static_cast<delete_helper_t*>(data);
1152  auto splits{xaccAccountGetSplits (account)};
1153 
1154  if (!splits.empty())
1155  {
1156  helper_res->has_splits = TRUE;
1157  for (auto s : splits)
1158  {
1159  Transaction *txn = xaccSplitGetParent (s);
1160  if (xaccTransGetReadOnly (txn))
1161  {
1162  helper_res->has_ro_splits = TRUE;
1163  break;
1164  }
1165  }
1166  }
1167 
1168  return GINT_TO_POINTER (helper_res->has_splits || helper_res->has_ro_splits);
1169 }
1170 
1171 /***
1172  *** The OK button of a Delete Account dialog is insensitive if
1173  *** and only if a sensitive account selector contains no accounts.
1174  ***/
1175 static void
1176 set_ok_sensitivity(GtkWidget *dialog)
1177 {
1178  gint sa_mas_cnt, trans_mas_cnt;
1179  gboolean sensitive;
1180 
1181  auto sa_mas = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_SA_MAS));
1182  auto trans_mas = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_TRANS_MAS));
1183  sa_mas_cnt = gnc_account_sel_get_visible_account_num(GNC_ACCOUNT_SEL(sa_mas));
1184  trans_mas_cnt = gnc_account_sel_get_visible_account_num(GNC_ACCOUNT_SEL(trans_mas));
1185 
1186  sensitive = (((NULL == sa_mas) ||
1187  (!gtk_widget_is_sensitive(sa_mas) || sa_mas_cnt)) &&
1188  ((NULL == trans_mas) ||
1189  (!gtk_widget_is_sensitive(trans_mas) || trans_mas_cnt)));
1190 
1191  auto button = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_OK_BUTTON));
1192  gtk_widget_set_sensitive(button, sensitive);
1193 }
1194 
1195 static GList *
1196 gppat_get_exclude_list (Account *acc, gboolean exclude_subaccounts)
1197 {
1198  GList *acct_list = NULL;
1199 
1200  if (exclude_subaccounts)
1201  acct_list = gnc_account_get_descendants (acc);
1202 
1203  acct_list = g_list_prepend (acct_list, acc);
1204 
1205  return acct_list;
1206 }
1207 
1208 static void
1209 gppat_populate_gas_list(GtkWidget *dialog,
1210  GNCAccountSel *gas,
1211  gboolean exclude_subaccounts)
1212 {
1213  Account *account;
1214  GList *filter;
1215  GList *exclude;
1216 
1217  g_return_if_fail(GTK_IS_DIALOG(dialog));
1218  if (gas == NULL)
1219  return;
1220  account = GNC_ACCOUNT(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_ACCOUNT));
1221  filter = static_cast<GList*>(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_FILTER));
1222 
1223  /* Setting the account type filter triggers GNCAccountSel population. */
1224  gnc_account_sel_set_acct_filters (gas, filter, NULL);
1225 
1226  /* Accounts to be deleted must be excluded from GAS. */
1227  exclude = gppat_get_exclude_list (account, exclude_subaccounts);
1228  gnc_account_sel_set_acct_exclude_filter (gas, exclude);
1229  g_list_free (exclude);
1230 
1231  gnc_account_sel_set_account (gas, NULL, TRUE);
1232 
1233  /* The sensitivity of the OK button needs to be reevaluated. */
1234  set_ok_sensitivity(dialog);
1235 }
1236 
1237 void
1238 gppat_populate_trans_mas_list(GtkToggleButton *sa_mrb,
1239  GtkWidget *dialog)
1240 {
1241  g_return_if_fail(GTK_IS_DIALOG(dialog));
1242 
1243  /* Cannot move transactions to subaccounts if they are to be deleted. */
1244  auto trans_mas = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_TRANS_MAS));
1245  gppat_populate_gas_list(dialog, GNC_ACCOUNT_SEL(trans_mas), !gtk_toggle_button_get_active(sa_mrb));
1246 }
1247 
1248 /* Note that the emitting object (the toggle button) and the signal data
1249  * are swapped in below callback function. This is a gtkbuilder feature:
1250  * it swaps if you explicitly set an object for a signal handler in the
1251  * gtkbuilder xml file.
1252  */
1253 void
1254 gppat_set_insensitive_iff_rb_active(GtkWidget *widget, GtkToggleButton *b)
1255 {
1256  GtkWidget *dialog = gtk_widget_get_toplevel(widget);
1257  auto subaccount_trans = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_SA_TRANS));
1258  auto sa_mas = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_SA_MAS));
1259  auto have_splits = g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_SA_SPLITS) != nullptr;
1260 
1261  gtk_widget_set_sensitive(widget, !gtk_toggle_button_get_active(b));
1262 
1263  // If we have subaccount splits & delete subaccounts, enable subaccount_trans
1264  if ((have_splits) && !gtk_widget_is_sensitive(sa_mas))
1265  gtk_widget_set_sensitive(subaccount_trans, TRUE);
1266  else
1267  gtk_widget_set_sensitive(subaccount_trans, FALSE);
1268 
1269  set_ok_sensitivity(dialog);
1270 }
1271 
1272 static GtkWidget *
1273 gppat_setup_account_selector (GtkBuilder *builder, GtkWidget *dialog,
1274  const gchar *hbox, const gchar *sel_name)
1275 {
1276  GtkWidget *selector = gnc_account_sel_new();
1277  GtkWidget *box = GTK_WIDGET(gtk_builder_get_object (builder, hbox));
1278 
1279  gtk_box_pack_start (GTK_BOX(box), selector, TRUE, TRUE, 0);
1280 
1281  // placeholder accounts are OK for this GAS
1282  if (g_strcmp0 (sel_name, DELETE_DIALOG_SA_MAS) == 0)
1283  g_object_set (selector, "hide-placeholder", FALSE, NULL);
1284 
1285  g_object_set_data(G_OBJECT(dialog), sel_name, selector);
1286 
1287  gppat_populate_gas_list(dialog, GNC_ACCOUNT_SEL(selector), TRUE);
1288  gtk_widget_show_all(box);
1289 
1290  return selector;
1291 }
1292 
1293 static int
1294 commodity_mismatch_dialog (const Account* account, GtkWindow* parent)
1295 {
1296  int response;
1297  char *account_name = gnc_account_get_full_name (account);
1298  char* message = g_strdup_printf (
1299  _("Account %s does not have the same currency as the one you're "
1300  "moving transactions from.\nAre you sure you want to do this?"),
1301  account_name);
1302  GtkWidget* error_dialog =
1303  gtk_message_dialog_new (parent, GTK_DIALOG_DESTROY_WITH_PARENT,
1304  GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE,
1305  "%s", message);
1306  gtk_dialog_add_buttons (GTK_DIALOG(error_dialog),
1307  _("_Pick another account"), GTK_RESPONSE_CANCEL,
1308  _("_Do it anyway"), GTK_RESPONSE_ACCEPT,
1309  (gchar *)NULL);
1310  response = gtk_dialog_run (GTK_DIALOG (error_dialog));
1311  gtk_widget_destroy (error_dialog);
1312  g_free (message);
1313  return response;
1314 }
1315 
1316 typedef struct
1317 {
1318  Account *new_account;
1319  Account *old_account;
1320  GNCAccountSel *selector;
1321  gboolean match;
1322  gboolean for_account;
1323 } Adopter;
1324 
1325 static void
1326 adopter_set_account_and_match (Adopter* adopter)
1327 {
1328  if (!(adopter->selector &&
1329  gtk_widget_is_sensitive (GTK_WIDGET (adopter->selector))))
1330  return;
1331  adopter->new_account = gnc_account_sel_get_account(adopter->selector);
1332 /* We care about the commodity only if we're moving transactions. */
1333  if (!adopter->for_account && adopter->old_account && adopter->new_account)
1334  adopter->match =
1335  xaccAccountGetCommodity (adopter->new_account) ==
1336  xaccAccountGetCommodity (adopter->old_account);
1337 }
1338 
1339 static void
1340 adopter_init (Adopter* adopter, GtkWidget *selector, Account* account,
1341  gboolean for_account)
1342 {
1343  adopter->selector = GNC_ACCOUNT_SEL (selector);
1344  adopter->new_account = NULL;
1345  adopter->old_account = account;
1346  adopter->match = TRUE;
1347  adopter->for_account = for_account;
1348 }
1349 
1350 static gboolean
1351 adopter_match (Adopter* adopter, GtkWindow *parent)
1352 {
1353  int result;
1354  if (adopter->match || adopter->for_account)
1355  return TRUE;
1356  result = commodity_mismatch_dialog (adopter->new_account, parent);
1357  return (result == GTK_RESPONSE_ACCEPT);
1358 }
1359 
1360 typedef struct
1361 {
1362  Adopter trans;
1363  Adopter subacct;
1364  Adopter subtrans;
1365  delete_helper_t delete_res;
1366 } Adopters;
1367 
1368 static Account*
1369 account_subaccount (Account* account)
1370 {
1371  Account* subaccount = NULL;
1372  GList *subs = gnc_account_get_children (account);
1373  if (!gnc_list_length_cmp (subs, 1))
1374  subaccount = GNC_ACCOUNT(subs->data);
1375  g_list_free (subs);
1376  return subaccount;
1377 }
1378 
1379 static GtkWidget*
1380 account_delete_dialog (Account *account, GtkWindow *parent, Adopters* adopt)
1381 {
1382  GtkWidget *dialog = NULL;
1383  GtkWidget *widget = NULL;
1384  gchar *title = NULL;
1385  GtkBuilder *builder = gtk_builder_new();
1386  gchar *acct_name = gnc_account_get_full_name(account);
1387  GList* filter = g_list_prepend(NULL, (gpointer)xaccAccountGetType(account));
1388 
1389  if (!acct_name)
1390  acct_name = g_strdup (_("(no name)"));
1391 
1392  gnc_builder_add_from_file (builder, "dialog-account.glade", "account_delete_dialog");
1393 
1394  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "account_delete_dialog"));
1395  gtk_window_set_transient_for(GTK_WINDOW(dialog), parent);
1396 
1397  /* FIXME: Same account type used for subaccount. */
1398  g_object_set_data_full (G_OBJECT(dialog), DELETE_DIALOG_FILTER, filter,
1399  (GDestroyNotify) g_list_free);
1400  g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_ACCOUNT, account);
1401  widget = GTK_WIDGET(gtk_builder_get_object (builder, "header"));
1402  title = g_strdup_printf(_("Deleting account %s"), acct_name);
1403  gtk_label_set_text(GTK_LABEL(widget), title);
1404  g_free(title);
1405  g_free(acct_name);
1406 
1407  widget = GTK_WIDGET(gtk_builder_get_object (builder, DELETE_DIALOG_OK_BUTTON));
1408  g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_OK_BUTTON, widget);
1409 
1410  // Add the account selectors and enable sections as appropriate
1411  // setup transactions selector
1412  adopter_init (&adopt->trans,
1413  gppat_setup_account_selector (builder, dialog,
1414  "trans_mas_hbox",
1415  DELETE_DIALOG_TRANS_MAS),
1416  account, FALSE);
1417 
1418  // Does the selected account have splits
1419  if (!xaccAccountGetSplits(account).empty())
1420  {
1421  delete_helper_t delete_res2 = { FALSE, FALSE };
1422 
1423  delete_account_helper(account, &delete_res2);
1424  if (delete_res2.has_ro_splits)
1425  {
1426  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "trans_rw")));
1427  widget = GTK_WIDGET(gtk_builder_get_object (builder, "trans_drb"));
1428  gtk_widget_set_sensitive(widget, FALSE);
1429  }
1430  else
1431  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "trans_ro")));
1432  }
1433  else
1434  {
1435  gtk_widget_set_sensitive (GTK_WIDGET(gtk_builder_get_object (builder, "transactions")), FALSE);
1436  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "trans_ro")));
1437  }
1438 
1439  // setup subaccount account selector
1440  adopter_init (&adopt->subacct,
1441  gppat_setup_account_selector (builder, dialog,
1442  "sa_mas_hbox",
1443  DELETE_DIALOG_SA_MAS),
1444  account, TRUE);
1445 
1446  // setup subaccount transaction selector
1447  adopter_init (&adopt->subtrans,
1448  gppat_setup_account_selector (builder, dialog,
1449  "sa_trans_mas_hbox",
1450  DELETE_DIALOG_SA_TRANS_MAS),
1451  account_subaccount (account), FALSE);
1452  g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_SA_TRANS,
1453  GTK_WIDGET(gtk_builder_get_object (builder, "subaccount_trans")));
1454 
1455  if (gnc_account_n_children(account) > 0)
1456  {
1457  // Check for RO txns in descendants
1458  gnc_account_foreach_descendant_until(account, delete_account_helper,
1459  &adopt->delete_res);
1460  if (adopt->delete_res.has_splits)
1461  {
1462  if (adopt->delete_res.has_ro_splits)
1463  {
1464  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_rw")));
1465  widget = GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_drb"));
1466  gtk_widget_set_sensitive(widget, FALSE);
1467  }
1468  else
1469  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_ro")));
1470 
1471  g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_SA_SPLITS, GINT_TO_POINTER(1));
1472  }
1473  else
1474  {
1475  g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_SA_SPLITS, GINT_TO_POINTER(0));
1476  gtk_widget_set_sensitive (GTK_WIDGET(gtk_builder_get_object (builder, "subaccount_trans")), FALSE);
1477  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_ro")));
1478  }
1479  }
1480  else
1481  {
1482  gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object (builder, "subaccounts")), FALSE);
1483  gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object (builder, "subaccount_trans")), FALSE);
1484  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_ro")));
1485  }
1486 
1487  /* default to cancel */
1488  gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1489 
1490  gtk_builder_connect_signals(builder, dialog);
1491  g_object_unref(G_OBJECT(builder));
1492 
1493  return dialog;
1494 }
1495 
1496 static void
1497 gnc_plugin_page_account_tree_cmd_delete_account (GSimpleAction *simple,
1498  GVariant *paramter,
1499  gpointer user_data)
1500 {
1501  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1503  gchar *acct_name;
1504  GtkWidget *window;
1505  Adopters adopt;
1506  GList* list;
1507  gint response;
1508  GtkWidget *dialog = NULL;
1509 
1510  if (account == NULL)
1511  return;
1512 
1513  memset (&adopt, 0, sizeof (adopt));
1514  /* If the account has objects referring to it, show the list - the account can't be deleted until these
1515  references are dealt with. */
1516  list = qof_instance_get_referring_object_list(QOF_INSTANCE(account));
1517  if (list != NULL)
1518  {
1519 #define EXPLANATION _("The list below shows objects which make use of the account which you want to delete.\nBefore you can delete it, you must either delete those objects or else modify them so they make use\nof another account")
1520 
1521  gnc_ui_object_references_show(EXPLANATION, list);
1522  g_list_free(list);
1523  return;
1524  }
1525 
1526  window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1527  acct_name = gnc_account_get_full_name(account);
1528  if (!acct_name)
1529  acct_name = g_strdup (_("(no name)"));
1530 
1531  if (gnc_account_n_children(account) > 1) {
1532  gchar* message = g_strdup_printf(_("The account \"%s\" has more than one subaccount.\n\nMove the subaccounts or delete them before attempting to delete this account."), acct_name);
1533  gnc_error_dialog(GTK_WINDOW(window),"%s", message);
1534  g_free (message);
1535  g_free(acct_name);
1536  return;
1537  }
1538 
1539  // If no transaction or children just delete it.
1540  if (xaccAccountGetSplits (account).empty() && gnc_account_n_children (account) == 0)
1541  {
1542  do_delete_account (account, NULL, NULL, NULL);
1543  return;
1544  }
1545 
1546  dialog = account_delete_dialog (account, GTK_WINDOW (window), &adopt);
1547 
1548  while (TRUE)
1549  {
1550  response = gtk_dialog_run(GTK_DIALOG(dialog));
1551 
1552  if (response != GTK_RESPONSE_ACCEPT)
1553  {
1554  gtk_widget_destroy(dialog);
1555  return;
1556  }
1557  adopter_set_account_and_match (&adopt.trans);
1558  adopter_set_account_and_match (&adopt.subacct);
1559  adopter_set_account_and_match (&adopt.subtrans);
1560 
1561  if (adopter_match (&adopt.trans, GTK_WINDOW (window)) &&
1562  adopter_match (&adopt.subacct, GTK_WINDOW (window)) &&
1563  adopter_match (&adopt.subtrans, GTK_WINDOW (window)))
1564  break;
1565  }
1566  gtk_widget_destroy(dialog);
1567  if (confirm_delete_account (simple, page, adopt.trans.new_account,
1568  adopt.subtrans.new_account,
1569  adopt.subacct.new_account,
1570  adopt.delete_res) == GTK_RESPONSE_ACCEPT)
1571  {
1572  do_delete_account (account, adopt.subacct.new_account,
1573  adopt.subtrans.new_account, adopt.trans.new_account);
1574  }
1575 }
1576 
1577 static int
1578 confirm_delete_account (GSimpleAction *simple, GncPluginPageAccountTree *page,
1579  Account* ta, Account* sta, Account* saa,
1580  delete_helper_t delete_res)
1581 {
1583  GtkWidget* window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1584  gint response;
1585 
1586  char *lines[6] = {0};
1587  char *message;
1588  int i = 0;
1589  GtkWidget *dialog;
1590  gchar* acct_name = gnc_account_get_full_name(account);
1591 
1592  lines[i] = g_strdup_printf (_("The account %s will be deleted."),
1593  acct_name);
1594  g_free(acct_name);
1595 
1596  if (!xaccAccountGetSplits (account).empty())
1597  {
1598  if (ta)
1599  {
1600  char *name = gnc_account_get_full_name(ta);
1601  lines[++i] = g_strdup_printf (_("All transactions in this account "
1602  "will be moved to the account %s."),
1603  name);
1604  g_free (name);
1605  }
1606  else
1607  {
1608  lines[++i] = g_strdup (_("All transactions in this account "
1609  "will be deleted."));
1610  }
1611  }
1612  if (gnc_account_n_children(account))
1613  {
1614  if (saa)
1615  {
1616  char *name = gnc_account_get_full_name(saa);
1617  lines[++i] = g_strdup_printf (_("Its sub-account will be "
1618  "moved to the account %s."), name);
1619  g_free (name);
1620  }
1621  else
1622  {
1623  lines[++i] = g_strdup (_("Its subaccount will be deleted."));
1624  if (sta)
1625  {
1626  char *name = gnc_account_get_full_name(sta);
1627  lines[++i] = g_strdup_printf (_("All sub-account transactions "
1628  "will be moved to the "
1629  "account %s."), name);
1630  g_free (name);
1631  }
1632  else if (delete_res.has_splits)
1633  {
1634  lines[++i] = g_strdup(_("All sub-account transactions "
1635  "will be deleted."));
1636  }
1637  }
1638  }
1639 
1640  lines[++i] = _("Are you sure you want to do this?");
1641 
1642  message = g_strjoinv(" ", lines);
1643  for (int j = 0; j < i; ++j) // Don't try to free the last one, it's const.
1644  g_free (lines[j]);
1645 
1646  dialog = gtk_message_dialog_new(GTK_WINDOW(window),
1647  GTK_DIALOG_DESTROY_WITH_PARENT,
1648  GTK_MESSAGE_QUESTION,
1649  GTK_BUTTONS_NONE,
1650  "%s", message);
1651  g_free(message);
1652  gtk_dialog_add_buttons(GTK_DIALOG(dialog),
1653  _("_Cancel"), GTK_RESPONSE_CANCEL,
1654  _("_Delete"), GTK_RESPONSE_ACCEPT,
1655  (gchar *)NULL);
1656  gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1657  response = gtk_dialog_run(GTK_DIALOG(dialog));
1658  gtk_widget_destroy(dialog);
1659  return response;
1660 }
1661 
1662 void
1663 do_delete_account (Account* account, Account* saa, Account* sta, Account* ta)
1664 {
1665  GList *acct_list, *ptr;
1666  const GncGUID *guid;
1667  gchar guidstr[GUID_ENCODING_LENGTH+1];
1668 
1669  gnc_set_busy_cursor(NULL, TRUE);
1670  gnc_suspend_gui_refresh ();
1671 
1672  /* Move subaccounts and transactions if this was requested */
1673  xaccAccountBeginEdit (account);
1674  if (saa)
1675  {
1676  xaccAccountBeginEdit (saa);
1677  acct_list = gnc_account_get_children(account);
1678  for (ptr = acct_list; ptr; ptr = g_list_next(ptr))
1679  gnc_account_append_child (saa, GNC_ACCOUNT(ptr->data));
1680  g_list_free(acct_list);
1681  xaccAccountCommitEdit (saa);
1682  }
1683  else if (sta)
1684  {
1685  /* Move the splits of its subaccounts, if any. */
1687  (AccountCb)xaccAccountMoveAllSplits,
1688  sta);
1689  }
1690  if (ta)
1691  {
1692  /* Move the splits of the account to be deleted. */
1693  xaccAccountMoveAllSplits (account, ta);
1694  }
1695  xaccAccountCommitEdit (account);
1696 
1697  /* Drop all references from the state file for
1698  * any subaccount the account still has
1699  */
1700  acct_list = gnc_account_get_children(account);
1701  for (ptr = acct_list; ptr; ptr = g_list_next(ptr))
1702  {
1703  guid = xaccAccountGetGUID (ptr->data);
1704  guid_to_string_buff (guid, guidstr);
1705  gnc_state_drop_sections_for (guidstr);
1706  }
1707  g_list_free(acct_list);
1708 
1709  /* Drop all references from the state file for this account
1710  */
1711  guid = xaccAccountGetGUID (account);
1712  guid_to_string_buff (guid, guidstr);
1713  gnc_state_drop_sections_for (guidstr);
1714 
1715  /*
1716  * Finally, delete the account, any subaccounts it may still
1717  * have, and any splits it or its subaccounts may still have.
1718  */
1719  xaccAccountBeginEdit (account);
1720  xaccAccountDestroy (account);
1721  gnc_resume_gui_refresh ();
1722  gnc_unset_busy_cursor(NULL);
1723 }
1724 
1725 static void
1726 gnc_plugin_page_account_tree_cmd_renumber_accounts (GSimpleAction *simple,
1727  GVariant *paramter,
1728  gpointer user_data)
1729 {
1730  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1731  Account *account;
1732  GtkWidget *window;
1733 
1734  window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1736  if (!window || !account)
1737  return;
1738 
1739  gnc_account_renumber_create_dialog(window, account);
1740 }
1741 
1742 static void
1743 gnc_plugin_page_account_tree_cmd_refresh (GSimpleAction *simple,
1744  GVariant *paramter,
1745  gpointer user_data)
1746 {
1747  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1749 
1750  g_return_if_fail(GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(page));
1751 
1752  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
1753 
1754  gnc_tree_view_account_clear_model_cache (GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
1755  gtk_widget_queue_draw (priv->widget);
1756 }
1757 
1758 /*********************/
1759 
1760 static void
1761 gnc_plugin_page_account_tree_cmd_view_filter_by (GSimpleAction *simple,
1762  GVariant *paramter,
1763  gpointer user_data)
1764 {
1765  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1767 
1768  g_return_if_fail(GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(page));
1769  ENTER("(action %p, page %p)", simple, page);
1770 
1771  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
1772  account_filter_dialog_create(&priv->fd, GNC_PLUGIN_PAGE(page));
1773  LEAVE(" ");
1774 }
1775 
1776 static void
1777 gnc_plugin_page_account_tree_cmd_reconcile (GSimpleAction *simple,
1778  GVariant *paramter,
1779  gpointer user_data)
1780 {
1781  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1782  GtkWidget *window;
1783  Account *account;
1784  RecnWindow *recnData;
1785 
1787  g_return_if_fail (account != NULL);
1788 
1789  /* To prevent mistakes involving saving an edited transaction after
1790  * finishing a reconciliation (reverting the reconcile state), we could look
1791  * at all open registers and determine if any of them have a transaction
1792  * being edited that involves the account to be reconciled.
1793  *
1794  * However, the reconcile window isn't modal so it's still possible to start
1795  * editing a transaction after opening it. Assume the user knows what
1796  * they're doing if they start a reconciliation from the account tree and
1797  * don't attempt to stop them.
1798  */
1799 
1800  window = GNC_PLUGIN_PAGE (page)->window;
1801  recnData = recnWindow (window, account);
1802  gnc_ui_reconcile_window_raise (recnData);
1803 }
1804 
1805 static void
1806 gnc_plugin_page_account_tree_cmd_autoclear (GSimpleAction *simple,
1807  GVariant *paramter,
1808  gpointer user_data)
1809 {
1810  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1811  GtkWidget *window;
1812  Account *account;
1813  AutoClearWindow *autoClearData;
1814 
1816  g_return_if_fail (account != NULL);
1817 
1818  window = GNC_PLUGIN_PAGE (page)->window;
1819  autoClearData = autoClearWindow (window, account);
1820  gnc_ui_autoclear_window_raise (autoClearData);
1821 }
1822 
1823 static void
1824 gnc_plugin_page_account_tree_cmd_transfer (GSimpleAction *simple,
1825  GVariant *paramter,
1826  gpointer user_data)
1827 {
1828  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1829  GtkWidget *window;
1830  Account *account;
1831 
1833  window = GNC_PLUGIN_PAGE (page)->window;
1834  gnc_xfer_dialog (window, account);
1835 }
1836 
1837 static void
1838 gnc_plugin_page_account_tree_cmd_stock_split (GSimpleAction *simple,
1839  GVariant *paramter,
1840  gpointer user_data)
1841 {
1842  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1843  GtkWidget *window;
1844  Account *account;
1845 
1847  window = GNC_PLUGIN_PAGE (page)->window;
1848  gnc_stock_split_dialog (window, account);
1849 }
1850 
1851 static void
1852 gnc_plugin_page_account_tree_cmd_stock_assistant (GSimpleAction *simple,
1853  GVariant *paramter,
1854  gpointer user_data)
1855 {
1856  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1857  Account *account;
1858  GtkWidget *window;
1859 
1860  ENTER ("(action %p, page %p)", simple, page);
1861 
1863  window = GNC_PLUGIN_PAGE(page)->window;
1864  gnc_stock_transaction_assistant (window, account);
1865 
1866  LEAVE (" ");
1867 }
1868 
1869 static void
1870 gnc_plugin_page_account_tree_cmd_edit_tax_options (GSimpleAction *simple,
1871  GVariant *paramter,
1872  gpointer user_data)
1873 {
1874  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1875  GtkWidget *window;
1876  Account *account;
1877 
1879  window = GNC_PLUGIN_PAGE (page)->window;
1880  gnc_tax_info_dialog (window, account);
1881 }
1882 
1883 static void
1884 gnc_plugin_page_account_tree_cmd_lots (GSimpleAction *simple,
1885  GVariant *paramter,
1886  gpointer user_data)
1887 {
1888  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1890  GtkWidget *window = GNC_PLUGIN_PAGE (page)->window;
1891  gnc_lot_viewer_dialog (GTK_WINDOW(window), account);
1892 }
1893 
1894 static gboolean
1895 scrub_kp_handler (GtkWidget *widget, GdkEventKey *event, gpointer data)
1896 {
1897  if (event->length == 0) return FALSE;
1898 
1899  switch (event->keyval)
1900  {
1901  case GDK_KEY_Escape:
1902  {
1903  gboolean abort_scrub = gnc_verify_dialog (GTK_WINDOW(widget), FALSE,
1904  "%s", _(check_repair_abort_YN));
1905 
1906  if (abort_scrub)
1907  gnc_set_abort_scrub (TRUE);
1908 
1909  return TRUE;
1910  }
1911  default:
1912  break;
1913  }
1914  return FALSE;
1915 }
1916 
1917 static void
1918 gnc_plugin_page_account_tree_cmd_scrub (GSimpleAction *simple,
1919  GVariant *paramter,
1920  gpointer user_data)
1921 {
1922  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1924  GncWindow *window;
1925  gulong scrub_kp_handler_ID;
1926 
1927  g_return_if_fail (account != NULL);
1928 
1929  prepare_scrubbing ();
1930 
1931  window = GNC_WINDOW(GNC_PLUGIN_PAGE (page)->window);
1932  scrub_kp_handler_ID = g_signal_connect (G_OBJECT(window), "key-press-event",
1933  G_CALLBACK(scrub_kp_handler), NULL);
1934  gnc_window_set_progressbar_window (window);
1935 
1936  xaccAccountScrubOrphans (account, gnc_window_show_progress);
1937  xaccAccountScrubImbalance (account, gnc_window_show_progress);
1938 
1939  // XXX: Lots/capital gains scrubbing is disabled
1940  if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
1941  xaccAccountScrubLots(account);
1942 
1943  gncScrubBusinessAccount(account, gnc_window_show_progress);
1944 
1945  finish_scrubbing (window, scrub_kp_handler_ID);
1946 }
1947 
1948 static void
1949 gnc_plugin_page_account_tree_cmd_scrub_sub (GSimpleAction *simple,
1950  GVariant *paramter,
1951  gpointer user_data)
1952 {
1953  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1955  GncWindow *window;
1956  gulong scrub_kp_handler_ID;
1957 
1958  g_return_if_fail (account != NULL);
1959 
1960  prepare_scrubbing ();
1961 
1962  window = GNC_WINDOW(GNC_PLUGIN_PAGE (page)->window);
1963  scrub_kp_handler_ID = g_signal_connect (G_OBJECT(window), "key-press-event",
1964  G_CALLBACK(scrub_kp_handler), NULL);
1965  gnc_window_set_progressbar_window (window);
1966 
1967  xaccAccountTreeScrubOrphans (account, gnc_window_show_progress);
1968  xaccAccountTreeScrubImbalance (account, gnc_window_show_progress);
1969 
1970  // XXX: Lots/capital gains scrubbing is disabled
1971  if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
1972  xaccAccountTreeScrubLots(account);
1973 
1974  gncScrubBusinessAccountTree(account, gnc_window_show_progress);
1975 
1976  finish_scrubbing (window, scrub_kp_handler_ID);
1977 }
1978 
1979 static void
1980 gnc_plugin_page_account_tree_cmd_scrub_all (GSimpleAction *simple,
1981  GVariant *paramter,
1982  gpointer user_data)
1983 {
1984  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1985  Account *root = gnc_get_current_root_account ();
1986  GncWindow *window;
1987  gulong scrub_kp_handler_ID;
1988 
1989  prepare_scrubbing ();
1990 
1991  window = GNC_WINDOW(GNC_PLUGIN_PAGE (page)->window);
1992  scrub_kp_handler_ID = g_signal_connect (G_OBJECT(window), "key-press-event",
1993  G_CALLBACK(scrub_kp_handler), NULL);
1994  gnc_window_set_progressbar_window (window);
1995 
1996  xaccAccountTreeScrubOrphans (root, gnc_window_show_progress);
1997  xaccAccountTreeScrubImbalance (root, gnc_window_show_progress);
1998  // XXX: Lots/capital gains scrubbing is disabled
1999  if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
2000  xaccAccountTreeScrubLots(root);
2001 
2002  gncScrubBusinessAccountTree(root, gnc_window_show_progress);
2003 
2004  finish_scrubbing (window, scrub_kp_handler_ID);
2005 }
2006 
Account * gnc_account_get_parent(const Account *acc)
This routine returns a pointer to the parent of the specified account.
Definition: Account.cpp:2877
GncPluginPage * gnc_plugin_page_register_new(Account *account, gboolean subaccounts)
Create a new "register" plugin page, given a pointer to an account.
Account * gnc_plugin_page_account_tree_get_current_account(GncPluginPageAccountTree *page)
Given a pointer to an account tree plugin page, return the selected account (if any).
Functions to load, save and get gui state.
High-Level API for imposing Lot constraints.
GtkWidget * gnc_plugin_page_get_window(GncPluginPage *page)
Retrieve a pointer to the GncMainWindow (GtkWindow) containing this page.
const gchar * tab_icon
The relative name of the icon that should be shown on the tab for this page.
gboolean(* focus_page_function)(GncPluginPage *plugin_page)
This function performs specific actions to set the focus on a specific widget.
void gnc_main_window_update_menu_and_toolbar(GncMainWindow *window, GncPluginPage *page, const gchar **ui_updates)
Update the main window menu with the placeholders listed in ui_updates and load the page specific too...
void gnc_account_append_child(Account *new_parent, Account *child)
This function will remove from the child account any pre-existing parent relationship, and will then add the account as a child of the new parent.
Definition: Account.cpp:2776
The instance data structure for a content plugin.
const GList * gnc_gobject_tracking_get_list(const gchar *name)
Get a list of all known objects of a specified type.
gboolean gnc_main_window_button_press_cb(GtkWidget *whatever, GdkEventButton *event, GncPluginPage *page)
Callback function invoked when the user clicks in the content of any Gnucash window.
gulong gnc_prefs_register_cb(const char *group, const gchar *pref_name, gpointer func, gpointer user_data)
Register a callback that gets triggered when the given preference changes.
Definition: gnc-prefs.c:128
void gnc_plugin_page_account_tree_open(Account *account, GtkWindow *win)
Given a pointer to an account, the account tree will open and the account will be selected (if any)...
This file contains the functions to present a gui to the user for creating a new account or editing a...
void gnc_account_foreach_descendant(const Account *acc, AccountCb thunk, gpointer user_data)
This method will traverse all children of this accounts and their descendants, calling &#39;func&#39; on each...
Definition: Account.cpp:3197
GncPluginPage * gnc_plugin_page_account_tree_new(void)
Create a new "account tree" plugin page.
void gnc_tree_view_account_set_editing_finished_cb(GncTreeViewAccount *view, GFunc editing_finished_cb, gpointer editing_cb_data)
Setup the callback for when the user finishes editing the account tree so actions can be enabled like...
gboolean xaccAccountIsPriced(const Account *acc)
Returns true if the account is a stock, mutual fund or currency, otherwise false. ...
Definition: Account.cpp:4640
gboolean gnc_get_ongoing_scrub(void)
The gnc_get_ongoing_scrub () method returns TRUE if a scrub operation is ongoing. ...
Definition: Scrub.cpp:86
utility functions for the GnuCash UI
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3228
GncPluginPage *(* recreate_page)(GtkWidget *window, GKeyFile *file, const gchar *group)
Create a new page based on the information saved during a previous instantiation of gnucash...
STRUCTS.
gint gnc_state_drop_sections_for(const gchar *partial_name)
Drop all sections from the state file whose name contains partial_name.
Definition: gnc-state.c:260
void gnc_main_window_init_short_names(GncMainWindow *window, GncToolBarShortNames *toolbar_labels)
Update the labels of the toolbar items with short names.
const char * xaccTransGetReadOnly(Transaction *trans)
Returns a non-NULL value if this Transaction was marked as read-only with some specific "reason" text...
Functions that are supported by all types of windows.
GSimpleActionGroup * gnc_plugin_page_get_action_group(GncPluginPage *page)
Retrieve the GSimpleActionGroup object associated with this page.
gpointer gnc_account_foreach_descendant_until(const Account *acc, AccountCb2 thunk, gpointer user_data)
This method will traverse all children of this accounts and their descendants, calling &#39;func&#39; on each...
Definition: Account.cpp:3205
GtkWidget * window
The window that contains the display widget for this plugin.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
GSimpleActionGroup * gnc_plugin_page_create_action_group(GncPluginPage *page, const gchar *group_name)
Create the GSimpleActionGroup object associated with this page.
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
Definition: guid.cpp:173
void xaccAccountScrubLots(Account *acc)
The xaccAccountScrubLots() routine makes sure that every split in the account is assigned to a lot...
Definition: Scrub3.cpp:159
void xaccAccountMoveAllSplits(Account *accfrom, Account *accto)
The xaccAccountMoveAllSplits() routine reassigns each of the splits in accfrom to accto...
Definition: Account.cpp:2198
void gnc_tree_view_account_set_editing_started_cb(GncTreeViewAccount *view, GFunc editing_started_cb, gpointer editing_cb_data)
Setup the callback for when the user starts editing the account tree so actions can be disabled like ...
GtkTreeViewColumn * gnc_tree_view_find_column_by_name(GncTreeView *view, const gchar *wanted)
Find a tree column given the "pref name" used with saved state.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
Cleanup functions for business objects.
GKeyFile * gnc_state_get_current(void)
Returns a pointer to the most recently loaded state.
Definition: gnc-state.c:248
This file contains the functions to present a dialog box with a list of object references and an expl...
void gnc_ui_edit_account_window(GtkWindow *parent, Account *account)
Display a window for editing the attributes of an existing account.
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
Display a data plugin page in a window.
void(* destroy_widget)(GncPluginPage *plugin_page)
Function called to destroy the display widget for a particular type of plugin.
void xaccAccountDestroy(Account *acc)
The xaccAccountDestroy() routine can be used to get rid of an account.
Definition: Account.cpp:1593
#define xaccAccountGetGUID(X)
Definition: Account.h:248
void gnc_set_abort_scrub(gboolean abort)
The gnc_set_abort_scrub () method causes a currently running scrub operation to stop, if abort is TRUE; gnc_set_abort_scrub(FALSE) must be called before any scrubbing operation.
Definition: Scrub.cpp:74
convert single-entry accounts to clean double-entry
GAction * gnc_main_window_find_action_in_group(GncMainWindow *window, const gchar *group_name, const gchar *action_name)
Find the GAction in a specific action group for window.
void gnc_tree_view_account_set_filter(GncTreeViewAccount *view, gnc_tree_view_account_filter_func func, gpointer data, GSourceFunc destroy)
This function attaches a filter function to the given account tree.
gchar * gnc_account_get_full_name(const Account *account)
The gnc_account_get_full_name routine returns the fully qualified name of the account using the given...
Definition: Account.cpp:3257
Functions providing a register page for the GnuCash UI.
The class data structure for a content plugin.
void gnc_tree_view_account_refilter(GncTreeViewAccount *view)
This function forces the account tree filter to be evaluated.
Gobject helper routines.
GtkTreeView implementation for gnucash account tree.
Account public routines (C++ api)
GAction * gnc_main_window_find_action(GncMainWindow *window, const gchar *action_name)
Find the GAction in the main window.
void xaccAccountTreeScrubOrphans(Account *acc, QofPercentageFunc percentagefunc)
The xaccAccountTreeScrubOrphans() method performs this scrub for the indicated account and its childr...
Definition: Scrub.cpp:178
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition: guid.h:84
void gnc_plugin_page_disconnect_page_changed(GncPluginPage *page)
Disconnect the page_changed_id signal callback.
GtkTreeView * gnc_tree_view_account_new(gboolean show_root)
Create a new account tree view.
void gnc_tree_view_configure_columns(GncTreeView *view)
Make all the correct columns visible, respecting their default visibility setting, their "always" visibility setting, and the last saved state if available.
gboolean gnc_plugin_page_account_tree_filter_accounts(Account *account, gpointer user_data)
This function tells the account tree view whether or not to filter out a particular account...
const gchar * plugin_name
The textual name of this plugin.
void gncScrubBusinessAccount(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccount() function will call all scrub functions relevant for a given account on ...
void gnc_tree_view_account_clear_model_cache(GncTreeViewAccount *view)
This function clears the tree model account cache so the values will be updated/refreshed.
GtkWidget *(* create_widget)(GncPluginPage *plugin_page)
Function called to create the display widget for a particular type of plugin.
Account * gnc_tree_view_account_get_account_from_path(GncTreeViewAccount *view, GtkTreePath *s_path)
This function returns the account associated with the specified path.
void xaccAccountScrubOrphans(Account *acc, QofPercentageFunc percentagefunc)
The xaccAccountScrubOrphans() method performs this scrub only for the indicated account, and not for any of its children.
Definition: Scrub.cpp:172
void gnc_plugin_set_actions_enabled(GActionMap *action_map, const gchar **action_names, gboolean enable)
This function sets the sensitivity of a GAction in a specific group.
Definition: gnc-plugin.c:250
Gnome specific utility functions.
gint gnc_account_n_children(const Account *account)
Return the number of children of the specified account.
Definition: Account.cpp:2924
gboolean(* finish_pending)(GncPluginPage *plugin_page)
This function vector is called to finish any outstanding activities.
All type declarations for the whole Gnucash engine.
void gnc_ui_new_account_window(GtkWindow *parent, QofBook *book, Account *parent_acct)
Display a window for creating a new account.
void(* save_page)(GncPluginPage *page, GKeyFile *file, const gchar *group)
Save enough information about this page so that it can be recreated next time the user starts gnucash...
GtkTreeModel implementation to display account types in a GtkTreeView.
GList * qof_instance_get_referring_object_list(const QofInstance *inst)
Returns a list of objects which refer to a specific object.
const gchar * gnc_tree_view_get_state_section(GncTreeView *view)
Get the name of the state section this tree view is associated with.
GLib helper routines.
Generic api to store and retrieve preferences.
GList * gnc_account_get_descendants(const Account *account)
This routine returns a flat list of all of the accounts that are descendants of the specified account...
Definition: Account.cpp:3005
Functions providing a chart of account page.
void gnc_tree_view_account_set_selected_account(GncTreeViewAccount *view, Account *account)
This function selects an account in the account tree view.
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition: qofbook.cpp:497
A structure for defining alternate action names for use in the toolbar.
GList * gnc_account_get_children(const Account *account)
This routine returns a GList of all children accounts of the specified account.
Definition: Account.cpp:2908
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition: Account.cpp:1479
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3413
gboolean xaccAccountGetPlaceholder(const Account *acc)
Get the "placeholder" flag for an account.
Definition: Account.cpp:4195
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
Provide the menus to create a chart of account page.
void gnc_plugin_page_inserted_cb(GncPluginPage *page, gpointer user_data)
Set up the page_changed callback for when the current page is changed.
void gnc_main_window_close_page(GncPluginPage *page)
Remove a data plugin page from a window and display the previous page.
Account * gnc_tree_view_account_get_selected_account(GncTreeViewAccount *view)
This function returns the account associated with the selected item in the account tree view...
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
void gnc_plugin_page_add_book(GncPluginPage *page, QofBook *book)
Add a book reference to the specified page.
void gncScrubBusinessAccountTree(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccountTreeLots() function will call gncScrubBusinessAccount() on the given accou...
gint gnc_list_length_cmp(const GList *list, size_t len)
Scans the GList elements the minimum number of iterations required to test it against a specified siz...
API for Transactions and Splits (journal entries)
The type used to store guids in C.
Definition: guid.h:75
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition: Account.cpp:1520
GtkWidget * summarybar
The summary bar widget (if any) that is associated with this plugin.
void gnc_prefs_remove_cb_by_func(const gchar *group, const gchar *pref_name, gpointer func, gpointer user_data)
Remove a function that was registered for a callback when the given preference changed.
Definition: gnc-prefs.c:143