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  gnc_prefs_remove_cb_by_func(GNC_PREFS_GROUP_ACCT_SUMMARY, GNC_PREF_START_CHOICE_ABS,
758  (gpointer)accounting_period_changed_cb, page);
759  gnc_prefs_remove_cb_by_func(GNC_PREFS_GROUP_ACCT_SUMMARY, GNC_PREF_START_DATE,
760  (gpointer)accounting_period_changed_cb, page);
761  gnc_prefs_remove_cb_by_func(GNC_PREFS_GROUP_ACCT_SUMMARY, GNC_PREF_START_PERIOD,
762  (gpointer)accounting_period_changed_cb, page);
763  gnc_prefs_remove_cb_by_func(GNC_PREFS_GROUP_ACCT_SUMMARY, GNC_PREF_END_CHOICE_ABS,
764  (gpointer)accounting_period_changed_cb, page);
765  gnc_prefs_remove_cb_by_func(GNC_PREFS_GROUP_ACCT_SUMMARY, GNC_PREF_END_DATE,
766  (gpointer)accounting_period_changed_cb, page);
767  gnc_prefs_remove_cb_by_func(GNC_PREFS_GROUP_ACCT_SUMMARY, GNC_PREF_END_PERIOD,
768  (gpointer)accounting_period_changed_cb, page);
769 
770 // Save account filter state information to account section
771  gnc_tree_view_account_save_filter (GNC_TREE_VIEW_ACCOUNT(priv->tree_view), &priv->fd,
772  gnc_state_get_current(), gnc_tree_view_get_state_section (GNC_TREE_VIEW(priv->tree_view)));
773 
774  // Destroy the filter override hash table
775  g_hash_table_destroy(priv->fd.filter_override);
776 
777  // Remove the page_changed signal callback
778  gnc_plugin_page_disconnect_page_changed (GNC_PLUGIN_PAGE(plugin_page));
779 
780  // Remove the page focus idle function if present
781  g_idle_remove_by_data (plugin_page);
782 
783  if (priv->widget)
784  {
785  g_object_unref(G_OBJECT(priv->widget));
786  priv->widget = NULL;
787  }
788 
789  if (priv->component_id)
790  {
791  gnc_unregister_gui_component(priv->component_id);
792  priv->component_id = 0;
793  }
794 
795  LEAVE("widget destroyed");
796 }
797 
798 static void
799 update_inactive_actions (GncPluginPage *plugin_page)
800 {
802  GSimpleActionGroup *simple_action_group = NULL;
803  Account *account = NULL;
804  gboolean allow_write = !qof_book_is_readonly (gnc_get_current_book());
805  gboolean has_account = FALSE;
806  gboolean subaccounts = FALSE;
807 
808  g_return_if_fail (plugin_page && GNC_IS_PLUGIN_PAGE(plugin_page));
809 
810  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE (plugin_page);
811 
812  if (gtk_tree_view_get_selection (priv->tree_view))
813  {
814  account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
815  has_account = (account != NULL);
816  subaccounts = (account && gnc_account_n_children (account) != 0);
817  /* Check here for placeholder accounts, etc. */
818  }
819 
820  /* Get the action group */
821  simple_action_group = gnc_plugin_page_get_action_group (plugin_page);
822  g_return_if_fail (G_IS_SIMPLE_ACTION_GROUP (simple_action_group));
823 
824  /* Set the action's sensitivity */
825  gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), readonly_inactive_actions,
826  allow_write);
827  gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), actions_requiring_account_rw,
828  allow_write && has_account);
829  gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), actions_requiring_account_always,
830  has_account);
831  gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), actions_requiring_subaccounts_rw,
832  allow_write && subaccounts);
833  gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), actions_requiring_priced_account,
834  account && xaccAccountIsPriced (account));
835 
836  g_signal_emit (plugin_page, plugin_page_signals[ACCOUNT_SELECTED], 0, account);
837 }
838 
843 static void
844 gnc_plugin_page_account_tree_selected (GObject *object, gpointer user_data)
845 {
846  GncPluginPage *plugin_page = GNC_PLUGIN_PAGE (object);
847  g_return_if_fail (GNC_IS_PLUGIN_PAGE (plugin_page));
848  update_inactive_actions(plugin_page);
849 }
850 
860 static void
861 gnc_plugin_page_account_tree_save_page (GncPluginPage *plugin_page,
862  GKeyFile *key_file,
863  const gchar *group_name)
864 {
865  GncPluginPageAccountTree *account_page;
867 
868  g_return_if_fail (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(plugin_page));
869  g_return_if_fail (key_file != NULL);
870  g_return_if_fail (group_name != NULL);
871 
872  ENTER("page %p, key_file %p, group_name %s", plugin_page, key_file,
873  group_name);
874 
875  account_page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(plugin_page);
876  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(account_page);
877 
878  gnc_tree_view_account_save(GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
879  &priv->fd, key_file, group_name);
880  LEAVE(" ");
881 }
882 
883 
884 
894 static GncPluginPage *
895 gnc_plugin_page_account_tree_recreate_page (GtkWidget *window,
896  GKeyFile *key_file,
897  const gchar *group_name)
898 {
899  GncPluginPageAccountTree *account_page;
901  GncPluginPage *page;
902 
903  g_return_val_if_fail(key_file, NULL);
904  g_return_val_if_fail(group_name, NULL);
905  ENTER("key_file %p, group_name %s", key_file, group_name);
906 
907  /* Create the new page. */
909  account_page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(page);
910  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(account_page);
911 
912  /* Install it now so we can then manipulate the created widget */
913  gnc_main_window_open_page(GNC_MAIN_WINDOW(window), page);
914 
915  gnc_tree_view_account_restore(GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
916  &priv->fd, key_file, group_name);
917  LEAVE(" ");
918  return page;
919 }
920 
921 
922 /* Callbacks */
923 
924 static void
925 gnc_plugin_page_account_tree_summarybar_position_changed (gpointer prefs,
926  gchar* pref,
927  gpointer user_data)
928 {
929  GncPluginPage *plugin_page;
932  GtkPositionType position = GTK_POS_BOTTOM;
933 
934  g_return_if_fail(user_data != NULL);
935 
936  plugin_page = GNC_PLUGIN_PAGE(user_data);
937  page = GNC_PLUGIN_PAGE_ACCOUNT_TREE (user_data);
938  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
939 
940  if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SUMMARYBAR_POSITION_TOP))
941  position = GTK_POS_TOP;
942 
943  gtk_box_reorder_child(GTK_BOX(priv->widget),
944  plugin_page->summarybar,
945  (position == GTK_POS_TOP ? 0 : -1) );
946 }
947 
955 static gboolean
956 gnc_plugin_page_account_tree_button_press_cb (GtkWidget *widget,
957  GdkEventButton *event,
958  GncPluginPage *page)
959 {
960 
961  g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
962 
963  ENTER("widget %p, event %p, page %p", widget, event, page);
964  gnc_main_window_button_press_cb(widget, event, page);
965  LEAVE(" ");
966 
967  /* Always return FALSE. This will let the tree view callback run as
968  * well which will select the item under the cursor. By the time
969  * the user sees the menu both callbacks will have run and the menu
970  * actions will operate on the just-selected account. */
971  return FALSE;
972 }
973 
974 static void
975 gppat_open_account_common (GncPluginPageAccountTree *page,
976  Account *account,
977  gboolean include_subs)
978 {
979  GtkWidget *window;
980  GncPluginPage *new_page;
981 
982  if (account == NULL)
983  return;
984 
985  window = GNC_PLUGIN_PAGE (page)->window;
986  new_page = gnc_plugin_page_register_new (account, include_subs);
987  gnc_main_window_open_page (GNC_MAIN_WINDOW(window), new_page);
988 }
989 
990 static void
991 gnc_plugin_page_account_tree_double_click_cb (GtkTreeView *treeview,
992  GtkTreePath *path,
993  GtkTreeViewColumn *col,
995 {
996  GtkTreeModel *model;
997  GtkTreeIter iter;
998 
999  g_return_if_fail (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE (page));
1000  g_return_if_fail (treeview);
1001 
1002  model = gtk_tree_view_get_model(treeview);
1003  if (gtk_tree_model_get_iter(model, &iter, path))
1004  {
1005  Account *account = gnc_tree_view_account_get_account_from_path (GNC_TREE_VIEW_ACCOUNT(treeview), path);
1006  if (xaccAccountGetPlaceholder (account))
1007  {
1008  /* This is a placeholder account. Only only show/hide
1009  * subaccount list if there is one.
1010  */
1011  if (gtk_tree_model_iter_has_child(model, &iter))
1012  {
1013  /* There are children,
1014  * just expand or collapse the row. */
1015  if (gtk_tree_view_row_expanded(treeview, path))
1016  gtk_tree_view_collapse_row(treeview, path);
1017  else
1018  gtk_tree_view_expand_row(treeview, path, FALSE);
1019  }
1020  }
1021  else
1022  {
1023  /* No placeholder account, so open its register */
1024  gppat_open_account_common (page, account, FALSE);
1025  }
1026  }
1027 }
1028 
1029 static void
1030 gnc_plugin_page_account_tree_selection_changed_cb (GtkTreeSelection *selection,
1032 {
1033  GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(page);
1034  update_inactive_actions (plugin_page);
1035 }
1036 
1037 static void
1038 accounting_period_changed_cb (gpointer prefs, gchar *pref, gpointer user_data)
1039 {
1040  gnc_plugin_page_account_tree_cmd_refresh (NULL, NULL, user_data);
1041 }
1042 
1043 /* Command callbacks */
1044 static void
1045 gnc_plugin_page_account_tree_cmd_new_account (GSimpleAction *simple,
1046  GVariant *paramter,
1047  gpointer user_data)
1048 {
1049  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1051  GtkWindow *parent = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE (page)));
1052  gnc_ui_new_account_window (parent, gnc_get_current_book(),
1053  account);
1054 }
1055 
1056 static void
1057 gnc_plugin_page_account_tree_cmd_file_new_hierarchy (GSimpleAction *simple,
1058  GVariant *paramter,
1059  gpointer user_data)
1060 {
1061  gnc_ui_hierarchy_assistant(FALSE);
1062 }
1063 
1064 static void
1065 gnc_plugin_page_account_tree_cmd_open_account (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, FALSE);
1075 }
1076 
1077 static void
1078 gnc_plugin_page_account_tree_cmd_open_subaccounts (GSimpleAction *simple,
1079  GVariant *paramter,
1080  gpointer user_data)
1081 {
1082  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1083  Account *account;
1084 
1085  g_return_if_fail (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE (page));
1087  gppat_open_account_common (page, account, TRUE);
1088 }
1089 
1090 static void
1091 gnc_plugin_page_account_tree_cmd_edit_account (GSimpleAction *simple,
1092  GVariant *paramter,
1093  gpointer user_data)
1094 {
1095  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1096  Account *account;
1097  GtkWindow *parent = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE (page)));
1098  ENTER("action %p, page %p", simple, page);
1099 
1101  g_return_if_fail (account != NULL);
1102 
1103  gnc_ui_edit_account_window (parent, account);
1104  LEAVE(" ");
1105 }
1106 
1107 static void
1108 gnc_plugin_page_account_tree_cmd_find_account (GSimpleAction *simple,
1109  GVariant *paramter,
1110  gpointer user_data)
1111 {
1112  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1113  GtkWidget *window;
1114 
1115  ENTER("action %p, page %p", simple, page);
1116 
1117  window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1118 
1119  gnc_find_account_dialog (window, NULL);
1120  LEAVE(" ");
1121 }
1122 
1123 static void
1124 gnc_plugin_page_account_tree_cmd_find_account_popup (GSimpleAction *simple,
1125  GVariant *paramter,
1126  gpointer user_data)
1127 {
1128  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1129  Account *account = NULL;
1130  GtkWidget *window;
1131 
1132  ENTER("action %p, page %p", simple, page);
1133 
1135 
1136  window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1137 
1138  gnc_find_account_dialog (window, account);
1139  LEAVE(" ");
1140 }
1141 
1142 static void
1143 gnc_plugin_page_account_tree_cmd_cascade_account_properties (GSimpleAction *simple,
1144  GVariant *paramter,
1145  gpointer user_data)
1146 {
1147  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1148  Account *account = NULL;
1149  GtkWidget *window;
1150 
1151  ENTER("action %p, page %p", simple, page);
1152 
1154 
1155  window = gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page));
1156 
1157  if (account != NULL)
1158  gnc_account_cascade_properties_dialog (window, account);
1159 
1160  LEAVE(" ");
1161 }
1162 
1163 static gpointer
1164 delete_account_helper (Account * account, gpointer data)
1165 {
1166  auto helper_res = static_cast<delete_helper_t*>(data);
1167  auto& splits{xaccAccountGetSplits (account)};
1168  auto split_ro = [](auto s) -> bool { return xaccTransGetReadOnly (xaccSplitGetParent (s)); };
1169 
1170  helper_res->has_splits = !splits.empty();
1171  helper_res->has_ro_splits = std::any_of (splits.begin(), splits.end(), split_ro);
1172 
1173  return GINT_TO_POINTER (helper_res->has_splits || helper_res->has_ro_splits);
1174 }
1175 
1176 /***
1177  *** The OK button of a Delete Account dialog is insensitive if
1178  *** and only if a sensitive account selector contains no accounts.
1179  ***/
1180 static void
1181 set_ok_sensitivity(GtkWidget *dialog)
1182 {
1183  gboolean sensitive;
1184 
1185  auto sa_mas = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_SA_MAS));
1186  auto trans_mas = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_TRANS_MAS));
1187 
1188  sensitive = ((!sa_mas ||
1189  !gtk_widget_is_sensitive (sa_mas) ||
1190  gnc_account_sel_get_visible_account_num (GNC_ACCOUNT_SEL (sa_mas))) &&
1191  (!trans_mas ||
1192  !gtk_widget_is_sensitive (trans_mas) ||
1193  gnc_account_sel_get_visible_account_num (GNC_ACCOUNT_SEL (trans_mas))));
1194 
1195  auto button = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_OK_BUTTON));
1196  gtk_widget_set_sensitive(button, sensitive);
1197 }
1198 
1199 static GList *
1200 gppat_get_exclude_list (Account *acc, gboolean exclude_subaccounts)
1201 {
1202  GList *acct_list = NULL;
1203 
1204  if (exclude_subaccounts)
1205  acct_list = gnc_account_get_descendants (acc);
1206 
1207  acct_list = g_list_prepend (acct_list, acc);
1208 
1209  return acct_list;
1210 }
1211 
1212 static void
1213 gppat_populate_gas_list(GtkWidget *dialog,
1214  GNCAccountSel *gas,
1215  gboolean exclude_subaccounts)
1216 {
1217  Account *account;
1218  GList *filter;
1219  GList *exclude;
1220 
1221  g_return_if_fail(GTK_IS_DIALOG(dialog));
1222  if (gas == NULL)
1223  return;
1224  account = GNC_ACCOUNT(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_ACCOUNT));
1225  filter = static_cast<GList*>(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_FILTER));
1226 
1227  /* Setting the account type filter triggers GNCAccountSel population. */
1228  gnc_account_sel_set_acct_filters (gas, filter, NULL);
1229 
1230  /* Accounts to be deleted must be excluded from GAS. */
1231  exclude = gppat_get_exclude_list (account, exclude_subaccounts);
1232  gnc_account_sel_set_acct_exclude_filter (gas, exclude);
1233  g_list_free (exclude);
1234 
1235  gnc_account_sel_set_account (gas, NULL, TRUE);
1236 
1237  /* The sensitivity of the OK button needs to be reevaluated. */
1238  set_ok_sensitivity(dialog);
1239 }
1240 
1241 void
1242 gppat_populate_trans_mas_list(GtkToggleButton *sa_mrb,
1243  GtkWidget *dialog)
1244 {
1245  g_return_if_fail(GTK_IS_DIALOG(dialog));
1246 
1247  /* Cannot move transactions to subaccounts if they are to be deleted. */
1248  auto trans_mas = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_TRANS_MAS));
1249  gppat_populate_gas_list(dialog, GNC_ACCOUNT_SEL(trans_mas), !gtk_toggle_button_get_active(sa_mrb));
1250 }
1251 
1252 /* Note that the emitting object (the toggle button) and the signal data
1253  * are swapped in below callback function. This is a gtkbuilder feature:
1254  * it swaps if you explicitly set an object for a signal handler in the
1255  * gtkbuilder xml file.
1256  */
1257 void
1258 gppat_set_insensitive_iff_rb_active(GtkWidget *widget, GtkToggleButton *b)
1259 {
1260  GtkWidget *dialog = gtk_widget_get_toplevel(widget);
1261  auto subaccount_trans = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_SA_TRANS));
1262  auto sa_mas = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_SA_MAS));
1263  auto have_splits = g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_SA_SPLITS) != nullptr;
1264 
1265  gtk_widget_set_sensitive(widget, !gtk_toggle_button_get_active(b));
1266 
1267  // If we have subaccount splits & delete subaccounts, enable subaccount_trans
1268  if ((have_splits) && !gtk_widget_is_sensitive(sa_mas))
1269  gtk_widget_set_sensitive(subaccount_trans, TRUE);
1270  else
1271  gtk_widget_set_sensitive(subaccount_trans, FALSE);
1272 
1273  set_ok_sensitivity(dialog);
1274 }
1275 
1276 static GtkWidget *
1277 gppat_setup_account_selector (GtkBuilder *builder, GtkWidget *dialog,
1278  const gchar *hbox, const gchar *sel_name)
1279 {
1280  GtkWidget *selector = gnc_account_sel_new();
1281  GtkWidget *box = GTK_WIDGET(gtk_builder_get_object (builder, hbox));
1282 
1283  gtk_box_pack_start (GTK_BOX(box), selector, TRUE, TRUE, 0);
1284 
1285  // placeholder accounts are OK for this GAS
1286  if (g_strcmp0 (sel_name, DELETE_DIALOG_SA_MAS) == 0)
1287  g_object_set (selector, "hide-placeholder", FALSE, NULL);
1288 
1289  g_object_set_data(G_OBJECT(dialog), sel_name, selector);
1290 
1291  gppat_populate_gas_list(dialog, GNC_ACCOUNT_SEL(selector), TRUE);
1292  gtk_widget_show_all(box);
1293 
1294  return selector;
1295 }
1296 
1297 static int
1298 commodity_mismatch_dialog (const Account* account, GtkWindow* parent)
1299 {
1300  int response;
1301  char *account_name = gnc_account_get_full_name (account);
1302  char* message = g_strdup_printf (
1303  _("Account %s does not have the same currency as the one you're "
1304  "moving transactions from.\nAre you sure you want to do this?"),
1305  account_name);
1306  GtkWidget* error_dialog =
1307  gtk_message_dialog_new (parent, GTK_DIALOG_DESTROY_WITH_PARENT,
1308  GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE,
1309  "%s", message);
1310  gtk_dialog_add_buttons (GTK_DIALOG(error_dialog),
1311  _("_Pick another account"), GTK_RESPONSE_CANCEL,
1312  _("_Do it anyway"), GTK_RESPONSE_ACCEPT,
1313  (gchar *)NULL);
1314  response = gtk_dialog_run (GTK_DIALOG (error_dialog));
1315  gtk_widget_destroy (error_dialog);
1316  g_free (message);
1317  return response;
1318 }
1319 
1320 typedef struct
1321 {
1322  Account *new_account;
1323  Account *old_account;
1324  GNCAccountSel *selector;
1325  gboolean match;
1326  gboolean for_account;
1327 } Adopter;
1328 
1329 static void
1330 adopter_set_account_and_match (Adopter* adopter)
1331 {
1332  if (!(adopter->selector &&
1333  gtk_widget_is_sensitive (GTK_WIDGET (adopter->selector))))
1334  return;
1335  adopter->new_account = gnc_account_sel_get_account(adopter->selector);
1336 /* We care about the commodity only if we're moving transactions. */
1337  if (!adopter->for_account && adopter->old_account && adopter->new_account)
1338  adopter->match =
1339  xaccAccountGetCommodity (adopter->new_account) ==
1340  xaccAccountGetCommodity (adopter->old_account);
1341 }
1342 
1343 static void
1344 adopter_init (Adopter* adopter, GtkWidget *selector, Account* account,
1345  gboolean for_account)
1346 {
1347  adopter->selector = GNC_ACCOUNT_SEL (selector);
1348  adopter->new_account = NULL;
1349  adopter->old_account = account;
1350  adopter->match = TRUE;
1351  adopter->for_account = for_account;
1352 }
1353 
1354 static gboolean
1355 adopter_match (Adopter* adopter, GtkWindow *parent)
1356 {
1357  int result;
1358  if (adopter->match || adopter->for_account)
1359  return TRUE;
1360  result = commodity_mismatch_dialog (adopter->new_account, parent);
1361  return (result == GTK_RESPONSE_ACCEPT);
1362 }
1363 
1364 typedef struct
1365 {
1366  Adopter trans;
1367  Adopter subacct;
1368  Adopter subtrans;
1369  delete_helper_t delete_res;
1370 } Adopters;
1371 
1372 static Account*
1373 account_subaccount (Account* account)
1374 {
1375  Account* subaccount = NULL;
1376  GList *subs = gnc_account_get_children (account);
1377  if (!gnc_list_length_cmp (subs, 1))
1378  subaccount = GNC_ACCOUNT(subs->data);
1379  g_list_free (subs);
1380  return subaccount;
1381 }
1382 
1383 static GtkWidget*
1384 account_delete_dialog (Account *account, GtkWindow *parent, Adopters* adopt)
1385 {
1386  GtkWidget *dialog = NULL;
1387  GtkWidget *widget = NULL;
1388  gchar *title = NULL;
1389  GtkBuilder *builder = gtk_builder_new();
1390  gchar *acct_name = gnc_account_get_full_name(account);
1391  GList* filter = g_list_prepend(NULL, (gpointer)xaccAccountGetType(account));
1392 
1393  if (!acct_name)
1394  acct_name = g_strdup (_("(no name)"));
1395 
1396  gnc_builder_add_from_file (builder, "dialog-account.glade", "account_delete_dialog");
1397 
1398  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "account_delete_dialog"));
1399  gtk_window_set_transient_for(GTK_WINDOW(dialog), parent);
1400 
1401  /* FIXME: Same account type used for subaccount. */
1402  g_object_set_data_full (G_OBJECT(dialog), DELETE_DIALOG_FILTER, filter,
1403  (GDestroyNotify) g_list_free);
1404  g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_ACCOUNT, account);
1405  widget = GTK_WIDGET(gtk_builder_get_object (builder, "header"));
1406  title = g_strdup_printf(_("Deleting account %s"), acct_name);
1407  gtk_label_set_text(GTK_LABEL(widget), title);
1408  g_free(title);
1409  g_free(acct_name);
1410 
1411  widget = GTK_WIDGET(gtk_builder_get_object (builder, DELETE_DIALOG_OK_BUTTON));
1412  g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_OK_BUTTON, widget);
1413 
1414  // Add the account selectors and enable sections as appropriate
1415  // setup transactions selector
1416  adopter_init (&adopt->trans,
1417  gppat_setup_account_selector (builder, dialog,
1418  "trans_mas_hbox",
1419  DELETE_DIALOG_TRANS_MAS),
1420  account, FALSE);
1421 
1422  // Does the selected account have splits
1423  if (!xaccAccountGetSplits(account).empty())
1424  {
1425  delete_helper_t delete_res2 = { FALSE, FALSE };
1426 
1427  delete_account_helper(account, &delete_res2);
1428  if (delete_res2.has_ro_splits)
1429  {
1430  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "trans_rw")));
1431  widget = GTK_WIDGET(gtk_builder_get_object (builder, "trans_drb"));
1432  gtk_widget_set_sensitive(widget, FALSE);
1433  }
1434  else
1435  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "trans_ro")));
1436  }
1437  else
1438  {
1439  gtk_widget_set_sensitive (GTK_WIDGET(gtk_builder_get_object (builder, "transactions")), FALSE);
1440  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "trans_ro")));
1441  }
1442 
1443  // setup subaccount account selector
1444  adopter_init (&adopt->subacct,
1445  gppat_setup_account_selector (builder, dialog,
1446  "sa_mas_hbox",
1447  DELETE_DIALOG_SA_MAS),
1448  account, TRUE);
1449 
1450  // setup subaccount transaction selector
1451  adopter_init (&adopt->subtrans,
1452  gppat_setup_account_selector (builder, dialog,
1453  "sa_trans_mas_hbox",
1454  DELETE_DIALOG_SA_TRANS_MAS),
1455  account_subaccount (account), FALSE);
1456  g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_SA_TRANS,
1457  GTK_WIDGET(gtk_builder_get_object (builder, "subaccount_trans")));
1458 
1459  if (gnc_account_n_children(account) > 0)
1460  {
1461  // Check for RO txns in descendants
1462  gnc_account_foreach_descendant_until(account, delete_account_helper,
1463  &adopt->delete_res);
1464  if (adopt->delete_res.has_splits)
1465  {
1466  if (adopt->delete_res.has_ro_splits)
1467  {
1468  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_rw")));
1469  widget = GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_drb"));
1470  gtk_widget_set_sensitive(widget, FALSE);
1471  }
1472  else
1473  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_ro")));
1474 
1475  g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_SA_SPLITS, GINT_TO_POINTER(1));
1476  }
1477  else
1478  {
1479  g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_SA_SPLITS, GINT_TO_POINTER(0));
1480  gtk_widget_set_sensitive (GTK_WIDGET(gtk_builder_get_object (builder, "subaccount_trans")), FALSE);
1481  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_ro")));
1482  }
1483  }
1484  else
1485  {
1486  gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object (builder, "subaccounts")), FALSE);
1487  gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object (builder, "subaccount_trans")), FALSE);
1488  gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_ro")));
1489  }
1490 
1491  /* default to cancel */
1492  gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1493 
1494  gtk_builder_connect_signals(builder, dialog);
1495  g_object_unref(G_OBJECT(builder));
1496 
1497  return dialog;
1498 }
1499 
1500 static void
1501 gnc_plugin_page_account_tree_cmd_delete_account (GSimpleAction *simple,
1502  GVariant *paramter,
1503  gpointer user_data)
1504 {
1505  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1507  gchar *acct_name;
1508  GtkWidget *window;
1509  Adopters adopt;
1510  GList* list;
1511  gint response;
1512  GtkWidget *dialog = NULL;
1513 
1514  if (account == NULL)
1515  return;
1516 
1518  return;
1519 
1520  memset (&adopt, 0, sizeof (adopt));
1521  /* If the account has objects referring to it, show the list - the account can't be deleted until these
1522  references are dealt with. */
1523  list = qof_instance_get_referring_object_list(QOF_INSTANCE(account));
1524  if (list != NULL)
1525  {
1526 #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")
1527 
1528  gnc_ui_object_references_show(EXPLANATION, list);
1529  g_list_free(list);
1530  return;
1531  }
1532 
1533  window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1534  acct_name = gnc_account_get_full_name(account);
1535  if (!acct_name)
1536  acct_name = g_strdup (_("(no name)"));
1537 
1538  if (gnc_account_n_children(account) > 1) {
1539  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);
1540  gnc_error_dialog(GTK_WINDOW(window),"%s", message);
1541  g_free (message);
1542  g_free(acct_name);
1543  return;
1544  }
1545 
1546  // If no transaction or children just delete it.
1547  if (xaccAccountGetSplits (account).empty() && gnc_account_n_children (account) == 0)
1548  {
1549  do_delete_account (account, NULL, NULL, NULL);
1550  return;
1551  }
1552 
1553  dialog = account_delete_dialog (account, GTK_WINDOW (window), &adopt);
1554 
1555  while (TRUE)
1556  {
1557  response = gtk_dialog_run(GTK_DIALOG(dialog));
1558 
1559  if (response != GTK_RESPONSE_ACCEPT)
1560  {
1561  gtk_widget_destroy(dialog);
1562  return;
1563  }
1564  adopter_set_account_and_match (&adopt.trans);
1565  adopter_set_account_and_match (&adopt.subacct);
1566  adopter_set_account_and_match (&adopt.subtrans);
1567 
1568  if (adopter_match (&adopt.trans, GTK_WINDOW (window)) &&
1569  adopter_match (&adopt.subacct, GTK_WINDOW (window)) &&
1570  adopter_match (&adopt.subtrans, GTK_WINDOW (window)))
1571  break;
1572  }
1573  gtk_widget_destroy(dialog);
1574  if (confirm_delete_account (simple, page, adopt.trans.new_account,
1575  adopt.subtrans.new_account,
1576  adopt.subacct.new_account,
1577  adopt.delete_res) == GTK_RESPONSE_ACCEPT)
1578  {
1579  do_delete_account (account, adopt.subacct.new_account,
1580  adopt.subtrans.new_account, adopt.trans.new_account);
1581  }
1582 }
1583 
1584 static int
1585 confirm_delete_account (GSimpleAction *simple, GncPluginPageAccountTree *page,
1586  Account* ta, Account* sta, Account* saa,
1587  delete_helper_t delete_res)
1588 {
1590  GtkWidget* window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1591  gint response;
1592 
1593  char *lines[6] = {0};
1594  char *message;
1595  int i = 0;
1596  GtkWidget *dialog;
1597  gchar* acct_name = gnc_account_get_full_name(account);
1598 
1599  lines[i] = g_strdup_printf (_("The account %s will be deleted."),
1600  acct_name);
1601  g_free(acct_name);
1602 
1603  if (!xaccAccountGetSplits (account).empty())
1604  {
1605  if (ta)
1606  {
1607  char *name = gnc_account_get_full_name(ta);
1608  lines[++i] = g_strdup_printf (_("All transactions in this account "
1609  "will be moved to the account %s."),
1610  name);
1611  g_free (name);
1612  }
1613  else
1614  {
1615  lines[++i] = g_strdup (_("All transactions in this account "
1616  "will be deleted."));
1617  }
1618  }
1619  if (gnc_account_n_children(account))
1620  {
1621  if (saa)
1622  {
1623  char *name = gnc_account_get_full_name(saa);
1624  lines[++i] = g_strdup_printf (_("Its sub-account will be "
1625  "moved to the account %s."), name);
1626  g_free (name);
1627  }
1628  else
1629  {
1630  lines[++i] = g_strdup (_("Its subaccount will be deleted."));
1631  if (sta)
1632  {
1633  char *name = gnc_account_get_full_name(sta);
1634  lines[++i] = g_strdup_printf (_("All sub-account transactions "
1635  "will be moved to the "
1636  "account %s."), name);
1637  g_free (name);
1638  }
1639  else if (delete_res.has_splits)
1640  {
1641  lines[++i] = g_strdup(_("All sub-account transactions "
1642  "will be deleted."));
1643  }
1644  }
1645  }
1646 
1647  lines[++i] = _("Are you sure you want to do this?");
1648 
1649  message = g_strjoinv(" ", lines);
1650  for (int j = 0; j < i; ++j) // Don't try to free the last one, it's const.
1651  g_free (lines[j]);
1652 
1653  dialog = gtk_message_dialog_new(GTK_WINDOW(window),
1654  GTK_DIALOG_DESTROY_WITH_PARENT,
1655  GTK_MESSAGE_QUESTION,
1656  GTK_BUTTONS_NONE,
1657  "%s", message);
1658  g_free(message);
1659  gtk_dialog_add_buttons(GTK_DIALOG(dialog),
1660  _("_Cancel"), GTK_RESPONSE_CANCEL,
1661  _("_Delete"), GTK_RESPONSE_ACCEPT,
1662  (gchar *)NULL);
1663  gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1664  response = gtk_dialog_run(GTK_DIALOG(dialog));
1665  gtk_widget_destroy(dialog);
1666  return response;
1667 }
1668 
1669 void
1670 do_delete_account (Account* account, Account* saa, Account* sta, Account* ta)
1671 {
1672  GList *acct_list, *ptr;
1673  const GncGUID *guid;
1674  gchar guidstr[GUID_ENCODING_LENGTH+1];
1675 
1676  gnc_set_busy_cursor(NULL, TRUE);
1677  gnc_suspend_gui_refresh ();
1678 
1679  /* Move subaccounts and transactions if this was requested */
1680  xaccAccountBeginEdit (account);
1681  if (saa)
1682  {
1683  xaccAccountBeginEdit (saa);
1684  acct_list = gnc_account_get_children(account);
1685  for (ptr = acct_list; ptr; ptr = g_list_next(ptr))
1686  gnc_account_append_child (saa, GNC_ACCOUNT(ptr->data));
1687  g_list_free(acct_list);
1688  xaccAccountCommitEdit (saa);
1689  }
1690  else if (sta)
1691  {
1692  /* Move the splits of its subaccounts, if any. */
1693  gnc_account_foreach_descendant(account,
1694  (AccountCb)xaccAccountMoveAllSplits,
1695  sta);
1696  }
1697  if (ta)
1698  {
1699  /* Move the splits of the account to be deleted. */
1700  xaccAccountMoveAllSplits (account, ta);
1701  }
1702  xaccAccountCommitEdit (account);
1703 
1704  /* Drop all references from the state file for
1705  * any subaccount the account still has
1706  */
1707  acct_list = gnc_account_get_children(account);
1708  for (ptr = acct_list; ptr; ptr = g_list_next(ptr))
1709  {
1710  guid = xaccAccountGetGUID (ptr->data);
1711  guid_to_string_buff (guid, guidstr);
1712  gnc_state_drop_sections_for (guidstr);
1713  }
1714  g_list_free(acct_list);
1715 
1716  /* Drop all references from the state file for this account
1717  */
1718  guid = xaccAccountGetGUID (account);
1719  guid_to_string_buff (guid, guidstr);
1720  gnc_state_drop_sections_for (guidstr);
1721 
1722  /*
1723  * Finally, delete the account, any subaccounts it may still
1724  * have, and any splits it or its subaccounts may still have.
1725  */
1726  xaccAccountBeginEdit (account);
1727  xaccAccountDestroy (account);
1728  gnc_resume_gui_refresh ();
1729  gnc_unset_busy_cursor(NULL);
1730 }
1731 
1732 static void
1733 gnc_plugin_page_account_tree_cmd_renumber_accounts (GSimpleAction *simple,
1734  GVariant *paramter,
1735  gpointer user_data)
1736 {
1737  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1738  Account *account;
1739  GtkWidget *window;
1740 
1741  window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1743  if (!window || !account)
1744  return;
1745 
1746  gnc_account_renumber_create_dialog(window, account);
1747 }
1748 
1749 static void
1750 gnc_plugin_page_account_tree_cmd_refresh (GSimpleAction *simple,
1751  GVariant *paramter,
1752  gpointer user_data)
1753 {
1754  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1756 
1757  g_return_if_fail(GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(page));
1758 
1759  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
1760 
1761  gnc_tree_view_account_clear_model_cache (GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
1762  gtk_widget_queue_draw (priv->widget);
1763 }
1764 
1765 /*********************/
1766 
1767 static void
1768 gnc_plugin_page_account_tree_cmd_view_filter_by (GSimpleAction *simple,
1769  GVariant *paramter,
1770  gpointer user_data)
1771 {
1772  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1774 
1775  g_return_if_fail(GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(page));
1776  ENTER("(action %p, page %p)", simple, page);
1777 
1778  priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
1779  account_filter_dialog_create(&priv->fd, GNC_PLUGIN_PAGE(page));
1780  LEAVE(" ");
1781 }
1782 
1783 static void
1784 gnc_plugin_page_account_tree_cmd_reconcile (GSimpleAction *simple,
1785  GVariant *paramter,
1786  gpointer user_data)
1787 {
1788  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1789  GtkWidget *window;
1790  Account *account;
1791  RecnWindow *recnData;
1792 
1794  g_return_if_fail (account != NULL);
1795 
1796  /* To prevent mistakes involving saving an edited transaction after
1797  * finishing a reconciliation (reverting the reconcile state), we could look
1798  * at all open registers and determine if any of them have a transaction
1799  * being edited that involves the account to be reconciled.
1800  *
1801  * However, the reconcile window isn't modal so it's still possible to start
1802  * editing a transaction after opening it. Assume the user knows what
1803  * they're doing if they start a reconciliation from the account tree and
1804  * don't attempt to stop them.
1805  */
1806 
1807  window = GNC_PLUGIN_PAGE (page)->window;
1808  recnData = recnWindow (window, account);
1809  gnc_ui_reconcile_window_raise (recnData);
1810 }
1811 
1812 static void
1813 gnc_plugin_page_account_tree_cmd_autoclear (GSimpleAction *simple,
1814  GVariant *paramter,
1815  gpointer user_data)
1816 {
1817  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1818  GtkWidget *window;
1819  Account *account;
1820  AutoClearWindow *autoClearData;
1821 
1823  g_return_if_fail (account != NULL);
1824 
1825  window = GNC_PLUGIN_PAGE (page)->window;
1826  autoClearData = autoClearWindow (window, account);
1827  gnc_ui_autoclear_window_raise (autoClearData);
1828 }
1829 
1830 static void
1831 gnc_plugin_page_account_tree_cmd_transfer (GSimpleAction *simple,
1832  GVariant *paramter,
1833  gpointer user_data)
1834 {
1835  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1836  GtkWidget *window;
1837  Account *account;
1838 
1840  window = GNC_PLUGIN_PAGE (page)->window;
1841  gnc_xfer_dialog (window, account);
1842 }
1843 
1844 static void
1845 gnc_plugin_page_account_tree_cmd_stock_split (GSimpleAction *simple,
1846  GVariant *paramter,
1847  gpointer user_data)
1848 {
1849  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1850  GtkWidget *window;
1851  Account *account;
1852 
1854  window = GNC_PLUGIN_PAGE (page)->window;
1855  gnc_stock_split_dialog (window, account);
1856 }
1857 
1858 static void
1859 gnc_plugin_page_account_tree_cmd_stock_assistant (GSimpleAction *simple,
1860  GVariant *paramter,
1861  gpointer user_data)
1862 {
1863  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1864  Account *account;
1865  GtkWidget *window;
1866 
1867  ENTER ("(action %p, page %p)", simple, page);
1868 
1870  window = GNC_PLUGIN_PAGE(page)->window;
1871  gnc_stock_transaction_assistant (window, account);
1872 
1873  LEAVE (" ");
1874 }
1875 
1876 static void
1877 gnc_plugin_page_account_tree_cmd_edit_tax_options (GSimpleAction *simple,
1878  GVariant *paramter,
1879  gpointer user_data)
1880 {
1881  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1882  GtkWidget *window;
1883  Account *account;
1884 
1886  window = GNC_PLUGIN_PAGE (page)->window;
1887  gnc_tax_info_dialog (window, account);
1888 }
1889 
1890 static void
1891 gnc_plugin_page_account_tree_cmd_lots (GSimpleAction *simple,
1892  GVariant *paramter,
1893  gpointer user_data)
1894 {
1895  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1897  GtkWidget *window = GNC_PLUGIN_PAGE (page)->window;
1898  gnc_lot_viewer_dialog (GTK_WINDOW(window), account);
1899 }
1900 
1901 static gboolean
1902 scrub_kp_handler (GtkWidget *widget, GdkEventKey *event, gpointer data)
1903 {
1904  if (event->length == 0) return FALSE;
1905 
1906  switch (event->keyval)
1907  {
1908  case GDK_KEY_Escape:
1909  {
1910  gboolean abort_scrub = gnc_verify_dialog (GTK_WINDOW(widget), FALSE,
1911  "%s", _(check_repair_abort_YN));
1912 
1913  if (abort_scrub)
1914  gnc_set_abort_scrub (TRUE);
1915 
1916  return TRUE;
1917  }
1918  default:
1919  break;
1920  }
1921  return FALSE;
1922 }
1923 
1924 static void
1925 gnc_plugin_page_account_tree_cmd_scrub (GSimpleAction *simple,
1926  GVariant *paramter,
1927  gpointer user_data)
1928 {
1929  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1931  GncWindow *window;
1932  gulong scrub_kp_handler_ID;
1933 
1934  g_return_if_fail (account != NULL);
1935 
1936  prepare_scrubbing ();
1937 
1938  window = GNC_WINDOW(GNC_PLUGIN_PAGE (page)->window);
1939  scrub_kp_handler_ID = g_signal_connect (G_OBJECT(window), "key-press-event",
1940  G_CALLBACK(scrub_kp_handler), NULL);
1941  gnc_window_set_progressbar_window (window);
1942 
1943  xaccAccountScrubOrphans (account, gnc_window_show_progress);
1944  xaccAccountScrubImbalance (account, gnc_window_show_progress);
1945 
1946  // XXX: Lots/capital gains scrubbing is disabled
1947  if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
1948  xaccAccountScrubLots(account);
1949 
1950  gncScrubBusinessAccount(account, gnc_window_show_progress);
1951 
1952  finish_scrubbing (window, scrub_kp_handler_ID);
1953 }
1954 
1955 static void
1956 gnc_plugin_page_account_tree_cmd_scrub_sub (GSimpleAction *simple,
1957  GVariant *paramter,
1958  gpointer user_data)
1959 {
1960  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1962  GncWindow *window;
1963  gulong scrub_kp_handler_ID;
1964 
1965  g_return_if_fail (account != NULL);
1966 
1967  prepare_scrubbing ();
1968 
1969  window = GNC_WINDOW(GNC_PLUGIN_PAGE (page)->window);
1970  scrub_kp_handler_ID = g_signal_connect (G_OBJECT(window), "key-press-event",
1971  G_CALLBACK(scrub_kp_handler), NULL);
1972  gnc_window_set_progressbar_window (window);
1973 
1974  xaccAccountTreeScrubOrphans (account, gnc_window_show_progress);
1975  xaccAccountTreeScrubImbalance (account, gnc_window_show_progress);
1976 
1977  // XXX: Lots/capital gains scrubbing is disabled
1978  if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
1979  xaccAccountTreeScrubLots(account);
1980 
1981  gncScrubBusinessAccountTree(account, gnc_window_show_progress);
1982 
1983  finish_scrubbing (window, scrub_kp_handler_ID);
1984 }
1985 
1986 static void
1987 gnc_plugin_page_account_tree_cmd_scrub_all (GSimpleAction *simple,
1988  GVariant *paramter,
1989  gpointer user_data)
1990 {
1991  auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1992  Account *root = gnc_get_current_root_account ();
1993  GncWindow *window;
1994  gulong scrub_kp_handler_ID;
1995 
1996  prepare_scrubbing ();
1997 
1998  window = GNC_WINDOW(GNC_PLUGIN_PAGE (page)->window);
1999  scrub_kp_handler_ID = g_signal_connect (G_OBJECT(window), "key-press-event",
2000  G_CALLBACK(scrub_kp_handler), NULL);
2001  gnc_window_set_progressbar_window (window);
2002 
2003  xaccAccountTreeScrubOrphans (root, gnc_window_show_progress);
2004  xaccAccountTreeScrubImbalance (root, gnc_window_show_progress);
2005  // XXX: Lots/capital gains scrubbing is disabled
2006  if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
2007  xaccAccountTreeScrubLots(root);
2008 
2009  gncScrubBusinessAccountTree(root, gnc_window_show_progress);
2010 
2011  finish_scrubbing (window, scrub_kp_handler_ID);
2012 }
2013 
Account * gnc_account_get_parent(const Account *acc)
This routine returns a pointer to the parent of the specified account.
Definition: Account.cpp:2886
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:2787
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:4490
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:3217
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:3194
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:2182
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:1590
#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:3246
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:2927
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:2994
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:2911
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition: Account.cpp:1475
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3359
gboolean xaccAccountGetPlaceholder(const Account *acc)
Get the "placeholder" flag for an account.
Definition: Account.cpp:4058
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:1516
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