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