1 /********************************************************************\
2  * dialog-account.c -- window for creating and editing accounts for *
3  *                     GnuCash                                      *
4  * Copyright (C) 2000 Dave Peticolas <dave@krondo.com>              *
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 
25 #include <config.h>
26 
27 #include <gtk/gtk.h>
28 #include <glib/gi18n.h>
29 #include <math.h>
30 #ifdef G_OS_WIN32
31 #include <pow.h>
32 #endif
33 #include <string.h>
34 
35 #include "Transaction.h"
36 #include "dialog-account.h"
37 #include "dialog-commodity.h"
38 #include "dialog-utils.h"
39 #include "gnc-amount-edit.h"
40 #include "gnc-general-select.h"
41 #include "gnc-commodity.h"
42 #include "gnc-commodity-edit.h"
43 #include "gnc-component-manager.h"
44 #include "gnc-date-edit.h"
45 #include "gnc-engine.h"
46 #include "gnc-gui-query.h"
47 #include "gnc-session.h"
48 #include "gnc-tree-model-account-types.h"
49 #include "gnc-tree-view-account.h"
50 #include "gnc-ui.h"
51 #include "gnc-ui-util.h"
52 
53 
54 #define DIALOG_NEW_ACCOUNT_CM_CLASS "dialog-new-account"
55 #define DIALOG_EDIT_ACCOUNT_CM_CLASS "dialog-edit-account"
56 #define GNC_PREFS_GROUP "dialogs.account"
57 #define DEFAULT_COLOR "rgb(237,236,235)"
58 
59 enum account_cols
60 {
61     ACCOUNT_COL_FULLNAME = 0,
62     ACCOUNT_COL_FIELDNAME,
63     ACCOUNT_COL_OLD_VALUE,
64     ACCOUNT_COL_NEW_VALUE,
65     NUM_ACCOUNT_COLS
66 };
67 
68 typedef enum
69 {
70     NEW_ACCOUNT,
71     EDIT_ACCOUNT
72 } AccountDialogType;
73 
74 typedef struct _AccountWindow
75 {
76     QofBook *book;
77     gboolean modal;
78     GtkWidget *dialog;
79 
80     AccountDialogType dialog_type;
81 
82     GncGUID    account;
83     Account *created_account;
84 
85     gchar **subaccount_names;
86     gchar **next_name;
87 
88     GNCAccountType type;
89 
90     GtkWidget * notebook;
91 
92     GtkWidget * name_entry;
93     GtkWidget * description_entry;
94     GtkWidget * color_entry_button;
95     GtkWidget * color_default_button;
96     GtkWidget * code_entry;
97     GtkTextBuffer * notes_text_buffer;
98 
99     GtkWidget * commodity_edit;
100     dialog_commodity_mode commodity_mode;
101     GtkWidget * account_scu;
102 
103     guint32 valid_types;
104     GNCAccountType preferred_account_type;
105     GtkWidget * type_view;
106     GtkTreeView * parent_tree;
107     GtkWidget * parent_scroll;
108 
109     GtkWidget * opening_balance_button;
110     GtkWidget * opening_balance_edit;
111     GtkWidget * opening_balance_date_edit;
112     GtkWidget * opening_balance_page;
113 
114     GtkWidget * opening_equity_radio;
115     GtkWidget * transfer_account_scroll;
116     GtkWidget * transfer_tree;
117 
118     GtkWidget * tax_related_button;
119     GtkWidget * placeholder_button;
120     GtkWidget * hidden_button;
121     GtkWidget * auto_interest_button;
122 
123     gint component_id;
124 } AccountWindow;
125 
126 typedef struct _RenumberDialog
127 {
128     GtkWidget *dialog;
129     GtkWidget *prefix;
130     GtkWidget *interval;
131     GtkWidget *example1;
132     GtkWidget *example2;
133 
134     Account *parent;
135     gint num_children;
136 } RenumberDialog;
137 
138 /** Static Globals *******************************************************/
139 static QofLogModule log_module = GNC_MOD_GUI;
140 
141 static GNCAccountType last_used_account_type = ACCT_TYPE_BANK;
142 
143 static GList *ac_destroy_cb_list = NULL;
144 
145 /** Declarations *********************************************************/
146 static void gnc_account_window_set_name (AccountWindow *aw);
147 
148 void gnc_account_renumber_prefix_changed_cb (GtkEditable *editable, RenumberDialog *data);
149 void gnc_account_renumber_interval_changed_cb (GtkSpinButton *spinbutton, RenumberDialog *data);
150 void gnc_account_renumber_response_cb (GtkDialog *dialog, gint response, RenumberDialog *data);
151 
152 void gnc_account_window_destroy_cb (GtkWidget *object, gpointer data);
153 void opening_equity_cb (GtkWidget *w, gpointer data);
154 static void gnc_account_parent_changed_cb (GtkTreeSelection *selection, gpointer data);
155 void gnc_account_name_changed_cb(GtkWidget *widget, gpointer data);
156 void gnc_account_color_default_cb(GtkWidget *widget, gpointer data);
157 void gnc_account_name_insert_text_cb (GtkWidget   *entry,
158                                       const gchar *text,
159                                       gint         length,
160                                       gint        *position,
161                                       gpointer     data);
162 static void set_auto_interest_box (AccountWindow *aw);
163 
164 /** Implementation *******************************************************/
165 
166 static void
aw_call_destroy_callbacks(Account * acc)167 aw_call_destroy_callbacks (Account* acc)
168 {
169     GList *node;
170     void (*cb)(Account*);
171 
172     for (node = ac_destroy_cb_list; node; node = node->next)
173     {
174         cb = node->data;
175         (cb)(acc);
176     }
177 }
178 
179 static Account *
aw_get_account(AccountWindow * aw)180 aw_get_account (AccountWindow *aw)
181 {
182     if (!aw)
183         return NULL;
184 
185     return xaccAccountLookup (&aw->account, aw->book);
186 }
187 
188 static void
gnc_account_commodity_from_type(AccountWindow * aw,gboolean update)189 gnc_account_commodity_from_type (AccountWindow * aw, gboolean update)
190 {
191     dialog_commodity_mode new_mode;
192 
193     if (aw->type == ACCT_TYPE_TRADING)
194         new_mode = DIAG_COMM_ALL;
195     else if ((aw->type == ACCT_TYPE_STOCK) || (aw->type == ACCT_TYPE_MUTUAL))
196         new_mode = DIAG_COMM_NON_CURRENCY_SELECT;
197     else
198         new_mode = DIAG_COMM_CURRENCY;
199 
200     if (update && (new_mode != aw->commodity_mode))
201     {
202         gnc_general_select_set_selected(GNC_GENERAL_SELECT (aw->commodity_edit),
203                                         NULL);
204     }
205 
206     aw->commodity_mode = new_mode;
207 }
208 
209 static void
gnc_account_opening_balance_button_update(AccountWindow * aw,gnc_commodity * commodity)210 gnc_account_opening_balance_button_update (AccountWindow *aw, gnc_commodity *commodity)
211 {
212     Account *account = aw_get_account (aw);
213     Account *ob_account = gnc_account_lookup_by_opening_balance (gnc_book_get_root_account (aw->book), commodity);
214     gboolean has_splits = (xaccAccountGetSplitList (account) != NULL);
215 
216     if (aw->type != ACCT_TYPE_EQUITY)
217     {
218         gtk_widget_set_sensitive (aw->opening_balance_button, FALSE);
219         return;
220     }
221 
222     /* The opening balance flag can be edited, if the associated feature is enabled and
223      * there is no opening balance account or we are editing the only opening balance account
224      * and it has no splits assigned.
225      */
226     if (!gnc_using_equity_type_opening_balance_account (gnc_get_current_book()))
227         return;
228 
229     switch(aw->dialog_type)
230     {
231     case EDIT_ACCOUNT:
232         gtk_widget_set_sensitive (aw->opening_balance_button, (ob_account == NULL || ob_account == account) && has_splits == 0);
233         break;
234     case NEW_ACCOUNT:
235         gtk_widget_set_sensitive (aw->opening_balance_button, ob_account == NULL);
236         break;
237     }
238 }
239 
240 /* Copy the account values to the GUI widgets */
241 static void
gnc_account_to_ui(AccountWindow * aw)242 gnc_account_to_ui(AccountWindow *aw)
243 {
244     Account *account;
245     gnc_commodity * commodity;
246     const char *string;
247     GdkRGBA color;
248     gboolean flag, nonstd_scu;
249     gint index;
250 
251     ENTER("%p", aw);
252     account = aw_get_account (aw);
253     if (!account)
254     {
255         LEAVE("no account");
256         return;
257     }
258 
259     string = xaccAccountGetName (account);
260     if (string == NULL) string = "";
261     gtk_entry_set_text(GTK_ENTRY(aw->name_entry), string);
262 
263     string = xaccAccountGetDescription (account);
264     if (string == NULL) string = "";
265     gtk_entry_set_text(GTK_ENTRY(aw->description_entry), string);
266 
267     string = xaccAccountGetColor (account);
268 
269     if (!string)
270         string = DEFAULT_COLOR;
271 
272     if (!gdk_rgba_parse (&color, string))
273         gdk_rgba_parse (&color, DEFAULT_COLOR);
274 
275     gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(aw->color_entry_button), &color);
276 
277     commodity = xaccAccountGetCommodity (account);
278     gnc_general_select_set_selected (GNC_GENERAL_SELECT (aw->commodity_edit),
279                                      commodity);
280     gnc_account_commodity_from_type (aw, FALSE);
281 
282     nonstd_scu = xaccAccountGetNonStdSCU (account);
283     if (nonstd_scu)
284     {
285         index = xaccAccountGetCommoditySCUi(account);
286         index = log10(index) + 1;
287     }
288     else
289     {
290         index = 0;
291     }
292     gtk_combo_box_set_active(GTK_COMBO_BOX(aw->account_scu), index);
293 
294     string = xaccAccountGetCode (account);
295     if (string == NULL) string = "";
296     gtk_entry_set_text(GTK_ENTRY(aw->code_entry), string);
297 
298     string = xaccAccountGetNotes (account);
299     if (string == NULL) string = "";
300 
301     gtk_text_buffer_set_text (aw->notes_text_buffer, string, strlen(string));
302 
303     gnc_account_opening_balance_button_update (aw, commodity);
304 
305     flag = xaccAccountGetIsOpeningBalance (account);
306     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (aw->opening_balance_button),
307                                   flag);
308 
309     flag = xaccAccountGetTaxRelated (account);
310     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (aw->tax_related_button),
311                                   flag);
312 
313     flag = xaccAccountGetPlaceholder (account);
314     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (aw->placeholder_button),
315                                   flag);
316 
317     flag = xaccAccountGetHidden (account);
318     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (aw->hidden_button),
319                                   flag);
320 
321     set_auto_interest_box (aw);
322     LEAVE(" ");
323 }
324 
325 
326 static gboolean
gnc_account_create_transfer_balance(QofBook * book,Account * account,Account * transfer,gnc_numeric balance,time64 date)327 gnc_account_create_transfer_balance (QofBook *book,
328                                      Account *account,
329                                      Account *transfer,
330                                      gnc_numeric balance,
331                                      time64 date)
332 {
333     Transaction *trans;
334     Split *split;
335 
336     if (gnc_numeric_zero_p (balance))
337         return TRUE;
338 
339     g_return_val_if_fail (account != NULL, FALSE);
340     g_return_val_if_fail (transfer != NULL, FALSE);
341 
342     xaccAccountBeginEdit (account);
343     xaccAccountBeginEdit (transfer);
344 
345     trans = xaccMallocTransaction (book);
346 
347     xaccTransBeginEdit (trans);
348 
349     xaccTransSetCurrency (trans, gnc_account_or_default_currency (account, NULL));
350     xaccTransSetDatePostedSecsNormalized (trans, date);
351     xaccTransSetDescription (trans, _("Opening Balance"));
352 
353     split = xaccMallocSplit (book);
354 
355     xaccTransAppendSplit (trans, split);
356     xaccAccountInsertSplit (account, split);
357 
358     xaccSplitSetAmount (split, balance);
359     xaccSplitSetValue (split, balance);
360 
361     balance = gnc_numeric_neg (balance);
362 
363     split = xaccMallocSplit (book);
364 
365     xaccTransAppendSplit (trans, split);
366     xaccAccountInsertSplit (transfer, split);
367 
368     xaccSplitSetAmount (split, balance);
369     xaccSplitSetValue (split, balance);
370 
371     xaccTransCommitEdit (trans);
372     xaccAccountCommitEdit (transfer);
373     xaccAccountCommitEdit (account);
374 
375     return TRUE;
376 }
377 
378 /* Record the GUI values into the Account structure */
379 static void
gnc_ui_to_account(AccountWindow * aw)380 gnc_ui_to_account(AccountWindow *aw)
381 {
382     Account *account;
383     gnc_commodity *commodity;
384     Account *parent_account;
385     const char *old_string;
386     const char *string;
387     GdkRGBA color;
388     gboolean flag;
389     gnc_numeric balance;
390     gboolean use_equity, nonstd;
391     time64 date;
392     gint index, old_scu, new_scu;
393     GtkTextIter start, end;
394 
395     account = aw_get_account (aw);
396     if (!account)
397     {
398         LEAVE("no account");
399         return;
400     }
401 
402     if (aw->dialog_type == EDIT_ACCOUNT
403             && aw->type != xaccAccountGetType (account))
404     {
405         /* Just refreshing won't work. */
406         aw_call_destroy_callbacks (account);
407     }
408 
409     xaccAccountBeginEdit (account);
410 
411     if (aw->type != xaccAccountGetType (account))
412         xaccAccountSetType (account, aw->type);
413 
414     last_used_account_type = aw->type;
415 
416     string = gtk_entry_get_text (GTK_ENTRY(aw->name_entry));
417     old_string = xaccAccountGetName (account);
418     if (g_strcmp0 (string, old_string) != 0)
419         xaccAccountSetName (account, string);
420 
421     string = gtk_entry_get_text (GTK_ENTRY(aw->description_entry));
422     old_string = xaccAccountGetDescription (account);
423     if (g_strcmp0 (string, old_string) != 0)
424         xaccAccountSetDescription (account, string);
425 
426     gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(aw->color_entry_button), &color );
427     string = gdk_rgba_to_string(&color);
428 
429     if (g_strcmp0 (string, DEFAULT_COLOR) == 0)
430         string = NULL;
431 
432     old_string = xaccAccountGetColor (account);
433 
434     if (!string && old_string)
435         xaccAccountSetColor (account, ""); // remove entry
436     else
437     {
438         if (g_strcmp0 (string, old_string) != 0)
439             xaccAccountSetColor (account, string); // update entry
440     }
441 
442     commodity = (gnc_commodity *)
443                 gnc_general_select_get_selected (GNC_GENERAL_SELECT (aw->commodity_edit));
444     if (commodity &&
445             !gnc_commodity_equiv(commodity, xaccAccountGetCommodity (account)))
446     {
447         xaccAccountSetCommodity (account, commodity);
448         old_scu = 0;
449     }
450     else
451     {
452         old_scu = xaccAccountGetCommoditySCU(account);
453     }
454 
455     index = gtk_combo_box_get_active(GTK_COMBO_BOX(aw->account_scu));
456     nonstd = (index != 0);
457     if (nonstd != xaccAccountGetNonStdSCU(account))
458         xaccAccountSetNonStdSCU(account, nonstd);
459     new_scu = (nonstd ? pow(10, index - 1) : gnc_commodity_get_fraction(commodity));
460     if (old_scu != new_scu)
461         xaccAccountSetCommoditySCU(account, new_scu);
462 
463     string = gtk_entry_get_text (GTK_ENTRY(aw->code_entry));
464     old_string = xaccAccountGetCode (account);
465     if (g_strcmp0 (string, old_string) != 0)
466         xaccAccountSetCode (account, string);
467 
468     gtk_text_buffer_get_start_iter (aw->notes_text_buffer, &start);
469     gtk_text_buffer_get_end_iter (aw->notes_text_buffer, &end);
470     string = gtk_text_buffer_get_text (aw->notes_text_buffer, &start, &end, FALSE);
471     old_string = xaccAccountGetNotes (account);
472     if (null_strcmp (string, old_string) != 0)
473         xaccAccountSetNotes (account, string);
474 
475     flag =
476         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (aw->opening_balance_button));
477     if (xaccAccountGetIsOpeningBalance (account) != flag)
478         xaccAccountSetIsOpeningBalance (account, flag);
479 
480     flag =
481         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (aw->tax_related_button));
482     if (xaccAccountGetTaxRelated (account) != flag)
483         xaccAccountSetTaxRelated (account, flag);
484 
485     flag =
486         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (aw->placeholder_button));
487     if (xaccAccountGetPlaceholder (account) != flag)
488         xaccAccountSetPlaceholder (account, flag);
489 
490     flag =
491         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (aw->hidden_button));
492     if (xaccAccountGetHidden (account) != flag)
493         xaccAccountSetHidden (account, flag);
494 
495     flag =
496     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (aw->auto_interest_button));
497     if (xaccAccountGetAutoInterest (account) != flag)
498         xaccAccountSetAutoInterest (account, flag);
499 
500     parent_account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT (aw->parent_tree));
501 
502     if (parent_account == NULL)
503         parent_account = gnc_book_get_root_account(aw->book);
504     if (parent_account != gnc_account_get_parent (account))
505         gnc_account_append_child (parent_account, account);
506 
507     xaccAccountCommitEdit (account);
508 
509     balance = gnc_amount_edit_get_amount
510               (GNC_AMOUNT_EDIT (aw->opening_balance_edit));
511 
512     if (gnc_numeric_zero_p (balance))
513     {
514         LEAVE("zero balance");
515         return;
516     }
517 
518     if (gnc_reverse_balance (account))
519         balance = gnc_numeric_neg (balance);
520 
521     date = gnc_date_edit_get_date (
522                GNC_DATE_EDIT (aw->opening_balance_date_edit));
523 
524     use_equity = gtk_toggle_button_get_active
525                  (GTK_TOGGLE_BUTTON (aw->opening_equity_radio));
526 
527     if (use_equity)
528     {
529         if (!gnc_account_create_opening_balance (account, balance, date, aw->book))
530         {
531             const char *message = _("Could not create opening balance.");
532             gnc_error_dialog (GTK_WINDOW (aw->dialog), "%s", message);
533         }
534     }
535     else
536     {
537         Account *transfer = NULL;
538 
539         transfer = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT (aw->transfer_tree));
540         if (!transfer)
541         {
542             LEAVE("no transfer account");
543             return;
544         }
545 
546         gnc_account_create_transfer_balance (aw->book, account, transfer, balance, date);
547     }
548     LEAVE(" ");
549 }
550 
551 
552 static void
set_children_types(Account * account,GNCAccountType type)553 set_children_types (Account *account, GNCAccountType type)
554 {
555     GList *children, *iter;
556 
557     children = gnc_account_get_children(account);
558     if (children == NULL)
559         return;
560 
561     for (iter = children; iter; iter = iter->next)
562     {
563         account = iter->data;
564         if (type == xaccAccountGetType(account))
565             continue;
566 
567         /* Just refreshing won't work. */
568         aw_call_destroy_callbacks (account);
569 
570         xaccAccountBeginEdit (account);
571         xaccAccountSetType (account, type);
572         xaccAccountCommitEdit (account);
573 
574         set_children_types (account, type);
575     }
576     g_list_free(children);
577 }
578 
579 static void
make_children_compatible(AccountWindow * aw)580 make_children_compatible (AccountWindow *aw)
581 {
582     Account *account;
583 
584     g_return_if_fail (aw);
585 
586     if (aw->dialog_type == NEW_ACCOUNT)
587         return;
588 
589     account = aw_get_account (aw);
590     g_return_if_fail (account);
591 
592     if (xaccAccountTypesCompatible (aw->type, xaccAccountGetType (account)))
593         return;
594 
595     set_children_types (account, aw->type);
596 }
597 
598 
599 static void
gnc_finish_ok(AccountWindow * aw)600 gnc_finish_ok (AccountWindow *aw)
601 {
602     ENTER("aw %p", aw);
603     gnc_suspend_gui_refresh ();
604 
605     /* make the account changes */
606     make_children_compatible (aw);
607     gnc_ui_to_account (aw);
608 
609     gnc_resume_gui_refresh ();
610 
611     /* do it all again, if needed */
612     if ((aw->dialog_type == NEW_ACCOUNT) && aw->next_name && *aw->next_name)
613     {
614         gnc_commodity *commodity;
615         Account *parent;
616         Account *account;
617         GtkTreeSelection *selection;
618 
619         /* Drop the old parent_tree so we can update it with an up to date one */
620         gtk_container_remove (GTK_CONTAINER(aw->parent_scroll), GTK_WIDGET(aw->parent_tree));
621         aw->parent_tree = gnc_tree_view_account_new (TRUE);
622         gtk_container_add (GTK_CONTAINER(aw->parent_scroll), GTK_WIDGET(aw->parent_tree));
623         gtk_widget_show (GTK_WIDGET(aw->parent_tree));
624 
625         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (aw->parent_tree));
626         g_signal_connect (G_OBJECT(selection), "changed",
627                           G_CALLBACK(gnc_account_parent_changed_cb), aw);
628 
629         gnc_suspend_gui_refresh ();
630 
631         parent = aw_get_account (aw);
632         account = xaccMallocAccount (aw->book);
633         aw->account = *xaccAccountGetGUID (account);
634         aw->type = xaccAccountGetType (parent);
635 
636         xaccAccountSetName (account, *aw->next_name);
637         aw->next_name++;
638 
639         gnc_account_to_ui (aw);
640 
641         gnc_account_window_set_name (aw);
642 
643         commodity = xaccAccountGetCommodity (parent);
644         gnc_general_select_set_selected (GNC_GENERAL_SELECT (aw->commodity_edit),
645                                          commodity);
646         gnc_account_commodity_from_type (aw, FALSE);
647 
648         gnc_tree_view_account_set_selected_account (
649             GNC_TREE_VIEW_ACCOUNT (aw->parent_tree), parent);
650 
651         gnc_resume_gui_refresh ();
652         LEAVE("1");
653         return;
654     }
655 
656     /* save for posterity */
657     aw->created_account = aw_get_account (aw);
658 
659     /* so it doesn't get freed on close */
660     aw->account = *guid_null ();
661 
662     gnc_close_gui_component (aw->component_id);
663     LEAVE("2");
664 }
665 
666 
667 static void
add_children_to_expander(GObject * object,GParamSpec * param_spec,gpointer data)668 add_children_to_expander (GObject *object, GParamSpec *param_spec, gpointer data)
669 {
670     GtkExpander *expander = GTK_EXPANDER (object);
671     Account *account = data;
672     GtkWidget *scrolled_window;
673     GtkTreeView *view;
674 
675     if (gtk_expander_get_expanded (expander) &&
676             !gtk_bin_get_child (GTK_BIN (expander)))
677     {
678 
679         view = gnc_tree_view_account_new_with_root (account, FALSE);
680 
681         scrolled_window = gtk_scrolled_window_new (NULL, NULL);
682         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
683                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
684         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
685                                              GTK_SHADOW_IN);
686         gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (view));
687 
688         gtk_container_add (GTK_CONTAINER (expander), scrolled_window);
689         gtk_widget_set_vexpand (GTK_WIDGET(scrolled_window), TRUE);
690         gtk_widget_show_all (scrolled_window);
691     }
692 }
693 
694 /* Check whether there are children needing a type adjustment because of a
695    a change to an incompatible type (like after some reparenting) and let the
696    user decide whether he wants that */
697 static gboolean
verify_children_compatible(AccountWindow * aw)698 verify_children_compatible (AccountWindow *aw)
699 {
700     Account *account;
701     GtkWidget *dialog, *vbox, *hbox, *label, *expander;
702     gchar *str;
703     gboolean result;
704 
705     if (aw == NULL)
706         return FALSE;
707 
708     account = aw_get_account (aw);
709     if (!account)
710         return FALSE;
711 
712     if (xaccAccountTypesCompatible (aw->type, xaccAccountGetType (account)))
713         return TRUE;
714 
715     if (gnc_account_n_children(account) == 0)
716         return TRUE;
717 
718     dialog = gtk_dialog_new_with_buttons ("",
719                                           GTK_WINDOW(aw->dialog),
720                                           GTK_DIALOG_DESTROY_WITH_PARENT |
721                                           GTK_DIALOG_MODAL,
722                                           _("_Cancel"), GTK_RESPONSE_CANCEL,
723                                           _("_OK"), GTK_RESPONSE_OK,
724                                           NULL);
725 
726     gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), TRUE);
727 
728     hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
729     gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
730     vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
731     gtk_box_set_homogeneous (GTK_BOX (vbox), FALSE);
732 
733     gtk_box_pack_start (
734         GTK_BOX (hbox),
735         gtk_image_new_from_icon_name ("dialog-information", GTK_ICON_SIZE_DIALOG),
736         FALSE, FALSE, 0);
737 
738     /* primary label */
739     label = gtk_label_new (_("Give the children the same type?"));
740     gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
741     gtk_label_set_selectable (GTK_LABEL (label), TRUE);
742     gnc_label_set_alignment (label, 0.0, 0.0);
743 
744     /* make label large */
745     gnc_widget_style_context_add_class (GTK_WIDGET(label), "gnc-class-title");
746 
747     gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
748 
749     /* secondary label */
750     str = g_strdup_printf (_("The children of the edited account have to be "
751                              "changed to type \"%s\" to make them compatible."),
752                            xaccAccountGetTypeStr (aw->type));
753     label = gtk_label_new (str);
754     g_free (str);
755     gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
756     gtk_label_set_selectable (GTK_LABEL (label), TRUE);
757     gnc_label_set_alignment (label, 0.0, 0.0);
758     gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
759 
760     /* children */
761     expander = gtk_expander_new_with_mnemonic (_("_Show children accounts"));
762     gtk_expander_set_spacing (GTK_EXPANDER (expander), 6);
763     g_signal_connect (G_OBJECT (expander), "notify::expanded",
764                       G_CALLBACK (add_children_to_expander), account);
765     gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
766 
767     gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
768 
769     gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), hbox,
770                         TRUE, TRUE, 0);
771 
772     /* spacings */
773     gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
774     gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
775     gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 14);
776 
777     gtk_widget_show_all (hbox);
778 
779     gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
780 
781     result = (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK);
782 
783     gtk_widget_destroy(dialog);
784 
785     return result;
786 }
787 
788 
789 static gboolean
gnc_filter_parent_accounts(Account * account,gpointer data)790 gnc_filter_parent_accounts (Account *account, gpointer data)
791 {
792     AccountWindow *aw = data;
793     Account *aw_account = aw_get_account (aw);
794 
795     if (account == NULL)
796         return FALSE;
797 
798     if (aw_account == NULL)
799         return FALSE;
800 
801     if (gnc_account_is_root(account))
802         return TRUE;
803 
804     if (account == aw_account)
805         return FALSE;
806 
807     if (xaccAccountHasAncestor(account, aw_account))
808         return FALSE;
809 
810     return TRUE;
811 }
812 
813 
814 static gboolean
gnc_common_ok(AccountWindow * aw)815 gnc_common_ok (AccountWindow *aw)
816 {
817     Account *root, *account, *parent;
818     gnc_commodity * commodity;
819     gchar *fullname, *fullname_parent;
820     const gchar *name, *separator;
821 
822     ENTER("aw %p", aw);
823     root = gnc_book_get_root_account (aw->book);
824 
825     separator = gnc_get_account_separator_string();
826 
827     /* check for valid name */
828     name = gtk_entry_get_text(GTK_ENTRY(aw->name_entry));
829     if (g_strcmp0(name, "") == 0)
830     {
831         const char *message = _("The account must be given a name.");
832         gnc_error_dialog (GTK_WINDOW (aw->dialog), "%s", message);
833         LEAVE("bad name");
834         return FALSE;
835     }
836 
837     /* check for a duplicate name */
838     parent = gnc_tree_view_account_get_selected_account
839              (GNC_TREE_VIEW_ACCOUNT (aw->parent_tree));
840     if (parent == NULL)
841     {
842         account = gnc_account_lookup_by_full_name(root, name);
843     }
844     else
845     {
846         fullname_parent = gnc_account_get_full_name(parent);
847         fullname = g_strconcat(fullname_parent, separator, name, NULL);
848 
849         account = gnc_account_lookup_by_full_name(root, fullname);
850 
851         g_free(fullname_parent);
852         g_free(fullname);
853     }
854     if ((account != NULL) &&
855             !guid_equal(&aw->account, xaccAccountGetGUID (account)))
856     {
857         const char *message = _("There is already an account with that name.");
858         gnc_error_dialog (GTK_WINDOW (aw->dialog), "%s", message);
859         LEAVE("duplicate name");
860         return FALSE;
861     }
862 
863     /* Parent check, probably not needed, but be safe */
864     if (!gnc_filter_parent_accounts(parent, aw))
865     {
866         const char *message = _("You must choose a valid parent account.");
867         gnc_error_dialog (GTK_WINDOW (aw->dialog), "%s", message);
868         LEAVE("invalid parent");
869         return FALSE;
870     }
871 
872     /* check for valid type */
873     if (aw->type == ACCT_TYPE_INVALID)
874     {
875         const char *message = _("You must select an account type.");
876         gnc_error_dialog (GTK_WINDOW (aw->dialog), "%s", message);
877         LEAVE("invalid type");
878         return FALSE;
879     }
880 
881     /* check whether the types of child and parent are compatible */
882     if (!xaccAccountTypesCompatible (xaccAccountGetType (parent), aw->type))
883     {
884         const char *message = _("The selected account type is incompatible with "
885                                 "the one of the selected parent.");
886         gnc_error_dialog (GTK_WINDOW (aw->dialog), "%s", message);
887         LEAVE("incompatible types");
888         return FALSE;
889     }
890 
891     /* check for commodity */
892     commodity = (gnc_commodity *)
893                 gnc_general_select_get_selected (GNC_GENERAL_SELECT (aw->commodity_edit));
894     if (!commodity)
895     {
896         const char *message = _("You must choose a commodity.");
897         gnc_error_dialog (GTK_WINDOW (aw->dialog), "%s", message);
898         LEAVE("invalid commodity");
899         return FALSE;
900     }
901 
902     LEAVE("passed");
903     return TRUE;
904 }
905 
906 static void
gnc_edit_account_ok(AccountWindow * aw)907 gnc_edit_account_ok(AccountWindow *aw)
908 {
909     Account *account;
910 
911     ENTER("aw %p", aw);
912 
913     account = aw_get_account (aw);
914     if (!account)
915     {
916         LEAVE(" ");
917         return;
918     }
919 
920     if (!gnc_common_ok(aw))
921     {
922         LEAVE(" ");
923         return;
924     }
925 
926     if (!verify_children_compatible (aw))
927     {
928         LEAVE(" ");
929         return;
930     }
931 
932     gnc_finish_ok (aw);
933     LEAVE(" ");
934 }
935 
936 
937 static void
gnc_new_account_ok(AccountWindow * aw)938 gnc_new_account_ok (AccountWindow *aw)
939 {
940     gnc_numeric balance;
941 
942     ENTER("aw %p", aw);
943 
944     if (!gnc_common_ok(aw))
945     {
946         LEAVE(" ");
947         return;
948     }
949 
950     if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (aw->opening_balance_edit), NULL))
951     {
952         const char *message = _("You must enter a valid opening balance "
953                                 "or leave it blank.");
954         gnc_error_dialog (GTK_WINDOW (aw->dialog), "%s", message);
955         LEAVE(" ");
956         return;
957     }
958 
959     balance = gnc_amount_edit_get_amount
960               (GNC_AMOUNT_EDIT (aw->opening_balance_edit));
961 
962     if (!gnc_numeric_zero_p (balance))
963     {
964         gboolean use_equity;
965 
966         use_equity = gtk_toggle_button_get_active
967                      (GTK_TOGGLE_BUTTON (aw->opening_equity_radio));
968 
969         if (!use_equity)
970         {
971             Account *transfer = NULL;
972 
973             transfer = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT (aw->transfer_tree));
974             if (!transfer)
975             {
976                 const char *message = _("You must select a transfer account or choose"
977                                         " the opening balances equity account.");
978                 gnc_error_dialog (GTK_WINDOW (aw->dialog), "%s", message);
979                 LEAVE(" ");
980                 return;
981             }
982         }
983     }
984 
985     gnc_finish_ok (aw);
986     LEAVE(" ");
987 }
988 
989 static void
gnc_account_window_response_cb(GtkDialog * dialog,gint response,gpointer data)990 gnc_account_window_response_cb (GtkDialog *dialog,
991                                 gint response,
992                                 gpointer data)
993 {
994     AccountWindow *aw = data;
995 
996     ENTER("dialog %p, response %d, aw %p", dialog, response, aw);
997     switch (response)
998     {
999     case GTK_RESPONSE_OK:
1000         switch (aw->dialog_type)
1001         {
1002         case NEW_ACCOUNT:
1003             DEBUG("new acct dialog, OK");
1004             gnc_new_account_ok (aw);
1005             break;
1006         case EDIT_ACCOUNT:
1007             DEBUG("edit acct dialog, OK");
1008             gnc_edit_account_ok (aw);
1009             break;
1010         default:
1011             g_assert_not_reached ();
1012             return;
1013         }
1014         break;
1015     case GTK_RESPONSE_HELP:
1016         switch (aw->dialog_type)
1017         {
1018         case NEW_ACCOUNT:
1019             DEBUG("new acct dialog, HELP");
1020             gnc_gnome_help (GTK_WINDOW(dialog), HF_HELP, HL_ACC);
1021             break;
1022         case EDIT_ACCOUNT:
1023             DEBUG("edit acct dialog, HELP");
1024             gnc_gnome_help (GTK_WINDOW(dialog), HF_HELP, HL_ACCEDIT);
1025             break;
1026         default:
1027             g_assert_not_reached ();
1028             return;
1029         }
1030         break;
1031     case GTK_RESPONSE_CANCEL:
1032     default:
1033         DEBUG("CANCEL");
1034         gnc_close_gui_component (aw->component_id);
1035         break;
1036     }
1037     LEAVE(" ");
1038 }
1039 
1040 void
gnc_account_window_destroy_cb(GtkWidget * object,gpointer data)1041 gnc_account_window_destroy_cb (GtkWidget *object, gpointer data)
1042 {
1043     AccountWindow *aw = data;
1044     Account *account;
1045 
1046     ENTER("object %p, aw %p", object, aw);
1047     account = aw_get_account (aw);
1048 
1049     gnc_suspend_gui_refresh ();
1050 
1051     switch (aw->dialog_type)
1052     {
1053     case NEW_ACCOUNT:
1054         if (account != NULL)
1055         {
1056             xaccAccountBeginEdit (account);
1057             xaccAccountDestroy (account);
1058             aw->account = *guid_null ();
1059         }
1060 
1061         DEBUG ("account add window destroyed\n");
1062         break;
1063 
1064     case EDIT_ACCOUNT:
1065         break;
1066 
1067     default:
1068         PERR ("unexpected dialog type\n");
1069         gnc_resume_gui_refresh ();
1070         LEAVE(" ");
1071         return;
1072     }
1073 
1074     gnc_unregister_gui_component (aw->component_id);
1075 
1076     gnc_resume_gui_refresh ();
1077 
1078     if (aw->subaccount_names)
1079     {
1080         g_strfreev(aw->subaccount_names);
1081         aw->subaccount_names = NULL;
1082         aw->next_name = NULL;
1083     }
1084 
1085     g_free (aw);
1086     LEAVE(" ");
1087 }
1088 
1089 static void
gnc_account_parent_changed_cb(GtkTreeSelection * selection,gpointer data)1090 gnc_account_parent_changed_cb (GtkTreeSelection *selection, gpointer data)
1091 {
1092     AccountWindow *aw = data;
1093     Account *parent_account;
1094     guint32 types, old_types;
1095     GtkTreeModel *type_model;
1096     GtkTreeSelection *type_selection;
1097     gboolean scroll_to = FALSE;
1098 
1099     g_return_if_fail (aw);
1100 
1101     parent_account = gnc_tree_view_account_get_selected_account (
1102                          GNC_TREE_VIEW_ACCOUNT (aw->parent_tree));
1103     if (!parent_account)
1104         return;
1105 
1106     if (gnc_account_is_root(parent_account))
1107     {
1108         types = aw->valid_types;
1109     }
1110     else
1111     {
1112         types = aw->valid_types &
1113                 xaccParentAccountTypesCompatibleWith (xaccAccountGetType (parent_account));
1114     }
1115 
1116     type_model = gtk_tree_view_get_model (GTK_TREE_VIEW (aw->type_view));
1117     if (!type_model)
1118         return;
1119 
1120     if (aw->type != aw->preferred_account_type &&
1121             (types & (1 << aw->preferred_account_type)) != 0)
1122     {
1123         /* we can change back to the preferred account type */
1124         aw->type = aw->preferred_account_type;
1125         scroll_to = TRUE;
1126     }
1127     else if ((types & (1 << aw->type)) == 0)
1128     {
1129         /* our type is invalid now */
1130         aw->type = ACCT_TYPE_INVALID;
1131     }
1132     else
1133     {
1134         /* no type change, but maybe list of valid types changed */
1135         old_types = gnc_tree_model_account_types_get_mask (type_model);
1136         if (old_types != types)
1137             scroll_to = TRUE;
1138     }
1139 
1140     gnc_tree_model_account_types_set_mask (type_model, types);
1141 
1142     if (scroll_to)
1143     {
1144         type_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (aw->type_view));
1145         gnc_tree_model_account_types_set_selection(type_selection, 1 << aw->type);
1146     }
1147 
1148     gnc_account_window_set_name(aw);
1149 }
1150 
1151 static void
set_auto_interest_box(AccountWindow * aw)1152 set_auto_interest_box(AccountWindow *aw)
1153 {
1154     Account* account = aw_get_account (aw);
1155     gboolean type_ok = account_type_has_auto_interest_xfer (aw->type);
1156     gboolean pref_set = xaccAccountGetAutoInterest (account);
1157     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (aw->auto_interest_button),
1158                                   type_ok && pref_set);
1159     gtk_widget_set_sensitive (GTK_WIDGET (aw->auto_interest_button), type_ok);
1160 }
1161 
1162 static void
gnc_account_type_changed_cb(GtkTreeSelection * selection,gpointer data)1163 gnc_account_type_changed_cb (GtkTreeSelection *selection, gpointer data)
1164 {
1165     AccountWindow *aw = data;
1166     gboolean sensitive;
1167     GNCAccountType type_id;
1168 
1169     g_return_if_fail (aw != NULL);
1170 
1171     sensitive = FALSE;
1172 
1173     type_id = gnc_tree_model_account_types_get_selection_single(selection);
1174     if (type_id == ACCT_TYPE_NONE)
1175     {
1176         aw->type = ACCT_TYPE_INVALID;
1177     }
1178     else
1179     {
1180         aw->type = type_id;
1181         aw->preferred_account_type = type_id;
1182 
1183         gnc_account_commodity_from_type (aw, TRUE);
1184 
1185         sensitive = (aw->type != ACCT_TYPE_EQUITY &&
1186                      aw->type != ACCT_TYPE_CURRENCY &&
1187                      aw->type != ACCT_TYPE_STOCK &&
1188                      aw->type != ACCT_TYPE_MUTUAL &&
1189                      aw->type != ACCT_TYPE_TRADING);
1190     }
1191 
1192     gtk_widget_set_sensitive (aw->opening_balance_page, sensitive);
1193 
1194     if (!sensitive)
1195     {
1196         gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (aw->opening_balance_edit),
1197                                     gnc_numeric_zero ());
1198     }
1199     set_auto_interest_box(aw);
1200 }
1201 
1202 static void
gnc_account_type_view_create(AccountWindow * aw,guint32 compat_types)1203 gnc_account_type_view_create (AccountWindow *aw, guint32 compat_types)
1204 {
1205     GtkTreeModel *model;
1206     GtkTreeSelection *selection;
1207     GtkCellRenderer *renderer;
1208     GtkTreeView *view;
1209 
1210     aw->valid_types &= compat_types;
1211     if (aw->valid_types == 0)
1212     {
1213         /* no type restrictions, choose aw->type */
1214         aw->valid_types = compat_types | (1 << aw->type);
1215         aw->preferred_account_type = aw->type;
1216     }
1217     else if ((aw->valid_types & (1 << aw->type)) != 0)
1218     {
1219         /* aw->type is valid */
1220         aw->preferred_account_type = aw->type;
1221     }
1222     else if ((aw->valid_types & (1 << last_used_account_type)) != 0)
1223     {
1224         /* last used account type is valid */
1225         aw->type = last_used_account_type;
1226         aw->preferred_account_type = last_used_account_type;
1227     }
1228     else
1229     {
1230         /* choose first valid account type */
1231         int i;
1232         aw->preferred_account_type = aw->type;
1233         aw->type = ACCT_TYPE_INVALID;
1234         for (i = 0; i < 32; i++)
1235             if ((aw->valid_types & (1 << i)) != 0)
1236             {
1237                 aw->type = i;
1238                 break;
1239             }
1240     }
1241 
1242     model = gnc_tree_model_account_types_filter_using_mask (aw->valid_types);
1243 
1244     view = GTK_TREE_VIEW (aw->type_view);
1245     gtk_tree_view_set_model (view, model);
1246     g_object_unref (G_OBJECT (model));
1247 
1248     renderer = gtk_cell_renderer_text_new ();
1249     gtk_tree_view_insert_column_with_attributes (
1250         view, -1, NULL, renderer,
1251         "text", GNC_TREE_MODEL_ACCOUNT_TYPES_COL_NAME,
1252         NULL);
1253     gtk_tree_view_set_search_column (view, GNC_TREE_MODEL_ACCOUNT_TYPES_COL_NAME);
1254 
1255     selection = gtk_tree_view_get_selection (view);
1256     g_signal_connect (G_OBJECT (selection), "changed",
1257                       G_CALLBACK (gnc_account_type_changed_cb), aw);
1258 
1259     gnc_tree_model_account_types_set_selection(selection, 1 << aw->type);
1260 }
1261 
1262 void
gnc_account_name_insert_text_cb(GtkWidget * entry,const gchar * text,gint length,gint * position,gpointer data)1263 gnc_account_name_insert_text_cb (GtkWidget   *entry,
1264                                  const gchar *text,
1265                                  gint         length,
1266                                  gint        *position,
1267                                  gpointer     data)
1268 {
1269     GtkEditable *editable = GTK_EDITABLE( entry );
1270     const gchar *separator = NULL;
1271     gchar **strsplit;
1272 
1273     separator = gnc_get_account_separator_string();
1274     strsplit = g_strsplit ( text, separator, 0 );
1275     if ( strsplit[1] != NULL )
1276     {
1277         gchar *result = g_strjoinv ( NULL, strsplit );
1278         g_signal_handlers_block_by_func ( G_OBJECT ( editable ),
1279                                           G_CALLBACK ( gnc_account_name_insert_text_cb ),
1280                                           data );
1281         gtk_editable_insert_text ( editable, result, g_utf8_strlen ( result, -1 ), position );
1282         g_signal_handlers_unblock_by_func ( G_OBJECT ( editable ),
1283                                             G_CALLBACK ( gnc_account_name_insert_text_cb ),
1284                                             data );
1285         g_signal_stop_emission_by_name (G_OBJECT ( editable ), "insert_text");
1286         g_free (result);
1287     }
1288 
1289     g_strfreev ( strsplit );
1290 }
1291 
1292 void
gnc_account_name_changed_cb(GtkWidget * widget,gpointer data)1293 gnc_account_name_changed_cb(GtkWidget *widget, gpointer data)
1294 {
1295     AccountWindow *aw = data;
1296 
1297     gnc_account_window_set_name (aw);
1298 }
1299 
1300 void
gnc_account_color_default_cb(GtkWidget * widget,gpointer data)1301 gnc_account_color_default_cb(GtkWidget *widget, gpointer data)
1302 {
1303     GdkRGBA color;
1304     AccountWindow *aw = data;
1305 
1306     gdk_rgba_parse(&color, DEFAULT_COLOR);
1307     gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(aw->color_entry_button), &color);
1308 
1309 }
1310 
1311 static void
commodity_changed_cb(GNCGeneralSelect * gsl,gpointer data)1312 commodity_changed_cb (GNCGeneralSelect *gsl, gpointer data)
1313 {
1314     AccountWindow *aw = data;
1315     gnc_commodity *currency;
1316     GtkTreeSelection *selection;
1317     Account *account = aw_get_account (aw);
1318 
1319     currency = (gnc_commodity *) gnc_general_select_get_selected (gsl);
1320     if (!currency)
1321         return;
1322 
1323     if (xaccAccountGetIsOpeningBalance (account))
1324     {
1325         Account *ob_account = gnc_account_lookup_by_opening_balance (gnc_book_get_root_account (aw->book), currency);
1326         if (ob_account != account)
1327         {
1328             gchar *dialog_msg = _("An account with opening balance already exists for the desired currency.");
1329             gchar *dialog_title = _("Cannot change currency");
1330             GtkWidget *dialog = gtk_message_dialog_new (gnc_ui_get_main_window (NULL),
1331                                                         0,
1332                                                         GTK_MESSAGE_ERROR,
1333                                                         GTK_BUTTONS_OK,
1334                                                         "%s", dialog_title);
1335             gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG(dialog),
1336                                  "%s", dialog_msg);
1337             gtk_dialog_run(GTK_DIALOG(dialog));
1338             gtk_widget_destroy(dialog);
1339             g_signal_handlers_block_by_func (gsl, commodity_changed_cb, data);
1340             gnc_general_select_set_selected (gsl, xaccAccountGetCommodity (account));
1341             g_signal_handlers_unblock_by_func (gsl, commodity_changed_cb, data);
1342             return;
1343         }
1344     }
1345 
1346     gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (aw->opening_balance_edit),
1347                                   gnc_commodity_get_fraction (currency));
1348     gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (aw->opening_balance_edit),
1349                                     gnc_commodity_print_info (currency, FALSE));
1350 
1351     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (aw->transfer_tree));
1352     gtk_tree_selection_unselect_all (selection);
1353     gnc_account_opening_balance_button_update (aw, currency);
1354 }
1355 
1356 static gboolean
account_commodity_filter(GtkTreeSelection * selection,GtkTreeModel * unused_model,GtkTreePath * s_path,gboolean path_currently_selected,gpointer user_data)1357 account_commodity_filter (GtkTreeSelection *selection,
1358                           GtkTreeModel *unused_model,
1359                           GtkTreePath *s_path,
1360                           gboolean path_currently_selected,
1361                           gpointer user_data)
1362 {
1363     gnc_commodity *commodity;
1364     AccountWindow *aw;
1365     Account *account;
1366 
1367     g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
1368 
1369     aw = user_data;
1370 
1371     if (path_currently_selected)
1372     {
1373         /* already selected, don't waste time. */
1374         return TRUE;
1375     }
1376 
1377     account = gnc_tree_view_account_get_account_from_path (GNC_TREE_VIEW_ACCOUNT (aw->transfer_tree), s_path);
1378     if (!account)
1379     {
1380         return FALSE;
1381     }
1382 
1383     commodity = (gnc_commodity *)
1384                 gnc_general_select_get_selected (GNC_GENERAL_SELECT (aw->commodity_edit));
1385 
1386     return gnc_commodity_equiv (xaccAccountGetCommodity (account), commodity);
1387 }
1388 
1389 void
opening_equity_cb(GtkWidget * w,gpointer data)1390 opening_equity_cb (GtkWidget *w, gpointer data)
1391 {
1392     AccountWindow *aw = data;
1393     gboolean use_equity;
1394 
1395     use_equity = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w));
1396 
1397     gtk_widget_set_sensitive (aw->transfer_account_scroll, !use_equity);
1398 }
1399 
1400 /********************************************************************\
1401  * gnc_account_window_create                                        *
1402  *   creates a window to create a new account.                      *
1403  *                                                                  *
1404  * Args:   parent - the parent window dialog                        *
1405  * Args:   aw - the information structure for this window           *
1406  * Return: the created window                                       *
1407  \*******************************************************************/
1408 static void
gnc_account_window_create(GtkWindow * parent,AccountWindow * aw)1409 gnc_account_window_create(GtkWindow *parent, AccountWindow *aw)
1410 {
1411     GtkWidget *amount;
1412     GtkWidget *date_edit;
1413     GObject *awo;
1414     GtkWidget *box;
1415     GtkWidget *label;
1416     GtkBuilder  *builder;
1417     GtkTreeSelection *selection;
1418     const gchar *tt = _("This Account contains Transactions.\nChanging this option is not possible.");
1419     guint32 compat_types = xaccAccountTypesValid ();
1420 
1421     ENTER("aw %p, modal %d", aw, aw->modal);
1422     builder = gtk_builder_new();
1423     gnc_builder_add_from_file (builder, "dialog-account.glade", "fraction_liststore");
1424     gnc_builder_add_from_file (builder, "dialog-account.glade", "account_dialog");
1425 
1426     aw->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "account_dialog"));
1427     awo = G_OBJECT (aw->dialog);
1428 
1429     if (parent)
1430         gtk_window_set_transient_for (GTK_WINDOW (aw->dialog), parent);
1431 
1432     // Set the name for this dialog so it can be easily manipulated with css
1433     gtk_widget_set_name (GTK_WIDGET(aw->dialog), "gnc-id-account");
1434     gnc_widget_style_context_add_class (GTK_WIDGET(aw->dialog), "gnc-class-account");
1435 
1436 
1437     g_object_set_data (awo, "dialog_info", aw);
1438 
1439     if (!aw->modal)
1440         g_signal_connect (awo, "response",
1441                           G_CALLBACK (gnc_account_window_response_cb), aw);
1442     else
1443         gtk_window_set_modal (GTK_WINDOW (aw->dialog), TRUE);
1444 
1445     aw->notebook = GTK_WIDGET(gtk_builder_get_object (builder, "account_notebook"));
1446     aw->name_entry = GTK_WIDGET(gtk_builder_get_object (builder, "name_entry"));
1447     aw->description_entry = GTK_WIDGET(gtk_builder_get_object (builder, "description_entry"));
1448     aw->color_entry_button = GTK_WIDGET(gtk_builder_get_object (builder, "color_entry_button"));
1449     aw->color_default_button = GTK_WIDGET(gtk_builder_get_object (builder, "color_default_button"));
1450     aw->code_entry =        GTK_WIDGET(gtk_builder_get_object (builder, "code_entry"));
1451     aw->notes_text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (GTK_WIDGET(gtk_builder_get_object (builder, "notes_text"))));
1452 
1453     box = GTK_WIDGET(gtk_builder_get_object (builder, "commodity_hbox"));
1454     aw->commodity_edit = gnc_general_select_new (GNC_GENERAL_SELECT_TYPE_SELECT,
1455                          gnc_commodity_edit_get_string,
1456                          gnc_commodity_edit_new_select,
1457                          &aw->commodity_mode);
1458 
1459     // If the account has transactions, prevent changes by displaying a label and tooltip
1460     if (xaccAccountGetSplitList (aw_get_account (aw)) != NULL)
1461     {
1462         const gchar *sec_name = gnc_commodity_get_printname (xaccAccountGetCommodity(aw_get_account (aw)));
1463         GtkWidget *label = gtk_label_new (sec_name);
1464         gtk_widget_set_tooltip_text (label, tt);
1465         gtk_box_pack_start (GTK_BOX(box), label, FALSE, FALSE, 0);
1466         gtk_widget_show (label);
1467     }
1468     else
1469     {
1470         gtk_box_pack_start(GTK_BOX(box), aw->commodity_edit, TRUE, TRUE, 0);
1471         gtk_widget_show (aw->commodity_edit);
1472     }
1473 
1474     label = GTK_WIDGET(gtk_builder_get_object (builder, "security_label"));
1475     gnc_general_select_make_mnemonic_target (GNC_GENERAL_SELECT(aw->commodity_edit), label);
1476 
1477     g_signal_connect (G_OBJECT (aw->commodity_edit), "changed",
1478                       G_CALLBACK (commodity_changed_cb), aw);
1479 
1480     aw->account_scu = GTK_WIDGET(gtk_builder_get_object (builder, "account_scu"));
1481 
1482     aw->parent_scroll = GTK_WIDGET(gtk_builder_get_object (builder, "parent_scroll"));
1483 
1484     aw->parent_tree = gnc_tree_view_account_new (TRUE);
1485     gtk_container_add (GTK_CONTAINER(aw->parent_scroll), GTK_WIDGET(aw->parent_tree));
1486     gtk_widget_show (GTK_WIDGET(aw->parent_tree));
1487     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (aw->parent_tree));
1488     g_signal_connect (G_OBJECT (selection), "changed",
1489                       G_CALLBACK (gnc_account_parent_changed_cb), aw);
1490 
1491     aw->opening_balance_button = GTK_WIDGET(gtk_builder_get_object (builder, "opening_balance_button"));
1492     aw->tax_related_button = GTK_WIDGET(gtk_builder_get_object (builder, "tax_related_button"));
1493     aw->placeholder_button = GTK_WIDGET(gtk_builder_get_object (builder, "placeholder_button"));
1494     aw->hidden_button = GTK_WIDGET(gtk_builder_get_object (builder, "hidden_button"));
1495     aw->auto_interest_button = GTK_WIDGET(gtk_builder_get_object (builder, "auto_interest_button"));
1496     set_auto_interest_box(aw);
1497 
1498 
1499     box = GTK_WIDGET(gtk_builder_get_object (builder, "opening_balance_box"));
1500     amount = gnc_amount_edit_new ();
1501     aw->opening_balance_edit = amount;
1502     gtk_box_pack_start(GTK_BOX(box), amount, TRUE, TRUE, 0);
1503     gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT (amount), TRUE);
1504     gtk_widget_show (amount);
1505 
1506     label = GTK_WIDGET(gtk_builder_get_object (builder, "balance_label"));
1507     gnc_amount_edit_make_mnemonic_target (GNC_AMOUNT_EDIT(amount), label);
1508 
1509     box = GTK_WIDGET(gtk_builder_get_object (builder, "opening_balance_date_box"));
1510     label = GTK_WIDGET(gtk_builder_get_object (builder, "date_label"));
1511     date_edit = gnc_date_edit_new (gnc_time (NULL), 0, 0);
1512     gnc_date_make_mnemonic_target (GNC_DATE_EDIT(date_edit), label);
1513     aw->opening_balance_date_edit = date_edit;
1514     gtk_box_pack_start(GTK_BOX(box), date_edit, TRUE, TRUE, 0);
1515     gtk_widget_show (date_edit);
1516 
1517     aw->opening_balance_page =
1518         gtk_notebook_get_nth_page (GTK_NOTEBOOK (aw->notebook), 1);
1519 
1520     aw->opening_equity_radio = GTK_WIDGET(gtk_builder_get_object (builder,
1521                                           "opening_equity_radio"));
1522 
1523     box = GTK_WIDGET(gtk_builder_get_object (builder, "transfer_account_scroll"));
1524     aw->transfer_account_scroll = box;
1525 
1526     aw->transfer_tree = GTK_WIDGET(gnc_tree_view_account_new(FALSE));
1527     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(aw->transfer_tree));
1528     gtk_tree_selection_set_select_function(selection, account_commodity_filter, aw, NULL);
1529 
1530     gtk_container_add(GTK_CONTAINER(box), GTK_WIDGET(aw->transfer_tree));
1531     gtk_widget_show (GTK_WIDGET(aw->transfer_tree));
1532 
1533     label = GTK_WIDGET(gtk_builder_get_object (builder, "parent_label"));
1534     gtk_label_set_mnemonic_widget (GTK_LABEL(label), GTK_WIDGET(aw->parent_tree));
1535 
1536     /* This goes at the end so the select callback has good data. */
1537     aw->type_view = GTK_WIDGET(gtk_builder_get_object (builder, "type_view"));
1538 
1539     // If the account has transactions, reduce the available account types
1540     // to change the current account type to based on the following
1541     // restrictions:
1542     // - the new account type should not force a change of commodity
1543     // - the old/new type is not an immutable type. Types are marked as
1544     //   immutable if gnucash depends on details that would be lost/missing
1545     //   if changing from/to such a type. At the time of this writing the
1546     //   immutable types are AR, AP and trading types.
1547     if (xaccAccountGetSplitList (aw_get_account (aw)) != NULL)
1548     {
1549         GNCAccountType atype = xaccAccountGetType (aw_get_account (aw));
1550         compat_types = xaccAccountTypesCompatibleWith (atype);
1551         if (!compat_types)
1552             compat_types = xaccAccountTypesValid ();
1553     }
1554     gnc_account_type_view_create (aw, compat_types);
1555 
1556     gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(aw->dialog), parent);
1557 
1558     gtk_widget_grab_focus(GTK_WIDGET(aw->name_entry));
1559 
1560     gtk_builder_connect_signals(builder, aw);
1561     g_object_unref(G_OBJECT(builder));
1562 
1563     LEAVE(" ");
1564 }
1565 
1566 
1567 static char *
get_ui_fullname(AccountWindow * aw)1568 get_ui_fullname (AccountWindow *aw)
1569 {
1570     Account *parent_account;
1571     char *fullname;
1572     const gchar *name;
1573 
1574     name = gtk_entry_get_text (GTK_ENTRY(aw->name_entry));
1575     if (!name || *name == '\0')
1576         name = _("<No name>");
1577 
1578     parent_account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT (aw->parent_tree));
1579 
1580     if (parent_account && !gnc_account_is_root(parent_account))
1581     {
1582         char *parent_name;
1583         const gchar *separator;
1584 
1585         parent_name = gnc_account_get_full_name (parent_account);
1586 
1587         separator = gnc_get_account_separator_string ();
1588         fullname = g_strconcat (parent_name, separator, name, NULL);
1589 
1590         g_free (parent_name);
1591     }
1592     else
1593         fullname = g_strdup (name);
1594 
1595     return fullname;
1596 }
1597 
1598 static void
gnc_account_window_set_name(AccountWindow * aw)1599 gnc_account_window_set_name (AccountWindow *aw)
1600 {
1601     char *fullname;
1602     char *title;
1603 
1604     if (!aw || !aw->parent_tree)
1605         return;
1606 
1607     fullname = get_ui_fullname (aw);
1608 
1609     if (aw->dialog_type == EDIT_ACCOUNT)
1610         title = g_strconcat(_("Edit Account"), " - ", fullname, NULL);
1611     else if (aw->next_name && (g_strv_length(aw->next_name) > 0))
1612     {
1613         const char *format = _("(%d) New Accounts");
1614         char *prefix;
1615 
1616         prefix = g_strdup_printf (format, g_strv_length(aw->next_name) + 1);
1617 
1618         title = g_strconcat (prefix, " - ", fullname, " ...", NULL);
1619 
1620         g_free (prefix);
1621     }
1622     else
1623         title = g_strconcat (_("New Account"), " - ", fullname, NULL);
1624 
1625     gtk_window_set_title (GTK_WINDOW(aw->dialog), title);
1626 
1627     g_free (fullname);
1628     g_free (title);
1629 }
1630 
1631 
1632 static void
close_handler(gpointer user_data)1633 close_handler (gpointer user_data)
1634 {
1635     AccountWindow *aw = user_data;
1636 
1637     ENTER("aw %p, modal %d", aw, aw->modal);
1638     gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(aw->dialog));
1639 
1640     gtk_widget_destroy (GTK_WIDGET (aw->dialog));
1641     LEAVE(" ");
1642 }
1643 
1644 
1645 /********************************************************************\
1646  * gnc_ui_refresh_account_window                                    *
1647  *   refreshes the edit window                                      *
1648  *                                                                  *
1649  * Args:   aw - the account window to refresh                       *
1650  * Return: none                                                     *
1651 \********************************************************************/
1652 static void
gnc_ui_refresh_account_window(AccountWindow * aw)1653 gnc_ui_refresh_account_window (AccountWindow *aw)
1654 {
1655     if (aw == NULL)
1656         return;
1657 
1658     /*  gnc_account_tree_refresh (GNC_ACCOUNT_TREE(aw->parent_tree));*/
1659 
1660     gnc_account_window_set_name (aw);
1661 }
1662 
1663 
1664 static void
refresh_handler(GHashTable * changes,gpointer user_data)1665 refresh_handler (GHashTable *changes, gpointer user_data)
1666 {
1667     AccountWindow *aw = user_data;
1668     const EventInfo *info;
1669     Account *account;
1670 
1671     account = aw_get_account (aw);
1672     if (!account)
1673     {
1674         gnc_close_gui_component (aw->component_id);
1675         return;
1676     }
1677 
1678     if (changes)
1679     {
1680         info = gnc_gui_get_entity_events (changes, &aw->account);
1681         if (info && (info->event_mask & QOF_EVENT_DESTROY))
1682         {
1683             gnc_close_gui_component (aw->component_id);
1684             return;
1685         }
1686     }
1687 
1688     gnc_ui_refresh_account_window (aw);
1689 }
1690 
1691 
1692 static AccountWindow *
gnc_ui_new_account_window_internal(GtkWindow * parent,QofBook * book,Account * base_account,gchar ** subaccount_names,GList * valid_types,const gnc_commodity * default_commodity,gboolean modal)1693 gnc_ui_new_account_window_internal (GtkWindow *parent,
1694                                     QofBook *book,
1695                                     Account *base_account,
1696                                     gchar **subaccount_names,
1697                                     GList *valid_types,
1698                                     const gnc_commodity * default_commodity,
1699                                     gboolean modal)
1700 {
1701     const gnc_commodity *commodity, *parent_commodity;
1702     AccountWindow *aw;
1703     Account *account;
1704     GList *list;
1705 
1706     g_return_val_if_fail(book, NULL);
1707 
1708     aw = g_new0 (AccountWindow, 1);
1709 
1710     aw->book = book;
1711     aw->modal = modal;
1712     aw->dialog_type = NEW_ACCOUNT;
1713 
1714     aw->valid_types = 0;
1715     for (list = valid_types; list; list = list->next)
1716         aw->valid_types |= (1 << GPOINTER_TO_INT (list->data));
1717 
1718     account = xaccMallocAccount (book);
1719     aw->account = *xaccAccountGetGUID (account);
1720 
1721     if (base_account)
1722     {
1723         aw->type = xaccAccountGetType (base_account);
1724         parent_commodity = xaccAccountGetCommodity (base_account);
1725     }
1726     else
1727     {
1728         aw->type = last_used_account_type;
1729         parent_commodity = gnc_default_currency ();
1730     }
1731 
1732     gnc_suspend_gui_refresh ();
1733 
1734     if (subaccount_names && *subaccount_names)
1735     {
1736         xaccAccountSetName (account, subaccount_names[0]);
1737         aw->subaccount_names = subaccount_names;
1738         aw->next_name = subaccount_names + 1;
1739     }
1740 
1741     gnc_account_window_create (parent, aw);
1742     gnc_account_to_ui (aw);
1743 
1744     gnc_resume_gui_refresh ();
1745 
1746     if (default_commodity != NULL)
1747     {
1748         commodity = default_commodity;
1749         if ((aw->type == ACCT_TYPE_STOCK) || (aw->type == ACCT_TYPE_MUTUAL))
1750         {
1751             gtk_entry_set_text(GTK_ENTRY(aw->name_entry),
1752                                (gpointer) gnc_commodity_get_mnemonic(commodity));
1753             gtk_entry_set_text(GTK_ENTRY(aw->description_entry),
1754                                (gpointer) gnc_commodity_get_fullname(commodity));
1755         }
1756     }
1757     else if ((aw->type != ACCT_TYPE_STOCK) && (aw->type != ACCT_TYPE_MUTUAL))
1758     {
1759         commodity = parent_commodity;
1760     }
1761     else
1762     {
1763         commodity = NULL;
1764     }
1765     gnc_general_select_set_selected (GNC_GENERAL_SELECT (aw->commodity_edit),
1766                                      (gpointer) commodity);
1767     gnc_account_commodity_from_type (aw, FALSE);
1768 
1769     if (base_account == NULL)
1770     {
1771         base_account = gnc_book_get_root_account(book);
1772     }
1773 
1774     gtk_tree_view_collapse_all (aw->parent_tree);
1775     gnc_tree_view_account_set_selected_account (
1776         GNC_TREE_VIEW_ACCOUNT (aw->parent_tree), base_account);
1777 
1778     gtk_widget_show (aw->dialog);
1779 
1780     gnc_window_adjust_for_screen (GTK_WINDOW(aw->dialog));
1781 
1782     gnc_account_window_set_name (aw);
1783 
1784     aw->component_id = gnc_register_gui_component (DIALOG_NEW_ACCOUNT_CM_CLASS,
1785                        refresh_handler,
1786                        modal ? NULL : close_handler,
1787                        aw);
1788 
1789     gnc_gui_component_set_session (aw->component_id, gnc_get_current_session());
1790     gnc_gui_component_watch_entity_type (aw->component_id,
1791                                          GNC_ID_ACCOUNT,
1792                                          QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
1793     return aw;
1794 }
1795 
1796 
1797 static gchar **
gnc_split_account_name(QofBook * book,const char * in_name,Account ** base_account)1798 gnc_split_account_name (QofBook *book, const char *in_name, Account **base_account)
1799 {
1800     Account *root, *account;
1801     gchar **names, **ptr, **out_names;
1802     GList *list, *node;
1803 
1804     root = gnc_book_get_root_account (book);
1805     list = gnc_account_get_children(root);
1806     names = g_strsplit(in_name, gnc_get_account_separator_string(), -1);
1807 
1808     for (ptr = names; *ptr; ptr++)
1809     {
1810         /* Stop if there are no children at the current level. */
1811         if (list == NULL)
1812             break;
1813 
1814         /* Look for the first name in the children. */
1815         for (node = list; node; node = g_list_next(node))
1816         {
1817             account = node->data;
1818 
1819             if (g_strcmp0(xaccAccountGetName (account), *ptr) == 0)
1820             {
1821                 /* We found an account. */
1822                 *base_account = account;
1823                 break;
1824             }
1825         }
1826 
1827         /* Was there a match?  If no, stop the traversal. */
1828         if (node == NULL)
1829             break;
1830 
1831         g_list_free(list);
1832         list = gnc_account_get_children (account);
1833     }
1834 
1835     out_names = g_strdupv(ptr);
1836     g_strfreev(names);
1837     if (list)
1838         g_list_free(list);
1839     return out_names;
1840 }
1841 
1842 
1843 /************************************************************
1844  *              Entry points for a Modal Dialog             *
1845  ************************************************************/
1846 
1847 Account *
gnc_ui_new_accounts_from_name_window(GtkWindow * parent,const char * name)1848 gnc_ui_new_accounts_from_name_window (GtkWindow *parent, const char *name)
1849 {
1850     return  gnc_ui_new_accounts_from_name_with_defaults (parent, name, NULL,
1851                                                          NULL, NULL);
1852 }
1853 
1854 Account *
gnc_ui_new_accounts_from_name_window_with_types(GtkWindow * parent,const char * name,GList * valid_types)1855 gnc_ui_new_accounts_from_name_window_with_types (GtkWindow *parent,
1856                                                  const char *name,
1857                                                  GList *valid_types)
1858 {
1859     return gnc_ui_new_accounts_from_name_with_defaults(parent, name,
1860                                                        valid_types, NULL, NULL);
1861 }
1862 
1863 Account *
gnc_ui_new_accounts_from_name_with_defaults(GtkWindow * parent,const char * name,GList * valid_types,const gnc_commodity * default_commodity,Account * parent_acct)1864 gnc_ui_new_accounts_from_name_with_defaults (GtkWindow *parent,
1865                                              const char *name,
1866                                              GList *valid_types,
1867                                              const gnc_commodity * default_commodity,
1868                                              Account * parent_acct)
1869 {
1870     QofBook *book;
1871     AccountWindow *aw;
1872     Account *base_account = NULL;
1873     Account *created_account = NULL;
1874     gchar ** subaccount_names;
1875     gint response;
1876     gboolean done = FALSE;
1877 
1878     ENTER("name %s, valid %p, commodity %p, account %p",
1879           name, valid_types, default_commodity, parent_acct);
1880     book = gnc_get_current_book();
1881     if (!name || *name == '\0')
1882     {
1883         subaccount_names = NULL;
1884         base_account = NULL;
1885     }
1886     else
1887         subaccount_names = gnc_split_account_name (book, name, &base_account);
1888 
1889     if (parent_acct != NULL)
1890     {
1891         base_account = parent_acct;
1892     }
1893     aw = gnc_ui_new_account_window_internal (parent, book, base_account,
1894                                              subaccount_names,
1895                                              valid_types,
1896                                              default_commodity,
1897                                              TRUE);
1898 
1899     while (!done)
1900     {
1901         response = gtk_dialog_run (GTK_DIALOG(aw->dialog));
1902 
1903         /* This can destroy the dialog */
1904         gnc_account_window_response_cb (GTK_DIALOG(aw->dialog), response, (gpointer)aw);
1905 
1906         switch (response)
1907         {
1908         case GTK_RESPONSE_OK:
1909             created_account = aw->created_account;
1910             done = (created_account != NULL);
1911             break;
1912 
1913         case GTK_RESPONSE_HELP:
1914             done = FALSE;
1915             break;
1916 
1917         default:
1918             done = TRUE;
1919             break;
1920         }
1921     }
1922 
1923     close_handler(aw);
1924     LEAVE("created %s (%p)", xaccAccountGetName(created_account), created_account);
1925     return created_account;
1926 }
1927 
1928 /************************************************************
1929  *            Entry points for a non-Modal Dialog           *
1930  ************************************************************/
1931 
1932 static gboolean
find_by_account(gpointer find_data,gpointer user_data)1933 find_by_account (gpointer find_data, gpointer user_data)
1934 {
1935     Account *account = find_data;
1936     AccountWindow *aw = user_data;
1937 
1938     if (!aw)
1939         return FALSE;
1940 
1941     return guid_equal (&aw->account, xaccAccountGetGUID (account));
1942 }
1943 
1944 /*
1945  * opens up a window to edit an account
1946  *
1947  * Args:   account - the account to edit
1948  * Return: EditAccountWindow object
1949  */
1950 void
gnc_ui_edit_account_window(GtkWindow * parent,Account * account)1951 gnc_ui_edit_account_window(GtkWindow *parent, Account *account)
1952 {
1953     AccountWindow * aw;
1954     Account *parent_acct;
1955 
1956     if (account == NULL)
1957         return;
1958 
1959     aw = gnc_find_first_gui_component (DIALOG_EDIT_ACCOUNT_CM_CLASS,
1960                                        find_by_account, account);
1961     if (aw)
1962     {
1963         gtk_window_present(GTK_WINDOW(aw->dialog));
1964         return;
1965     }
1966 
1967     aw = g_new0 (AccountWindow, 1);
1968 
1969     aw->book = gnc_account_get_book(account);
1970     aw->modal = FALSE;
1971     aw->dialog_type = EDIT_ACCOUNT;
1972     aw->account = *xaccAccountGetGUID (account);
1973     aw->subaccount_names = NULL;
1974     aw->type = xaccAccountGetType (account);
1975 
1976     gnc_suspend_gui_refresh ();
1977 
1978     gnc_account_window_create (parent, aw);
1979     gnc_account_to_ui (aw);
1980 
1981     gnc_resume_gui_refresh ();
1982 
1983     gtk_widget_show_all (aw->dialog);
1984     if (xaccAccountGetSplitList (account) != NULL)
1985         gtk_widget_hide (aw->opening_balance_page);
1986 
1987     parent_acct = gnc_account_get_parent (account);
1988     if (parent_acct == NULL)
1989         parent_acct = account;      /* must be at the root */
1990 
1991     gtk_tree_view_collapse_all (aw->parent_tree);
1992     gnc_tree_view_account_set_selected_account (
1993         GNC_TREE_VIEW_ACCOUNT(aw->parent_tree), parent_acct);
1994 
1995     gnc_account_window_set_name (aw);
1996 
1997     gnc_window_adjust_for_screen(GTK_WINDOW(aw->dialog));
1998 
1999     aw->component_id = gnc_register_gui_component (DIALOG_EDIT_ACCOUNT_CM_CLASS,
2000                        refresh_handler,
2001                        close_handler, aw);
2002 
2003     gnc_gui_component_set_session (aw->component_id, gnc_get_current_session());
2004     gnc_gui_component_watch_entity_type (aw->component_id,
2005                                          GNC_ID_ACCOUNT,
2006                                          QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
2007 
2008     gtk_window_present(GTK_WINDOW(aw->dialog));
2009 }
2010 
2011 
2012 /*
2013  * opens up a window to create a new account
2014  *
2015  * Args:    book - containing book for the new account
2016  *   parent_acct - The initial parent for the new account (optional)
2017  */
2018 void
gnc_ui_new_account_window(GtkWindow * parent,QofBook * book,Account * parent_acct)2019 gnc_ui_new_account_window (GtkWindow *parent, QofBook *book,
2020                            Account *parent_acct)
2021 {
2022     g_return_if_fail(book != NULL);
2023     if (parent_acct && book)
2024         g_return_if_fail(gnc_account_get_book(parent_acct) == book);
2025 
2026     gnc_ui_new_account_window_internal (parent, book, parent_acct, NULL, NULL,
2027                                         NULL, FALSE);
2028 }
2029 
2030 void
gnc_ui_new_account_with_types(GtkWindow * parent,QofBook * book,GList * valid_types)2031 gnc_ui_new_account_with_types (GtkWindow *parent, QofBook *book,
2032                                GList *valid_types)
2033 {
2034     gnc_ui_new_account_window_internal (parent, book, NULL, NULL,
2035                                         valid_types, NULL, FALSE);
2036 }
2037 
2038 /************************************************************
2039  *             Callbacks for a non-Modal Dialog             *
2040  ************************************************************/
2041 
2042 /*
2043  * register a callback that gets called when the account has changed
2044  * so significantly that you need to destroy yourself.  In particular
2045  * this is used by the ledger display to destroy ledgers when the
2046  * account type has changed.
2047  */
2048 void
gnc_ui_register_account_destroy_callback(void (* cb)(Account *))2049 gnc_ui_register_account_destroy_callback (void (*cb)(Account *))
2050 {
2051     if (!cb)
2052         return;
2053 
2054     if (g_list_index (ac_destroy_cb_list, cb) == -1)
2055         ac_destroy_cb_list = g_list_append (ac_destroy_cb_list, cb);
2056 
2057     return;
2058 }
2059 
2060 /**************************************************/
2061 
2062 static void
gnc_account_renumber_update_examples(RenumberDialog * data)2063 gnc_account_renumber_update_examples (RenumberDialog *data)
2064 {
2065     gchar *str;
2066     gchar *prefix;
2067     gint interval;
2068     unsigned int num_digits = 1;
2069 
2070     g_return_if_fail (data->num_children > 0);
2071     prefix = gtk_editable_get_chars(GTK_EDITABLE(data->prefix), 0, -1);
2072     interval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->interval));
2073     if (interval <= 0)
2074     interval = 10;
2075     num_digits = (unsigned int)log10((double)(data->num_children * interval)) + 1;
2076 
2077     if (strlen (prefix))
2078     str = g_strdup_printf("%s-%0*d", prefix, num_digits, interval);
2079     else
2080     str = g_strdup_printf("%0*d", num_digits, interval);
2081 
2082     gtk_label_set_text(GTK_LABEL(data->example1), str);
2083     g_free(str);
2084 
2085     if (strlen (prefix))
2086     str = g_strdup_printf("%s-%0*d", prefix, num_digits,
2087                   interval * data->num_children);
2088     else
2089     str = g_strdup_printf("%0*d", num_digits,
2090                   interval * data->num_children);
2091 
2092     gtk_label_set_text(GTK_LABEL(data->example2), str);
2093     g_free(str);
2094 
2095     g_free(prefix);
2096 }
2097 
2098 void
gnc_account_renumber_prefix_changed_cb(GtkEditable * editable,RenumberDialog * data)2099 gnc_account_renumber_prefix_changed_cb (GtkEditable *editable,
2100                                         RenumberDialog *data)
2101 {
2102     gnc_account_renumber_update_examples(data);
2103 }
2104 
2105 void
gnc_account_renumber_interval_changed_cb(GtkSpinButton * spinbutton,RenumberDialog * data)2106 gnc_account_renumber_interval_changed_cb (GtkSpinButton *spinbutton,
2107         RenumberDialog *data)
2108 {
2109     gnc_account_renumber_update_examples(data);
2110 }
2111 
2112 void
gnc_account_renumber_response_cb(GtkDialog * dialog,gint response,RenumberDialog * data)2113 gnc_account_renumber_response_cb (GtkDialog *dialog,
2114                                   gint response,
2115                                   RenumberDialog *data)
2116 {
2117     GList *children = NULL, *tmp;
2118     gchar *str;
2119     gchar *prefix;
2120     gint interval;
2121     unsigned int num_digits, i;
2122 
2123     if (response == GTK_RESPONSE_OK)
2124     {
2125         gtk_widget_hide(data->dialog);
2126         children = gnc_account_get_children_sorted(data->parent);
2127         if (children == NULL)
2128         {
2129             PWARN ("Can't renumber children of an account with no children!");
2130             g_free (data);
2131             return;
2132         }
2133         prefix = gtk_editable_get_chars(GTK_EDITABLE(data->prefix), 0, -1);
2134         interval = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->interval));
2135 
2136         if (interval <= 0)
2137             interval = 10;
2138 
2139         num_digits = (unsigned int)log10 ((double)(data->num_children * interval) + 1);
2140 
2141         gnc_set_busy_cursor (NULL, TRUE);
2142         for (tmp = children, i = 1; tmp; tmp = g_list_next(tmp), i += 1)
2143         {
2144             if (strlen (prefix))
2145                 str = g_strdup_printf("%s-%0*d", prefix,
2146                           num_digits, interval * i);
2147             else
2148                 str = g_strdup_printf("%0*d", num_digits, interval * i);
2149             xaccAccountSetCode(tmp->data, str);
2150             g_free(str);
2151         }
2152         gnc_unset_busy_cursor (NULL);
2153         g_list_free(children);
2154     }
2155 
2156     gtk_widget_destroy(data->dialog);
2157     g_free(data);
2158 }
2159 
2160 void
gnc_account_renumber_create_dialog(GtkWidget * window,Account * account)2161 gnc_account_renumber_create_dialog (GtkWidget *window, Account *account)
2162 {
2163     RenumberDialog *data;
2164     GtkBuilder *builder;
2165     GtkWidget *widget;
2166     gchar *string, *fullname;
2167 
2168     /* This is a safety check; the menu item calling this dialog
2169      * should be disabled if the account has no children.
2170      */
2171     g_return_if_fail (gnc_account_n_children (account) > 0);
2172     data = g_new(RenumberDialog, 1);
2173     data->parent = account;
2174     data->num_children = gnc_account_n_children(account);
2175 
2176     builder = gtk_builder_new();
2177     gnc_builder_add_from_file (builder, "dialog-account.glade", "interval_adjustment");
2178     gnc_builder_add_from_file (builder, "dialog-account.glade", "account_renumber_dialog");
2179     data->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "account_renumber_dialog"));
2180     gtk_window_set_transient_for(GTK_WINDOW(data->dialog), GTK_WINDOW(window));
2181     g_object_set_data_full(G_OBJECT(data->dialog), "builder", builder,
2182                g_object_unref);
2183 
2184     widget = GTK_WIDGET(gtk_builder_get_object (builder, "header_label"));
2185     fullname = gnc_account_get_full_name (account);
2186     string = g_strdup_printf(_( "Renumber the immediate sub-accounts of %s? "
2187                                 "This will replace the account code field of "
2188                                 "each child account with a newly generated code."),
2189                              fullname);
2190     gtk_label_set_text(GTK_LABEL(widget), string);
2191     g_free(string);
2192     g_free (fullname);
2193 
2194     data->prefix = GTK_WIDGET(gtk_builder_get_object (builder, "prefix_entry"));
2195     data->interval = GTK_WIDGET(gtk_builder_get_object (builder, "interval_spin"));
2196     data->example1 = GTK_WIDGET(gtk_builder_get_object (builder, "example1_label"));
2197     data->example2 = GTK_WIDGET(gtk_builder_get_object (builder, "example2_label"));
2198 
2199     gtk_entry_set_text(GTK_ENTRY(data->prefix), xaccAccountGetCode(account));
2200     gnc_account_renumber_update_examples(data);
2201 
2202     gtk_builder_connect_signals(builder, data);
2203 
2204     gtk_widget_show_all(data->dialog);
2205 }
2206 
2207 static void
default_color_button_cb(GtkButton * button,gpointer user_data)2208 default_color_button_cb (GtkButton *button, gpointer user_data)
2209 {
2210     GdkRGBA color;
2211 
2212     if (gdk_rgba_parse (&color, DEFAULT_COLOR))
2213         gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER(user_data), &color);
2214 }
2215 
2216 static void
update_account_color(Account * acc,const gchar * old_color,const gchar * new_color,gboolean replace)2217 update_account_color (Account *acc, const gchar *old_color, const gchar *new_color, gboolean replace)
2218 {
2219     PINFO("Account is '%s', old_color is '%s', new_color is '%s', replace is %d",
2220             xaccAccountGetName (acc), old_color, new_color, replace);
2221 
2222     // have a new color, update if we can
2223     if (new_color)
2224     {
2225         if (!old_color || replace)
2226         {
2227             // check to see if the color is different from old one
2228             if (g_strcmp0 (new_color, old_color) != 0)
2229                 xaccAccountSetColor (acc, new_color);
2230         }
2231     }
2232     else // change from a color to default one, remove color entry if we can
2233     {
2234         if (old_color && replace)
2235             xaccAccountSetColor (acc, ""); // remove entry
2236     }
2237 }
2238 
2239 static void
enable_box_cb(GtkToggleButton * toggle_button,gpointer user_data)2240 enable_box_cb (GtkToggleButton *toggle_button, gpointer user_data)
2241 {
2242     gboolean sensitive = FALSE;
2243 
2244     if (gtk_toggle_button_get_active (toggle_button))
2245         sensitive = TRUE;
2246 
2247     gtk_widget_set_sensitive (GTK_WIDGET(user_data), sensitive);
2248 }
2249 
2250 void
gnc_account_cascade_properties_dialog(GtkWidget * window,Account * account)2251 gnc_account_cascade_properties_dialog (GtkWidget *window, Account *account)
2252 {
2253     GtkWidget *dialog;
2254     GtkBuilder *builder;
2255     GtkWidget *label;
2256     GtkWidget *color_button, *over_write, *color_button_default;
2257     GtkWidget *enable_color, *enable_placeholder, *enable_hidden;
2258     GtkWidget *color_box, *placeholder_box, *hidden_box;
2259     GtkWidget *placeholder_button, *hidden_button;
2260 
2261     gchar *string, *fullname;
2262     const char *color_string;
2263     gchar *old_color_string = NULL;
2264     GdkRGBA color;
2265     gint response;
2266 
2267     // check if we actually do have sub accounts
2268     g_return_if_fail (gnc_account_n_children (account) > 0);
2269 
2270     builder = gtk_builder_new();
2271     gnc_builder_add_from_file (builder, "dialog-account.glade", "account_cascade_dialog");
2272     dialog = GTK_WIDGET(gtk_builder_get_object (builder, "account_cascade_dialog"));
2273     gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(window));
2274 
2275     // Color section
2276     enable_color = GTK_WIDGET(gtk_builder_get_object (builder, "enable_cascade_color"));
2277     color_box = GTK_WIDGET(gtk_builder_get_object (builder, "color_box"));
2278 
2279     label = GTK_WIDGET(gtk_builder_get_object (builder, "color_label"));
2280     over_write = GTK_WIDGET(gtk_builder_get_object (builder, "replace_check"));
2281     color_button = GTK_WIDGET(gtk_builder_get_object (builder, "color_button"));
2282     color_button_default = GTK_WIDGET(gtk_builder_get_object (builder, "color_button_default"));
2283 
2284     gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER(color_button), FALSE);
2285 
2286     g_signal_connect (G_OBJECT(enable_color), "toggled",
2287                       G_CALLBACK(enable_box_cb), (gpointer)color_box);
2288 
2289     g_signal_connect (G_OBJECT(color_button_default), "clicked",
2290                       G_CALLBACK(default_color_button_cb), (gpointer)color_button);
2291 
2292     fullname = gnc_account_get_full_name (account);
2293     string = g_strdup_printf (_( "Set the account color for account '%s' "
2294                                  "including all sub-accounts to the selected color"),
2295                               fullname);
2296     gtk_label_set_text (GTK_LABEL(label), string);
2297     g_free (string);
2298 
2299     color_string = xaccAccountGetColor (account); // get existing account color
2300 
2301     if (!color_string)
2302         color_string = DEFAULT_COLOR;
2303     else
2304        old_color_string = g_strdup (color_string); // save the old color string
2305 
2306     if (!gdk_rgba_parse (&color, color_string))
2307         gdk_rgba_parse (&color, DEFAULT_COLOR);
2308 
2309     // set the color chooser to account color
2310     gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER(color_button), &color);
2311 
2312     // Placeholder section
2313     enable_placeholder = GTK_WIDGET(gtk_builder_get_object (builder, "enable_cascade_placeholder"));
2314     placeholder_box = GTK_WIDGET(gtk_builder_get_object (builder, "placeholder_box"));
2315     label = GTK_WIDGET(gtk_builder_get_object (builder, "placeholder_label"));
2316     placeholder_button = GTK_WIDGET(gtk_builder_get_object (builder, "placeholder_check_button"));
2317     g_signal_connect (G_OBJECT(enable_placeholder), "toggled",
2318                       G_CALLBACK(enable_box_cb), (gpointer)placeholder_box);
2319 
2320     string = g_strdup_printf (_( "Set the account placeholder value for account '%s' "
2321                                  "including all sub-accounts"),
2322                               fullname);
2323     gtk_label_set_text (GTK_LABEL(label), string);
2324     g_free (string);
2325 
2326     // Hidden section
2327     enable_hidden = GTK_WIDGET(gtk_builder_get_object (builder, "enable_cascade_hidden"));
2328     hidden_box = GTK_WIDGET(gtk_builder_get_object (builder, "hidden_box"));
2329     label = GTK_WIDGET(gtk_builder_get_object (builder, "hidden_label"));
2330     hidden_button = GTK_WIDGET(gtk_builder_get_object (builder, "hidden_check_button"));
2331     g_signal_connect (G_OBJECT(enable_hidden), "toggled",
2332                       G_CALLBACK(enable_box_cb), (gpointer)hidden_box);
2333 
2334     string = g_strdup_printf (_( "Set the account hidden value for account '%s' "
2335                                  "including all sub-accounts"),
2336                               fullname);
2337     gtk_label_set_text (GTK_LABEL(label), string);
2338     g_free (string);
2339     g_free (fullname);
2340 
2341     /* default to cancel */
2342     gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
2343 
2344     gtk_builder_connect_signals (builder, dialog);
2345     g_object_unref (G_OBJECT(builder));
2346 
2347     gtk_widget_show_all (dialog);
2348 
2349     response = gtk_dialog_run (GTK_DIALOG(dialog));
2350 
2351     if (response == GTK_RESPONSE_OK)
2352     {
2353         GList *accounts = gnc_account_get_descendants (account);
2354         GdkRGBA new_color;
2355         const gchar *new_color_string = NULL;
2356         gboolean color_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(enable_color));
2357         gboolean placeholder_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(enable_placeholder));
2358         gboolean hidden_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(enable_hidden));
2359         gboolean replace = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(over_write));
2360         gboolean placeholder = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(placeholder_button));
2361         gboolean hidden = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(hidden_button));
2362 
2363         // Update Account Colors
2364         if (color_active)
2365         {
2366             gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER(color_button), &new_color);
2367             new_color_string = gdk_rgba_to_string (&new_color);
2368 
2369             if (g_strcmp0 (new_color_string, DEFAULT_COLOR) == 0)
2370                 new_color_string = NULL;
2371 
2372             // check/update selected account
2373             update_account_color (account, old_color_string, new_color_string, replace);
2374         }
2375 
2376         // Update Account Placeholder value
2377         if (placeholder_active)
2378             xaccAccountSetPlaceholder (account, placeholder);
2379 
2380         // Update Account Hidden value
2381         if (hidden_active)
2382             xaccAccountSetHidden (account, hidden);
2383 
2384         // Update SubAccounts
2385         if (accounts)
2386         {
2387             for (GList *acct = accounts; acct; acct = g_list_next(acct))
2388             {
2389                 // Update SubAccount Colors
2390                 if (color_active)
2391                 {
2392                     const char *string = xaccAccountGetColor (acct->data);
2393                     update_account_color (acct->data, string, new_color_string, replace);
2394                 }
2395                 // Update SubAccount PlaceHolder
2396                 if (placeholder_active)
2397                     xaccAccountSetPlaceholder (acct->data, placeholder);
2398                 // Update SubAccount Hidden
2399                 if (hidden_active)
2400                     xaccAccountSetHidden (acct->data, hidden);
2401             }
2402         }
2403         g_list_free (accounts);
2404     }
2405     if (old_color_string)
2406         g_free (old_color_string);
2407 
2408     gtk_widget_destroy (dialog);
2409 }
2410