dialog-account.c

00001 /********************************************************************\
00002  * dialog-account.c -- window for creating and editing accounts for *
00003  *                     GnuCash                                      *
00004  * Copyright (C) 2000 Dave Peticolas <dave@krondo.com>              *
00005  * Copyright (C) 2003,2005,2006 David Hampton <hampton@employees.org> *
00006  *                                                                  *
00007  * This program is free software; you can redistribute it and/or    *
00008  * modify it under the terms of the GNU General Public License as   *
00009  * published by the Free Software Foundation; either version 2 of   *
00010  * the License, or (at your option) any later version.              *
00011  *                                                                  *
00012  * This program is distributed in the hope that it will be useful,  *
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00015  * GNU General Public License for more details.                     *
00016  *                                                                  *
00017  * You should have received a copy of the GNU General Public License*
00018  * along with this program; if not, contact:                        *
00019  *                                                                  *
00020  * Free Software Foundation           Voice:  +1-617-542-5942       *
00021  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00022  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00023 \********************************************************************/
00024 
00025 #include "config.h"
00026 
00027 #include <gnome.h>
00028 #include <glib/gi18n.h>
00029 #include <math.h>
00030 #ifdef G_OS_WIN32
00031 #include <pow.h>
00032 #endif
00033 #include <string.h>
00034 
00035 #include "Transaction.h"
00036 #include "dialog-account.h"
00037 #include "dialog-commodity.h"
00038 #include "dialog-utils.h"
00039 #include "gnc-amount-edit.h"
00040 #include "gnc-general-select.h"
00041 #include "gnc-commodity.h"
00042 #include "gnc-commodity-edit.h"
00043 #include "gnc-component-manager.h"
00044 #include "gnc-engine.h"
00045 #include "gnc-gui-query.h"
00046 #include "gnc-session.h"
00047 #include "gnc-tree-model-account-types.h"
00048 #include "gnc-tree-view-account.h"
00049 #include "gnc-ui.h"
00050 #include "gnc-ui-util.h"
00051 
00052 
00053 #define DIALOG_NEW_ACCOUNT_CM_CLASS "dialog-new-account"
00054 #define DIALOG_EDIT_ACCOUNT_CM_CLASS "dialog-edit-account"
00055 #define GCONF_SECTION "dialogs/account"
00056 
00057 enum account_cols {
00058   ACCOUNT_COL_FULLNAME = 0,
00059   ACCOUNT_COL_FIELDNAME,
00060   ACCOUNT_COL_OLD_VALUE,
00061   ACCOUNT_COL_NEW_VALUE,
00062   NUM_ACCOUNT_COLS
00063 };
00064 
00065 typedef enum
00066 {
00067   NEW_ACCOUNT,
00068   EDIT_ACCOUNT
00069 } AccountDialogType;
00070 
00071 typedef struct _AccountWindow
00072 {
00073   QofBook *book;
00074   gboolean modal;
00075   GtkWidget *dialog;
00076 
00077   AccountDialogType dialog_type;
00078 
00079   GUID    account;
00080   Account *created_account;
00081 
00082   gchar **subaccount_names;
00083   gchar **next_name;
00084 
00085   GNCAccountType type;
00086 
00087   GtkWidget * notebook;
00088 
00089   GtkWidget * name_entry;
00090   GtkWidget * description_entry;
00091   GtkWidget * code_entry;
00092   GtkTextBuffer * notes_text_buffer;
00093 
00094   GtkWidget * commodity_edit;
00095   dialog_commodity_mode commodity_mode;
00096   GtkWidget * account_scu;
00097 
00098   guint32 valid_types;
00099   GNCAccountType preferred_account_type;
00100   GtkWidget * type_view;
00101   GtkTreeView * parent_tree;
00102 
00103   GtkWidget * opening_balance_edit;
00104   GtkWidget * opening_balance_date_edit;
00105   GtkWidget * opening_balance_page;
00106 
00107   GtkWidget * opening_equity_radio;
00108   GtkWidget * transfer_account_scroll;
00109   GtkWidget * transfer_tree;
00110 
00111   GtkWidget * tax_related_button;
00112   GtkWidget * placeholder_button;
00113   GtkWidget * hidden_button;
00114 
00115   gint component_id;
00116 } AccountWindow;
00117 
00118 typedef struct _RenumberDialog
00119 {
00120   GtkWidget *dialog;
00121   GtkWidget *prefix;
00122   GtkWidget *interval;
00123   GtkWidget *example1;
00124   GtkWidget *example2;
00125 
00126   Account *parent;
00127   gint num_children;
00128 } RenumberDialog;
00129 
00131 static QofLogModule log_module = GNC_MOD_GUI;
00132 
00133 static GNCAccountType last_used_account_type = ACCT_TYPE_BANK;
00134 
00135 static GList *ac_destroy_cb_list = NULL;
00136 
00138 static void gnc_account_window_set_name (AccountWindow *aw);
00139 
00140 void gnc_account_renumber_prefix_changed_cb (GtkEditable *editable, RenumberDialog *data);
00141 void gnc_account_renumber_interval_changed_cb (GtkSpinButton *spinbutton, RenumberDialog *data);
00142 void gnc_account_renumber_response_cb (GtkDialog *dialog, gint response, RenumberDialog *data);
00143 
00146 static void
00147 aw_call_destroy_callbacks (Account* acc)
00148 {
00149   GList *node;
00150   void (*cb)(Account*);
00151 
00152   for (node = ac_destroy_cb_list; node; node = node->next) {
00153     cb = node->data;
00154     (cb)(acc);
00155   }
00156 }
00157 
00158 static Account *
00159 aw_get_account (AccountWindow *aw)
00160 {
00161   if (!aw)
00162     return NULL;
00163 
00164   return xaccAccountLookup (&aw->account, aw->book);
00165 }
00166 
00167 static void
00168 gnc_account_commodity_from_type (AccountWindow * aw, gboolean update)
00169 {
00170   dialog_commodity_mode new_mode;
00171 
00172   if ((aw->type == ACCT_TYPE_STOCK) || (aw->type == ACCT_TYPE_MUTUAL))
00173     new_mode = DIAG_COMM_NON_CURRENCY;
00174   else
00175     new_mode = DIAG_COMM_CURRENCY;
00176 
00177   if (update && (new_mode != aw->commodity_mode)) {
00178     gnc_general_select_set_selected(GNC_GENERAL_SELECT (aw->commodity_edit),
00179                                     NULL);
00180   }
00181 
00182   aw->commodity_mode = new_mode;
00183 }
00184 
00185 /* Copy the account values to the GUI widgets */
00186 static void
00187 gnc_account_to_ui(AccountWindow *aw)
00188 {
00189   Account *account;
00190   gnc_commodity * commodity;
00191   const char *string;
00192   gboolean flag, nonstd_scu;
00193   gint index;
00194 
00195   ENTER("%p", aw);
00196   account = aw_get_account (aw);
00197   if (!account) {
00198     LEAVE("no account");
00199     return;
00200   }
00201 
00202   string = xaccAccountGetName (account);
00203   if (string == NULL) string = "";
00204   gtk_entry_set_text(GTK_ENTRY(aw->name_entry), string);
00205 
00206   string = xaccAccountGetDescription (account);
00207   if (string == NULL) string = "";
00208   gtk_entry_set_text(GTK_ENTRY(aw->description_entry), string);
00209 
00210   commodity = xaccAccountGetCommodity (account);
00211   gnc_general_select_set_selected (GNC_GENERAL_SELECT (aw->commodity_edit),
00212                                     commodity);
00213   gnc_account_commodity_from_type (aw, FALSE);
00214 
00215   nonstd_scu = xaccAccountGetNonStdSCU (account);
00216   if (nonstd_scu) {
00217     index = xaccAccountGetCommoditySCUi(account);
00218     index = log10(index) + 1;
00219   } else {
00220     index = 0;
00221   }
00222   gtk_combo_box_set_active(GTK_COMBO_BOX(aw->account_scu), index);
00223 
00224   string = xaccAccountGetCode (account);
00225   if (string == NULL) string = "";
00226   gtk_entry_set_text(GTK_ENTRY(aw->code_entry), string);
00227 
00228   string = xaccAccountGetNotes (account);
00229   if (string == NULL) string = "";
00230 
00231   gtk_text_buffer_set_text (aw->notes_text_buffer, string, strlen(string));
00232 
00233   flag = xaccAccountGetTaxRelated (account);
00234   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (aw->tax_related_button),
00235                                 flag);
00236 
00237   flag = xaccAccountGetPlaceholder (account);
00238   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (aw->placeholder_button),
00239                                 flag);
00240 
00241   flag = xaccAccountGetHidden (account);
00242   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (aw->hidden_button),
00243                                 flag);
00244   LEAVE(" ");
00245 }
00246 
00247 
00248 static gboolean
00249 gnc_account_create_transfer_balance (QofBook *book,
00250                                      Account *account,
00251                                      Account *transfer,
00252                                      gnc_numeric balance,
00253                                      time_t date)
00254 {
00255   Transaction *trans;
00256   Split *split;
00257 
00258   if (gnc_numeric_zero_p (balance))
00259     return TRUE;
00260 
00261   g_return_val_if_fail (account != NULL, FALSE);
00262   g_return_val_if_fail (transfer != NULL, FALSE);
00263 
00264   xaccAccountBeginEdit (account);
00265   xaccAccountBeginEdit (transfer);
00266 
00267   trans = xaccMallocTransaction (book);
00268 
00269   xaccTransBeginEdit (trans);
00270 
00271   xaccTransSetCurrency (trans, xaccAccountGetCommodity (account));
00272   xaccTransSetDateSecs (trans, date);
00273   xaccTransSetDescription (trans, _("Opening Balance"));
00274 
00275   split = xaccMallocSplit (book);
00276 
00277   xaccTransAppendSplit (trans, split);
00278   xaccAccountInsertSplit (account, split);
00279 
00280   xaccSplitSetAmount (split, balance);
00281   xaccSplitSetValue (split, balance);
00282 
00283   balance = gnc_numeric_neg (balance);
00284 
00285   split = xaccMallocSplit (book);
00286 
00287   xaccTransAppendSplit (trans, split);
00288   xaccAccountInsertSplit (transfer, split);
00289 
00290   xaccSplitSetAmount (split, balance);
00291   xaccSplitSetValue (split, balance);
00292 
00293   xaccTransCommitEdit (trans);
00294   xaccAccountCommitEdit (transfer);
00295   xaccAccountCommitEdit (account);
00296 
00297   return TRUE;
00298 }
00299 
00300 /* Record the GUI values into the Account structure */
00301 static void
00302 gnc_ui_to_account(AccountWindow *aw)
00303 {
00304   Account *account;
00305   gnc_commodity *commodity;
00306   Account *parent_account;
00307   const char *old_string;
00308   const char *string;
00309   gboolean flag;
00310   gnc_numeric balance;
00311   gboolean use_equity, nonstd;
00312   time_t date;
00313   gint index, old_scu, new_scu;
00314   GtkTextIter start, end;
00315 
00316   account = aw_get_account (aw);
00317   if (!account) {
00318     LEAVE("no account");
00319     return;
00320   }
00321 
00322   if (aw->dialog_type == EDIT_ACCOUNT
00323       && aw->type != xaccAccountGetType (account)) {
00324     /* Just refreshing won't work. */
00325     aw_call_destroy_callbacks (account);
00326   }
00327 
00328   xaccAccountBeginEdit (account);
00329 
00330   if (aw->type != xaccAccountGetType (account))
00331     xaccAccountSetType (account, aw->type);
00332 
00333   last_used_account_type = aw->type;
00334 
00335   string = gtk_entry_get_text (GTK_ENTRY(aw->name_entry));
00336   old_string = xaccAccountGetName (account);
00337   if (safe_strcmp (string, old_string) != 0)
00338     xaccAccountSetName (account, string);
00339 
00340   string = gtk_entry_get_text (GTK_ENTRY(aw->description_entry));
00341   old_string = xaccAccountGetDescription (account);
00342   if (safe_strcmp (string, old_string) != 0)
00343     xaccAccountSetDescription (account, string);
00344 
00345   commodity = (gnc_commodity *)
00346     gnc_general_select_get_selected (GNC_GENERAL_SELECT (aw->commodity_edit));
00347   if (commodity &&
00348       !gnc_commodity_equiv(commodity, xaccAccountGetCommodity (account))) {
00349     xaccAccountSetCommodity (account, commodity);
00350     old_scu = 0;
00351   } else {
00352     old_scu = xaccAccountGetCommoditySCU(account);
00353   }
00354 
00355   index = gtk_combo_box_get_active(GTK_COMBO_BOX(aw->account_scu));
00356   nonstd = (index != 0);
00357   if (nonstd != xaccAccountGetNonStdSCU(account))
00358     xaccAccountSetNonStdSCU(account, nonstd);
00359   new_scu = (nonstd ? pow(10,index-1) : gnc_commodity_get_fraction(commodity));
00360   if (old_scu != new_scu)
00361     xaccAccountSetCommoditySCU(account, new_scu);
00362 
00363   string = gtk_entry_get_text (GTK_ENTRY(aw->code_entry));
00364   old_string = xaccAccountGetCode (account);
00365   if (safe_strcmp (string, old_string) != 0)
00366     xaccAccountSetCode (account, string);
00367 
00368   gtk_text_buffer_get_start_iter (aw->notes_text_buffer, &start);
00369   gtk_text_buffer_get_end_iter (aw->notes_text_buffer, &end);
00370   string = gtk_text_buffer_get_text (aw->notes_text_buffer, &start, &end, FALSE);
00371   old_string = xaccAccountGetNotes (account);
00372   if (safe_strcmp (string, old_string) != 0)
00373     xaccAccountSetNotes (account, string);
00374 
00375   flag =
00376     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (aw->tax_related_button));
00377   xaccAccountSetTaxRelated (account, flag);
00378 
00379   flag =
00380     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (aw->placeholder_button));
00381   xaccAccountSetPlaceholder (account, flag);
00382 
00383   flag =
00384     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (aw->hidden_button));
00385   xaccAccountSetHidden (account, flag);
00386 
00387   parent_account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT (aw->parent_tree));
00388 
00389   if (parent_account == NULL)
00390     parent_account = gnc_book_get_root_account(aw->book);
00391   if (parent_account != gnc_account_get_parent (account))
00392     gnc_account_append_child (parent_account, account);
00393 
00394   xaccAccountCommitEdit (account);
00395 
00396   balance = gnc_amount_edit_get_amount
00397     (GNC_AMOUNT_EDIT (aw->opening_balance_edit));
00398 
00399   if (gnc_numeric_zero_p (balance)) {
00400     LEAVE("zero balance");
00401     return;
00402   }
00403 
00404   if (gnc_reverse_balance (account))
00405     balance = gnc_numeric_neg (balance);
00406 
00407   date = gnome_date_edit_get_time (
00408                   GNOME_DATE_EDIT (aw->opening_balance_date_edit));
00409 
00410   use_equity = gtk_toggle_button_get_active
00411     (GTK_TOGGLE_BUTTON (aw->opening_equity_radio));
00412 
00413   if (use_equity)
00414   {
00415     if (!gnc_account_create_opening_balance (account, balance, date, aw->book))
00416     {
00417       const char *message = _("Could not create opening balance.");
00418       gnc_error_dialog(aw->dialog, message);
00419     }
00420   }
00421   else
00422   {
00423     Account *transfer = NULL;
00424 
00425     transfer = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT (aw->transfer_tree));
00426     if (!transfer) {
00427       LEAVE("no transfer account");
00428       return;
00429     }
00430 
00431     gnc_account_create_transfer_balance (aw->book, account, transfer, balance, date);
00432   }
00433     LEAVE(" ");
00434 }
00435 
00436 
00437 static void
00438 set_children_types (Account *account, GNCAccountType type)
00439 {
00440   GList *children, *iter;
00441 
00442   children = gnc_account_get_children(account);
00443   if (children == NULL)
00444     return;
00445 
00446   for (iter=children; iter; iter=iter->next) {
00447     account = iter->data;
00448     if (type == xaccAccountGetType(account))
00449       continue;
00450 
00451     /* Just refreshing won't work. */
00452     aw_call_destroy_callbacks (account);
00453 
00454     xaccAccountBeginEdit (account);
00455     xaccAccountSetType (account, type);
00456     xaccAccountCommitEdit (account);
00457 
00458     set_children_types (account, type);
00459   }
00460   g_list_free(children);
00461 }
00462 
00463 static void
00464 make_children_compatible (AccountWindow *aw)
00465 {
00466   Account *account;
00467 
00468   g_return_if_fail (aw);
00469 
00470   if (aw->dialog_type == NEW_ACCOUNT)
00471       return;
00472 
00473   account = aw_get_account (aw);
00474   g_return_if_fail (account);
00475 
00476   if (xaccAccountTypesCompatible (xaccAccountGetType (account), aw->type))
00477     return;
00478 
00479   set_children_types (account, aw->type);
00480 }
00481 
00482 
00483 static void
00484 gnc_finish_ok (AccountWindow *aw)
00485 {
00486   ENTER("aw %p", aw);
00487   gnc_suspend_gui_refresh ();
00488 
00489   /* make the account changes */
00490   make_children_compatible (aw);
00491   gnc_ui_to_account (aw);
00492 
00493   gnc_resume_gui_refresh ();
00494 
00495   /* do it all again, if needed */
00496   if ((aw->dialog_type == NEW_ACCOUNT) && aw->next_name && *aw->next_name)
00497   {
00498     gnc_commodity *commodity;
00499     Account *parent;
00500     Account *account;
00501 
00502     gnc_suspend_gui_refresh ();
00503 
00504     parent = aw_get_account (aw);
00505     account = xaccMallocAccount (aw->book);
00506     aw->account = *xaccAccountGetGUID (account);
00507     aw->type = xaccAccountGetType (parent);
00508 
00509     xaccAccountSetName (account, *aw->next_name);
00510     aw->next_name++;
00511 
00512     gnc_account_to_ui (aw);
00513 
00514     gnc_account_window_set_name (aw);
00515 
00516     commodity = xaccAccountGetCommodity (parent);
00517     gnc_general_select_set_selected (GNC_GENERAL_SELECT (aw->commodity_edit),
00518                                       commodity);
00519     gnc_account_commodity_from_type (aw, FALSE);
00520 
00521     gnc_tree_view_account_set_selected_account (
00522         GNC_TREE_VIEW_ACCOUNT (aw->parent_tree), parent);
00523 
00524     gnc_resume_gui_refresh ();
00525     LEAVE("1");
00526     return;
00527   }
00528 
00529   /* save for posterity */
00530   aw->created_account = aw_get_account (aw);
00531 
00532   /* so it doesn't get freed on close */
00533   aw->account = *xaccGUIDNULL ();
00534 
00535   gnc_close_gui_component (aw->component_id);
00536   LEAVE("2");
00537 }
00538 
00539 
00540 static void
00541 add_children_to_expander (GObject *object, GParamSpec *param_spec, gpointer data)
00542 {
00543   GtkExpander *expander = GTK_EXPANDER (object);
00544   Account *account = data;
00545   GtkWidget *scrolled_window;
00546   GtkTreeView *view;
00547 
00548   if (gtk_expander_get_expanded (expander) &&
00549       !gtk_bin_get_child (GTK_BIN (expander))) {
00550 
00551     view = gnc_tree_view_account_new_with_root (account, FALSE);
00552 
00553     scrolled_window = gtk_scrolled_window_new (NULL, NULL);
00554     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
00555                                     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
00556     gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
00557                                          GTK_SHADOW_IN);
00558     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (view));
00559 
00560     gtk_container_add (GTK_CONTAINER (expander), scrolled_window);
00561     gtk_widget_show_all (scrolled_window);
00562   }
00563 }
00564 
00565 /* Check whether there are children needing a type adjustment because of a
00566    a change to an incompatible type (like after some reparenting) and let the
00567    user decide whether he wants that */
00568 static gboolean
00569 verify_children_compatible (AccountWindow *aw)
00570 {
00571   Account *account;
00572   GtkWidget *dialog, *vbox, *hbox, *label, *expander;
00573   gchar *str;
00574   gboolean result;
00575 
00576   if (aw == NULL)
00577     return FALSE;
00578 
00579   account = aw_get_account (aw);
00580   if (!account)
00581     return FALSE;
00582 
00583   if (xaccAccountTypesCompatible (xaccAccountGetType (account), aw->type))
00584     return TRUE;
00585 
00586   if (gnc_account_n_children(account) == 0)
00587     return TRUE;
00588 
00589   dialog = gtk_dialog_new_with_buttons ("",
00590                                         GTK_WINDOW(aw->dialog),
00591                                         GTK_DIALOG_DESTROY_WITH_PARENT |
00592                                         GTK_DIALOG_MODAL,
00593                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
00594                                         GTK_STOCK_OK, GTK_RESPONSE_OK,
00595                                         NULL);
00596 
00597   gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), TRUE);
00598 
00599   hbox = gtk_hbox_new (FALSE, 12);
00600   vbox = gtk_vbox_new (FALSE, 12);
00601 
00602   gtk_box_pack_start (
00603     GTK_BOX (hbox),
00604     gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG),
00605     FALSE, FALSE, 0);
00606 
00607   /* primary label */
00608   label = gtk_label_new (_("Give the children the same type?"));
00609   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
00610   gtk_label_set_selectable (GTK_LABEL (label), TRUE);
00611   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
00612   {
00613     gint size;
00614     PangoFontDescription *font_desc;
00615 
00616     size = pango_font_description_get_size (label->style->font_desc);
00617     font_desc = pango_font_description_new ();
00618     pango_font_description_set_weight (font_desc, PANGO_WEIGHT_BOLD);
00619     pango_font_description_set_size (font_desc, size * PANGO_SCALE_LARGE);
00620     gtk_widget_modify_font (label, font_desc);
00621     pango_font_description_free (font_desc);
00622   }
00623   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
00624 
00625   /* secondary label */
00626   str = g_strdup_printf (_("The children of the edited account have to be "
00627                            "changed to type \"%s\" to make them compatible."),
00628                          xaccAccountGetTypeStr (aw->type));
00629   label = gtk_label_new (str);
00630   g_free (str);
00631   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
00632   gtk_label_set_selectable (GTK_LABEL (label), TRUE);
00633   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
00634   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
00635 
00636   /* children */
00637   expander = gtk_expander_new_with_mnemonic (_("_Show children accounts"));
00638   gtk_expander_set_spacing (GTK_EXPANDER (expander), 6);
00639   g_signal_connect (G_OBJECT (expander), "notify::expanded",
00640                     G_CALLBACK (add_children_to_expander), account);
00641   gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
00642 
00643   gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
00644 
00645   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
00646                       TRUE, TRUE, 0);
00647 
00648   /* spacings */
00649   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
00650   gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
00651   gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
00652   gtk_container_set_border_width (
00653     GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 5);
00654   gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->action_area), 6);
00655 
00656   gtk_widget_show_all (hbox);
00657 
00658   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
00659 
00660   result = (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK);
00661 
00662   gtk_widget_destroy(dialog);
00663 
00664   return result;
00665 }
00666 
00667 
00668 static gboolean
00669 gnc_filter_parent_accounts (Account *account, gpointer data)
00670 {
00671   AccountWindow *aw = data;
00672   Account *aw_account = aw_get_account (aw);
00673 
00674   if (account == NULL)
00675     return FALSE;
00676 
00677   if (aw_account == NULL)
00678     return FALSE;
00679 
00680   if (gnc_account_is_root(account))
00681     return TRUE;
00682 
00683   if (account == aw_account)
00684     return FALSE;
00685 
00686   if (xaccAccountHasAncestor(account, aw_account))
00687     return FALSE;
00688 
00689   return TRUE;
00690 }
00691 
00692 
00693 static gboolean
00694 gnc_common_ok (AccountWindow *aw)
00695 {
00696   Account *root, *account, *parent;
00697   gnc_commodity * commodity;
00698   gchar *fullname, *fullname_parent;
00699   const gchar *name, *separator;
00700 
00701   ENTER("aw %p", aw);
00702   root = gnc_book_get_root_account (aw->book);
00703 
00704   separator = gnc_get_account_separator_string();
00705 
00706   /* check for valid name */
00707   name = gtk_entry_get_text(GTK_ENTRY(aw->name_entry));
00708   if (safe_strcmp(name, "") == 0) {
00709     const char *message = _("The account must be given a name.");
00710     gnc_error_dialog(aw->dialog, message);
00711     LEAVE("bad name");
00712     return FALSE;
00713   }
00714 
00715   /* check for a duplicate name */
00716   parent = gnc_tree_view_account_get_selected_account
00717     (GNC_TREE_VIEW_ACCOUNT (aw->parent_tree));
00718   if (parent == NULL) {
00719     account = gnc_account_lookup_by_full_name(root, name);
00720   } else {
00721     fullname_parent = xaccAccountGetFullName(parent);
00722     fullname = g_strconcat(fullname_parent, separator, name, NULL);
00723 
00724     account = gnc_account_lookup_by_full_name(root, fullname);
00725 
00726     g_free(fullname_parent);
00727     g_free(fullname);
00728   }
00729   if ((account != NULL) &&
00730       !guid_equal(&aw->account, xaccAccountGetGUID (account))) {
00731     const char *message = _("There is already an account with that name.");
00732     gnc_error_dialog(aw->dialog, message);
00733     LEAVE("duplicate name");
00734     return FALSE;
00735   }
00736 
00737   /* Parent check, probably not needed, but be safe */
00738   if (!gnc_filter_parent_accounts(parent, aw)) {
00739     const char *message = _("You must choose a valid parent account.");
00740     gnc_error_dialog(aw->dialog, message);
00741     LEAVE("invalid parent");
00742     return FALSE;
00743   }
00744 
00745   /* check for valid type */
00746   if (aw->type == ACCT_TYPE_INVALID) {
00747     const char *message = _("You must select an account type.");
00748     gnc_error_dialog(aw->dialog, message);
00749     LEAVE("invalid type");
00750     return FALSE;
00751   }
00752 
00753   /* check whether the types of child and parent are compatible */
00754   if (!xaccAccountTypesCompatible (aw->type, xaccAccountGetType (parent))) {
00755     const char *message = _("The selected account type is incompatible with "
00756                             "the one of the selected parent.");
00757     gnc_error_dialog(aw->dialog, message);
00758     LEAVE("incompatible types");
00759     return FALSE;
00760   }
00761 
00762   /* check for commodity */
00763   commodity = (gnc_commodity *)
00764     gnc_general_select_get_selected (GNC_GENERAL_SELECT (aw->commodity_edit));
00765   if (!commodity) {
00766     const char *message = _("You must choose a commodity.");
00767     gnc_error_dialog(aw->dialog, message);
00768     LEAVE("invalid commodity");
00769     return FALSE;
00770   }
00771 
00772   LEAVE("passed");
00773   return TRUE;
00774 }
00775 
00776 static void
00777 gnc_edit_account_ok(AccountWindow *aw)
00778 {
00779   Account *account;
00780 
00781   ENTER("aw %p", aw);
00782 
00783   account = aw_get_account (aw);
00784   if (!account) {
00785     LEAVE(" ");
00786     return;
00787   }
00788 
00789   if (!gnc_common_ok(aw)) {
00790     LEAVE(" ");
00791     return;
00792   }
00793 
00794   if (!verify_children_compatible (aw)) {
00795     LEAVE(" ");
00796     return;
00797   }
00798 
00799   gnc_finish_ok (aw);
00800   LEAVE(" ");
00801 }
00802 
00803 
00804 static void
00805 gnc_new_account_ok (AccountWindow *aw)
00806 {
00807   gnc_numeric balance;
00808 
00809   ENTER("aw %p", aw);
00810 
00811   if (!gnc_common_ok(aw)) {
00812     LEAVE(" ");
00813     return;
00814   }
00815 
00816   if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT (aw->opening_balance_edit)))
00817   {
00818     const char *message = _("You must enter a valid opening balance "
00819                             "or leave it blank.");
00820     gnc_error_dialog(aw->dialog, message);
00821     LEAVE(" ");
00822     return;
00823   }
00824 
00825   balance = gnc_amount_edit_get_amount
00826     (GNC_AMOUNT_EDIT (aw->opening_balance_edit));
00827 
00828   if (!gnc_numeric_zero_p (balance))
00829   {
00830     gboolean use_equity;
00831 
00832     use_equity = gtk_toggle_button_get_active
00833       (GTK_TOGGLE_BUTTON (aw->opening_equity_radio));
00834 
00835     if (!use_equity)
00836     {
00837       Account *transfer = NULL;
00838 
00839       transfer = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT (aw->transfer_tree));
00840       if (!transfer)
00841       {
00842         const char *message = _("You must select a transfer account or choose"
00843                                 " the opening balances equity account.");
00844         gnc_error_dialog(aw->dialog, message);
00845         LEAVE(" ");
00846         return;
00847       }
00848     }
00849   }
00850 
00851   gnc_finish_ok (aw);
00852   LEAVE(" ");
00853 }
00854 
00855 static void
00856 gnc_account_window_response_cb (GtkDialog *dialog,
00857                                 gint response,
00858                                 gpointer data)
00859 {
00860         AccountWindow *aw = data; 
00861 
00862         ENTER("dialog %p, response %d, aw %p", dialog, response, aw);
00863         switch (response) {
00864                 case GTK_RESPONSE_OK:
00865                         switch (aw->dialog_type) {
00866                                 case NEW_ACCOUNT:
00867                                         DEBUG("new acct dialog, OK");
00868                                         gnc_new_account_ok (aw);
00869                                         break;
00870                                 case EDIT_ACCOUNT:
00871                                         DEBUG("edit acct dialog, OK");
00872                                         gnc_edit_account_ok (aw);
00873                                         break;
00874                                 default:
00875                                         g_assert_not_reached ();
00876                                         return;
00877                         }
00878                         break;
00879                 case GTK_RESPONSE_HELP:
00880                         switch (aw->dialog_type) {
00881                                 case NEW_ACCOUNT:
00882                                         DEBUG("new acct dialog, HELP");
00883                                         gnc_gnome_help(HF_HELP, HL_ACC);
00884                                         break;
00885                                 case EDIT_ACCOUNT:
00886                                         DEBUG("edit acct dialog, HELP");
00887                                         gnc_gnome_help(HF_HELP, HL_ACCEDIT);
00888                                         break;
00889                                 default:
00890                                         g_assert_not_reached ();
00891                                         return;
00892                         }
00893                         break;
00894                 case GTK_RESPONSE_CANCEL:
00895                 default:
00896                         DEBUG("CANCEL");
00897                         gnc_close_gui_component (aw->component_id);
00898                         break;
00899         }
00900         LEAVE(" ");
00901 }
00902 
00903 static void
00904 gnc_account_window_destroy_cb (GtkObject *object, gpointer data)
00905 {
00906   AccountWindow *aw = data;
00907   Account *account;
00908 
00909   ENTER("object %p, aw %p", object, aw);
00910   account = aw_get_account (aw);
00911 
00912   gnc_suspend_gui_refresh ();
00913 
00914   switch (aw->dialog_type)
00915   {
00916     case NEW_ACCOUNT:
00917       if (account != NULL)
00918       {
00919         xaccAccountBeginEdit (account);
00920         xaccAccountDestroy (account);
00921         aw->account = *xaccGUIDNULL ();
00922       }
00923 
00924       DEBUG ("account add window destroyed\n");
00925       break;
00926 
00927     case EDIT_ACCOUNT:
00928       break;
00929 
00930     default:
00931       PERR ("unexpected dialog type\n");
00932       gnc_resume_gui_refresh ();
00933       LEAVE(" ");
00934       return;
00935   }
00936 
00937   gnc_unregister_gui_component (aw->component_id);
00938 
00939   gnc_resume_gui_refresh ();
00940 
00941   if (aw->subaccount_names) {
00942     g_strfreev(aw->subaccount_names);
00943     aw->subaccount_names = NULL;
00944     aw->next_name = NULL;
00945   }
00946 
00947   g_free (aw);
00948   LEAVE(" ");
00949 }
00950 
00951 static void
00952 gnc_account_parent_changed_cb (GtkTreeSelection *selection, gpointer data)
00953 {
00954   AccountWindow *aw = data;
00955   Account *parent_account;
00956   guint32 types, old_types;
00957   GtkTreeModel *type_model;
00958   GtkTreeSelection *type_selection;
00959   gboolean scroll_to = FALSE;
00960 
00961   g_return_if_fail (aw);
00962 
00963   parent_account = gnc_tree_view_account_get_selected_account (
00964     GNC_TREE_VIEW_ACCOUNT (aw->parent_tree));
00965   if (!parent_account)
00966     return;
00967 
00968   if (gnc_account_is_root(parent_account)) {
00969     types = aw->valid_types;
00970   } else {
00971     types = aw->valid_types &
00972       xaccParentAccountTypesCompatibleWith (xaccAccountGetType (parent_account));
00973   }
00974 
00975   type_model = gtk_tree_view_get_model (GTK_TREE_VIEW (aw->type_view));
00976   if (!type_model)
00977     return;
00978 
00979   if (aw->type != aw->preferred_account_type &&
00980       (types & (1 << aw->preferred_account_type)) != 0) {
00981     /* we can change back to the preferred account type */
00982     aw->type = aw->preferred_account_type;
00983     scroll_to = TRUE;
00984   }
00985   else if ((types & (1 << aw->type)) == 0) {
00986     /* our type is invalid now */
00987     aw->type = ACCT_TYPE_INVALID;
00988   }
00989   else {
00990     /* no type change, but maybe list of valid types changed */
00991     old_types = gnc_tree_model_account_types_get_mask (type_model);
00992     if (old_types != types)
00993       scroll_to = TRUE;
00994   }
00995 
00996   gnc_tree_model_account_types_set_mask (type_model, types);
00997 
00998   if (scroll_to) {
00999     type_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (aw->type_view));
01000     gnc_tree_model_account_types_set_selection(type_selection, 1 << aw->type);
01001   }
01002 
01003   gnc_account_window_set_name(aw);
01004 }
01005 
01006 static void
01007 gnc_account_type_changed_cb (GtkTreeSelection *selection, gpointer data)
01008 {
01009   AccountWindow *aw = data;
01010   gboolean sensitive;
01011   GNCAccountType type_id;
01012 
01013   g_return_if_fail (aw != NULL);
01014 
01015   sensitive = FALSE;
01016 
01017   type_id = gnc_tree_model_account_types_get_selection_single(selection);
01018   if (type_id == ACCT_TYPE_NONE) {
01019     aw->type = ACCT_TYPE_INVALID;
01020   } else {
01021     aw->type = type_id;
01022     aw->preferred_account_type = type_id;
01023 
01024     gnc_account_commodity_from_type (aw, TRUE);
01025 
01026     sensitive = (aw->type != ACCT_TYPE_EQUITY &&
01027                  aw->type != ACCT_TYPE_CURRENCY &&
01028                  aw->type != ACCT_TYPE_STOCK &&
01029                  aw->type != ACCT_TYPE_MUTUAL);
01030   }
01031 
01032   gtk_widget_set_sensitive (aw->opening_balance_page, sensitive);
01033 
01034   if (!sensitive) {
01035     gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (aw->opening_balance_edit),
01036                                 gnc_numeric_zero ());
01037   }
01038 }
01039 
01040 static void
01041 gnc_account_type_view_create (AccountWindow *aw)
01042 {
01043   GtkTreeModel *model;
01044   GtkTreeSelection *selection;
01045   GtkCellRenderer *renderer;
01046   GtkTreeView *view;
01047 
01048   if (aw->valid_types == 0) {
01049     /* no type restrictions, choose aw->type */
01050     aw->valid_types = xaccAccountTypesValid () | (1 << aw->type);
01051     aw->preferred_account_type = aw->type;
01052   }
01053   else if ((aw->valid_types & (1 << aw->type)) != 0) {
01054     /* aw->type is valid */
01055     aw->preferred_account_type = aw->type;
01056   }
01057   else if ((aw->valid_types & (1 << last_used_account_type)) != 0) {
01058     /* last used account type is valid */
01059     aw->type = last_used_account_type;
01060     aw->preferred_account_type = last_used_account_type;
01061   }
01062   else {
01063     /* choose first valid account type */
01064     int i;
01065     aw->preferred_account_type = aw->type;
01066     aw->type = ACCT_TYPE_INVALID;
01067     for (i=0; i<32; i++)
01068       if ((aw->valid_types & (1 << i)) != 0) {
01069         aw->type = i;
01070         break;
01071       }
01072   }
01073 
01074   model = gnc_tree_model_account_types_filter_using_mask (aw->valid_types);
01075 
01076   view = GTK_TREE_VIEW (aw->type_view);
01077   gtk_tree_view_set_model (view, model);
01078   g_object_unref (G_OBJECT (model));
01079 
01080   renderer = gtk_cell_renderer_text_new ();
01081   gtk_tree_view_insert_column_with_attributes (
01082     view, -1, NULL, renderer,
01083     "text", GNC_TREE_MODEL_ACCOUNT_TYPES_COL_NAME,
01084     NULL);
01085   gtk_tree_view_set_search_column (view, GNC_TREE_MODEL_ACCOUNT_TYPES_COL_NAME);
01086 
01087   selection = gtk_tree_view_get_selection (view);
01088   g_signal_connect (G_OBJECT (selection), "changed",
01089                     G_CALLBACK (gnc_account_type_changed_cb), aw);
01090 
01091   gnc_tree_model_account_types_set_selection(selection, 1 << aw->type);
01092 }
01093 
01094 static void
01095 gnc_account_name_changed_cb(GtkWidget *widget, gpointer data)
01096 {
01097   AccountWindow *aw = data;
01098 
01099   gnc_account_window_set_name (aw);
01100 }
01101 
01102 static void
01103 commodity_changed_cb (GNCGeneralSelect *gsl, gpointer data)
01104 {
01105   AccountWindow *aw = data;
01106   gnc_commodity *currency;
01107   GtkTreeSelection *selection;
01108 
01109   currency = (gnc_commodity *) gnc_general_select_get_selected (gsl);
01110   if (!currency)
01111     return;
01112 
01113   gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (aw->opening_balance_edit),
01114                                 gnc_commodity_get_fraction (currency));
01115   gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (aw->opening_balance_edit),
01116                                   gnc_commodity_print_info (currency, FALSE));
01117 
01118   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (aw->transfer_tree));
01119   gtk_tree_selection_unselect_all (selection);
01120 }
01121 
01122 static gboolean
01123 account_commodity_filter (GtkTreeSelection *selection,
01124                           GtkTreeModel *unused_model,
01125                           GtkTreePath *s_path,
01126                           gboolean path_currently_selected,
01127                           gpointer user_data)
01128 {
01129   gnc_commodity *commodity;
01130   AccountWindow *aw;
01131   Account *account;
01132 
01133   g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
01134 
01135   aw = user_data;
01136 
01137   if (path_currently_selected) {
01138     /* already selected, don't waste time. */
01139     return TRUE;
01140   }
01141 
01142   account = gnc_tree_view_account_get_account_from_path (GNC_TREE_VIEW_ACCOUNT (aw->transfer_tree), s_path);
01143   if (!account) {
01144     return FALSE;
01145   }
01146 
01147   commodity = (gnc_commodity *)
01148     gnc_general_select_get_selected (GNC_GENERAL_SELECT (aw->commodity_edit));
01149 
01150   return gnc_commodity_equiv (xaccAccountGetCommodity (account), commodity);
01151 }
01152 
01153 static void
01154 opening_equity_cb (GtkWidget *w, gpointer data)
01155 {
01156   AccountWindow *aw = data;
01157   gboolean use_equity;
01158 
01159   use_equity = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w));
01160 
01161   gtk_widget_set_sensitive (aw->transfer_account_scroll, !use_equity);
01162 }
01163 
01164 /********************************************************************\
01165  * gnc_account_window_create                                        *
01166  *   creates a window to create a new account.                      *
01167  *                                                                  * 
01168  * Args:   aw - the information structure for this window           *
01169  * Return: the created window                                       *
01170  \*******************************************************************/
01171 static void
01172 gnc_account_window_create(AccountWindow *aw)
01173 {
01174   GtkWidget *amount;
01175   GObject *awo;
01176   GtkWidget *box;
01177   GtkWidget *label;
01178   GladeXML  *xml;
01179   GtkTreeSelection *selection;
01180 
01181   ENTER("aw %p, modal %d", aw, aw->modal);
01182   xml = gnc_glade_xml_new ("account.glade", "Account Dialog");
01183 
01184   aw->dialog = glade_xml_get_widget (xml, "Account Dialog");
01185   awo = G_OBJECT (aw->dialog);
01186 
01187   g_object_set_data (awo, "dialog_info", aw);
01188 
01189   g_signal_connect (awo, "destroy",
01190                     G_CALLBACK (gnc_account_window_destroy_cb), aw);
01191 
01192   if (!aw->modal)
01193     g_signal_connect (awo, "response",
01194                       G_CALLBACK (gnc_account_window_response_cb), aw);
01195   else 
01196     gtk_window_set_modal (GTK_WINDOW (aw->dialog), TRUE);
01197 
01198   aw->notebook = glade_xml_get_widget (xml, "account_notebook");
01199 
01200   aw->name_entry = glade_xml_get_widget (xml, "name_entry");
01201   g_signal_connect (G_OBJECT (aw->name_entry), "changed",
01202                     G_CALLBACK (gnc_account_name_changed_cb), aw);
01203 
01204   aw->description_entry = glade_xml_get_widget (xml, "description_entry");
01205   aw->code_entry =        glade_xml_get_widget (xml, "code_entry");
01206   aw->notes_text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (glade_xml_get_widget (xml, "notes_text")));
01207 
01208   box = glade_xml_get_widget (xml, "commodity_hbox");
01209   aw->commodity_edit = gnc_general_select_new (GNC_GENERAL_SELECT_TYPE_SELECT,
01210                                                gnc_commodity_edit_get_string,
01211                                                gnc_commodity_edit_new_select,
01212                                                &aw->commodity_mode);
01213   gtk_box_pack_start(GTK_BOX(box), aw->commodity_edit, TRUE, TRUE, 0);
01214   gtk_widget_show (aw->commodity_edit);
01215 
01216   label = glade_xml_get_widget (xml, "security_label");
01217   gnc_general_select_make_mnemonic_target (GNC_GENERAL_SELECT(aw->commodity_edit), label);
01218 
01219   g_signal_connect (G_OBJECT (aw->commodity_edit), "changed",
01220                     G_CALLBACK (commodity_changed_cb), aw);
01221 
01222   aw->account_scu = glade_xml_get_widget (xml, "account_scu");
01223 
01224   box = glade_xml_get_widget (xml, "parent_scroll");
01225 
01226   aw->parent_tree = gnc_tree_view_account_new(TRUE);
01227   gtk_container_add(GTK_CONTAINER(box), GTK_WIDGET(aw->parent_tree));
01228   gtk_widget_show(GTK_WIDGET(aw->parent_tree));
01229   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (aw->parent_tree));
01230   g_signal_connect (G_OBJECT (selection), "changed",
01231                     G_CALLBACK (gnc_account_parent_changed_cb), aw);
01232 
01233   aw->tax_related_button = glade_xml_get_widget (xml, "tax_related_button");
01234   aw->placeholder_button = glade_xml_get_widget (xml