GnuCash c935c2f+
Loading...
Searching...
No Matches
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"
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. */
79static 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 ********************************************************************/
88typedef 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
107enum
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 */
128static void gnc_plugin_page_account_tree_finalize (GObject *object);
129static void gnc_plugin_page_account_tree_selected (GObject *object, gpointer user_data);
130
131static gboolean gnc_plugin_page_account_tree_focus_widget (GncPluginPage *plugin_page);
132static GtkWidget *gnc_plugin_page_account_tree_create_widget (GncPluginPage *plugin_page);
133static void gnc_plugin_page_account_tree_destroy_widget (GncPluginPage *plugin_page);
134static void gnc_plugin_page_account_tree_save_page (GncPluginPage *plugin_page, GKeyFile *file, const gchar *group);
135static GncPluginPage *gnc_plugin_page_account_tree_recreate_page (GtkWidget *window, GKeyFile *file, const gchar *group);
136
137/* Callbacks */
138static void gnc_plugin_page_account_tree_summarybar_position_changed(gpointer prefs, gchar* pref, gpointer user_data);
139static gboolean gnc_plugin_page_account_tree_button_press_cb (GtkWidget *widget, GdkEventButton *event, GncPluginPage *page);
140static void gnc_plugin_page_account_tree_double_click_cb (GtkTreeView *treeview,
141 GtkTreePath *path,
142 GtkTreeViewColumn *col,
144
145static void gnc_plugin_page_account_tree_selection_changed_cb (GtkTreeSelection *selection,
147static void accounting_period_changed_cb(gpointer prefs, gchar *pref, gpointer user_data);
148
149extern "C" {
150void gppat_populate_trans_mas_list(GtkToggleButton *sa_mrb, GtkWidget *dialog);
151void gppat_set_insensitive_iff_rb_active(GtkWidget *widget, GtkToggleButton *b);
152}
153
154/* Command callbacks */
155static void gnc_plugin_page_account_tree_cmd_new_account (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
156static void gnc_plugin_page_account_tree_cmd_file_new_hierarchy (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
157static void gnc_plugin_page_account_tree_cmd_open_account (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
158static void gnc_plugin_page_account_tree_cmd_open_subaccounts (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
159static void gnc_plugin_page_account_tree_cmd_edit_account (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
160static void gnc_plugin_page_account_tree_cmd_find_account (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
161static void gnc_plugin_page_account_tree_cmd_find_account_popup (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
162static void gnc_plugin_page_account_tree_cmd_delete_account (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
163static void gnc_plugin_page_account_tree_cmd_renumber_accounts (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
164static void gnc_plugin_page_account_tree_cmd_view_filter_by (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
165static void gnc_plugin_page_account_tree_cmd_reconcile (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
166static void gnc_plugin_page_account_tree_cmd_refresh (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
167static void gnc_plugin_page_account_tree_cmd_autoclear (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
168static void gnc_plugin_page_account_tree_cmd_transfer (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
169static void gnc_plugin_page_account_tree_cmd_stock_split (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
170static void gnc_plugin_page_account_tree_cmd_stock_assistant (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
171static void gnc_plugin_page_account_tree_cmd_edit_tax_options (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
172static void gnc_plugin_page_account_tree_cmd_lots (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
173static void gnc_plugin_page_account_tree_cmd_scrub (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
174static void gnc_plugin_page_account_tree_cmd_scrub_sub (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
175static void gnc_plugin_page_account_tree_cmd_scrub_all (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
176static void gnc_plugin_page_account_tree_cmd_cascade_account_properties (GSimpleAction *simple, GVariant *paramter, gpointer user_data);
177
178/* Account Deletion Actions. */
179static int confirm_delete_account (GSimpleAction *simple,
181 Account* sta, Account* saa,
182 delete_helper_t delete_res);
183static void do_delete_account (Account* account, Account* saa, Account* sta,
184 Account* ta);
185
186
187
188static guint plugin_page_signals[LAST_SIGNAL] = { 0 };
189
190
191static 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};
217static guint gnc_plugin_page_account_tree_n_actions = G_N_ELEMENTS(gnc_plugin_page_account_tree_actions);
218
220static 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
240static const gchar *actions_requiring_account_rw[] =
241{
242 "EditEditAccountAction",
243 "EditDeleteAccountAction",
244 "ActionsReconcileAction",
245 "ActionsAutoClearAction",
246 NULL
247};
248
251static const gchar *actions_requiring_subaccounts_rw[] =
252{
253 "EditRenumberSubaccountsAction",
254 "EditCascadeAccountAction",
255 NULL
256};
257
260static const gchar *actions_requiring_account_always[] =
261{
262 "EditOpenAccountAction",
263 "EditOpenSubaccountsAction",
264 "ActionsLotsAction",
265 NULL
266};
267
268static 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. */
275static 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
292static 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
312G_DEFINE_TYPE_WITH_PRIVATE(GncPluginPageAccountTree, gnc_plugin_page_account_tree, GNC_TYPE_PLUGIN_PAGE)
313
314static gboolean show_abort_verify = TRUE;
315
316static void
317prepare_scrubbing ()
318{
319 gnc_suspend_gui_refresh ();
320 gnc_set_abort_scrub (FALSE);
321}
322
323static void
324finish_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
331static const char*
332check_repair_abort_YN = N_("'Check & Repair' is currently running, do you want to abort it?");
333
334static gboolean
335gnc_plugin_page_account_finish_pending (GncPluginPage* page)
336{
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
364static void
365gnc_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
392static void
393gnc_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
442static void
443gnc_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
458void
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
517Account *
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
540static gboolean
541gnc_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
574static void
575gnc_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
582static void
583gnc_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
589static void
590gnc_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
600static void
601gnc_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
611static GtkWidget *
612gnc_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
738static void
739gnc_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 priv->tree_view = NULL;
796 LEAVE("widget destroyed");
797}
798
799static void
800update_inactive_actions (GncPluginPage *plugin_page)
801{
803 GSimpleActionGroup *simple_action_group = NULL;
804 Account *account = NULL;
805 gboolean allow_write = !qof_book_is_readonly (gnc_get_current_book());
806 gboolean has_account = FALSE;
807 gboolean subaccounts = FALSE;
808
809 g_return_if_fail (plugin_page && GNC_IS_PLUGIN_PAGE(plugin_page));
810
811 priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE (plugin_page);
812
813 if (gtk_tree_view_get_selection (priv->tree_view))
814 {
815 account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
816 has_account = (account != NULL);
817 subaccounts = (account && gnc_account_n_children (account) != 0);
818 /* Check here for placeholder accounts, etc. */
819 }
820
821 /* Get the action group */
822 simple_action_group = gnc_plugin_page_get_action_group (plugin_page);
823 g_return_if_fail (G_IS_SIMPLE_ACTION_GROUP (simple_action_group));
824
825 /* Set the action's sensitivity */
826 gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), readonly_inactive_actions,
827 allow_write);
828 gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), actions_requiring_account_rw,
829 allow_write && has_account);
830 gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), actions_requiring_account_always,
831 has_account);
832 gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), actions_requiring_subaccounts_rw,
833 allow_write && subaccounts);
834 gnc_plugin_set_actions_enabled (G_ACTION_MAP(simple_action_group), actions_requiring_priced_account,
835 account && xaccAccountIsPriced (account));
836
837 g_signal_emit (plugin_page, plugin_page_signals[ACCOUNT_SELECTED], 0, account);
838}
839
844static void
845gnc_plugin_page_account_tree_selected (GObject *object, gpointer user_data)
846{
847 GncPluginPage *plugin_page = GNC_PLUGIN_PAGE (object);
848 g_return_if_fail (GNC_IS_PLUGIN_PAGE (plugin_page));
849 update_inactive_actions(plugin_page);
850}
851
861static void
862gnc_plugin_page_account_tree_save_page (GncPluginPage *plugin_page,
863 GKeyFile *key_file,
864 const gchar *group_name)
865{
866 GncPluginPageAccountTree *account_page;
868
869 g_return_if_fail (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(plugin_page));
870 g_return_if_fail (key_file != NULL);
871 g_return_if_fail (group_name != NULL);
872
873 ENTER("page %p, key_file %p, group_name %s", plugin_page, key_file,
874 group_name);
875
876 account_page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(plugin_page);
877 priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(account_page);
878
879 gnc_tree_view_account_save(GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
880 &priv->fd, key_file, group_name);
881 LEAVE(" ");
882}
883
884
885
895static GncPluginPage *
896gnc_plugin_page_account_tree_recreate_page (GtkWidget *window,
897 GKeyFile *key_file,
898 const gchar *group_name)
899{
900 GncPluginPageAccountTree *account_page;
902 GncPluginPage *page;
903
904 g_return_val_if_fail(key_file, NULL);
905 g_return_val_if_fail(group_name, NULL);
906 ENTER("key_file %p, group_name %s", key_file, group_name);
907
908 /* Create the new page. */
910 account_page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(page);
911 priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(account_page);
912
913 /* Install it now so we can then manipulate the created widget */
914 gnc_main_window_open_page(GNC_MAIN_WINDOW(window), page);
915
916 gnc_tree_view_account_restore(GNC_TREE_VIEW_ACCOUNT(priv->tree_view),
917 &priv->fd, key_file, group_name);
918 LEAVE(" ");
919 return page;
920}
921
922
923/* Callbacks */
924
925static void
926gnc_plugin_page_account_tree_summarybar_position_changed (gpointer prefs,
927 gchar* pref,
928 gpointer user_data)
929{
930 GncPluginPage *plugin_page;
933 GtkPositionType position = GTK_POS_BOTTOM;
934
935 g_return_if_fail(user_data != NULL);
936
937 plugin_page = GNC_PLUGIN_PAGE(user_data);
938 page = GNC_PLUGIN_PAGE_ACCOUNT_TREE (user_data);
939 priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
940
941 if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SUMMARYBAR_POSITION_TOP))
942 position = GTK_POS_TOP;
943
944 gtk_box_reorder_child(GTK_BOX(priv->widget),
945 plugin_page->summarybar,
946 (position == GTK_POS_TOP ? 0 : -1) );
947}
948
956static gboolean
957gnc_plugin_page_account_tree_button_press_cb (GtkWidget *widget,
958 GdkEventButton *event,
959 GncPluginPage *page)
960{
961
962 g_return_val_if_fail(GNC_IS_PLUGIN_PAGE(page), FALSE);
963
964 ENTER("widget %p, event %p, page %p", widget, event, page);
965 gnc_main_window_button_press_cb(widget, event, page);
966 LEAVE(" ");
967
968 /* Always return FALSE. This will let the tree view callback run as
969 * well which will select the item under the cursor. By the time
970 * the user sees the menu both callbacks will have run and the menu
971 * actions will operate on the just-selected account. */
972 return FALSE;
973}
974
975static void
976gppat_open_account_common (GncPluginPageAccountTree *page,
977 Account *account,
978 gboolean include_subs)
979{
980 GtkWidget *window;
981 GncPluginPage *new_page;
982
983 if (account == NULL)
984 return;
985
986 window = GNC_PLUGIN_PAGE (page)->window;
987 new_page = gnc_plugin_page_register_new (account, include_subs);
988 gnc_main_window_open_page (GNC_MAIN_WINDOW(window), new_page);
989}
990
991static void
992gnc_plugin_page_account_tree_double_click_cb (GtkTreeView *treeview,
993 GtkTreePath *path,
994 GtkTreeViewColumn *col,
996{
997 GtkTreeModel *model;
998 GtkTreeIter iter;
999
1000 g_return_if_fail (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE (page));
1001 g_return_if_fail (treeview);
1002
1003 model = gtk_tree_view_get_model(treeview);
1004 if (gtk_tree_model_get_iter(model, &iter, path))
1005 {
1006 Account *account = gnc_tree_view_account_get_account_from_path (GNC_TREE_VIEW_ACCOUNT(treeview), path);
1007 if (xaccAccountGetPlaceholder (account))
1008 {
1009 /* This is a placeholder account. Only only show/hide
1010 * subaccount list if there is one.
1011 */
1012 if (gtk_tree_model_iter_has_child(model, &iter))
1013 {
1014 /* There are children,
1015 * just expand or collapse the row. */
1016 if (gtk_tree_view_row_expanded(treeview, path))
1017 gtk_tree_view_collapse_row(treeview, path);
1018 else
1019 gtk_tree_view_expand_row(treeview, path, FALSE);
1020 }
1021 }
1022 else
1023 {
1024 /* No placeholder account, so open its register */
1025 gppat_open_account_common (page, account, FALSE);
1026 }
1027 }
1028}
1029
1030static void
1031gnc_plugin_page_account_tree_selection_changed_cb (GtkTreeSelection *selection,
1033{
1034 GncPluginPage *plugin_page = GNC_PLUGIN_PAGE(page);
1035 update_inactive_actions (plugin_page);
1036}
1037
1038static void
1039accounting_period_changed_cb (gpointer prefs, gchar *pref, gpointer user_data)
1040{
1041 gnc_plugin_page_account_tree_cmd_refresh (NULL, NULL, user_data);
1042}
1043
1044/* Command callbacks */
1045static void
1046gnc_plugin_page_account_tree_cmd_new_account (GSimpleAction *simple,
1047 GVariant *paramter,
1048 gpointer user_data)
1049{
1050 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1052 GtkWindow *parent = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE (page)));
1053 gnc_ui_new_account_window (parent, gnc_get_current_book(),
1054 account);
1055}
1056
1057static void
1058gnc_plugin_page_account_tree_cmd_file_new_hierarchy (GSimpleAction *simple,
1059 GVariant *paramter,
1060 gpointer user_data)
1061{
1062 gnc_ui_hierarchy_assistant(FALSE);
1063}
1064
1065static void
1066gnc_plugin_page_account_tree_cmd_open_account (GSimpleAction *simple,
1067 GVariant *paramter,
1068 gpointer user_data)
1069{
1070 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1071 Account *account;
1072
1073 g_return_if_fail (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE (page));
1075 gppat_open_account_common (page, account, FALSE);
1076}
1077
1078static void
1079gnc_plugin_page_account_tree_cmd_open_subaccounts (GSimpleAction *simple,
1080 GVariant *paramter,
1081 gpointer user_data)
1082{
1083 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1084 Account *account;
1085
1086 g_return_if_fail (GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE (page));
1088 gppat_open_account_common (page, account, TRUE);
1089}
1090
1091static void
1092gnc_plugin_page_account_tree_cmd_edit_account (GSimpleAction *simple,
1093 GVariant *paramter,
1094 gpointer user_data)
1095{
1096 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1097 Account *account;
1098 GtkWindow *parent = GTK_WINDOW (gnc_plugin_page_get_window (GNC_PLUGIN_PAGE (page)));
1099 ENTER("action %p, page %p", simple, page);
1100
1102 g_return_if_fail (account != NULL);
1103
1104 gnc_ui_edit_account_window (parent, account);
1105 LEAVE(" ");
1106}
1107
1108static void
1109gnc_plugin_page_account_tree_cmd_find_account (GSimpleAction *simple,
1110 GVariant *paramter,
1111 gpointer user_data)
1112{
1113 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1114 GtkWidget *window;
1115
1116 ENTER("action %p, page %p", simple, page);
1117
1118 window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1119
1120 gnc_find_account_dialog (window, NULL);
1121 LEAVE(" ");
1122}
1123
1124static void
1125gnc_plugin_page_account_tree_cmd_find_account_popup (GSimpleAction *simple,
1126 GVariant *paramter,
1127 gpointer user_data)
1128{
1129 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1130 Account *account = NULL;
1131 GtkWidget *window;
1132
1133 ENTER("action %p, page %p", simple, page);
1134
1136
1137 window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1138
1139 gnc_find_account_dialog (window, account);
1140 LEAVE(" ");
1141}
1142
1143static void
1144gnc_plugin_page_account_tree_cmd_cascade_account_properties (GSimpleAction *simple,
1145 GVariant *paramter,
1146 gpointer user_data)
1147{
1148 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1149 Account *account = NULL;
1150 GtkWidget *window;
1151
1152 ENTER("action %p, page %p", simple, page);
1153
1155
1156 window = gnc_plugin_page_get_window (GNC_PLUGIN_PAGE(page));
1157
1158 if (account != NULL)
1159 gnc_account_cascade_properties_dialog (window, account);
1160
1161 LEAVE(" ");
1162}
1163
1164static gpointer
1165delete_account_helper (Account * account, gpointer data)
1166{
1167 auto helper_res = static_cast<delete_helper_t*>(data);
1168 auto& splits{xaccAccountGetSplits (account)};
1169 auto split_ro = [](auto s) -> bool { return xaccTransGetReadOnly (xaccSplitGetParent (s)); };
1170
1171 helper_res->has_splits = !splits.empty();
1172 helper_res->has_ro_splits = std::any_of (splits.begin(), splits.end(), split_ro);
1173
1174 return GINT_TO_POINTER (helper_res->has_splits || helper_res->has_ro_splits);
1175}
1176
1177/***
1178 *** The OK button of a Delete Account dialog is insensitive if
1179 *** and only if a sensitive account selector contains no accounts.
1180 ***/
1181static void
1182set_ok_sensitivity(GtkWidget *dialog)
1183{
1184 gboolean sensitive;
1185
1186 auto sa_mas = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_SA_MAS));
1187 auto trans_mas = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_TRANS_MAS));
1188
1189 sensitive = ((!sa_mas ||
1190 !gtk_widget_is_sensitive (sa_mas) ||
1191 gnc_account_sel_get_visible_account_num (GNC_ACCOUNT_SEL (sa_mas))) &&
1192 (!trans_mas ||
1193 !gtk_widget_is_sensitive (trans_mas) ||
1194 gnc_account_sel_get_visible_account_num (GNC_ACCOUNT_SEL (trans_mas))));
1195
1196 auto button = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_OK_BUTTON));
1197 gtk_widget_set_sensitive(button, sensitive);
1198}
1199
1200static GList *
1201gppat_get_exclude_list (Account *acc, gboolean exclude_subaccounts)
1202{
1203 GList *acct_list = NULL;
1204
1205 if (exclude_subaccounts)
1206 acct_list = gnc_account_get_descendants (acc);
1207
1208 acct_list = g_list_prepend (acct_list, acc);
1209
1210 return acct_list;
1211}
1212
1213static void
1214gppat_populate_gas_list(GtkWidget *dialog,
1215 GNCAccountSel *gas,
1216 gboolean exclude_subaccounts)
1217{
1218 Account *account;
1219 GList *filter;
1220 GList *exclude;
1221
1222 g_return_if_fail(GTK_IS_DIALOG(dialog));
1223 if (gas == NULL)
1224 return;
1225 account = GNC_ACCOUNT(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_ACCOUNT));
1226 filter = static_cast<GList*>(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_FILTER));
1227
1228 /* Setting the account type filter triggers GNCAccountSel population. */
1229 gnc_account_sel_set_acct_filters (gas, filter, NULL);
1230
1231 /* Accounts to be deleted must be excluded from GAS. */
1232 exclude = gppat_get_exclude_list (account, exclude_subaccounts);
1233 gnc_account_sel_set_acct_exclude_filter (gas, exclude);
1234 g_list_free (exclude);
1235
1236 gnc_account_sel_set_account (gas, NULL, TRUE);
1237
1238 /* The sensitivity of the OK button needs to be reevaluated. */
1239 set_ok_sensitivity(dialog);
1240}
1241
1242void
1243gppat_populate_trans_mas_list(GtkToggleButton *sa_mrb,
1244 GtkWidget *dialog)
1245{
1246 g_return_if_fail(GTK_IS_DIALOG(dialog));
1247
1248 /* Cannot move transactions to subaccounts if they are to be deleted. */
1249 auto trans_mas = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_TRANS_MAS));
1250 gppat_populate_gas_list(dialog, GNC_ACCOUNT_SEL(trans_mas), !gtk_toggle_button_get_active(sa_mrb));
1251}
1252
1253/* Note that the emitting object (the toggle button) and the signal data
1254 * are swapped in below callback function. This is a gtkbuilder feature:
1255 * it swaps if you explicitly set an object for a signal handler in the
1256 * gtkbuilder xml file.
1257 */
1258void
1259gppat_set_insensitive_iff_rb_active(GtkWidget *widget, GtkToggleButton *b)
1260{
1261 GtkWidget *dialog = gtk_widget_get_toplevel(widget);
1262 auto subaccount_trans = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_SA_TRANS));
1263 auto sa_mas = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_SA_MAS));
1264 auto have_splits = g_object_get_data(G_OBJECT(dialog), DELETE_DIALOG_SA_SPLITS) != nullptr;
1265
1266 gtk_widget_set_sensitive(widget, !gtk_toggle_button_get_active(b));
1267
1268 // If we have subaccount splits & delete subaccounts, enable subaccount_trans
1269 if ((have_splits) && !gtk_widget_is_sensitive(sa_mas))
1270 gtk_widget_set_sensitive(subaccount_trans, TRUE);
1271 else
1272 gtk_widget_set_sensitive(subaccount_trans, FALSE);
1273
1274 set_ok_sensitivity(dialog);
1275}
1276
1277static GtkWidget *
1278gppat_setup_account_selector (GtkBuilder *builder, GtkWidget *dialog,
1279 const gchar *hbox, const gchar *sel_name)
1280{
1281 GtkWidget *selector = gnc_account_sel_new();
1282 GtkWidget *box = GTK_WIDGET(gtk_builder_get_object (builder, hbox));
1283
1284 gtk_box_pack_start (GTK_BOX(box), selector, TRUE, TRUE, 0);
1285
1286 // placeholder accounts are OK for this GAS
1287 if (g_strcmp0 (sel_name, DELETE_DIALOG_SA_MAS) == 0)
1288 g_object_set (selector, "hide-placeholder", FALSE, NULL);
1289
1290 g_object_set_data(G_OBJECT(dialog), sel_name, selector);
1291
1292 gppat_populate_gas_list(dialog, GNC_ACCOUNT_SEL(selector), TRUE);
1293 gtk_widget_show_all(box);
1294
1295 return selector;
1296}
1297
1298static int
1299commodity_mismatch_dialog (const Account* account, GtkWindow* parent)
1300{
1301 int response;
1302 char *account_name = gnc_account_get_full_name (account);
1303 char* message = g_strdup_printf (
1304 _("Account %s does not have the same currency as the one you're "
1305 "moving transactions from.\nAre you sure you want to do this?"),
1306 account_name);
1307 GtkWidget* error_dialog =
1308 gtk_message_dialog_new (parent, GTK_DIALOG_DESTROY_WITH_PARENT,
1309 GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE,
1310 "%s", message);
1311 gtk_dialog_add_buttons (GTK_DIALOG(error_dialog),
1312 _("_Pick another account"), GTK_RESPONSE_CANCEL,
1313 _("_Do it anyway"), GTK_RESPONSE_ACCEPT,
1314 (gchar *)NULL);
1315 response = gtk_dialog_run (GTK_DIALOG (error_dialog));
1316 gtk_widget_destroy (error_dialog);
1317 g_free (message);
1318 g_free (account_name);
1319 return response;
1320}
1321
1322typedef struct
1323{
1324 Account *new_account;
1325 Account *old_account;
1326 GNCAccountSel *selector;
1327 gboolean match;
1328 gboolean for_account;
1329} Adopter;
1330
1331static void
1332adopter_set_account_and_match (Adopter* adopter)
1333{
1334 if (!(adopter->selector &&
1335 gtk_widget_is_sensitive (GTK_WIDGET (adopter->selector))))
1336 return;
1337 adopter->new_account = gnc_account_sel_get_account(adopter->selector);
1338/* We care about the commodity only if we're moving transactions. */
1339 if (!adopter->for_account && adopter->old_account && adopter->new_account)
1340 adopter->match =
1341 xaccAccountGetCommodity (adopter->new_account) ==
1342 xaccAccountGetCommodity (adopter->old_account);
1343}
1344
1345static void
1346adopter_init (Adopter* adopter, GtkWidget *selector, Account* account,
1347 gboolean for_account)
1348{
1349 adopter->selector = GNC_ACCOUNT_SEL (selector);
1350 adopter->new_account = NULL;
1351 adopter->old_account = account;
1352 adopter->match = TRUE;
1353 adopter->for_account = for_account;
1354}
1355
1356static gboolean
1357adopter_match (Adopter* adopter, GtkWindow *parent)
1358{
1359 int result;
1360 if (adopter->match || adopter->for_account)
1361 return TRUE;
1362 result = commodity_mismatch_dialog (adopter->new_account, parent);
1363 return (result == GTK_RESPONSE_ACCEPT);
1364}
1365
1366typedef struct
1367{
1368 Adopter trans;
1369 Adopter subacct;
1370 Adopter subtrans;
1371 delete_helper_t delete_res;
1372} Adopters;
1373
1374static Account*
1375account_subaccount (Account* account)
1376{
1377 Account* subaccount = NULL;
1378 GList *subs = gnc_account_get_children (account);
1379 if (!gnc_list_length_cmp (subs, 1))
1380 subaccount = GNC_ACCOUNT(subs->data);
1381 g_list_free (subs);
1382 return subaccount;
1383}
1384
1385static GtkWidget*
1386account_delete_dialog (Account *account, GtkWindow *parent, Adopters* adopt)
1387{
1388 GtkWidget *dialog = NULL;
1389 GtkWidget *widget = NULL;
1390 gchar *title = NULL;
1391 GtkBuilder *builder = gtk_builder_new();
1392 gchar *acct_name = gnc_account_get_full_name(account);
1393 GList* filter = g_list_prepend(NULL, (gpointer)xaccAccountGetType(account));
1394
1395 if (!acct_name)
1396 acct_name = g_strdup (_("(no name)"));
1397
1398 gnc_builder_add_from_file (builder, "dialog-account.glade", "account_delete_dialog");
1399
1400 dialog = GTK_WIDGET(gtk_builder_get_object (builder, "account_delete_dialog"));
1401 gtk_window_set_transient_for(GTK_WINDOW(dialog), parent);
1402
1403 /* FIXME: Same account type used for subaccount. */
1404 g_object_set_data_full (G_OBJECT(dialog), DELETE_DIALOG_FILTER, filter,
1405 (GDestroyNotify) g_list_free);
1406 g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_ACCOUNT, account);
1407 widget = GTK_WIDGET(gtk_builder_get_object (builder, "header"));
1408 title = g_strdup_printf(_("Deleting account %s"), acct_name);
1409 gtk_label_set_text(GTK_LABEL(widget), title);
1410 g_free(title);
1411 g_free(acct_name);
1412
1413 widget = GTK_WIDGET(gtk_builder_get_object (builder, DELETE_DIALOG_OK_BUTTON));
1414 g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_OK_BUTTON, widget);
1415
1416 // Add the account selectors and enable sections as appropriate
1417 // setup transactions selector
1418 adopter_init (&adopt->trans,
1419 gppat_setup_account_selector (builder, dialog,
1420 "trans_mas_hbox",
1421 DELETE_DIALOG_TRANS_MAS),
1422 account, FALSE);
1423
1424 // Does the selected account have splits
1425 if (!xaccAccountGetSplits(account).empty())
1426 {
1427 delete_helper_t delete_res2 = { FALSE, FALSE };
1428
1429 delete_account_helper(account, &delete_res2);
1430 if (delete_res2.has_ro_splits)
1431 {
1432 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "trans_rw")));
1433 widget = GTK_WIDGET(gtk_builder_get_object (builder, "trans_drb"));
1434 gtk_widget_set_sensitive(widget, FALSE);
1435 }
1436 else
1437 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "trans_ro")));
1438 }
1439 else
1440 {
1441 gtk_widget_set_sensitive (GTK_WIDGET(gtk_builder_get_object (builder, "transactions")), FALSE);
1442 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "trans_ro")));
1443 }
1444
1445 // setup subaccount account selector
1446 adopter_init (&adopt->subacct,
1447 gppat_setup_account_selector (builder, dialog,
1448 "sa_mas_hbox",
1449 DELETE_DIALOG_SA_MAS),
1450 account, TRUE);
1451
1452 // setup subaccount transaction selector
1453 adopter_init (&adopt->subtrans,
1454 gppat_setup_account_selector (builder, dialog,
1455 "sa_trans_mas_hbox",
1456 DELETE_DIALOG_SA_TRANS_MAS),
1457 account_subaccount (account), FALSE);
1458 g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_SA_TRANS,
1459 GTK_WIDGET(gtk_builder_get_object (builder, "subaccount_trans")));
1460
1461 if (gnc_account_n_children(account) > 0)
1462 {
1463 // Check for RO txns in descendants
1464 gnc_account_foreach_descendant_until(account, delete_account_helper,
1465 &adopt->delete_res);
1466 if (adopt->delete_res.has_splits)
1467 {
1468 if (adopt->delete_res.has_ro_splits)
1469 {
1470 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_rw")));
1471 widget = GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_drb"));
1472 gtk_widget_set_sensitive(widget, FALSE);
1473 }
1474 else
1475 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_ro")));
1476
1477 g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_SA_SPLITS, GINT_TO_POINTER(1));
1478 }
1479 else
1480 {
1481 g_object_set_data(G_OBJECT(dialog), DELETE_DIALOG_SA_SPLITS, GINT_TO_POINTER(0));
1482 gtk_widget_set_sensitive (GTK_WIDGET(gtk_builder_get_object (builder, "subaccount_trans")), FALSE);
1483 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_ro")));
1484 }
1485 }
1486 else
1487 {
1488 gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object (builder, "subaccounts")), FALSE);
1489 gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object (builder, "subaccount_trans")), FALSE);
1490 gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object (builder, "sa_trans_ro")));
1491 }
1492
1493 /* default to cancel */
1494 gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1495
1496 gtk_builder_connect_signals(builder, dialog);
1497 g_object_unref(G_OBJECT(builder));
1498
1499 return dialog;
1500}
1501
1502static void
1503gnc_plugin_page_account_tree_cmd_delete_account (GSimpleAction *simple,
1504 GVariant *paramter,
1505 gpointer user_data)
1506{
1507 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1509 gchar *acct_name;
1510 GtkWidget *window;
1511 Adopters adopt;
1512 GList* list;
1513 gint response;
1514 GtkWidget *dialog = NULL;
1515
1516 if (account == NULL)
1517 return;
1518
1520 return;
1521
1522 memset (&adopt, 0, sizeof (adopt));
1523 /* If the account has objects referring to it, show the list - the account can't be deleted until these
1524 references are dealt with. */
1525 list = qof_instance_get_referring_object_list(QOF_INSTANCE(account));
1526 if (list != NULL)
1527 {
1528#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")
1529
1530 gnc_ui_object_references_show(EXPLANATION, list);
1531 g_list_free(list);
1532 return;
1533 }
1534 g_list_free (list);
1535
1536 window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1537 acct_name = gnc_account_get_full_name(account);
1538 if (!acct_name)
1539 acct_name = g_strdup (_("(no name)"));
1540
1541 if (gnc_account_n_children(account) > 1) {
1542 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);
1543 gnc_error_dialog(GTK_WINDOW(window),"%s", message);
1544 g_free (message);
1545 g_free(acct_name);
1546 return;
1547 }
1548 g_free (acct_name);
1549
1550 // If no transaction or children just delete it.
1551 if (xaccAccountGetSplits (account).empty() && gnc_account_n_children (account) == 0)
1552 {
1553 do_delete_account (account, NULL, NULL, NULL);
1554 return;
1555 }
1556
1557 dialog = account_delete_dialog (account, GTK_WINDOW (window), &adopt);
1558
1559 while (TRUE)
1560 {
1561 response = gtk_dialog_run(GTK_DIALOG(dialog));
1562
1563 if (response != GTK_RESPONSE_ACCEPT)
1564 {
1565 gtk_widget_destroy(dialog);
1566 return;
1567 }
1568 adopter_set_account_and_match (&adopt.trans);
1569 adopter_set_account_and_match (&adopt.subacct);
1570 adopter_set_account_and_match (&adopt.subtrans);
1571
1572 if (adopter_match (&adopt.trans, GTK_WINDOW (window)) &&
1573 adopter_match (&adopt.subacct, GTK_WINDOW (window)) &&
1574 adopter_match (&adopt.subtrans, GTK_WINDOW (window)))
1575 break;
1576 }
1577 gtk_widget_destroy(dialog);
1578 if (confirm_delete_account (simple, page, adopt.trans.new_account,
1579 adopt.subtrans.new_account,
1580 adopt.subacct.new_account,
1581 adopt.delete_res) == GTK_RESPONSE_ACCEPT)
1582 {
1583 do_delete_account (account, adopt.subacct.new_account,
1584 adopt.subtrans.new_account, adopt.trans.new_account);
1585 }
1586}
1587
1588static int
1589confirm_delete_account (GSimpleAction *simple, GncPluginPageAccountTree *page,
1590 Account* ta, Account* sta, Account* saa,
1591 delete_helper_t delete_res)
1592{
1594 GtkWidget* window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1595 gint response;
1596
1597 char *lines[6] = {0};
1598 char *message;
1599 int i = 0;
1600 GtkWidget *dialog;
1601 gchar* acct_name = gnc_account_get_full_name(account);
1602
1603 lines[i] = g_strdup_printf (_("The account %s will be deleted."),
1604 acct_name);
1605 g_free(acct_name);
1606
1607 if (!xaccAccountGetSplits (account).empty())
1608 {
1609 if (ta)
1610 {
1611 char *name = gnc_account_get_full_name(ta);
1612 lines[++i] = g_strdup_printf (_("All transactions in this account "
1613 "will be moved to the account %s."),
1614 name);
1615 g_free (name);
1616 }
1617 else
1618 {
1619 lines[++i] = g_strdup (_("All transactions in this account "
1620 "will be deleted."));
1621 }
1622 }
1623 if (gnc_account_n_children(account))
1624 {
1625 if (saa)
1626 {
1627 char *name = gnc_account_get_full_name(saa);
1628 lines[++i] = g_strdup_printf (_("Its sub-account will be "
1629 "moved to the account %s."), name);
1630 g_free (name);
1631 }
1632 else
1633 {
1634 lines[++i] = g_strdup (_("Its subaccount will be deleted."));
1635 if (sta)
1636 {
1637 char *name = gnc_account_get_full_name(sta);
1638 lines[++i] = g_strdup_printf (_("All sub-account transactions "
1639 "will be moved to the "
1640 "account %s."), name);
1641 g_free (name);
1642 }
1643 else if (delete_res.has_splits)
1644 {
1645 lines[++i] = g_strdup(_("All sub-account transactions "
1646 "will be deleted."));
1647 }
1648 }
1649 }
1650
1651 lines[++i] = _("Are you sure you want to do this?");
1652
1653 message = g_strjoinv(" ", lines);
1654 for (int j = 0; j < i; ++j) // Don't try to free the last one, it's const.
1655 g_free (lines[j]);
1656
1657 dialog = gtk_message_dialog_new(GTK_WINDOW(window),
1658 GTK_DIALOG_DESTROY_WITH_PARENT,
1659 GTK_MESSAGE_QUESTION,
1660 GTK_BUTTONS_NONE,
1661 "%s", message);
1662 g_free(message);
1663 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
1664 _("_Cancel"), GTK_RESPONSE_CANCEL,
1665 _("_Delete"), GTK_RESPONSE_ACCEPT,
1666 (gchar *)NULL);
1667 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1668 response = gtk_dialog_run(GTK_DIALOG(dialog));
1669 gtk_widget_destroy(dialog);
1670 return response;
1671}
1672
1673void
1674do_delete_account (Account* account, Account* saa, Account* sta, Account* ta)
1675{
1676 GList *acct_list, *ptr;
1677 const GncGUID *guid;
1678 gchar guidstr[GUID_ENCODING_LENGTH+1];
1679
1680 gnc_set_busy_cursor(NULL, TRUE);
1681 gnc_suspend_gui_refresh ();
1682
1683 /* Move subaccounts and transactions if this was requested */
1684 xaccAccountBeginEdit (account);
1685 if (saa)
1686 {
1688 acct_list = gnc_account_get_children(account);
1689 for (ptr = acct_list; ptr; ptr = g_list_next(ptr))
1690 gnc_account_append_child (saa, GNC_ACCOUNT(ptr->data));
1691 g_list_free(acct_list);
1693 }
1694 else if (sta)
1695 {
1696 /* Move the splits of its subaccounts, if any. */
1697 gnc_account_foreach_descendant(account,
1698 (AccountCb)xaccAccountMoveAllSplits,
1699 sta);
1700 }
1701 else
1702 {
1703 gnc_account_foreach_descendant (account,
1704 [](auto acc, [[maybe_unused]] auto data)
1706 nullptr);
1707 }
1708 if (ta)
1709 {
1710 /* Move the splits of the account to be deleted. */
1711 xaccAccountMoveAllSplits (account, ta);
1712 }
1713 else
1714 {
1716 }
1717 xaccAccountCommitEdit (account);
1718
1719 /* Drop all references from the state file for
1720 * any subaccount the account still has
1721 */
1722 acct_list = gnc_account_get_children(account);
1723 for (ptr = acct_list; ptr; ptr = g_list_next(ptr))
1724 {
1725 guid = xaccAccountGetGUID (ptr->data);
1726 guid_to_string_buff (guid, guidstr);
1728 }
1729 g_list_free(acct_list);
1730
1731 /* Drop all references from the state file for this account
1732 */
1733 guid = xaccAccountGetGUID (account);
1734 guid_to_string_buff (guid, guidstr);
1736
1737 /*
1738 * Finally, delete the account, any subaccounts it may still
1739 * have, and any splits it or its subaccounts may still have.
1740 */
1741 xaccAccountBeginEdit (account);
1742 xaccAccountDestroy (account);
1743 gnc_resume_gui_refresh ();
1744 gnc_unset_busy_cursor(NULL);
1745}
1746
1747static void
1748gnc_plugin_page_account_tree_cmd_renumber_accounts (GSimpleAction *simple,
1749 GVariant *paramter,
1750 gpointer user_data)
1751{
1752 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1753 Account *account;
1754 GtkWidget *window;
1755
1756 window = gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page));
1758 if (!window || !account)
1759 return;
1760
1761 gnc_account_renumber_create_dialog(window, account);
1762}
1763
1764static void
1765gnc_plugin_page_account_tree_cmd_refresh (GSimpleAction *simple,
1766 GVariant *paramter,
1767 gpointer user_data)
1768{
1769 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1771
1772 g_return_if_fail(GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(page));
1773
1774 priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
1775
1776 gnc_tree_view_account_clear_model_cache (GNC_TREE_VIEW_ACCOUNT(priv->tree_view));
1777 gtk_widget_queue_draw (priv->widget);
1778}
1779
1780/*********************/
1781
1782static void
1783gnc_plugin_page_account_tree_cmd_view_filter_by (GSimpleAction *simple,
1784 GVariant *paramter,
1785 gpointer user_data)
1786{
1787 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1789
1790 g_return_if_fail(GNC_IS_PLUGIN_PAGE_ACCOUNT_TREE(page));
1791 ENTER("(action %p, page %p)", simple, page);
1792
1793 priv = GNC_PLUGIN_PAGE_ACCOUNT_TREE_GET_PRIVATE(page);
1794 account_filter_dialog_create(&priv->fd, GNC_PLUGIN_PAGE(page));
1795 LEAVE(" ");
1796}
1797
1798static void
1799gnc_plugin_page_account_tree_cmd_reconcile (GSimpleAction *simple,
1800 GVariant *paramter,
1801 gpointer user_data)
1802{
1803 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1804 GtkWidget *window;
1805 Account *account;
1806 RecnWindow *recnData;
1807
1809 g_return_if_fail (account != NULL);
1810
1811 /* To prevent mistakes involving saving an edited transaction after
1812 * finishing a reconciliation (reverting the reconcile state), we could look
1813 * at all open registers and determine if any of them have a transaction
1814 * being edited that involves the account to be reconciled.
1815 *
1816 * However, the reconcile window isn't modal so it's still possible to start
1817 * editing a transaction after opening it. Assume the user knows what
1818 * they're doing if they start a reconciliation from the account tree and
1819 * don't attempt to stop them.
1820 */
1821
1822 window = GNC_PLUGIN_PAGE (page)->window;
1823 recnData = recnWindow (window, account);
1824 gnc_ui_reconcile_window_raise (recnData);
1825}
1826
1827static void
1828gnc_plugin_page_account_tree_cmd_autoclear (GSimpleAction *simple,
1829 GVariant *paramter,
1830 gpointer user_data)
1831{
1832 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1833 GtkWidget *window;
1834 Account *account;
1835 AutoClearWindow *autoClearData;
1836
1838 g_return_if_fail (account != NULL);
1839
1840 window = GNC_PLUGIN_PAGE (page)->window;
1841 autoClearData = autoClearWindow (window, account);
1842 gnc_ui_autoclear_window_raise (autoClearData);
1843}
1844
1845static void
1846gnc_plugin_page_account_tree_cmd_transfer (GSimpleAction *simple,
1847 GVariant *paramter,
1848 gpointer user_data)
1849{
1850 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1851 GtkWidget *window;
1852 Account *account;
1853
1855 window = GNC_PLUGIN_PAGE (page)->window;
1856 gnc_xfer_dialog (window, account);
1857}
1858
1859static void
1860gnc_plugin_page_account_tree_cmd_stock_split (GSimpleAction *simple,
1861 GVariant *paramter,
1862 gpointer user_data)
1863{
1864 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1865 GtkWidget *window;
1866 Account *account;
1867
1869 window = GNC_PLUGIN_PAGE (page)->window;
1870 gnc_stock_split_dialog (window, account);
1871}
1872
1873static void
1874gnc_plugin_page_account_tree_cmd_stock_assistant (GSimpleAction *simple,
1875 GVariant *paramter,
1876 gpointer user_data)
1877{
1878 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1879 Account *account;
1880 GtkWidget *window;
1881
1882 ENTER ("(action %p, page %p)", simple, page);
1883
1885 window = GNC_PLUGIN_PAGE(page)->window;
1886 gnc_stock_transaction_assistant (window, account);
1887
1888 LEAVE (" ");
1889}
1890
1891static void
1892gnc_plugin_page_account_tree_cmd_edit_tax_options (GSimpleAction *simple,
1893 GVariant *paramter,
1894 gpointer user_data)
1895{
1896 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1897 GtkWidget *window;
1898 Account *account;
1899
1901 window = GNC_PLUGIN_PAGE (page)->window;
1902 gnc_tax_info_dialog (window, account);
1903}
1904
1905static void
1906gnc_plugin_page_account_tree_cmd_lots (GSimpleAction *simple,
1907 GVariant *paramter,
1908 gpointer user_data)
1909{
1910 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1912 GtkWidget *window = GNC_PLUGIN_PAGE (page)->window;
1913 gnc_lot_viewer_dialog (GTK_WINDOW(window), account);
1914}
1915
1916static gboolean
1917scrub_kp_handler (GtkWidget *widget, GdkEventKey *event, gpointer data)
1918{
1919 if (event->length == 0) return FALSE;
1920
1921 switch (event->keyval)
1922 {
1923 case GDK_KEY_Escape:
1924 {
1925 gboolean abort_scrub = gnc_verify_dialog (GTK_WINDOW(widget), FALSE,
1926 "%s", _(check_repair_abort_YN));
1927
1928 if (abort_scrub)
1929 gnc_set_abort_scrub (TRUE);
1930
1931 return TRUE;
1932 }
1933 default:
1934 break;
1935 }
1936 return FALSE;
1937}
1938
1939static void
1940gnc_plugin_page_account_tree_cmd_scrub (GSimpleAction *simple,
1941 GVariant *paramter,
1942 gpointer user_data)
1943{
1944 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1946 GncWindow *window;
1947 gulong scrub_kp_handler_ID;
1948
1949 g_return_if_fail (account != NULL);
1950
1951 prepare_scrubbing ();
1952
1953 window = GNC_WINDOW(GNC_PLUGIN_PAGE (page)->window);
1954 scrub_kp_handler_ID = g_signal_connect (G_OBJECT(window), "key-press-event",
1955 G_CALLBACK(scrub_kp_handler), NULL);
1956 gnc_window_set_progressbar_window (window);
1957
1958 xaccAccountScrubOrphans (account, gnc_window_show_progress);
1959 xaccAccountScrubImbalance (account, gnc_window_show_progress);
1960
1961 // XXX: Lots/capital gains scrubbing is disabled
1962 if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
1963 xaccAccountScrubLots(account);
1964
1965 gncScrubBusinessAccount(account, gnc_window_show_progress);
1966
1967 finish_scrubbing (window, scrub_kp_handler_ID);
1968}
1969
1970static void
1971gnc_plugin_page_account_tree_cmd_scrub_sub (GSimpleAction *simple,
1972 GVariant *paramter,
1973 gpointer user_data)
1974{
1975 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
1977 GncWindow *window;
1978 gulong scrub_kp_handler_ID;
1979
1980 g_return_if_fail (account != NULL);
1981
1982 prepare_scrubbing ();
1983
1984 window = GNC_WINDOW(GNC_PLUGIN_PAGE (page)->window);
1985 scrub_kp_handler_ID = g_signal_connect (G_OBJECT(window), "key-press-event",
1986 G_CALLBACK(scrub_kp_handler), NULL);
1987 gnc_window_set_progressbar_window (window);
1988
1989 xaccAccountTreeScrubOrphans (account, gnc_window_show_progress);
1990 xaccAccountTreeScrubImbalance (account, gnc_window_show_progress);
1991
1992 // XXX: Lots/capital gains scrubbing is disabled
1993 if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
1994 xaccAccountTreeScrubLots(account);
1995
1996 gncScrubBusinessAccountTree(account, gnc_window_show_progress);
1997
1998 finish_scrubbing (window, scrub_kp_handler_ID);
1999}
2000
2001static void
2002gnc_plugin_page_account_tree_cmd_scrub_all (GSimpleAction *simple,
2003 GVariant *paramter,
2004 gpointer user_data)
2005{
2006 auto page = GNC_PLUGIN_PAGE_ACCOUNT_TREE(user_data);
2007 Account *root = gnc_get_current_root_account ();
2008 GncWindow *window;
2009 gulong scrub_kp_handler_ID;
2010
2011 prepare_scrubbing ();
2012
2013 window = GNC_WINDOW(GNC_PLUGIN_PAGE (page)->window);
2014 scrub_kp_handler_ID = g_signal_connect (G_OBJECT(window), "key-press-event",
2015 G_CALLBACK(scrub_kp_handler), NULL);
2016 gnc_window_set_progressbar_window (window);
2017
2018 xaccAccountTreeScrubOrphans (root, gnc_window_show_progress);
2019 xaccAccountTreeScrubImbalance (root, gnc_window_show_progress);
2020 // XXX: Lots/capital gains scrubbing is disabled
2021 if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
2022 xaccAccountTreeScrubLots(root);
2023
2024 gncScrubBusinessAccountTree(root, gnc_window_show_progress);
2025
2026 finish_scrubbing (window, scrub_kp_handler_ID);
2027}
2028
Account public routines (C++ api)
High-Level API for imposing Lot constraints.
Cleanup functions for business objects.
convert single-entry accounts to clean double-entry
API for Transactions and Splits (journal entries)
This file contains the functions to present a gui to the user for creating a new account or editing a...
This file contains the functions to present a dialog box with a list of object references and an expl...
All type declarations for the whole Gnucash engine.
GLib helper routines.
Gnome specific utility functions.
Gobject helper routines.
Provide the menus to create a chart of account page.
Functions providing a chart of account page.
Functions providing a register page for the GnuCash UI.
Generic api to store and retrieve preferences.
Functions to load, save and get gui state.
GtkTreeModel implementation to display account types in a GtkTreeView.
GtkTreeView implementation for gnucash account tree.
utility functions for the GnuCash UI
Functions that are supported by all types of windows.
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition Account.cpp:1516
void xaccAccountDestroyAllTransactions(Account *acc)
Destroy all of the transactions that parent splits in an account.
Definition Account.cpp:1600
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition Account.cpp:1475
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:3305
void gnc_account_append_child(Account *new_parent, Account *child)
This function will remove from the child account any pre-existing parent relationship,...
Definition Account.cpp:2836
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account's account type.
Definition Account.cpp:3267
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:3044
GList * gnc_account_get_children(const Account *account)
This routine returns a GList of all children accounts of the specified account.
Definition Account.cpp:2960
#define xaccAccountGetGUID(X)
Definition Account.h:252
gboolean xaccAccountIsPriced(const Account *acc)
Returns true if the account is a stock, mutual fund or currency, otherwise false.
Definition Account.cpp:4551
void xaccAccountDestroy(Account *acc)
The xaccAccountDestroy() routine can be used to get rid of an account.
Definition Account.cpp:1590
Account * gnc_account_get_parent(const Account *acc)
This routine returns a pointer to the parent of the specified account.
Definition Account.cpp:2935
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 'func' on each...
Definition Account.cpp:3244
gint gnc_account_n_children(const Account *account)
Return the number of children of the specified account.
Definition Account.cpp:2976
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity
Definition Account.cpp:3408
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition qofbook.cpp:497
GSimpleActionGroup * gnc_plugin_page_create_action_group(GncPluginPage *page, const gchar *group_name)
Create the GSimpleActionGroup object associated with this page.
GtkWidget * gnc_plugin_page_get_window(GncPluginPage *page)
Retrieve a pointer to the GncMainWindow (GtkWindow) containing this page.
void gnc_plugin_page_disconnect_page_changed(GncPluginPage *page)
Disconnect the page_changed_id signal callback.
void gnc_plugin_page_add_book(GncPluginPage *page, QofBook *book)
Add a book reference to the specified 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.
GSimpleActionGroup * gnc_plugin_page_get_action_group(GncPluginPage *page)
Retrieve the GSimpleActionGroup object associated with this page.
gboolean xaccAccountGetPlaceholder(const Account *acc)
Get the "placeholder" flag for an account.
Definition Account.cpp:4119
void xaccAccountMoveAllSplits(Account *accfrom, Account *accto)
The xaccAccountMoveAllSplits() routine reassigns each of the splits in accfrom to accto.
Definition Account.cpp:2196
const char * xaccTransGetReadOnly(Transaction *trans)
Returns a non-NULL value if this Transaction was marked as read-only with some specific "reason" text...
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
Definition guid.h:84
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:208
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.
gboolean gnc_main_window_all_finish_pending(void)
Tell all pages in all windows to finish any outstanding activities.
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.
GAction * gnc_main_window_find_action(GncMainWindow *window, const gchar *action_name)
Find the GAction in the main window.
void gnc_main_window_init_short_names(GncMainWindow *window, GncToolBarShortNames *toolbar_labels)
Update the labels of the toolbar items with short names.
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
Display a data plugin page in a window.
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_main_window_close_page(GncPluginPage *page)
Remove a data plugin page from a window and display the previous page.
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).
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).
GncPluginPage * gnc_plugin_page_account_tree_new(void)
Create a new "account tree" plugin page.
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
GKeyFile * gnc_state_get_current(void)
Returns a pointer to the most recently loaded state.
Definition gnc-state.c:248
void gnc_tree_view_configure_columns(GncTreeView *view)
Make all the correct columns visible, respecting their default visibility setting,...
const gchar * gnc_tree_view_get_state_section(GncTreeView *view)
Get the name of the state section this tree view is associated with.
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.
const GList * gnc_gobject_tracking_get_list(const gchar *name)
Get a list of all known objects of a specified type.
void gnc_ui_edit_account_window(GtkWindow *parent, Account *account)
Display a window for editing the attributes of an existing account.
void gnc_ui_new_account_window(GtkWindow *parent, QofBook *book, Account *parent_acct)
Display a window for creating a new account.
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.
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...
void gnc_tree_view_account_refilter(GncTreeViewAccount *view)
This function forces the account tree filter to be evaluated.
Account * gnc_tree_view_account_get_account_from_path(GncTreeViewAccount *view, GtkTreePath *s_path)
This function returns the account associated with the specified path.
GtkTreeView * gnc_tree_view_account_new(gboolean show_root)
Create a new account tree view.
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.
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.
void gnc_tree_view_account_set_selected_account(GncTreeViewAccount *view, Account *account)
This function selects an account in the account tree view.
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 ...
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.
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...
GList * qof_instance_get_referring_object_list(const QofInstance *inst)
Returns a list of objects which refer to a specific object.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
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
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.
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.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
GncPluginPage * gnc_plugin_page_register_new(Account *account, gboolean subaccounts)
Create a new "register" plugin page, given a pointer to an account.
void gncScrubBusinessAccountTree(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccountTreeLots() function will call gncScrubBusinessAccount() on the given accou...
void gncScrubBusinessAccount(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccount() function will call all scrub functions relevant for a given account on ...
gboolean gnc_get_ongoing_scrub(void)
The gnc_get_ongoing_scrub () method returns TRUE if a scrub operation is ongoing.
Definition Scrub.cpp:87
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 gnc_set_abort_scrub(gboolean abort)
The gnc_set_abort_scrub () method causes a currently running scrub operation to stop,...
Definition Scrub.cpp:75
void xaccAccountScrubOrphans(Account *acc, QofPercentageFunc percentagefunc)
The xaccAccountScrubOrphans() method performs this scrub only for the indicated account,...
Definition Scrub.cpp:167
void xaccAccountTreeScrubOrphans(Account *acc, QofPercentageFunc percentagefunc)
The xaccAccountTreeScrubOrphans() method performs this scrub for the indicated account and its childr...
Definition Scrub.cpp:173
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
STRUCTS.
The type used to store guids in C.
Definition guid.h:75
The class data structure for a content plugin.
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...
const gchar * tab_icon
The relative name of the icon that should be shown on the tab for this page.
const gchar * plugin_name
The textual name of this plugin.
void(* destroy_widget)(GncPluginPage *plugin_page)
Function called to destroy the display widget for a particular type of plugin.
gboolean(* finish_pending)(GncPluginPage *plugin_page)
This function vector is called to finish any outstanding activities.
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.
GtkWidget *(* create_widget)(GncPluginPage *plugin_page)
Function called to create the display widget for a particular type of plugin.
gboolean(* focus_page_function)(GncPluginPage *plugin_page)
This function performs specific actions to set the focus on a specific widget.
The instance data structure for a content plugin.
GtkWidget * window
The window that contains the display widget for this plugin.
GtkWidget * summarybar
The summary bar widget (if any) that is associated with this plugin.
A structure for defining alternate action names for use in the toolbar.