GnuCash 2.4.99
gnc-tree-view-account.c
00001 /********************************************************************\
00002  * gnc-tree-view-account.c -- GtkTreeView implementation to display *
00003  *                            accounts in a GtkTreeView.            *
00004  * Copyright (C) 2003,2005,2006 David Hampton <hampton@employees.org> *
00005  *                                                                  *
00006  * This program is free software; you can redistribute it and/or    *
00007  * modify it under the terms of the GNU General Public License as   *
00008  * published by the Free Software Foundation; either version 2 of   *
00009  * the License, or (at your option) any later version.              *
00010  *                                                                  *
00011  * This program is distributed in the hope that it will be useful,  *
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00014  * GNU General Public License for more details.                     *
00015  *                                                                  *
00016  * You should have received a copy of the GNU General Public License*
00017  * along with this program; if not, contact:                        *
00018  *                                                                  *
00019  * Free Software Foundation           Voice:  +1-617-542-5942       *
00020  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00021  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00022  *                                                                  *
00023 \********************************************************************/
00024 
00025 #include "config.h"
00026 
00027 #include <gtk/gtk.h>
00028 #include <glib/gi18n.h>
00029 #include <string.h>
00030 
00031 #include "gnc-tree-view.h"
00032 #include "gnc-tree-model-account.h"
00033 #include "gnc-tree-model-account-types.h"
00034 #include "gnc-tree-view-account.h"
00035 
00036 #include "Account.h"
00037 #include "gnc-accounting-period.h"
00038 #include "gnc-commodity.h"
00039 #include "gnc-component-manager.h"
00040 #include "gnc-engine.h"
00041 #include "gnc-glib-utils.h"
00042 #include "gnc-gobject-utils.h"
00043 #include "gnc-hooks.h"
00044 #include "gnc-session.h"
00045 #include "gnc-icons.h"
00046 #include "gnc-ui-balances.h"
00047 #include "dialog-utils.h"
00048 #include "window-main-summarybar.h"
00049 
00050 #define SAMPLE_ACCOUNT_VALUE "$1,000,000.00"
00051 
00054 /* This static indicates the debugging module that this .o belongs to.  */
00055 static QofLogModule log_module = GNC_MOD_GUI;
00056 
00058 static void gnc_tree_view_account_class_init (GncTreeViewAccountClass *klass);
00059 static void gnc_tree_view_account_init (GncTreeViewAccount *view);
00060 static void gnc_tree_view_account_finalize (GObject *object);
00061 
00062 static void gtva_update_column_names (GncTreeView *view);
00063 static void gtva_currency_changed_cb (void);
00064 
00065 static gboolean gnc_tree_view_account_filter_helper (GtkTreeModel *model,
00066         GtkTreeIter *iter,
00067         gpointer data);
00068 
00069 static void gtva_setup_column_renderer_edited_cb(GncTreeViewAccount *account_view,
00070         GtkTreeViewColumn *column,
00071         GtkCellRenderer *renderer,
00072         GncTreeViewAccountColumnTextEdited col_edited_cb);
00073 
00074 static void tax_info_data_func (GtkTreeViewColumn *col,
00075                                 GtkCellRenderer   *renderer,
00076                                 GtkTreeModel      *model,
00077                                 GtkTreeIter       *iter,
00078                                 gpointer           view);
00079 
00080 typedef struct GncTreeViewAccountPrivate
00081 {
00082     AccountViewInfo avi;
00083 
00084     gnc_tree_view_account_filter_func filter_fn;
00085     gpointer                          filter_data;
00086     GtkFunction                       filter_destroy;
00087 
00088     GtkTreeViewColumn *name_column;
00089     GtkTreeViewColumn *code_column;
00090     GtkTreeViewColumn *desc_column;
00091     GtkTreeViewColumn *present_report_column;
00092     GtkTreeViewColumn *balance_report_column;
00093     GtkTreeViewColumn *cleared_report_column;
00094     GtkTreeViewColumn *reconciled_report_column;
00095     GtkTreeViewColumn *future_min_report_column;
00096     GtkTreeViewColumn *total_report_column;
00097     GtkTreeViewColumn *notes_column;
00098 } GncTreeViewAccountPrivate;
00099 
00100 #define GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(o)  \
00101    (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_TREE_VIEW_ACCOUNT, GncTreeViewAccountPrivate))
00102 
00103 
00104 /************************************************************/
00105 /*               g_object required functions                */
00106 /************************************************************/
00107 
00108 static GObjectClass *parent_class = NULL;
00109 
00110 GType
00111 gnc_tree_view_account_get_type (void)
00112 {
00113     static GType gnc_tree_view_account_type = 0;
00114 
00115     if (gnc_tree_view_account_type == 0)
00116     {
00117         static const GTypeInfo our_info =
00118         {
00119             sizeof (GncTreeViewAccountClass),
00120             NULL,
00121             NULL,
00122             (GClassInitFunc) gnc_tree_view_account_class_init,
00123             NULL,
00124             NULL,
00125             sizeof (GncTreeViewAccount),
00126             0,
00127             (GInstanceInitFunc) gnc_tree_view_account_init
00128         };
00129 
00130         gnc_tree_view_account_type = g_type_register_static (
00131                                          GNC_TYPE_TREE_VIEW, GNC_TREE_VIEW_ACCOUNT_NAME,
00132                                          &our_info, 0);
00133     }
00134 
00135     return gnc_tree_view_account_type;
00136 }
00137 
00138 static void
00139 gnc_tree_view_account_class_init (GncTreeViewAccountClass *klass)
00140 {
00141     GObjectClass *o_class;
00142 
00143     parent_class = g_type_class_peek_parent (klass);
00144 
00145     /* GObject signals */
00146     o_class = G_OBJECT_CLASS (klass);
00147     o_class->finalize = gnc_tree_view_account_finalize;
00148 
00149     g_type_class_add_private(klass, sizeof(GncTreeViewAccountPrivate));
00150 
00151     gnc_hook_add_dangler(HOOK_CURRENCY_CHANGED,
00152                          (GFunc)gtva_currency_changed_cb, NULL);
00153 }
00154 
00155 /********************************************************************\
00156  * gnc_init_account_view_info                                       *
00157  *   initialize an account view info structure with default values  *
00158  *                                                                  *
00159  * Args: avi - structure to initialize                              *
00160  * Returns: nothing                                                 *
00161 \********************************************************************/
00162 static void
00163 gnc_init_account_view_info(AccountViewInfo *avi)
00164 {
00165     int i;
00166 
00167     for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
00168         avi->include_type[i] = TRUE;
00169     avi->show_hidden = FALSE;
00170 }
00171 
00172 static void
00173 gnc_tree_view_account_init (GncTreeViewAccount *view)
00174 {
00175     GncTreeViewAccountPrivate *priv;
00176 
00177     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
00178     gnc_init_account_view_info(&priv->avi);
00179 }
00180 
00181 static void
00182 gnc_tree_view_account_finalize (GObject *object)
00183 {
00184     GncTreeViewAccount *account_view;
00185     GncTreeViewAccountPrivate *priv;
00186 
00187     ENTER("view %p", object);
00188     g_return_if_fail (object != NULL);
00189     g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (object));
00190 
00191     account_view = GNC_TREE_VIEW_ACCOUNT (object);
00192 
00193     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(account_view);
00194     if (priv->filter_destroy)
00195     {
00196         priv->filter_destroy(priv->filter_data);
00197         priv->filter_destroy = NULL;
00198     }
00199     priv->filter_fn = NULL;
00200 
00201     if (G_OBJECT_CLASS (parent_class)->finalize)
00202         (* G_OBJECT_CLASS (parent_class)->finalize) (object);
00203     LEAVE(" ");
00204 }
00205 
00206 
00207 /************************************************************
00208  *                        Callbacks                         *
00209  ************************************************************/
00210 static void
00211 gnc_tree_view_account_placeholder_toggled (GtkCellRendererToggle *cell,
00212         const gchar *s_path_str,
00213         gpointer user_data)
00214 {
00215     GncTreeViewAccount *tree_view;
00216     GtkTreePath *s_path;
00217     Account *account;
00218     gboolean placeholder;
00219 
00220     /* Change the requested account */
00221     tree_view = user_data;
00222     s_path = gtk_tree_path_new_from_string (s_path_str);
00223     account = gnc_tree_view_account_get_account_from_path (tree_view, s_path);
00224     if (account)
00225     {
00226         placeholder = !gtk_cell_renderer_toggle_get_active (cell); // hasn't changed yet.
00227         xaccAccountSetPlaceholder (account, placeholder);
00228     }
00229 
00230     /* Clean up */
00231     gtk_tree_path_free (s_path);
00232 }
00233 
00234 
00235 /************************************************************/
00236 /*                      sort functions                      */
00237 /************************************************************/
00238 
00239 static GtkTreeModel *
00240 sort_cb_setup_w_iters (GtkTreeModel *f_model,
00241                        GtkTreeIter *f_iter_a,
00242                        GtkTreeIter *f_iter_b,
00243                        GtkTreeIter *iter_a,
00244                        GtkTreeIter *iter_b,
00245                        const Account **account_a,
00246                        const Account **account_b)
00247 {
00248     GtkTreeModel *model;
00249 
00250     model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
00251     gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
00252             iter_a,
00253             f_iter_a);
00254     gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
00255             iter_b,
00256             f_iter_b);
00257     *account_a = gnc_tree_model_account_get_account (GNC_TREE_MODEL_ACCOUNT(model), iter_a);
00258     *account_b = gnc_tree_model_account_get_account (GNC_TREE_MODEL_ACCOUNT(model), iter_b);
00259     return model;
00260 }
00261 
00262 static void
00263 sort_cb_setup (GtkTreeModel *f_model,
00264                GtkTreeIter *f_iter_a,
00265                GtkTreeIter *f_iter_b,
00266                const Account **account_a,
00267                const Account **account_b)
00268 {
00269     GtkTreeIter iter_a, iter_b;
00270 
00271     sort_cb_setup_w_iters (f_model, f_iter_a, f_iter_b,
00272                            &iter_a, &iter_b, account_a, account_b);
00273 }
00274 
00275 static gint
00276 sort_by_string (GtkTreeModel *f_model,
00277                 GtkTreeIter *f_iter1,
00278                 GtkTreeIter *f_iter2,
00279                 gpointer user_data)
00280 {
00281     GtkTreeModel *model;
00282     GtkTreeIter iter1, iter2;
00283     const Account *account1, *account2;
00284     gchar *str1, *str2;
00285     gint column = GPOINTER_TO_INT(user_data);
00286     gint result;
00287 
00288     model = sort_cb_setup_w_iters(f_model, f_iter1, f_iter2, &iter1, &iter2, &account1, &account2);
00289 
00290     /* Get the strings. */
00291     gtk_tree_model_get(GTK_TREE_MODEL(model), &iter1,  column, &str1, -1);
00292     gtk_tree_model_get(GTK_TREE_MODEL(model), &iter2,  column, &str2, -1);
00293 
00294     result = safe_utf8_collate(str1, str2);
00295     g_free(str1);
00296     g_free(str2);
00297     if (result != 0)
00298         return result;
00299     return xaccAccountOrder(account1, account2);
00300 }
00301 
00302 static gint
00303 sort_by_code (GtkTreeModel *f_model,
00304               GtkTreeIter *f_iter_a,
00305               GtkTreeIter *f_iter_b,
00306               gpointer user_data)
00307 {
00308     const Account *account_a, *account_b;
00309 
00310     sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
00311 
00312     /* Default ordering uses this column first. */
00313     return xaccAccountOrder(account_a, account_b);
00314 }
00315 
00316 static gint
00317 sort_by_xxx_value (xaccGetBalanceInCurrencyFn fn,
00318                    gboolean recurse,
00319                    GtkTreeModel *f_model,
00320                    GtkTreeIter *f_iter_a,
00321                    GtkTreeIter *f_iter_b,
00322                    gpointer user_data)
00323 {
00324     const Account *account_a, *account_b;
00325     gnc_numeric balance_a, balance_b;
00326     gint result;
00327 
00328     /* Find the accounts */
00329     sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
00330 
00331     /* Get balances */
00332     balance_a = gnc_ui_account_get_balance_full(fn, account_a, recurse, NULL, NULL);
00333     balance_b = gnc_ui_account_get_balance_full(fn, account_b, recurse, NULL, NULL);
00334 
00335     result = gnc_numeric_compare(balance_a, balance_b);
00336     if (result != 0)
00337         return result;
00338     return xaccAccountOrder(account_a, account_b);
00339 }
00340 
00341 static gint
00342 sort_by_present_value (GtkTreeModel *f_model,
00343                        GtkTreeIter *f_iter_a,
00344                        GtkTreeIter *f_iter_b,
00345                        gpointer user_data)
00346 {
00347     return sort_by_xxx_value (xaccAccountGetPresentBalanceInCurrency, TRUE,
00348                               f_model, f_iter_a, f_iter_b, user_data);
00349 }
00350 
00351 static gint
00352 sort_by_balance_value (GtkTreeModel *f_model,
00353                        GtkTreeIter *f_iter_a,
00354                        GtkTreeIter *f_iter_b,
00355                        gpointer user_data)
00356 {
00357     return sort_by_xxx_value (xaccAccountGetBalanceInCurrency, TRUE,
00358                               f_model, f_iter_a, f_iter_b, user_data);
00359 }
00360 
00361 static gint
00362 sort_by_cleared_value (GtkTreeModel *f_model,
00363                        GtkTreeIter *f_iter_a,
00364                        GtkTreeIter *f_iter_b,
00365                        gpointer user_data)
00366 {
00367     return sort_by_xxx_value (xaccAccountGetClearedBalanceInCurrency, TRUE,
00368                               f_model, f_iter_a, f_iter_b, user_data);
00369 }
00370 
00371 static gint
00372 sort_by_reconciled_value (GtkTreeModel *f_model,
00373                           GtkTreeIter *f_iter_a,
00374                           GtkTreeIter *f_iter_b,
00375                           gpointer user_data)
00376 {
00377     return sort_by_xxx_value (xaccAccountGetReconciledBalanceInCurrency, TRUE,
00378                               f_model, f_iter_a, f_iter_b, user_data);
00379 }
00380 
00381 static gint
00382 sort_by_future_min_value (GtkTreeModel *f_model,
00383                           GtkTreeIter *f_iter_a,
00384                           GtkTreeIter *f_iter_b,
00385                           gpointer user_data)
00386 {
00387     return sort_by_xxx_value (xaccAccountGetProjectedMinimumBalanceInCurrency, TRUE,
00388                               f_model, f_iter_a, f_iter_b, user_data);
00389 }
00390 
00391 static gint
00392 sort_by_total_value (GtkTreeModel *f_model,
00393                      GtkTreeIter *f_iter_a,
00394                      GtkTreeIter *f_iter_b,
00395                      gpointer user_data)
00396 {
00397     return sort_by_xxx_value (xaccAccountGetBalanceInCurrency, TRUE,
00398                               f_model, f_iter_a, f_iter_b, user_data);
00399 }
00400 
00401 static gint
00402 sort_by_placeholder (GtkTreeModel *f_model,
00403                      GtkTreeIter *f_iter_a,
00404                      GtkTreeIter *f_iter_b,
00405                      gpointer user_data)
00406 {
00407     const Account *account_a, *account_b;
00408     gboolean flag_a, flag_b;
00409 
00410     /* Find the accounts */
00411     sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
00412 
00413     /* Get the placeholder flags. */
00414     flag_a = xaccAccountGetPlaceholder(account_a);
00415     flag_b = xaccAccountGetPlaceholder(account_b);
00416 
00417     if (flag_a > flag_b)
00418         return -1;
00419     else if (flag_a < flag_b)
00420         return 1;
00421     return xaccAccountOrder(account_a, account_b);
00422 }
00423 
00424 static gint
00425 sort_by_xxx_period_value (GtkTreeModel *f_model,
00426                           GtkTreeIter *f_iter_a,
00427                           GtkTreeIter *f_iter_b,
00428                           gboolean recurse)
00429 {
00430     Account *acct1, *acct2;
00431     time_t t1, t2;
00432     gnc_numeric b1, b2;
00433     gint result;
00434 
00435     sort_cb_setup (f_model, f_iter_a, f_iter_b,
00436                    (const Account **)&acct1, (const Account **)&acct2);
00437 
00438     t1 = gnc_accounting_period_fiscal_start();
00439     t2 = gnc_accounting_period_fiscal_end();
00440 
00441     b1 = xaccAccountGetBalanceChangeForPeriod(acct1, t1, t2, recurse);
00442     b2 = xaccAccountGetBalanceChangeForPeriod(acct2, t1, t2, recurse);
00443 
00444     result = gnc_numeric_compare(b1, b2);
00445     if (result != 0)
00446         return result;
00447     return xaccAccountOrder(acct1, acct2);
00448 }
00449 
00450 static gint
00451 sort_by_balance_period_value (GtkTreeModel *f_model,
00452                               GtkTreeIter *f_iter_a,
00453                               GtkTreeIter *f_iter_b,
00454                               gpointer user_data)
00455 {
00456     return sort_by_xxx_period_value (f_model, f_iter_a, f_iter_b, FALSE);
00457 }
00458 
00459 static gint
00460 sort_by_total_period_value (GtkTreeModel *f_model,
00461                             GtkTreeIter *f_iter_a,
00462                             GtkTreeIter *f_iter_b,
00463                             gpointer user_data)
00464 {
00465     return sort_by_xxx_period_value (f_model, f_iter_a, f_iter_b, TRUE);
00466 }
00467 
00468 /************************************************************/
00469 /*                 Tax_Info data function                   */
00470 /************************************************************/
00471 
00472 /*
00473  * The tax-info column in the account tree view is based on the
00474  * combination of two columns in the account tree model. The data
00475  * function displays only the the data in the
00476  * GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO model column if the row is
00477  * expanded; otherwise it combines it with the data
00478  * in the GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO_SUB_ACCT model column.
00479  */
00480 static void
00481 tax_info_data_func (GtkTreeViewColumn *col,
00482                     GtkCellRenderer   *renderer,
00483                     GtkTreeModel      *model,
00484                     GtkTreeIter       *iter,
00485                     gpointer           view)
00486 {
00487     gchar *tax_info = NULL;
00488     GtkTreePath *path;
00489 
00490     gtk_tree_model_get(model,
00491                        iter,
00492                        GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO,
00493                        &tax_info,
00494                        -1);
00495 
00496     path = gtk_tree_model_get_path(model, iter);
00497     if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(view), path))
00498         g_object_set(renderer, "text",
00499                      (tax_info == NULL ? "" : tax_info), NULL);
00500     else
00501     {
00502         gchar *tax_info_sub_acct = NULL;
00503 
00504         gtk_tree_model_get(model,
00505                            iter,
00506                            GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO_SUB_ACCT,
00507                            &tax_info_sub_acct,
00508                            -1);
00509         if ((safe_strcmp (tax_info_sub_acct, "") == 0) ||
00510                 (tax_info_sub_acct == NULL))
00511             g_object_set(renderer, "text",
00512                          (tax_info == NULL ? "" : tax_info), NULL);
00513         else
00514         {
00515             if ((safe_strcmp (tax_info, "") == 0) ||
00516                     (tax_info == NULL))
00517                 g_object_set(renderer, "text",
00518                              (tax_info_sub_acct == NULL ? "" : tax_info_sub_acct),
00519                              NULL);
00520             else
00521             {
00522                 gchar *combined_tax_info;
00523                 combined_tax_info = g_strdup_printf ("%s; %s",
00524                                                      (tax_info == NULL ? "" : tax_info),
00525                                                      (tax_info_sub_acct == NULL ? "" :
00526                                                       tax_info_sub_acct));
00527                 g_object_set(renderer, "text", combined_tax_info, NULL);
00528                 g_free(combined_tax_info);
00529             }
00530         }
00531         g_free(tax_info_sub_acct);
00532     }
00533     g_free(tax_info);
00534     gtk_tree_path_free(path);
00535 }
00536 
00537 /************************************************************/
00538 /*                    New View Creation                     */
00539 /************************************************************/
00540 
00541 /*
00542  * Create a new account tree view with (optional) top level root node.
00543  * This view will be based on a model that is common to all view of
00544  * the same set of books, but will have its own private filter on that
00545  * model.
00546  */
00547 GtkTreeView *
00548 gnc_tree_view_account_new_with_root (Account *root, gboolean show_root)
00549 {
00550     GncTreeView *view;
00551     GtkTreeModel *model, *f_model, *s_model;
00552     GtkTreePath *virtual_root_path = NULL;
00553     const gchar *sample_type, *sample_commodity;
00554     GncTreeViewAccountPrivate *priv;
00555     GtkTreeViewColumn *tax_info_column;
00556     GtkCellRenderer *renderer;
00557 
00558     ENTER(" ");
00559     /* Create our view */
00560     view = g_object_new (GNC_TYPE_TREE_VIEW_ACCOUNT,
00561                          "name", "account_tree", NULL);
00562 
00563     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(GNC_TREE_VIEW_ACCOUNT (view));
00564 
00565     /* Create/get a pointer to the existing model for this set of books. */
00566     model = gnc_tree_model_account_new (root);
00567 
00568     /* Set up the view private filter layer on the common model. */
00569     if (!show_root)
00570         virtual_root_path = gtk_tree_path_new_first ();
00571     f_model = gtk_tree_model_filter_new (model, virtual_root_path);
00572     /* A GncTreeModelAccount is based on a GncTreeModel, which is a
00573      * GObject that provides a GtkTreeModel interface. */
00574     g_object_unref(G_OBJECT(model));
00575     if (virtual_root_path)
00576         gtk_tree_path_free(virtual_root_path);
00577 
00578     /* Set up the view private sort layer on the common model. */
00579     s_model = gtk_tree_model_sort_new_with_model(f_model);
00580     g_object_unref(G_OBJECT(f_model));
00581     gnc_tree_view_set_model (view, s_model);
00582     g_object_unref(G_OBJECT(s_model));
00583 
00584     /* Set default visibilities */
00585     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(view), FALSE);
00586 
00587     sample_type = xaccAccountGetTypeStr(ACCT_TYPE_CREDIT);
00588     sample_commodity = gnc_commodity_get_fullname(gnc_default_currency());
00589 
00590     priv->name_column
00591     = gnc_tree_view_add_text_column(view, _("Account Name"), "name",
00592                                     GNC_STOCK_ACCOUNT, "Expenses:Entertainment",
00593                                     GNC_TREE_MODEL_ACCOUNT_COL_NAME,
00594                                     GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00595                                     sort_by_string);
00596     gnc_tree_view_add_text_column(view, _("Type"), "type", NULL, sample_type,
00597                                   GNC_TREE_MODEL_ACCOUNT_COL_TYPE,
00598                                   GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00599                                   sort_by_string);
00600     gnc_tree_view_add_text_column(view, _("Commodity"), "commodity", NULL,
00601                                   sample_commodity,
00602                                   GNC_TREE_MODEL_ACCOUNT_COL_COMMODITY,
00603                                   GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00604                                   sort_by_string);
00605     priv->code_column
00606     = gnc_tree_view_add_text_column(view, _("Account Code"), "account-code", NULL,
00607                                     "1-123-1234",
00608                                     GNC_TREE_MODEL_ACCOUNT_COL_CODE,
00609                                     GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00610                                     sort_by_code);
00611     priv->desc_column
00612     = gnc_tree_view_add_text_column(view, _("Description"), "description", NULL,
00613                                     "Sample account description.",
00614                                     GNC_TREE_MODEL_ACCOUNT_COL_DESCRIPTION,
00615                                     GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00616                                     sort_by_string);
00617     gnc_tree_view_add_numeric_column(view, _("Last Num"), "lastnum", "12345",
00618                                      GNC_TREE_MODEL_ACCOUNT_COL_LASTNUM,
00619                                      GNC_TREE_VIEW_COLUMN_COLOR_NONE,
00620                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00621                                      sort_by_string);
00622     gnc_tree_view_add_numeric_column(view, _("Present"), "present",
00623                                      SAMPLE_ACCOUNT_VALUE,
00624                                      GNC_TREE_MODEL_ACCOUNT_COL_PRESENT,
00625                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_PRESENT,
00626                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00627                                      sort_by_present_value);
00628     priv->present_report_column
00629     = gnc_tree_view_add_numeric_column(view, _("Present (Report)"), "present_report",
00630                                        SAMPLE_ACCOUNT_VALUE,
00631                                        GNC_TREE_MODEL_ACCOUNT_COL_PRESENT_REPORT,
00632                                        GNC_TREE_MODEL_ACCOUNT_COL_COLOR_PRESENT,
00633                                        GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00634                                        sort_by_present_value);
00635     gnc_tree_view_add_numeric_column(view, _("Balance"), "balance",
00636                                      SAMPLE_ACCOUNT_VALUE,
00637                                      GNC_TREE_MODEL_ACCOUNT_COL_BALANCE,
00638                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE,
00639                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00640                                      sort_by_balance_value);
00641     priv->balance_report_column
00642     = gnc_tree_view_add_numeric_column(view, _("Balance (Report)"), "balance_report",
00643                                        SAMPLE_ACCOUNT_VALUE,
00644                                        GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_REPORT,
00645                                        GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE,
00646                                        GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00647                                        sort_by_balance_value);
00648 
00649     gnc_tree_view_add_numeric_column(view, _("Balance (Period)"), "balance-period",
00650                                      SAMPLE_ACCOUNT_VALUE,
00651                                      GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_PERIOD,
00652                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE_PERIOD,
00653                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00654                                      sort_by_balance_period_value);
00655     gnc_tree_view_add_numeric_column(view, _("Cleared"), "cleared",
00656                                      SAMPLE_ACCOUNT_VALUE,
00657                                      GNC_TREE_MODEL_ACCOUNT_COL_CLEARED,
00658                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_CLEARED,
00659                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00660                                      sort_by_cleared_value);
00661     priv->cleared_report_column
00662     = gnc_tree_view_add_numeric_column(view, _("Cleared (Report)"), "cleared_report",
00663                                        SAMPLE_ACCOUNT_VALUE,
00664                                        GNC_TREE_MODEL_ACCOUNT_COL_CLEARED_REPORT,
00665                                        GNC_TREE_MODEL_ACCOUNT_COL_COLOR_CLEARED,
00666                                        GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00667                                        sort_by_cleared_value);
00668     gnc_tree_view_add_numeric_column(view, _("Reconciled"), "reconciled",
00669                                      SAMPLE_ACCOUNT_VALUE,
00670                                      GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED,
00671                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_RECONCILED,
00672                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00673                                      sort_by_reconciled_value);
00674     priv->reconciled_report_column
00675     = gnc_tree_view_add_numeric_column(view, _("Reconciled (Report)"), "reconciled_report",
00676                                        SAMPLE_ACCOUNT_VALUE,
00677                                        GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_REPORT,
00678                                        GNC_TREE_MODEL_ACCOUNT_COL_COLOR_RECONCILED,
00679                                        GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00680                                        sort_by_reconciled_value);
00681     gnc_tree_view_add_text_column(view, _("Last Reconcile Date"), "last-recon-date", NULL,
00682                                   "Last Reconcile Date",
00683                                   GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_DATE,
00684                                   GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00685                                   sort_by_string);
00686     gnc_tree_view_add_numeric_column(view, _("Future Minimum"), "future_min",
00687                                      SAMPLE_ACCOUNT_VALUE,
00688                                      GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN,
00689                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_FUTURE_MIN,
00690                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00691                                      sort_by_future_min_value);
00692     priv->future_min_report_column
00693     =  gnc_tree_view_add_numeric_column(view, _("Future Minimum (Report)"), "future_min_report",
00694                                         SAMPLE_ACCOUNT_VALUE,
00695                                         GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN_REPORT,
00696                                         GNC_TREE_MODEL_ACCOUNT_COL_COLOR_FUTURE_MIN,
00697                                         GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00698                                         sort_by_future_min_value);
00699     gnc_tree_view_add_numeric_column(view, _("Total"), "total",
00700                                      SAMPLE_ACCOUNT_VALUE,
00701                                      GNC_TREE_MODEL_ACCOUNT_COL_TOTAL,
00702                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL,
00703                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00704                                      sort_by_total_value);
00705     priv->total_report_column
00706     = gnc_tree_view_add_numeric_column(view, _("Total (Report)"), "total_report",
00707                                        SAMPLE_ACCOUNT_VALUE,
00708                                        GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_REPORT,
00709                                        GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL,
00710                                        GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00711                                        sort_by_total_value);
00712     gnc_tree_view_add_numeric_column(view, _("Total (Period)"), "total-period",
00713                                      SAMPLE_ACCOUNT_VALUE,
00714                                      GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_PERIOD,
00715                                      GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL_PERIOD,
00716                                      GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00717                                      sort_by_total_period_value);
00718     priv->notes_column
00719     = gnc_tree_view_add_text_column(view, _("Notes"), "notes", NULL,
00720                                     "Sample account notes.",
00721                                     GNC_TREE_MODEL_ACCOUNT_COL_NOTES,
00722                                     GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00723                                     sort_by_string);
00724     tax_info_column
00725     = gnc_tree_view_add_text_column(view, _("Tax Info"), "tax-info", NULL,
00726                                     "Sample tax info.",
00727                                     GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO,
00728                                     GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00729                                     sort_by_string);
00730     renderer = gnc_tree_view_column_get_renderer(tax_info_column);
00731     gtk_tree_view_column_set_cell_data_func(tax_info_column,
00732                                             renderer,
00733                                             tax_info_data_func,
00734                                             GTK_TREE_VIEW(view),
00735                                             NULL);
00736     gnc_tree_view_add_toggle_column(view, _("Placeholder"),
00737                                     /* Translators: This string has a context prefix; the translation
00738                                         must only contain the part after the | character. */
00739                                     Q_("Column letter for 'Placeholder'|P"),
00740                                     "placeholder",
00741                                     GNC_TREE_MODEL_ACCOUNT_COL_PLACEHOLDER,
00742                                     GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
00743                                     sort_by_placeholder,
00744                                     gnc_tree_view_account_placeholder_toggled);
00745 
00746     /* Update column titles to use the currency name. */
00747     gtva_update_column_names(view);
00748 
00749     /* By default only the first column is visible. */
00750     gnc_tree_view_configure_columns(view);
00751     gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (f_model),
00752                                             gnc_tree_view_account_filter_helper,
00753                                             view,
00754                                             NULL);
00755 
00756     /* Default the sorting to account name */
00757     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(s_model),
00758                                          GNC_TREE_MODEL_ACCOUNT_COL_NAME,
00759                                          GTK_SORT_ASCENDING);
00760 
00761     gtk_widget_show(GTK_WIDGET(view));
00762     LEAVE("%p", view);
00763     return GTK_TREE_VIEW(view);
00764 }
00765 
00766 /*
00767  * Create a new account tree view with (optional) top level root node.
00768  * This view will be based on a model that is common to all view of
00769  * the same set of books, but will have its own private filter on that
00770  * model.
00771  */
00772 GtkTreeView *
00773 gnc_tree_view_account_new (gboolean show_root)
00774 {
00775     Account *root;
00776 
00777     root = gnc_book_get_root_account (gnc_get_current_book ());
00778     return gnc_tree_view_account_new_with_root (root, show_root);
00779 }
00780 
00781 /************************************************************/
00782 /*                   Auxiliary Functions                    */
00783 /************************************************************/
00784 
00785 #define debug_path(fn, path) {                          \
00786     gchar *path_string = gtk_tree_path_to_string(path); \
00787     fn("tree path %s", path_string);                    \
00788     g_free(path_string);                                \
00789   }
00790 
00791 static GtkTreePath *
00792 gnc_tree_view_account_get_path_from_account (GncTreeViewAccount *view,
00793         Account *account)
00794 {
00795     GtkTreeModel *model, *f_model, *s_model;
00796     GtkTreePath *path, *f_path, *s_path;
00797 
00798     ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
00799 
00800     if (account == NULL)
00801     {
00802         LEAVE("no account");
00803         return NULL;
00804     }
00805 
00806     /* Reach down to the real model and get a path for this account */
00807     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
00808     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
00809     model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
00810     path = gnc_tree_model_account_get_path_from_account (GNC_TREE_MODEL_ACCOUNT(model), account);
00811     if (path == NULL)
00812     {
00813         LEAVE("no path");
00814         return NULL;
00815     }
00816 
00817     /* convert back to a filtered path */
00818     f_path = gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (f_model), path);
00819     gtk_tree_path_free(path);
00820     if (!f_path)
00821     {
00822         LEAVE("no filter path");
00823         return NULL;
00824     }
00825 
00826     /* convert back to a sorted path */
00827     s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model), f_path);
00828     gtk_tree_path_free(f_path);
00829     debug_path(LEAVE, s_path);
00830     return s_path;
00831 }
00832 
00833 static gboolean
00834 gnc_tree_view_account_get_iter_from_account (GncTreeViewAccount *view,
00835         Account *account,
00836         GtkTreeIter *s_iter)
00837 {
00838     GtkTreeModel *model, *f_model, *s_model;
00839     GtkTreeIter iter, f_iter;
00840 
00841     g_return_val_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view), FALSE);
00842     g_return_val_if_fail(account != NULL, FALSE);
00843     g_return_val_if_fail(s_iter != NULL, FALSE);
00844 
00845     ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
00846 
00847     /* Reach down to the real model and get an iter for this account */
00848     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
00849     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
00850     model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
00851     if (!gnc_tree_model_account_get_iter_from_account (
00852                 GNC_TREE_MODEL_ACCOUNT(model), account, &iter))
00853     {
00854         LEAVE("model_get_iter_from_account failed");
00855         return FALSE;
00856     }
00857 
00858     /* convert back to a sort iter */
00859     gtk_tree_model_filter_convert_child_iter_to_iter (
00860         GTK_TREE_MODEL_FILTER(f_model), &f_iter, &iter);
00861     gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(s_model),
00862             s_iter, &f_iter);
00863     LEAVE(" ");
00864     return TRUE;
00865 }
00866 
00867 gint
00868 gnc_tree_view_account_count_children (GncTreeViewAccount *view,
00869                                       Account *account)
00870 {
00871     GtkTreeModel *s_model;
00872     GtkTreeIter s_iter;
00873     gint num_children;
00874 
00875     ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
00876 
00877     if (account == NULL)
00878     {
00879         LEAVE("no account");
00880         return 0;
00881     }
00882 
00883     if (!gnc_tree_view_account_get_iter_from_account (view, account, &s_iter))
00884     {
00885         LEAVE("view_get_iter_from_account failed");
00886         return 0;
00887     }
00888 
00889     /* Any children? */
00890     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
00891     num_children = gtk_tree_model_iter_n_children(s_model, &s_iter);
00892     LEAVE("%d children", num_children);
00893     return num_children;
00894 }
00895 
00896 
00897 /************************************************************/
00898 /*            Account Tree View Filter Functions            */
00899 /************************************************************/
00900 
00901 /*
00902  * Get a copy of the account view info structure in use by the
00903  * specified tree.
00904  */
00905 void
00906 gnc_tree_view_account_get_view_info (GncTreeViewAccount *account_view,
00907                                      AccountViewInfo *avi)
00908 {
00909     GncTreeViewAccountPrivate *priv;
00910 
00911     g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(account_view));
00912     g_return_if_fail(avi != NULL);
00913 
00914     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(account_view);
00915 
00916     *avi = priv->avi;
00917 }
00918 
00919 /*
00920  * Set the account view info data in use by the specified tree to
00921  * match the callers request.
00922  *
00923  * DRH - COMPATABILITY WARNING
00924  *
00925  * This function does not do anything with the 'include_type' field.
00926  * Should there be a automatic filter for backward compatability
00927  * that uses these flags, or should all uses of this be converted to
00928  * a GtkTreeModelFilter?
00929  *
00930  * CAS - For now, I'll try the automatic filter approach by making
00931  * this function use GtkTreeModelFilter.
00932  */
00933 void
00934 gnc_tree_view_account_set_view_info (GncTreeViewAccount *account_view,
00935                                      AccountViewInfo *avi)
00936 {
00937     GncTreeViewAccountPrivate *priv;
00938     gint i;
00939     guint sel_bits = 0;
00940 
00941     ENTER("%p", account_view);
00942     g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(account_view));
00943     g_return_if_fail(avi != NULL);
00944 
00945     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(account_view);
00946     priv->avi = *avi;
00947 
00948     for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
00949     {
00950         sel_bits |= avi->include_type[i] ? (1 << i) : 0;
00951     }
00952 
00953     gnc_tree_view_account_set_filter(
00954         account_view, gnc_tree_view_account_filter_by_view_info,
00955         &priv->avi, NULL);
00956 
00957     LEAVE(" ");
00958 }
00959 
00960 static gboolean
00961 gnc_tree_view_account_filter_helper (GtkTreeModel *model,
00962                                      GtkTreeIter *iter,
00963                                      gpointer data)
00964 {
00965     Account *account;
00966     GncTreeViewAccount *view = data;
00967     GncTreeViewAccountPrivate *priv;
00968 
00969     g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model), FALSE);
00970     g_return_val_if_fail (iter != NULL, FALSE);
00971 
00972     account = gnc_tree_model_account_get_account (
00973                   GNC_TREE_MODEL_ACCOUNT(model), iter);
00974 
00975     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
00976     if (priv->filter_fn)
00977         return priv->filter_fn(account, priv->filter_data);
00978     else return TRUE;
00979 }
00980 
00981 /*
00982  * Set an GtkTreeModel visible filter on this account.  This filter will be
00983  * called for each account that the tree is about to show, and the
00984  * account will be passed to the callback function.
00985  *
00986  * Use NULL as func to remove filter.
00987  */
00988 void
00989 gnc_tree_view_account_set_filter (GncTreeViewAccount *view,
00990                                   gnc_tree_view_account_filter_func func,
00991                                   gpointer data,
00992                                   GtkFunction destroy)
00993 {
00994     GncTreeViewAccountPrivate *priv;
00995 
00996     ENTER("view %p, filter func %p, data %p, destroy %p",
00997           view, func, data, destroy);
00998 
00999     g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view));
01000 
01001     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
01002     if (priv->filter_destroy)
01003     {
01004         priv->filter_destroy(priv->filter_data);
01005     }
01006     priv->filter_destroy = destroy;
01007     priv->filter_data = data;
01008     priv->filter_fn = func;
01009 
01010     gnc_tree_view_account_refilter(view);
01011     LEAVE(" ");
01012 }
01013 
01014 /*
01015  * Forces the entire account tree to be re-evaluated for visibility.
01016  */
01017 void
01018 gnc_tree_view_account_refilter (GncTreeViewAccount *view)
01019 {
01020     GtkTreeModel *f_model, *s_model;
01021 
01022     g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view));
01023 
01024     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
01025     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
01026     gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (f_model));
01027 }
01028 
01029 gboolean
01030 gnc_tree_view_account_filter_by_view_info(Account* acct, gpointer data)
01031 {
01032     GNCAccountType acct_type;
01033     AccountViewInfo* avi = (AccountViewInfo*)data;
01034 
01035     g_return_val_if_fail(GNC_IS_ACCOUNT(acct), FALSE);
01036     acct_type = xaccAccountGetType(acct);
01037 
01038     if (!avi->include_type[acct_type]) return FALSE;
01039     if (!avi->show_hidden && xaccAccountIsHidden(acct)) return FALSE;
01040     return TRUE;
01041 }
01042 
01043 /************************************************************/
01044 /*           Account Tree View Get/Set Functions            */
01045 /************************************************************/
01046 
01047 /*
01048  * Retrieve the selected account from an account tree view.  The
01049  * account tree must be in single selection mode.
01050  */
01051 Account *
01052 gnc_tree_view_account_get_account_from_path (GncTreeViewAccount *view,
01053         GtkTreePath *s_path)
01054 {
01055     GtkTreeModel *model, *f_model, *s_model;
01056     GtkTreePath *path, *f_path;
01057     GtkTreeIter iter;
01058     Account *account;
01059 
01060     ENTER("view %p", view);
01061     g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
01062     g_return_val_if_fail (s_path != NULL, NULL);
01063 
01064     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
01065     f_path = gtk_tree_model_sort_convert_path_to_child_path (
01066                  GTK_TREE_MODEL_SORT (s_model), s_path);
01067     if (!f_path)
01068     {
01069         LEAVE("no filter path");
01070         return NULL;
01071     }
01072 
01073     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
01074     path = gtk_tree_model_filter_convert_path_to_child_path (
01075                GTK_TREE_MODEL_FILTER (f_model), f_path);
01076     gtk_tree_path_free(f_path);
01077     if (!path)
01078     {
01079         LEAVE("no path");
01080         return NULL;
01081     }
01082 
01083     model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
01084     if (!gtk_tree_model_get_iter (model, &iter, path))
01085     {
01086         LEAVE("no iter");
01087         return NULL;
01088     }
01089 
01090     account = iter.user_data;
01091     gtk_tree_path_free(path);
01092     LEAVE("account %p (%s)", account, xaccAccountGetName (account));
01093     return account;
01094 }
01095 
01096 
01097 Account *
01098 gnc_tree_view_account_get_account_from_iter (GtkTreeModel *s_model,
01099         GtkTreeIter  *s_iter)
01100 {
01101     GtkTreeModel *model, *f_model;
01102     GtkTreeIter iter, f_iter;
01103     Account *account;
01104 
01105     g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT(s_model), NULL);
01106     g_return_val_if_fail (s_iter != NULL, NULL);
01107 
01108     ENTER("model %p, iter %p", s_model, s_iter);
01109 
01110     gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT(s_model),
01111             &f_iter,
01112             s_iter);
01113     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
01114     gtk_tree_model_filter_convert_iter_to_child_iter (
01115         GTK_TREE_MODEL_FILTER(f_model), &iter, &f_iter);
01116     model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
01117     account = gnc_tree_model_account_get_account (
01118                   GNC_TREE_MODEL_ACCOUNT(model), &iter);
01119     LEAVE("account %p (%s)", account, xaccAccountGetName (account));
01120     return account;
01121 }
01122 
01123 
01124 /*
01125  * Retrieve the selected account from an account tree view.  The
01126  * account tree must be in single selection mode.
01127  */
01128 Account *
01129 gnc_tree_view_account_get_selected_account (GncTreeViewAccount *view)
01130 {
01131     GtkTreeSelection *selection;
01132     GtkTreeModel *f_model, *s_model;
01133     GtkTreeIter iter, f_iter, s_iter;
01134     Account *account;
01135     GtkSelectionMode mode;
01136 
01137     ENTER("view %p", view);
01138     g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
01139 
01140     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
01141     mode = gtk_tree_selection_get_mode(selection);
01142     if ((mode != GTK_SELECTION_SINGLE) && (mode != GTK_SELECTION_BROWSE))
01143     {
01144         return NULL;
01145     }
01146     if (!gtk_tree_selection_get_selected (selection, &s_model, &s_iter))
01147     {
01148         LEAVE("no account, get_selected failed");
01149         return FALSE;
01150     }
01151 
01152     gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model),
01153             &f_iter, &s_iter);
01154 
01155     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
01156     gtk_tree_model_filter_convert_iter_to_child_iter (
01157         GTK_TREE_MODEL_FILTER (f_model), &iter, &f_iter);
01158 
01159     account = iter.user_data;
01160     LEAVE("account %p (%s)", account, xaccAccountGetName (account));
01161     return account;
01162 }
01163 
01164 /*
01165  * Selects a single account in the account tree view.  The account
01166  * tree must be in single selection mode.
01167  */
01168 void
01169 gnc_tree_view_account_set_selected_account (GncTreeViewAccount *view,
01170         Account *account)
01171 {
01172     GtkTreeModel *model, *f_model, *s_model;
01173     GtkTreePath *path, *f_path, *s_path, *parent_path;
01174     GtkTreeSelection *selection;
01175 
01176     ENTER("view %p, account %p (%s)", view,
01177           account, xaccAccountGetName (account));
01178     g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view));
01179 
01180     /* Clear any existing selection. */
01181     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
01182     gtk_tree_selection_unselect_all (selection);
01183 
01184     if (account == NULL)
01185         return;
01186 
01187     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
01188     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
01189     model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
01190 
01191     path = gnc_tree_model_account_get_path_from_account (
01192                GNC_TREE_MODEL_ACCOUNT(model), account);
01193     if (path == NULL)
01194     {
01195         LEAVE("no path");
01196         return;
01197     }
01198     debug_path(DEBUG, path);
01199 
01200     f_path = gtk_tree_model_filter_convert_child_path_to_path (
01201                  GTK_TREE_MODEL_FILTER (f_model), path);
01202     gtk_tree_path_free(path);
01203     if (f_path == NULL)
01204     {
01205         LEAVE("no filter path");
01206         return;
01207     }
01208     debug_path(DEBUG, f_path);
01209 
01210     s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model),
01211              f_path);
01212     gtk_tree_path_free(f_path);
01213     if (s_path == NULL)
01214     {
01215         LEAVE("no sort path");
01216         return;
01217     }
01218 
01219     /* gtk_tree_view requires that a row be visible before it can be selected */
01220     parent_path = gtk_tree_path_copy (s_path);
01221     if (gtk_tree_path_up (parent_path))
01222     {
01223         /* This function is misnamed.  It expands the actual item
01224          * specified, not the path to the item specified. I.E. It expands
01225          * one level too many, thus the get of the parent. */
01226         gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), parent_path);
01227     }
01228     gtk_tree_path_free(parent_path);
01229 
01230     gtk_tree_selection_select_path (selection, s_path);
01231 
01232     /* give gtk+ a chance to resize the tree view first by handling pending
01233      * configure events */
01234     while (gtk_events_pending ())
01235         gtk_main_iteration ();
01236     gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(view), s_path, NULL, FALSE, 0.0, 0.0);
01237     debug_path(LEAVE, s_path);
01238     gtk_tree_path_free(s_path);
01239 }
01240 
01241 /* Information re selection process */
01242 typedef struct
01243 {
01244     GList* return_list;
01245     GncTreeViewAccountPrivate* priv;
01246 } GncTreeViewSelectionInfo;
01247 
01248 /*
01249  * This helper function is called once for each row in the tree view
01250  * that is currently selected.  Its task is to append the corresponding
01251  * account to the end of a glist.
01252  */
01253 static void
01254 get_selected_accounts_helper (GtkTreeModel *s_model,
01255                               GtkTreePath *s_path,
01256                               GtkTreeIter *s_iter,
01257                               gpointer data)
01258 {
01259     GncTreeViewSelectionInfo *gtvsi = data;
01260     GtkTreeModel *f_model;
01261     GtkTreeIter iter, f_iter;
01262     Account *account;
01263 
01264     gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model),
01265             &f_iter, s_iter);
01266 
01267     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
01268     gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (f_model),
01269             &iter, &f_iter);
01270     account = iter.user_data;
01271 
01272     /* Only selected if it passes the filter */
01273     if (gtvsi->priv->filter_fn == NULL || gtvsi->priv->filter_fn(account, gtvsi->priv->filter_data))
01274     {
01275         gtvsi->return_list = g_list_append(gtvsi->return_list, account);
01276     }
01277 }
01278 
01279 /*
01280  * Given an account tree view, return a list of the selected accounts. The
01281  * account tree must be in multiple selection mode.
01282  *
01283  * Note: It is the responsibility of the caller to free the returned
01284  * list.
01285  */
01286 GList *
01287 gnc_tree_view_account_get_selected_accounts (GncTreeViewAccount *view)
01288 {
01289     GtkTreeSelection *selection;
01290     GncTreeViewSelectionInfo info;
01291 
01292     g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
01293 
01294     info.return_list = NULL;
01295     info.priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
01296     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
01297     gtk_tree_selection_selected_foreach(selection, get_selected_accounts_helper, &info);
01298     return info.return_list;
01299 }
01300 
01301 /*
01302  * Given an account tree view and a list of accounts, select those
01303  * accounts in the tree view.
01304  */
01305 void
01306 gnc_tree_view_account_set_selected_accounts (GncTreeViewAccount *view,
01307         GList *account_list,
01308         gboolean show_last)
01309 {
01310     GtkTreeModel *model, *f_model, *s_model;
01311     GtkTreePath *path, *f_path, *s_path, *parent_path;
01312     GtkTreeSelection *selection;
01313     GList *element;
01314     Account *account;
01315 
01316     g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view));
01317 
01318     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
01319     f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
01320     model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
01321 
01322     /* Clear any existing selection. */
01323     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
01324     gtk_tree_selection_unselect_all (selection);
01325     gtk_tree_view_collapse_all (GTK_TREE_VIEW(view));
01326 
01327     /* Now go select what the user requested. */
01328     for (element = account_list; element; )
01329     {
01330         account = element->data;
01331         element = g_list_next(element);
01332 
01333         path = gnc_tree_model_account_get_path_from_account (GNC_TREE_MODEL_ACCOUNT(model), account);
01334         if (path == NULL)
01335         {
01336             /*
01337              * Oops.  Someone must have deleted this account and not cleaned
01338              * up all references to it.
01339              */
01340             continue;
01341         }
01342 
01343         f_path = gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (f_model),
01344                  path);
01345         gtk_tree_path_free(path);
01346         if (f_path == NULL)
01347             continue;
01348 
01349         s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model),
01350                  f_path);
01351         gtk_tree_path_free(f_path);
01352         if (s_path == NULL)
01353             continue;
01354 
01355         /* gtk_tree_view requires that a row be visible before it can be selected */
01356         parent_path = gtk_tree_path_copy (s_path);
01357         if (gtk_tree_path_up (parent_path))
01358         {
01359             /* This function is misnamed.  It expands the actual item
01360              * specified, not the path to the item specified. I.E. It
01361              * expands one level too many, thus the get of the parent. */
01362             gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), parent_path);
01363         }
01364         gtk_tree_path_free(parent_path);
01365 
01366         gtk_tree_selection_select_path (selection, s_path);
01367         if (show_last && (element == NULL))
01368             gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(view), s_path, NULL, FALSE, 0.0, 0.0);
01369         gtk_tree_path_free(s_path);
01370     }
01371 }
01372 
01373 /*
01374  * Selects all sub-accounts of an acccount.
01375  */
01376 void
01377 gnc_tree_view_account_select_subaccounts (GncTreeViewAccount *view,
01378         Account *account)
01379 {
01380     GtkTreeModel *s_model;
01381     GtkTreeSelection *selection;
01382     GtkTreePath *sp_account, *sp_start, *sp_end;
01383     GtkTreeIter si_account, si_start, si_end;
01384     gboolean have_start, have_end;
01385     gint num_children;
01386 
01387     ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
01388 
01389     g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view));
01390 
01391     if (account == NULL)
01392     {
01393         LEAVE("no account");
01394         return;
01395     }
01396 
01397     if (!gnc_tree_view_account_get_iter_from_account (view, account, &si_account))
01398     {
01399         LEAVE("view_get_iter_from_account failed");
01400         return;
01401     }
01402 
01403     /* Any children? */
01404     s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
01405     num_children = gtk_tree_model_iter_n_children(s_model, &si_account);
01406     if (num_children == 0)
01407     {
01408         LEAVE("no children");
01409         return;
01410     }
01411 
01412     /* Expand the tree.  Required for selection to work */
01413     sp_account = gtk_tree_model_get_path (s_model, &si_account);
01414     gtk_tree_view_expand_row (GTK_TREE_VIEW(view), sp_account, TRUE);
01415 
01416     /* compute start/end paths */
01417     have_start = gtk_tree_model_iter_nth_child(s_model, &si_start, &si_account, 0);
01418     si_end = si_account;
01419     while (num_children)
01420     {
01421         GtkTreeIter tmp_iter = si_end;
01422         have_end = gtk_tree_model_iter_nth_child(s_model, &si_end, &tmp_iter,
01423                    num_children - 1);
01424         if (have_end)
01425             num_children = gtk_tree_model_iter_n_children(s_model, &si_end);
01426         else
01427             num_children = 0;
01428     }
01429 
01430     if (have_start && have_end)
01431     {
01432         sp_start = gtk_tree_model_get_path (s_model, &si_start);
01433         sp_end = gtk_tree_model_get_path (s_model, &si_end);
01434 
01435         /* select everything between */
01436         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
01437         gtk_tree_selection_select_range (selection, sp_start, sp_end);
01438 
01439         /* clean up */
01440         gtk_tree_path_free(sp_start);
01441         gtk_tree_path_free(sp_end);
01442     }
01443     gtk_tree_path_free(sp_account);
01444     LEAVE(" ");
01445     return;
01446 }
01447 
01448 void
01449 gnc_tree_view_account_expand_to_account (GncTreeViewAccount *view,
01450         Account *account)
01451 {
01452     GtkTreePath *path;
01453 
01454     g_return_if_fail(view != NULL);
01455     g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view));
01456     ENTER("view %p, account %p", view, account);
01457 
01458     path = gnc_tree_view_account_get_path_from_account(view, account);
01459     if (path)
01460     {
01461         gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), path);
01462         gtk_tree_path_free(path);
01463     }
01464     LEAVE(" ");
01465 }
01466 
01467 
01468 /*
01469  * Retrieve the account currently under the cursor.
01470  */
01471 Account *
01472 gnc_tree_view_account_get_cursor_account (GncTreeViewAccount *view)
01473 {
01474     GtkTreeModel *s_model;
01475     GtkTreePath *s_path;
01476     Account *account;
01477 
01478     ENTER("view %p", view);
01479     g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
01480 
01481     s_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
01482     gtk_tree_view_get_cursor (GTK_TREE_VIEW(view), &s_path, NULL);
01483     if (!s_path)
01484     {
01485         LEAVE("no account");
01486         return NULL;
01487     }
01488 
01489     account = gnc_tree_view_account_get_account_from_path (view, s_path);
01490     gtk_tree_path_free(s_path);
01491     LEAVE("account %p (%s)", account, xaccAccountGetName (account));
01492     return account;
01493 }
01494 
01495 
01496 /************************************************************/
01497 /*         Account Tree View Add Column Functions           */
01498 /************************************************************/
01499 
01500 static void
01501 gtva_update_column_name (GtkTreeViewColumn *column,
01502                          const gchar *fmt,
01503                          const gchar *mnemonic)
01504 {
01505     gchar *name;
01506 
01507     g_return_if_fail(column);
01508 
01509     name = g_strdup_printf(fmt, mnemonic);
01510     gtk_tree_view_column_set_title(column, name);
01511     g_free(name);
01512 }
01513 
01514 
01515 static void
01516 gtva_update_column_names (GncTreeView *view)
01517 {
01518     GncTreeViewAccountPrivate *priv;
01519     const gchar *mnemonic;
01520 
01521     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
01522     mnemonic = gnc_commodity_get_mnemonic(gnc_default_report_currency());
01523 
01524     gtva_update_column_name(priv->present_report_column,
01525                             /* Translators: %s is a currency mnemonic.*/
01526                             _("Present (%s)"), mnemonic);
01527     gtva_update_column_name(priv->balance_report_column,
01528                             /* Translators: %s is a currency mnemonic.*/
01529                             _("Balance (%s)"), mnemonic);
01530     gtva_update_column_name(priv->cleared_report_column,
01531                             /* Translators: %s is a currency mnemonic.*/
01532                             _("Cleared (%s)"), mnemonic);
01533     gtva_update_column_name(priv->reconciled_report_column,
01534                             /* Translators: %s is a currency mnemonic.*/
01535                             _("Reconciled (%s)"), mnemonic);
01536     gtva_update_column_name(priv->future_min_report_column,
01537                             /* Translators: %s is a currency mnemonic.*/
01538                             _("Future Minimum (%s)"), mnemonic);
01539     gtva_update_column_name(priv->total_report_column,
01540                             /* Translators: %s is a currency mnemonic.*/
01541                             _("Total (%s)"), mnemonic);
01542     gnc_tree_view_set_show_column_menu(view, FALSE);
01543     gnc_tree_view_set_show_column_menu(view, TRUE);
01544 }
01545 
01546 
01547 static void
01548 gtva_currency_changed_cb (void)
01549 {
01550     const GList *views, *ptr;
01551 
01552     views = gnc_gobject_tracking_get_list (GNC_TREE_VIEW_ACCOUNT_NAME);
01553     for (ptr = views; ptr; ptr = g_list_next(ptr))
01554     {
01555         gtva_update_column_names (ptr->data);
01556     }
01557 }
01558 /* This function implements a custom mapping between an account's KVP
01559  * and the cell renderer's 'text' property. */
01560 static void
01561 account_cell_kvp_data_func (GtkTreeViewColumn *tree_column,
01562                             GtkCellRenderer *cell,
01563                             GtkTreeModel *s_model,
01564                             GtkTreeIter *s_iter,
01565                             gpointer key)
01566 {
01567     Account *account;
01568     kvp_frame * frame;
01569 
01570     g_return_if_fail (GTK_IS_TREE_MODEL_SORT (s_model));
01571     account = gnc_tree_view_account_get_account_from_iter(s_model, s_iter);
01572     frame = xaccAccountGetSlots(account);
01573 
01574     g_object_set (G_OBJECT (cell),
01575                   "text", kvp_frame_get_string(frame, (gchar *)key),
01576                   "xalign", 0.0,
01577                   NULL);
01578 
01579 }
01580 
01581 
01582 GtkTreeViewColumn *
01583 gnc_tree_view_account_add_kvp_column (GncTreeViewAccount *view,
01584                                       const gchar *column_title,
01585                                       const gchar *kvp_key)
01586 {
01587     GtkCellRenderer *renderer;
01588     GtkTreeViewColumn *column;
01589 
01590     g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
01591     g_return_val_if_fail (kvp_key != NULL, NULL);
01592 
01593     column = gnc_tree_view_add_text_column(GNC_TREE_VIEW(view), column_title,
01594                                            kvp_key, NULL, "Sample text",
01595                                            -1, -1, NULL);
01596 
01597     /* This new kvp column has only had one renderer added to it so
01598      * far.  Find that renderer. */
01599     renderer = gnc_tree_view_column_get_renderer(column);
01600     g_object_set (G_OBJECT (renderer), "xalign", 1.0, NULL);
01601 
01602     gtk_tree_view_column_set_cell_data_func (column, renderer,
01603             account_cell_kvp_data_func,
01604             g_strdup(kvp_key), g_free);
01605     return column;
01606 }
01607 
01608 static void col_edited_helper(GtkCellRendererText *cell, gchar *path_string,
01609                               gchar *new_text, gpointer _s_model)
01610 {
01611     Account *account;
01612     GtkTreeModel *s_model;
01613     GtkTreeIter s_iter;
01614     GncTreeViewAccountColumnTextEdited col_edited_cb;
01615     GtkTreeViewColumn *col;
01616 
01617     col_edited_cb = g_object_get_data(G_OBJECT(cell),
01618                                       "column_edited_callback");
01619     col = GTK_TREE_VIEW_COLUMN(g_object_get_data(G_OBJECT(cell),
01620                                "column_view"));
01621     s_model = GTK_TREE_MODEL(_s_model);
01622 
01623     if (!gtk_tree_model_get_iter_from_string(s_model, &s_iter, path_string))
01624         return;
01625 
01626     account = gnc_tree_view_account_get_account_from_iter(s_model, &s_iter);
01627     col_edited_cb(account, col, new_text);
01628 }
01629 
01630 static void col_source_helper(GtkTreeViewColumn *col, GtkCellRenderer *cell,
01631                               GtkTreeModel *s_model, GtkTreeIter *s_iter,
01632                               gpointer _col_source_cb)
01633 {
01634     Account *account;
01635     gchar *text;
01636     GncTreeViewAccountColumnSource col_source_cb;
01637 
01638     g_return_if_fail (GTK_IS_TREE_MODEL_SORT (s_model));
01639     col_source_cb = (GncTreeViewAccountColumnSource) _col_source_cb;
01640     account = gnc_tree_view_account_get_account_from_iter(s_model, s_iter);
01641     text = col_source_cb(account, col, cell);
01642     g_object_set (G_OBJECT (cell), "text", text, "xalign", 1.0, NULL);
01643     g_free(text);
01644 }
01645 
01650 void
01651 gtva_setup_column_renderer_edited_cb(GncTreeViewAccount *account_view,
01652                                      GtkTreeViewColumn *column,
01653                                      GtkCellRenderer *renderer,
01654                                      GncTreeViewAccountColumnTextEdited col_edited_cb)
01655 {
01656     GtkTreeModel *s_model;
01657 
01658     if (col_edited_cb == NULL)
01659     {
01660         g_object_set(G_OBJECT(renderer), "editable", FALSE, NULL);
01661         g_object_set_data(G_OBJECT(renderer), "column_edited_callback", col_edited_cb);
01662         s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(account_view));
01663         g_signal_handlers_disconnect_by_func(G_OBJECT(renderer), col_edited_cb, s_model);
01664         g_object_set_data(G_OBJECT(renderer), "column_view", column);
01665     }
01666     else
01667     {
01668         g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
01669         g_object_set_data(G_OBJECT(renderer), "column_edited_callback",
01670                           col_edited_cb);
01671         s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(account_view));
01672         g_signal_connect(G_OBJECT(renderer), "edited",
01673                          (GCallback) col_edited_helper, s_model);
01674         g_object_set_data(G_OBJECT(renderer), "column_view", column);
01675     }
01676 }
01677 
01678 GtkTreeViewColumn *
01679 gnc_tree_view_account_add_custom_column(GncTreeViewAccount *account_view,
01680                                         const gchar *column_title,
01681                                         GncTreeViewAccountColumnSource
01682                                         col_source_cb,
01683                                         GncTreeViewAccountColumnTextEdited
01684                                         col_edited_cb)
01685 {
01686     GtkCellRenderer *renderer;
01687     GtkTreeViewColumn *column;
01688 
01689     g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (account_view), NULL);
01690 
01691     renderer = gtk_cell_renderer_text_new ();
01692     g_object_set (G_OBJECT (renderer), "xalign", 1.0, NULL);
01693 
01694     column = gtk_tree_view_column_new_with_attributes (column_title,
01695              renderer, NULL);
01696     if (col_edited_cb)
01697     {
01698         gtva_setup_column_renderer_edited_cb(account_view, column,
01699                                              renderer, col_edited_cb);
01700     }
01701     gtk_tree_view_column_set_cell_data_func (column, renderer,
01702             col_source_helper,
01703             col_source_cb, NULL);
01704     gnc_tree_view_append_column (GNC_TREE_VIEW(account_view), column);
01705     return column;
01706 }
01707 
01708 
01709 /* BEGIN FILTER FUNCTIONS */
01710 #define FILTER_TREE_VIEW "types_tree_view"
01711 
01723 gboolean
01724 gnc_plugin_page_account_tree_filter_accounts (Account *account,
01725         gpointer user_data)
01726 {
01727     AccountFilterDialog *fd = user_data;
01728     GNCAccountType acct_type;
01729     gnc_numeric total;
01730     gboolean result;
01731 
01732     ENTER("account %p:%s", account, xaccAccountGetName(account));
01733 
01734     if (!fd->show_hidden && xaccAccountIsHidden (account))
01735     {
01736         LEAVE(" hide: hidden");
01737         return FALSE;
01738     }
01739 
01740     if (!fd->show_zero_total)
01741     {
01742         total = xaccAccountGetBalanceInCurrency (account, NULL, TRUE);
01743         if (gnc_numeric_zero_p(total))
01744         {
01745             LEAVE(" hide: zero balance");
01746             return FALSE;
01747         }
01748     }
01749 
01750     acct_type = xaccAccountGetType(account);
01751     result = (fd->visible_types & (1 << acct_type)) ? TRUE : FALSE;
01752     LEAVE(" %s", result ? "show" : "hide");
01753     return result;
01754 }
01755 
01763 void
01764 gppat_filter_show_hidden_toggled_cb (GtkToggleButton *button,
01765                                      AccountFilterDialog *fd)
01766 {
01767     g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
01768 
01769     ENTER("button %p", button);
01770     fd->show_hidden = gtk_toggle_button_get_active(button);
01771     gnc_tree_view_account_refilter(fd->tree_view);
01772     LEAVE("show_hidden %d", fd->show_hidden);
01773 }
01774 
01782 void
01783 gppat_filter_show_zero_toggled_cb (GtkToggleButton *button,
01784                                    AccountFilterDialog *fd)
01785 {
01786     g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
01787 
01788     ENTER("button %p", button);
01789     fd->show_zero_total = gtk_toggle_button_get_active(button);
01790     gnc_tree_view_account_refilter(fd->tree_view);
01791     LEAVE("show_zero %d", fd->show_zero_total);
01792 }
01793 
01802 void
01803 gppat_filter_clear_all_cb (GtkWidget *button,
01804                            AccountFilterDialog *fd)
01805 {
01806     g_return_if_fail(GTK_IS_BUTTON(button));
01807 
01808     ENTER("button %p", button);
01809     fd->visible_types = 0;
01810     gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(fd->model));
01811     gnc_tree_view_account_refilter(fd->tree_view);
01812     LEAVE("types 0x%x", fd->visible_types);
01813 }
01814 
01821 void
01822 gppat_filter_select_all_cb (GtkWidget *button,
01823                             AccountFilterDialog *fd)
01824 {
01825     g_return_if_fail(GTK_IS_BUTTON(button));
01826 
01827     ENTER("button %p", button);
01828     fd->visible_types = -1;
01829     gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(fd->model));
01830     gnc_tree_view_account_refilter(fd->tree_view);
01831     LEAVE("types 0x%x", fd->visible_types);
01832 }
01833 
01862 static void
01863 gppat_filter_visible_set_func (GtkTreeViewColumn *column,
01864                                GtkCellRenderer *renderer,
01865                                GtkTreeModel *model,
01866                                GtkTreeIter *iter,
01867                                gpointer data)
01868 {
01869     AccountFilterDialog *fd = data;
01870     GNCAccountType type;
01871     gboolean active;
01872 
01873     gtk_tree_model_get(model, iter, GNC_TREE_MODEL_ACCOUNT_TYPES_COL_TYPE, &type, -1);
01874 
01875     active = (fd->visible_types & (1 << type)) ? TRUE : FALSE;
01876     g_object_set (G_OBJECT (renderer), "active", active, NULL);
01877 }
01878 
01884 static void
01885 gppat_filter_visible_toggled_cb (GtkCellRendererToggle *renderer,
01886                                  gchar *path_str,
01887                                  AccountFilterDialog *fd)
01888 {
01889     GtkTreeModel *model = fd->model;
01890     GtkTreeIter iter;
01891     GtkTreePath *path;
01892     GNCAccountType type;
01893 
01894     ENTER("toggled %p", path_str);
01895     path = gtk_tree_path_new_from_string(path_str);
01896 
01897     if (gtk_tree_model_get_iter(model, &iter, path))
01898     {
01899         gtk_tree_model_get(model, &iter, GNC_TREE_MODEL_ACCOUNT_TYPES_COL_TYPE, &type, -1);
01900         fd->visible_types ^= (1 << type);
01901         gnc_tree_view_account_refilter(fd->tree_view);
01902     }
01903     gtk_tree_path_free(path);
01904     LEAVE("types 0x%x", fd->visible_types);
01905 }
01906 
01917 void
01918 gppat_filter_response_cb (GtkWidget *dialog,
01919                           gint       response,
01920                           AccountFilterDialog *fd)
01921 {
01922     gpointer gptemp;
01923 
01924     g_return_if_fail(GTK_IS_DIALOG(dialog));
01925 
01926     ENTER("dialog %p, response %d", dialog, response);
01927 
01928     if (response != GTK_RESPONSE_OK)
01929     {
01930         fd->visible_types = fd->original_visible_types;
01931         fd->show_hidden = fd->original_show_hidden;
01932         fd->show_zero_total = fd->original_show_zero_total;
01933         gnc_tree_view_account_refilter(fd->tree_view);
01934     }
01935 
01936     /* Clean up and delete dialog */
01937     gptemp = (gpointer *)fd->dialog;
01938     g_atomic_pointer_compare_and_exchange(&gptemp,
01939                                           dialog, NULL);
01940     fd->dialog = gptemp;
01941     gtk_widget_destroy(dialog);
01942     LEAVE("types 0x%x", fd->visible_types);
01943 }
01944 
01945 void
01946 account_filter_dialog_create(AccountFilterDialog *fd, GncPluginPage *page)
01947 {
01948     GtkWidget *dialog, *button;
01949     GtkTreeView *view;
01950     GtkCellRenderer *renderer;
01951     GtkBuilder *builder;
01952     gchar *title;
01953 
01954     ENTER("(fd %p, page %p)", fd, page);
01955 
01956     if (fd->dialog)
01957     {
01958         gtk_window_present(GTK_WINDOW(fd->dialog));
01959         LEAVE("existing dialog");
01960         return;
01961     }
01962 
01963     /* Create the dialog */
01964     builder = gtk_builder_new();
01965     gnc_builder_add_from_file (builder, "dialog-account.glade", "Filter By");
01966     dialog = GTK_WIDGET(gtk_builder_get_object (builder, "Filter By"));
01967     fd->dialog = dialog;
01968     gtk_window_set_transient_for(GTK_WINDOW(dialog),
01969                                  GTK_WINDOW(GNC_PLUGIN_PAGE(page)->window));
01970     /* Translators: The %s is the name of the plugin page */
01971     title = g_strdup_printf(_("Filter %s by..."),
01972                             gnc_plugin_page_get_page_name(GNC_PLUGIN_PAGE(page)));
01973     gtk_window_set_title(GTK_WINDOW(dialog), title);
01974     g_free(title);
01975 
01976     /* Remember current state */
01977     fd->original_visible_types = fd->visible_types;
01978     fd->original_show_hidden = fd->show_hidden;
01979     fd->original_show_zero_total = fd->show_zero_total;
01980 
01981     /* Update the dialog widgets for the current state */
01982     button = GTK_WIDGET(gtk_builder_get_object (builder, "show_hidden"));
01983     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
01984                                  fd->show_hidden);
01985     button = GTK_WIDGET(gtk_builder_get_object (builder, "show_zero"));
01986     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
01987                                  fd->show_zero_total);
01988 
01989     /* Set up the tree view and model */
01990     view = GTK_TREE_VIEW(gtk_builder_get_object (builder, FILTER_TREE_VIEW));
01991 
01992     fd->model = gnc_tree_model_account_types_filter_using_mask
01993                 (~(1 << ACCT_TYPE_ROOT));
01994     gtk_tree_view_set_model(view, fd->model);
01995     g_object_unref (fd->model);
01996 
01997     renderer = gtk_cell_renderer_toggle_new();
01998 
01999     g_signal_connect(renderer, "toggled",
02000                      G_CALLBACK(gppat_filter_visible_toggled_cb), fd);
02001 
02002     gtk_tree_view_insert_column_with_data_func
02003     (view,
02004      -1, NULL, renderer,
02005      gppat_filter_visible_set_func, fd, NULL);
02006 
02007     gtk_tree_view_insert_column_with_attributes
02008     (view,
02009      -1, _("Account Types"), gtk_cell_renderer_text_new(),
02010      "text", GNC_TREE_MODEL_ACCOUNT_TYPES_COL_NAME, NULL);
02011 
02012     /* Wire up the rest of the callbacks */
02013     gtk_builder_connect_signals(builder, fd);
02014     g_object_unref(G_OBJECT(builder));
02015 
02016     /* Show it */
02017     gtk_widget_show_all(dialog);
02018     LEAVE(" ");
02019 }
02020 
02021 #define ACCT_COUNT    "NumberOfOpenAccounts"
02022 #define ACCT_OPEN     "OpenAccount%d"
02023 #define ACCT_SELECTED "SelectedAccount"
02024 #define SHOW_HIDDEN   "ShowHidden"
02025 #define SHOW_ZERO     "ShowZeroTotal"
02026 #define ACCT_TYPES    "AccountTypes"
02027 
02028 typedef struct foo
02029 {
02030     GKeyFile *key_file;
02031     const gchar *group_name;
02032     int count;
02033 } bar_t;
02034 
02046 static void
02047 tree_save_expanded_row (GncTreeViewAccount *view,
02048                         GtkTreePath *path,
02049                         gpointer user_data)
02050 {
02051     Account *account;
02052     bar_t *bar = user_data;
02053     gchar *key;
02054     gchar *account_name;
02055 
02056     account = gnc_tree_view_account_get_account_from_path (view, path);
02057     if (account == NULL)
02058         return;
02059 
02060     account_name = gnc_account_get_full_name(account);
02061     if (account_name == NULL)
02062         return;
02063 
02064     key = g_strdup_printf(ACCT_OPEN, ++bar->count);
02065     g_key_file_set_string(bar->key_file, bar->group_name, key, account_name);
02066     g_free(key);
02067     g_free(account_name);
02068 }
02069 
02070 
02081 static void
02082 tree_save_selected_row (GncTreeViewAccount *view,
02083                         gpointer user_data)
02084 {
02085     Account *account;
02086     bar_t *bar = user_data;
02087     gchar *account_name;
02088 
02089     account = gnc_tree_view_account_get_selected_account(view);
02090     if (account == NULL)
02091         return;
02092 
02093     account_name = gnc_account_get_full_name (account);
02094     if (account_name == NULL)
02095         return;
02096 
02097     g_key_file_set_string(bar->key_file, bar->group_name, ACCT_SELECTED,
02098                           account_name);
02099     g_free(account_name);
02100 }
02101 
02102 void
02103 gnc_tree_view_account_save(GncTreeViewAccount *view,
02104                            AccountFilterDialog *fd,
02105                            GKeyFile *key_file, const gchar *group_name)
02106 {
02107     bar_t bar;
02108 
02109     g_return_if_fail (key_file != NULL);
02110     g_return_if_fail (group_name != NULL);
02111 
02112     ENTER("view %p, key_file %p, group_name %s", view, key_file,
02113           group_name);
02114 
02115     g_key_file_set_integer(key_file, group_name, ACCT_TYPES,
02116                            fd->visible_types);
02117     g_key_file_set_boolean(key_file, group_name, SHOW_HIDDEN,
02118                            fd->show_hidden);
02119     g_key_file_set_boolean(key_file, group_name, SHOW_ZERO,
02120                            fd->show_zero_total);
02121 
02122     bar.key_file = key_file;
02123     bar.group_name = group_name;
02124     bar.count = 0;
02125     tree_save_selected_row(view, &bar);
02126     gtk_tree_view_map_expanded_rows(
02127         GTK_TREE_VIEW(view), (GtkTreeViewMappingFunc) tree_save_expanded_row,
02128         &bar);
02129     g_key_file_set_integer(key_file, group_name, ACCT_COUNT, bar.count);
02130     LEAVE(" ");
02131 
02132 }
02133 
02141 static void
02142 tree_restore_expanded_row (GncTreeViewAccount *view,
02143                            const gchar *account_name)
02144 {
02145     Account *account;
02146     QofBook *book;
02147 
02148     book = qof_session_get_book(gnc_get_current_session());
02149     account = gnc_account_lookup_by_full_name(gnc_book_get_root_account(book),
02150               account_name);
02151     if (account)
02152         gnc_tree_view_account_expand_to_account(view, account);
02153 }
02154 
02155 
02163 static void
02164 tree_restore_selected_row (GncTreeViewAccount *view,
02165                            const gchar *account_name)
02166 {
02167     Account *account;
02168     QofBook *book;
02169 
02170     book = qof_session_get_book(gnc_get_current_session());
02171     account = gnc_account_lookup_by_full_name(gnc_book_get_root_account(book),
02172               account_name);
02173     if (account)
02174         gnc_tree_view_account_set_selected_account(view, account);
02175 }
02176 
02177 void
02178 gnc_tree_view_account_restore(GncTreeViewAccount *view,
02179                               AccountFilterDialog *fd,
02180                               GKeyFile *key_file, const gchar *group_name)
02181 {
02182     GError *error = NULL;
02183     gchar *key, *value;
02184     gint i, count;
02185     gboolean show;
02186 
02187     /* Filter information. Ignore missing keys. */
02188     show = g_key_file_get_boolean(key_file, group_name, SHOW_HIDDEN, &error);
02189     if (error)
02190     {
02191         g_warning("error reading group %s key %s: %s",
02192                   group_name, SHOW_HIDDEN, error->message);
02193         g_error_free(error);
02194         error = NULL;
02195         show = TRUE;
02196     }
02197     fd->show_hidden = show;
02198 
02199     show = g_key_file_get_boolean(key_file, group_name, SHOW_ZERO, &error);
02200     if (error)
02201     {
02202         g_warning("error reading group %s key %s: %s",
02203                   group_name, SHOW_ZERO, error->message);
02204         g_error_free(error);
02205         error = NULL;
02206         show = TRUE;
02207     }
02208     fd->show_zero_total = show;
02209 
02210     i = g_key_file_get_integer(key_file, group_name, ACCT_TYPES, &error);
02211     if (error)
02212     {
02213         g_warning("error reading group %s key %s: %s",
02214                   group_name, ACCT_TYPES, error->message);
02215         g_error_free(error);
02216         error = NULL;
02217         i = -1;
02218     }
02219     fd->visible_types = i;
02220 
02221     /* Expanded accounts. Skip if count key missing. */
02222     count = g_key_file_get_integer(key_file, group_name, ACCT_COUNT, &error);
02223     if (error == NULL)
02224     {
02225         for (i = 1; i <= count; i++)
02226         {
02227             key = g_strdup_printf(ACCT_OPEN, i);
02228             value = g_key_file_get_string(key_file, group_name, key, &error);
02229             if (error)
02230             {
02231                 g_warning("error reading group %s key %s: %s",
02232                           group_name, key, error->message);
02233                 g_error_free(error);
02234                 error = NULL;
02235             }
02236             else
02237             {
02238                 tree_restore_expanded_row(view, value);
02239                 g_free(value);
02240             }
02241             g_free(key);
02242         }
02243     }
02244     else
02245     {
02246         g_warning("error reading group %s key %s: %s",
02247                   group_name, ACCT_COUNT, error->message);
02248         g_error_free(error);
02249     }
02250 
02251     /* Selected account (if any) */
02252     value = g_key_file_get_string(key_file, group_name, ACCT_SELECTED, NULL);
02253     if (value)
02254     {
02255         tree_restore_selected_row(view, value);
02256         g_free(value);
02257     }
02258 
02259     /* Update tree view for any changes */
02260     gnc_tree_view_account_refilter(view);
02261 }
02262 
02263 // @@fixme -- factor this app-not-gui-specific-logic out.
02264 void
02265 gnc_tree_view_account_name_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_name)
02266 {
02267     // check for accounts with the same name among our parent's children.
02268     // should probably factor this consistency check out to the account
02269     // itself....
02270     {
02271         Account *parent = gnc_account_get_parent(account);
02272         Account *existing = gnc_account_lookup_by_name(parent, new_name);
02273         if (existing != NULL && existing != account)
02274         {
02275             PERR("account with the same name [%s] already exists.", new_name);
02276             return;
02277         }
02278     }
02279     xaccAccountSetName(account, new_name);
02280 }
02281 
02282 void
02283 gnc_tree_view_account_code_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_code)
02284 {
02285     if (safe_strcmp(xaccAccountGetCode(account), new_code) == 0)
02286         return;
02287     xaccAccountSetCode(account, new_code);
02288 }
02289 
02290 void
02291 gnc_tree_view_account_description_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_desc)
02292 {
02293     if (safe_strcmp(xaccAccountGetDescription(account), new_desc) == 0)
02294         return;
02295     xaccAccountSetDescription(account, new_desc);
02296 }
02297 
02298 void
02299 gnc_tree_view_account_notes_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_notes)
02300 {
02301     if (safe_strcmp(xaccAccountGetNotes(account), new_notes) == 0)
02302         return;
02303     xaccAccountSetNotes(account, new_notes);
02304 }
02305 
02306 static void
02307 gtva_set_column_editor(GncTreeViewAccount *view,
02308                        GtkTreeViewColumn *column,
02309                        GncTreeViewAccountColumnTextEdited edited_cb)
02310 {
02311     GList *renderers_orig, *renderers;
02312     GtkCellRenderer *renderer;
02313 
02314     // look for the first text-renderer; on the 0th column of the account tree,
02315     // there are two renderers: pixbuf and text.  So find the text one.
02316     for (renderers_orig = renderers = gtk_tree_view_column_get_cell_renderers(column);
02317             renderers && !GTK_IS_CELL_RENDERER_TEXT(renderers->data);
02318             renderers = renderers->next);
02319     renderer = GTK_CELL_RENDERER(renderers->data);
02320     g_list_free(renderers_orig);
02321     g_return_if_fail(renderer != NULL);
02322     gtva_setup_column_renderer_edited_cb(GNC_TREE_VIEW_ACCOUNT(view), column, renderer, edited_cb);
02323 }
02324 
02325 void
02326 gnc_tree_view_account_set_name_edited(GncTreeViewAccount *view,
02327                                       GncTreeViewAccountColumnTextEdited edited_cb)
02328 {
02329     GncTreeViewAccountPrivate *priv;
02330     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
02331     gtva_set_column_editor(view, priv->name_column, edited_cb);
02332 }
02333 
02334 void
02335 gnc_tree_view_account_set_code_edited(GncTreeViewAccount *view,
02336                                       GncTreeViewAccountColumnTextEdited edited_cb)
02337 {
02338     GncTreeViewAccountPrivate *priv;
02339     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
02340     gtva_set_column_editor(view, priv->code_column, edited_cb);
02341 }
02342 
02343 void
02344 gnc_tree_view_account_set_description_edited(GncTreeViewAccount *view,
02345         GncTreeViewAccountColumnTextEdited edited_cb)
02346 {
02347     GncTreeViewAccountPrivate *priv;
02348     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
02349     gtva_set_column_editor(view, priv->desc_column, edited_cb);
02350 }
02351 
02352 void
02353 gnc_tree_view_account_set_notes_edited(GncTreeViewAccount *view,
02354                                        GncTreeViewAccountColumnTextEdited edited_cb)
02355 {
02356     GncTreeViewAccountPrivate *priv;
02357     priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
02358     gtva_set_column_editor(view, priv->notes_column, edited_cb);
02359 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines