Files
gnucash/gnucash/gnome-utils/gnc-tree-view-account.c
Frank H. Ellenberger 48e81d221d Unify "Placeholder" headers with previous commit
Only impact: removed obsolete translator comment
2019-09-06 00:39:51 +02:00

2775 lines
95 KiB
C

/**********************************************************************\
* gnc-tree-view-account.c -- GtkTreeView implementation to display *
* accounts in a GtkTreeView. *
* Copyright (C) 2003,2005,2006 David Hampton <hampton@employees.org> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
* *
\**********************************************************************/
#include <config.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <string.h>
#include "gnc-tree-view.h"
#include "gnc-tree-model-account.h"
#include "gnc-tree-model-account-types.h"
#include "gnc-tree-view-account.h"
#include "Account.h"
#include "gnc-accounting-period.h"
#include "gnc-commodity.h"
#include "gnc-component-manager.h"
#include "gnc-engine.h"
#include "gnc-glib-utils.h"
#include "gnc-gobject-utils.h"
#include "gnc-prefs.h"
#include "gnc-hooks.h"
#include "gnc-session.h"
#include "gnc-icons.h"
#include "gnc-ui-balances.h"
#include "dialog-utils.h"
#include "window-main-summarybar.h"
#define SAMPLE_ACCOUNT_VALUE "$1,000,000.00"
#define GNC_PREF_ACCOUNT_COLOR "show-account-color"
/** Static Globals *******************************************************/
/* This static indicates the debugging module that this .o belongs to. */
static QofLogModule log_module = GNC_MOD_GUI;
/** Declarations *********************************************************/
static void gnc_tree_view_account_class_init (GncTreeViewAccountClass *klass);
static void gnc_tree_view_account_init (GncTreeViewAccount *view);
static void gnc_tree_view_account_finalize (GObject *object);
static gboolean gnc_tree_view_search_compare (GtkTreeModel *model, gint column,
const gchar *key, GtkTreeIter *iter, gpointer search_data);
static void gtva_update_column_names (GncTreeView *view);
static void gtva_currency_changed_cb (void);
static gboolean gnc_tree_view_account_filter_helper (GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data);
static void gtva_setup_column_renderer_edited_cb(GncTreeViewAccount *account_view,
GtkTreeViewColumn *column,
GtkCellRenderer *renderer,
GncTreeViewAccountColumnTextEdited col_edited_cb);
static void tax_info_data_func (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer view);
static void acc_color_data_func (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer view);
static void gnc_tree_view_account_color_update (gpointer gsettings,
gchar *key, gpointer user_data);
typedef struct GncTreeViewAccountPrivate
{
AccountViewInfo avi;
gnc_tree_view_account_filter_func filter_fn;
gpointer filter_data;
GSourceFunc filter_destroy;
GtkTreeViewColumn *name_column;
GtkTreeViewColumn *code_column;
GtkTreeViewColumn *desc_column;
GtkTreeViewColumn *present_report_column;
GtkTreeViewColumn *balance_report_column;
GtkTreeViewColumn *cleared_report_column;
GtkTreeViewColumn *reconciled_report_column;
GtkTreeViewColumn *future_min_report_column;
GtkTreeViewColumn *total_report_column;
GtkTreeViewColumn *notes_column;
gboolean show_account_color;
} GncTreeViewAccountPrivate;
#define GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_TREE_VIEW_ACCOUNT, GncTreeViewAccountPrivate))
/************************************************************/
/* g_object required functions */
/************************************************************/
static GObjectClass *parent_class = NULL;
G_DEFINE_TYPE_WITH_PRIVATE(GncTreeViewAccount, gnc_tree_view_account, GNC_TYPE_TREE_VIEW)
static void
gnc_tree_view_account_class_init (GncTreeViewAccountClass *klass)
{
GObjectClass *o_class;
parent_class = g_type_class_peek_parent (klass);
/* GObject signals */
o_class = G_OBJECT_CLASS (klass);
o_class->finalize = gnc_tree_view_account_finalize;
gnc_hook_add_dangler(HOOK_CURRENCY_CHANGED,
(GFunc)gtva_currency_changed_cb, NULL);
}
/********************************************************************\
* gnc_init_account_view_info *
* initialize an account view info structure with default values *
* *
* Args: avi - structure to initialize *
* Returns: nothing *
\********************************************************************/
static void
gnc_init_account_view_info(AccountViewInfo *avi)
{
int i;
for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
avi->include_type[i] = TRUE;
avi->show_hidden = FALSE;
}
static void
gnc_tree_view_account_init (GncTreeViewAccount *view)
{
GncTreeViewAccountPrivate *priv;
priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
gnc_prefs_register_cb (GNC_PREFS_GROUP_GENERAL,
GNC_PREF_ACCOUNT_COLOR,
gnc_tree_view_account_color_update,
view);
gnc_init_account_view_info(&priv->avi);
}
static void
gnc_tree_view_account_finalize (GObject *object)
{
GncTreeViewAccount *account_view;
GncTreeViewAccountPrivate *priv;
ENTER("view %p", object);
g_return_if_fail (object != NULL);
g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (object));
account_view = GNC_TREE_VIEW_ACCOUNT (object);
priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(account_view);
gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_GENERAL,
GNC_PREF_ACCOUNT_COLOR,
gnc_tree_view_account_color_update,
account_view);
if (priv->filter_destroy)
{
priv->filter_destroy(priv->filter_data);
priv->filter_destroy = NULL;
}
priv->filter_fn = NULL;
if (G_OBJECT_CLASS (parent_class)->finalize)
(* G_OBJECT_CLASS (parent_class)->finalize) (object);
LEAVE(" ");
}
/************************************************************
* Callbacks *
************************************************************/
static void
gnc_tree_view_account_placeholder_toggled (GtkCellRendererToggle *cell,
const gchar *s_path_str,
gpointer user_data)
{
GncTreeViewAccount *tree_view;
GtkTreePath *s_path;
Account *account;
gboolean placeholder;
/* Change the requested account */
tree_view = user_data;
s_path = gtk_tree_path_new_from_string (s_path_str);
account = gnc_tree_view_account_get_account_from_path (tree_view, s_path);
if (account)
{
placeholder = !gtk_cell_renderer_toggle_get_active (cell); // hasn't changed yet.
xaccAccountSetPlaceholder (account, placeholder);
}
/* Clean up */
gtk_tree_path_free (s_path);
}
/************************************************************/
/* sort functions */
/************************************************************/
static GtkTreeModel *
sort_cb_setup_w_iters (GtkTreeModel *f_model,
GtkTreeIter *f_iter_a,
GtkTreeIter *f_iter_b,
GtkTreeIter *iter_a,
GtkTreeIter *iter_b,
const Account **account_a,
const Account **account_b)
{
GtkTreeModel *model;
model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
iter_a,
f_iter_a);
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER(f_model),
iter_b,
f_iter_b);
*account_a = gnc_tree_model_account_get_account (GNC_TREE_MODEL_ACCOUNT(model), iter_a);
*account_b = gnc_tree_model_account_get_account (GNC_TREE_MODEL_ACCOUNT(model), iter_b);
return model;
}
static void
sort_cb_setup (GtkTreeModel *f_model,
GtkTreeIter *f_iter_a,
GtkTreeIter *f_iter_b,
const Account **account_a,
const Account **account_b)
{
GtkTreeIter iter_a, iter_b;
sort_cb_setup_w_iters (f_model, f_iter_a, f_iter_b,
&iter_a, &iter_b, account_a, account_b);
}
static gint
sort_by_last_reconcile_date (GtkTreeModel *f_model,
GtkTreeIter *f_iter1,
GtkTreeIter *f_iter2,
gpointer user_data)
{
const Account *account1, *account2;
time64 account1_date, account2_date;
sort_cb_setup (f_model, f_iter1, f_iter2, &account1, &account2);
if (!xaccAccountGetReconcileLastDate (account1, &account1_date))
account1_date = 0;
if (!xaccAccountGetReconcileLastDate (account2, &account2_date))
account2_date = 0;
if (account1_date < account2_date)
return -1;
else if (account1_date > account2_date)
return 1;
else
return xaccAccountOrder (account1, account2);
}
static gint
sort_by_string (GtkTreeModel *f_model,
GtkTreeIter *f_iter1,
GtkTreeIter *f_iter2,
gpointer user_data)
{
GtkTreeModel *model;
GtkTreeIter iter1, iter2;
const Account *account1, *account2;
gchar *str1, *str2;
gint column = GPOINTER_TO_INT(user_data);
gint result;
model = sort_cb_setup_w_iters(f_model, f_iter1, f_iter2, &iter1, &iter2, &account1, &account2);
/* Get the strings. */
gtk_tree_model_get(GTK_TREE_MODEL(model), &iter1, column, &str1, -1);
gtk_tree_model_get(GTK_TREE_MODEL(model), &iter2, column, &str2, -1);
result = safe_utf8_collate(str1, str2);
g_free(str1);
g_free(str2);
if (result != 0)
return result;
return xaccAccountOrder(account1, account2);
}
static gint
sort_by_code (GtkTreeModel *f_model,
GtkTreeIter *f_iter_a,
GtkTreeIter *f_iter_b,
gpointer user_data)
{
const Account *account_a, *account_b;
sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
/* Default ordering uses this column first. */
return xaccAccountOrder(account_a, account_b);
}
static gint
sort_by_xxx_value (xaccGetBalanceInCurrencyFn fn,
gboolean recurse,
GtkTreeModel *f_model,
GtkTreeIter *f_iter_a,
GtkTreeIter *f_iter_b,
gpointer user_data)
{
const Account *account_a, *account_b;
gnc_numeric balance_a, balance_b;
gint result;
/* Find the accounts */
sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
/* Get balances */
balance_a = gnc_ui_account_get_balance_full(fn, account_a, recurse, NULL, NULL);
balance_b = gnc_ui_account_get_balance_full(fn, account_b, recurse, NULL, NULL);
result = gnc_numeric_compare(balance_a, balance_b);
if (result != 0)
return result;
return xaccAccountOrder(account_a, account_b);
}
static gint
sort_by_present_value (GtkTreeModel *f_model,
GtkTreeIter *f_iter_a,
GtkTreeIter *f_iter_b,
gpointer user_data)
{
return sort_by_xxx_value (xaccAccountGetPresentBalanceInCurrency, TRUE,
f_model, f_iter_a, f_iter_b, user_data);
}
static gint
sort_by_balance_value (GtkTreeModel *f_model,
GtkTreeIter *f_iter_a,
GtkTreeIter *f_iter_b,
gpointer user_data)
{
return sort_by_xxx_value (xaccAccountGetBalanceInCurrency, TRUE,
f_model, f_iter_a, f_iter_b, user_data);
}
static gint
sort_by_cleared_value (GtkTreeModel *f_model,
GtkTreeIter *f_iter_a,
GtkTreeIter *f_iter_b,
gpointer user_data)
{
return sort_by_xxx_value (xaccAccountGetClearedBalanceInCurrency, TRUE,
f_model, f_iter_a, f_iter_b, user_data);
}
static gint
sort_by_reconciled_value (GtkTreeModel *f_model,
GtkTreeIter *f_iter_a,
GtkTreeIter *f_iter_b,
gpointer user_data)
{
return sort_by_xxx_value (xaccAccountGetReconciledBalanceInCurrency, TRUE,
f_model, f_iter_a, f_iter_b, user_data);
}
static gint
sort_by_future_min_value (GtkTreeModel *f_model,
GtkTreeIter *f_iter_a,
GtkTreeIter *f_iter_b,
gpointer user_data)
{
return sort_by_xxx_value (xaccAccountGetProjectedMinimumBalanceInCurrency, TRUE,
f_model, f_iter_a, f_iter_b, user_data);
}
static gint
sort_by_total_value (GtkTreeModel *f_model,
GtkTreeIter *f_iter_a,
GtkTreeIter *f_iter_b,
gpointer user_data)
{
return sort_by_xxx_value (xaccAccountGetBalanceInCurrency, TRUE,
f_model, f_iter_a, f_iter_b, user_data);
}
static gint
sort_by_placeholder (GtkTreeModel *f_model,
GtkTreeIter *f_iter_a,
GtkTreeIter *f_iter_b,
gpointer user_data)
{
const Account *account_a, *account_b;
gboolean flag_a, flag_b;
/* Find the accounts */
sort_cb_setup (f_model, f_iter_a, f_iter_b, &account_a, &account_b);
/* Get the placeholder flags. */
flag_a = xaccAccountGetPlaceholder(account_a);
flag_b = xaccAccountGetPlaceholder(account_b);
if (flag_a > flag_b)
return -1;
else if (flag_a < flag_b)
return 1;
return xaccAccountOrder(account_a, account_b);
}
static gint
sort_by_xxx_period_value (GtkTreeModel *f_model,
GtkTreeIter *f_iter_a,
GtkTreeIter *f_iter_b,
gboolean recurse)
{
Account *acct1, *acct2;
time64 t1, t2;
gnc_numeric b1, b2;
gint result;
sort_cb_setup (f_model, f_iter_a, f_iter_b,
(const Account **)&acct1, (const Account **)&acct2);
t1 = gnc_accounting_period_fiscal_start();
t2 = gnc_accounting_period_fiscal_end();
b1 = xaccAccountGetBalanceChangeForPeriod(acct1, t1, t2, recurse);
b2 = xaccAccountGetBalanceChangeForPeriod(acct2, t1, t2, recurse);
result = gnc_numeric_compare(b1, b2);
if (result != 0)
return result;
return xaccAccountOrder(acct1, acct2);
}
static gint
sort_by_balance_period_value (GtkTreeModel *f_model,
GtkTreeIter *f_iter_a,
GtkTreeIter *f_iter_b,
gpointer user_data)
{
return sort_by_xxx_period_value (f_model, f_iter_a, f_iter_b, FALSE);
}
static gint
sort_by_total_period_value (GtkTreeModel *f_model,
GtkTreeIter *f_iter_a,
GtkTreeIter *f_iter_b,
gpointer user_data)
{
return sort_by_xxx_period_value (f_model, f_iter_a, f_iter_b, TRUE);
}
/************************************************************/
/* Tax_Info data function */
/************************************************************/
/*
* The tax-info column in the account tree view is based on the
* combination of two columns in the account tree model. The data
* function displays only the the data in the
* GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO model column if the row is
* expanded; otherwise it combines it with the data
* in the GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO_SUB_ACCT model column.
*/
static void
tax_info_data_func (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer view)
{
gchar *tax_info = NULL;
GtkTreePath *path;
gtk_tree_model_get(model,
iter,
GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO,
&tax_info,
-1);
path = gtk_tree_model_get_path(model, iter);
if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(view), path))
g_object_set(renderer, "text",
(tax_info == NULL ? "" : tax_info), NULL);
else
{
gchar *tax_info_sub_acct = NULL;
gtk_tree_model_get(model,
iter,
GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO_SUB_ACCT,
&tax_info_sub_acct,
-1);
if ((g_strcmp0 (tax_info_sub_acct, "") == 0) ||
(tax_info_sub_acct == NULL))
g_object_set(renderer, "text",
(tax_info == NULL ? "" : tax_info), NULL);
else
{
if ((g_strcmp0 (tax_info, "") == 0) ||
(tax_info == NULL))
g_object_set(renderer, "text",
(tax_info_sub_acct == NULL ? "" : tax_info_sub_acct),
NULL);
else
{
gchar *combined_tax_info;
combined_tax_info = g_strdup_printf ("%s; %s",
(tax_info == NULL ? "" : tax_info),
(tax_info_sub_acct == NULL ? "" :
tax_info_sub_acct));
g_object_set(renderer, "text", combined_tax_info, NULL);
g_free(combined_tax_info);
}
}
g_free(tax_info_sub_acct);
}
g_free(tax_info);
gtk_tree_path_free(path);
}
/************************************************************/
/* acc_color data function */
/************************************************************/
/*
* The account-color column in the account tree view is obtained
* from the GNC_TREE_MODEL_ACCOUNT_COL_COLOR_ACCOUNT which is
* checked for a valid color string to set the background color
* of the cell.
*/
static void
update_cell_renderers (GList *renderers, gchar *account_color)
{
GtkCellRenderer *cell;
GList *node;
/* Update the cell background in the list of renderers */
for (node = renderers; node; node = node->next)
{
cell = node->data;
g_object_set (cell, "cell-background", account_color, NULL);
}
}
/* Colorizes a cell in the account tree view if
* - a color is assigned to the given account
* - the user enabled account colorization in the preferences
* Only the account color column is special: it will always
* be colored if a valid color was set, regardless of the
* preference setting.
*/
static void
acc_color_data_func (GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer view)
{
GncTreeViewAccountPrivate *priv;
gchar *acc_color = NULL, *acc_cond_color = NULL;
gchar *item;
GdkRGBA color;
gchar *column_name;
GList *renderers;
gtk_tree_model_get(model,
iter,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_ACCOUNT,
&item,
-1);
/* Check if color was set for the account */
if ((item) && (*item != '\0'))
acc_color = g_strstrip(g_strdup(item));
g_free (item);
/* Test if the color string represents a valid color */
if (acc_color && (!gdk_rgba_parse(&color, acc_color)))
{
g_free (acc_color);
acc_color = NULL;
}
/* Determine whether columns other than the
* Account Color column should be colored. */
priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
if (priv->show_account_color)
acc_cond_color = acc_color;
column_name = g_object_get_data(G_OBJECT(col), PREF_NAME);
renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (col));
/* Account Color column is always colored, other columns only conditionally. */
if (g_strcmp0(column_name, "account-color") == 0)
update_cell_renderers (renderers, acc_color);
else
update_cell_renderers (renderers, acc_cond_color);
g_list_free (renderers);
g_free (acc_color);
}
/** Tell the GncTreeViewAccount code to show or not show the
* Account name column with background in the account color.
*
* @internal
*/
static void
gnc_tree_view_account_color_update (gpointer gsettings, gchar *key, gpointer user_data)
{
GncTreeViewAccountPrivate *priv;
GncTreeViewAccount *view;
g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(user_data));
view = user_data;
priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
if (g_strcmp0 (key, GNC_PREF_ACCOUNT_COLOR) == 0)
priv->show_account_color = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, key);
// do a refilter so the tree view background color gets updated
gnc_tree_view_account_refilter (view);
}
/** Add the account color background data function to the GncTreeViewAccount column to
* show or not the column background in the account color.
*/
void
gnc_tree_view_account_column_add_color (GncTreeViewAccount *view, GtkTreeViewColumn *col)
{
GtkCellRenderer *renderer = gnc_tree_view_column_get_renderer(col);
gtk_tree_view_column_set_cell_data_func (col, renderer, acc_color_data_func,
GTK_TREE_VIEW(view), NULL);
}
/************************************************************/
/* New View Creation */
/************************************************************/
/*
* Create a new account tree view with (optional) top level root node.
* This view will be based on a model that is common to all view of
* the same set of books, but will have its own private filter on that
* model.
*/
GtkTreeView *
gnc_tree_view_account_new_with_root (Account *root, gboolean show_root)
{
GncTreeView *view;
GtkTreeModel *model, *f_model, *s_model;
GtkTreePath *virtual_root_path = NULL;
const gchar *sample_type, *sample_commodity;
GncTreeViewAccountPrivate *priv;
GtkTreeViewColumn *tax_info_column, *acc_color_column;
GtkCellRenderer *renderer;
GList *col_list = NULL, *node = NULL;
ENTER(" ");
/* Create our view */
view = g_object_new (GNC_TYPE_TREE_VIEW_ACCOUNT,
"name", "account_tree", NULL);
priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(GNC_TREE_VIEW_ACCOUNT (view));
/* Get the show_account_color value from gsettings */
priv->show_account_color = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNT_COLOR);
/* Create/get a pointer to the existing model for this set of books. */
model = gnc_tree_model_account_new (root);
/* Set up the view private filter layer on the common model. */
if (!show_root)
virtual_root_path = gtk_tree_path_new_first ();
f_model = gtk_tree_model_filter_new (model, virtual_root_path);
/* A GncTreeModelAccount is based on a GncTreeModel, which is a
* GObject that provides a GtkTreeModel interface. */
g_object_unref(G_OBJECT(model));
if (virtual_root_path)
gtk_tree_path_free(virtual_root_path);
/* Set up the view private sort layer on the common model. */
s_model = gtk_tree_model_sort_new_with_model(f_model);
g_object_unref(G_OBJECT(f_model));
gtk_tree_view_set_model (GTK_TREE_VIEW (view), s_model);
g_object_unref(G_OBJECT(s_model));
/* Set default visibilities */
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(view), FALSE);
sample_type = xaccAccountGetTypeStr(ACCT_TYPE_CREDIT);
sample_commodity = gnc_commodity_get_fullname(gnc_default_currency());
priv->name_column
= gnc_tree_view_add_text_column(view, _("Account Name"), "name",
GNC_ICON_ACCOUNT, "Expenses:Entertainment",
GNC_TREE_MODEL_ACCOUNT_COL_NAME,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_string);
gnc_tree_view_add_text_column(view, _("Type"), "type", NULL, sample_type,
GNC_TREE_MODEL_ACCOUNT_COL_TYPE,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_string);
gnc_tree_view_add_text_column(view, _("Commodity"), "commodity", NULL,
sample_commodity,
GNC_TREE_MODEL_ACCOUNT_COL_COMMODITY,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_string);
priv->code_column
= gnc_tree_view_add_text_column(view, _("Account Code"), "account-code", NULL,
"1-123-1234",
GNC_TREE_MODEL_ACCOUNT_COL_CODE,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_code);
priv->desc_column
= gnc_tree_view_add_text_column(view, _("Description"), "description", NULL,
"Sample account description.",
GNC_TREE_MODEL_ACCOUNT_COL_DESCRIPTION,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_string);
gnc_tree_view_add_numeric_column(view, _("Last Num"), "lastnum", "12345",
GNC_TREE_MODEL_ACCOUNT_COL_LASTNUM,
GNC_TREE_VIEW_COLUMN_COLOR_NONE,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_string);
gnc_tree_view_add_numeric_column(view, _("Present"), "present",
SAMPLE_ACCOUNT_VALUE,
GNC_TREE_MODEL_ACCOUNT_COL_PRESENT,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_PRESENT,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_present_value);
priv->present_report_column
= gnc_tree_view_add_numeric_column(view, _("Present (Report)"), "present_report",
SAMPLE_ACCOUNT_VALUE,
GNC_TREE_MODEL_ACCOUNT_COL_PRESENT_REPORT,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_PRESENT,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_present_value);
gnc_tree_view_add_numeric_column(view, _("Balance"), "balance",
SAMPLE_ACCOUNT_VALUE,
GNC_TREE_MODEL_ACCOUNT_COL_BALANCE,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_balance_value);
priv->balance_report_column
= gnc_tree_view_add_numeric_column(view, _("Balance (Report)"), "balance_report",
SAMPLE_ACCOUNT_VALUE,
GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_REPORT,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_balance_value);
gnc_tree_view_add_numeric_column(view, _("Balance (Period)"), "balance-period",
SAMPLE_ACCOUNT_VALUE,
GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_PERIOD,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_BALANCE_PERIOD,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_balance_period_value);
gnc_tree_view_add_numeric_column(view, _("Cleared"), "cleared",
SAMPLE_ACCOUNT_VALUE,
GNC_TREE_MODEL_ACCOUNT_COL_CLEARED,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_CLEARED,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_cleared_value);
priv->cleared_report_column
= gnc_tree_view_add_numeric_column(view, _("Cleared (Report)"), "cleared_report",
SAMPLE_ACCOUNT_VALUE,
GNC_TREE_MODEL_ACCOUNT_COL_CLEARED_REPORT,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_CLEARED,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_cleared_value);
gnc_tree_view_add_numeric_column(view, _("Reconciled"), "reconciled",
SAMPLE_ACCOUNT_VALUE,
GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_RECONCILED,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_reconciled_value);
priv->reconciled_report_column
= gnc_tree_view_add_numeric_column(view, _("Reconciled (Report)"), "reconciled_report",
SAMPLE_ACCOUNT_VALUE,
GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_REPORT,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_RECONCILED,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_reconciled_value);
gnc_tree_view_add_text_column(view, _("Last Reconcile Date"), "last-recon-date", NULL,
"Last Reconcile Date",
GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED_DATE,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_last_reconcile_date);
gnc_tree_view_add_numeric_column(view, _("Future Minimum"), "future_min",
SAMPLE_ACCOUNT_VALUE,
GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_FUTURE_MIN,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_future_min_value);
priv->future_min_report_column
= gnc_tree_view_add_numeric_column(view, _("Future Minimum (Report)"), "future_min_report",
SAMPLE_ACCOUNT_VALUE,
GNC_TREE_MODEL_ACCOUNT_COL_FUTURE_MIN_REPORT,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_FUTURE_MIN,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_future_min_value);
gnc_tree_view_add_numeric_column(view, _("Total"), "total",
SAMPLE_ACCOUNT_VALUE,
GNC_TREE_MODEL_ACCOUNT_COL_TOTAL,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_total_value);
priv->total_report_column
= gnc_tree_view_add_numeric_column(view, _("Total (Report)"), "total_report",
SAMPLE_ACCOUNT_VALUE,
GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_REPORT,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_total_value);
gnc_tree_view_add_numeric_column(view, _("Total (Period)"), "total-period",
SAMPLE_ACCOUNT_VALUE,
GNC_TREE_MODEL_ACCOUNT_COL_TOTAL_PERIOD,
GNC_TREE_MODEL_ACCOUNT_COL_COLOR_TOTAL_PERIOD,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_total_period_value);
/* Translators: The C is the column title and stands for Color, this should be one character */
acc_color_column
= gnc_tree_view_add_text_column(view, _("C"), "account-color", NULL,
"xx",
GNC_TREE_VIEW_COLUMN_DATA_NONE,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
NULL);
/* Add the full title to the object for menu creation */
g_object_set_data_full(G_OBJECT(acc_color_column), REAL_TITLE,
g_strdup(_("Account Color")), g_free);
priv->notes_column
= gnc_tree_view_add_text_column(view, _("Notes"), "notes", NULL,
"Sample account notes.",
GNC_TREE_MODEL_ACCOUNT_COL_NOTES,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_string);
tax_info_column
= gnc_tree_view_add_text_column(view, _("Tax Info"), "tax-info", NULL,
"Sample tax info.",
GNC_TREE_MODEL_ACCOUNT_COL_TAX_INFO,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_string);
renderer = gnc_tree_view_column_get_renderer(tax_info_column);
gtk_tree_view_column_set_cell_data_func(tax_info_column,
renderer,
tax_info_data_func,
GTK_TREE_VIEW(view),
NULL);
gnc_tree_view_add_toggle_column(view, _("Placeholder"),
C_("Column header for 'Placeholder'", "P"),
"placeholder",
GNC_TREE_MODEL_ACCOUNT_COL_PLACEHOLDER,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
sort_by_placeholder,
gnc_tree_view_account_placeholder_toggled);
/* Add function to each column that optionally sets a background color for accounts */
col_list = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
for (node = col_list; node; node = node->next)
{
renderer = gnc_tree_view_column_get_renderer(node->data);
gtk_tree_view_column_set_cell_data_func(node->data,
renderer,
acc_color_data_func,
GTK_TREE_VIEW(view),
NULL);
}
g_list_free (col_list);
/* Update column titles to use the currency name. */
gtva_update_column_names(view);
/* By default only the first column is visible. */
gnc_tree_view_configure_columns(view);
gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (f_model),
gnc_tree_view_account_filter_helper,
view,
NULL);
/* Default the sorting to account name */
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(s_model),
GNC_TREE_MODEL_ACCOUNT_COL_NAME,
GTK_SORT_ASCENDING);
/* Set account find-as-you-type search function */
gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW(view), gnc_tree_view_search_compare, NULL, NULL);
gtk_widget_show(GTK_WIDGET(view));
LEAVE("%p", view);
return GTK_TREE_VIEW(view);
}
/*
* Create a new account tree view with (optional) top level root node.
* This view will be based on a model that is common to all view of
* the same set of books, but will have its own private filter on that
* model.
*/
GtkTreeView *
gnc_tree_view_account_new (gboolean show_root)
{
Account *root;
root = gnc_book_get_root_account (gnc_get_current_book ());
return gnc_tree_view_account_new_with_root (root, show_root);
}
/************************************************************/
/* Auxiliary Functions */
/************************************************************/
#define debug_path(fn, path) { \
gchar *path_string = gtk_tree_path_to_string(path); \
fn("tree path %s", path_string); \
g_free(path_string); \
}
static GtkTreePath *
gnc_tree_view_account_get_path_from_account (GncTreeViewAccount *view,
Account *account)
{
GtkTreeModel *model, *f_model, *s_model;
GtkTreePath *path, *f_path, *s_path;
ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
if (account == NULL)
{
LEAVE("no account");
return NULL;
}
/* Reach down to the real model and get a path for this account */
s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
path = gnc_tree_model_account_get_path_from_account (GNC_TREE_MODEL_ACCOUNT(model), account);
if (path == NULL)
{
LEAVE("no path");
return NULL;
}
/* convert back to a filtered path */
f_path = gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (f_model), path);
gtk_tree_path_free(path);
if (!f_path)
{
LEAVE("no filter path");
return NULL;
}
/* convert back to a sorted path */
s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model), f_path);
gtk_tree_path_free(f_path);
debug_path(LEAVE, s_path);
return s_path;
}
static gboolean
gnc_tree_view_account_get_iter_from_account (GncTreeViewAccount *view,
Account *account,
GtkTreeIter *s_iter)
{
GtkTreeModel *model, *f_model, *s_model;
GtkTreeIter iter, f_iter;
g_return_val_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view), FALSE);
g_return_val_if_fail(account != NULL, FALSE);
g_return_val_if_fail(s_iter != NULL, FALSE);
ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
/* Reach down to the real model and get an iter for this account */
s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
if (!gnc_tree_model_account_get_iter_from_account (
GNC_TREE_MODEL_ACCOUNT(model), account, &iter))
{
LEAVE("model_get_iter_from_account failed");
return FALSE;
}
/* convert back to a sort iter */
gtk_tree_model_filter_convert_child_iter_to_iter (
GTK_TREE_MODEL_FILTER(f_model), &f_iter, &iter);
gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT(s_model),
s_iter, &f_iter);
LEAVE(" ");
return TRUE;
}
gint
gnc_tree_view_account_count_children (GncTreeViewAccount *view,
Account *account)
{
GtkTreeModel *s_model;
GtkTreeIter s_iter;
gint num_children;
ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
if (account == NULL)
{
LEAVE("no account");
return 0;
}
if (!gnc_tree_view_account_get_iter_from_account (view, account, &s_iter))
{
LEAVE("view_get_iter_from_account failed");
return 0;
}
/* Any children? */
s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
num_children = gtk_tree_model_iter_n_children(s_model, &s_iter);
LEAVE("%d children", num_children);
return num_children;
}
/************************************************************/
/* Account Tree View Filter Functions */
/************************************************************/
/*
* Get a copy of the account view info structure in use by the
* specified tree.
*/
void
gnc_tree_view_account_get_view_info (GncTreeViewAccount *account_view,
AccountViewInfo *avi)
{
GncTreeViewAccountPrivate *priv;
g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(account_view));
g_return_if_fail(avi != NULL);
priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(account_view);
*avi = priv->avi;
}
/*
* Set the account view info data in use by the specified tree to
* match the callers request.
*
* DRH - COMPATIBILITY WARNING
*
* This function does not do anything with the 'include_type' field.
* Should there be a automatic filter for backward compatibility
* that uses these flags, or should all uses of this be converted to
* a GtkTreeModelFilter?
*
* CAS - For now, I'll try the automatic filter approach by making
* this function use GtkTreeModelFilter.
*/
void
gnc_tree_view_account_set_view_info (GncTreeViewAccount *account_view,
AccountViewInfo *avi)
{
GncTreeViewAccountPrivate *priv;
gint i;
guint sel_bits = 0;
ENTER("%p", account_view);
g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(account_view));
g_return_if_fail(avi != NULL);
priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(account_view);
priv->avi = *avi;
for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
{
sel_bits |= avi->include_type[i] ? (1 << i) : 0;
}
gnc_tree_view_account_set_filter(
account_view, gnc_tree_view_account_filter_by_view_info,
&priv->avi, NULL);
LEAVE(" ");
}
static gboolean
gnc_tree_view_account_filter_helper (GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data)
{
Account *account;
GncTreeViewAccount *view = data;
GncTreeViewAccountPrivate *priv;
g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model), FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
account = gnc_tree_model_account_get_account (
GNC_TREE_MODEL_ACCOUNT(model), iter);
priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
if (priv->filter_fn)
return priv->filter_fn(account, priv->filter_data);
else return TRUE;
}
/*
* Set an GtkTreeModel visible filter on this account. This filter will be
* called for each account that the tree is about to show, and the
* account will be passed to the callback function.
*
* Use NULL as func to remove filter.
*/
void
gnc_tree_view_account_set_filter (GncTreeViewAccount *view,
gnc_tree_view_account_filter_func func,
gpointer data,
GSourceFunc destroy)
{
GncTreeViewAccountPrivate *priv;
ENTER("view %p, filter func %p, data %p, destroy %p",
view, func, data, destroy);
g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view));
priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
if (priv->filter_destroy)
{
priv->filter_destroy(priv->filter_data);
}
priv->filter_destroy = destroy;
priv->filter_data = data;
priv->filter_fn = func;
gnc_tree_view_account_refilter(view);
LEAVE(" ");
}
/*
* Forces the entire account tree to be re-evaluated for visibility.
*/
void
gnc_tree_view_account_refilter (GncTreeViewAccount *view)
{
GtkTreeModel *f_model, *s_model;
g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view));
s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (f_model));
}
gboolean
gnc_tree_view_account_filter_by_view_info(Account* acct, gpointer data)
{
GNCAccountType acct_type;
AccountViewInfo* avi = (AccountViewInfo*)data;
g_return_val_if_fail(GNC_IS_ACCOUNT(acct), FALSE);
acct_type = xaccAccountGetType(acct);
if (!avi->include_type[acct_type]) return FALSE;
if (!avi->show_hidden && xaccAccountIsHidden(acct)) return FALSE;
return TRUE;
}
/************************************************************/
/* Account Tree View Get/Set Functions */
/************************************************************/
/*
* Retrieve the selected account from an account tree view. The
* account tree must be in single selection mode.
*/
Account *
gnc_tree_view_account_get_account_from_path (GncTreeViewAccount *view,
GtkTreePath *s_path)
{
GtkTreeModel *model, *f_model, *s_model;
GtkTreePath *path, *f_path;
GtkTreeIter iter;
Account *account;
ENTER("view %p", view);
g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
g_return_val_if_fail (s_path != NULL, NULL);
s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
f_path = gtk_tree_model_sort_convert_path_to_child_path (
GTK_TREE_MODEL_SORT (s_model), s_path);
if (!f_path)
{
LEAVE("no filter path");
return NULL;
}
f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
path = gtk_tree_model_filter_convert_path_to_child_path (
GTK_TREE_MODEL_FILTER (f_model), f_path);
gtk_tree_path_free(f_path);
if (!path)
{
LEAVE("no path");
return NULL;
}
model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
if (!gtk_tree_model_get_iter (model, &iter, path))
{
LEAVE("no iter");
return NULL;
}
account = iter.user_data;
gtk_tree_path_free(path);
LEAVE("account %p (%s)", account, xaccAccountGetName (account));
return account;
}
Account *
gnc_tree_view_account_get_account_from_iter (GtkTreeModel *s_model,
GtkTreeIter *s_iter)
{
GtkTreeModel *model, *f_model;
GtkTreeIter iter, f_iter;
Account *account;
g_return_val_if_fail (GTK_IS_TREE_MODEL_SORT(s_model), NULL);
g_return_val_if_fail (s_iter != NULL, NULL);
ENTER("model %p, iter %p", s_model, s_iter);
gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT(s_model),
&f_iter,
s_iter);
f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
gtk_tree_model_filter_convert_iter_to_child_iter (
GTK_TREE_MODEL_FILTER(f_model), &iter, &f_iter);
model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
account = gnc_tree_model_account_get_account (
GNC_TREE_MODEL_ACCOUNT(model), &iter);
LEAVE("account %p (%s)", account, xaccAccountGetName (account));
return account;
}
/*
* Retrieve the selected account from an account tree view. The
* account tree must be in single selection mode.
*/
Account *
gnc_tree_view_account_get_selected_account (GncTreeViewAccount *view)
{
GtkTreeSelection *selection;
GtkTreeModel *f_model, *s_model;
GtkTreeIter iter, f_iter, s_iter;
Account *account;
GtkSelectionMode mode;
ENTER("view %p", view);
g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
mode = gtk_tree_selection_get_mode(selection);
if ((mode != GTK_SELECTION_SINGLE) && (mode != GTK_SELECTION_BROWSE))
{
return NULL;
}
if (!gtk_tree_selection_get_selected (selection, &s_model, &s_iter))
{
LEAVE("no account, get_selected failed");
return FALSE;
}
gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model),
&f_iter, &s_iter);
f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
gtk_tree_model_filter_convert_iter_to_child_iter (
GTK_TREE_MODEL_FILTER (f_model), &iter, &f_iter);
account = iter.user_data;
LEAVE("account %p (%s)", account, xaccAccountGetName (account));
return account;
}
/*
* Selects a single account in the account tree view. The account
* tree must be in single selection mode.
*/
void
gnc_tree_view_account_set_selected_account (GncTreeViewAccount *view,
Account *account)
{
GtkTreeModel *model, *f_model, *s_model;
GtkTreePath *path, *f_path, *s_path, *parent_path;
GtkTreeSelection *selection;
ENTER("view %p, account %p (%s)", view,
account, xaccAccountGetName (account));
g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view));
/* Clear any existing selection. */
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
gtk_tree_selection_unselect_all (selection);
if (account == NULL)
return;
s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
path = gnc_tree_model_account_get_path_from_account (
GNC_TREE_MODEL_ACCOUNT(model), account);
if (path == NULL)
{
LEAVE("no path");
return;
}
debug_path(DEBUG, path);
f_path = gtk_tree_model_filter_convert_child_path_to_path (
GTK_TREE_MODEL_FILTER (f_model), path);
gtk_tree_path_free(path);
if (f_path == NULL)
{
LEAVE("no filter path");
return;
}
debug_path(DEBUG, f_path);
s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model),
f_path);
gtk_tree_path_free(f_path);
if (s_path == NULL)
{
LEAVE("no sort path");
return;
}
/* gtk_tree_view requires that a row be visible before it can be selected */
parent_path = gtk_tree_path_copy (s_path);
if (gtk_tree_path_up (parent_path))
{
/* This function is misnamed. It expands the actual item
* specified, not the path to the item specified. I.E. It expands
* one level too many, thus the get of the parent. */
gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), parent_path);
}
gtk_tree_path_free(parent_path);
gtk_tree_selection_select_path (selection, s_path);
/* give gtk+ a chance to resize the tree view first by handling pending
* configure events */
while (gtk_events_pending ())
gtk_main_iteration ();
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(view), s_path, NULL, FALSE, 0.0, 0.0);
debug_path(LEAVE, s_path);
gtk_tree_path_free(s_path);
}
/* Information re selection process */
typedef struct
{
GList* return_list;
GncTreeViewAccountPrivate* priv;
} GncTreeViewSelectionInfo;
/*
* This helper function is called once for each row in the tree view
* that is currently selected. Its task is to append the corresponding
* account to the end of a glist.
*/
static void
get_selected_accounts_helper (GtkTreeModel *s_model,
GtkTreePath *s_path,
GtkTreeIter *s_iter,
gpointer data)
{
GncTreeViewSelectionInfo *gtvsi = data;
GtkTreeModel *f_model;
GtkTreeIter iter, f_iter;
Account *account;
gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (s_model),
&f_iter, s_iter);
f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (f_model),
&iter, &f_iter);
account = iter.user_data;
/* Only selected if it passes the filter */
if (gtvsi->priv->filter_fn == NULL || gtvsi->priv->filter_fn(account, gtvsi->priv->filter_data))
{
gtvsi->return_list = g_list_append(gtvsi->return_list, account);
}
}
/*
* Given an account tree view, return a list of the selected accounts. The
* account tree must be in multiple selection mode.
*
* Note: It is the responsibility of the caller to free the returned
* list.
*/
GList *
gnc_tree_view_account_get_selected_accounts (GncTreeViewAccount *view)
{
GtkTreeSelection *selection;
GncTreeViewSelectionInfo info;
g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
info.return_list = NULL;
info.priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
gtk_tree_selection_selected_foreach(selection, get_selected_accounts_helper, &info);
return info.return_list;
}
/*
* Given an account tree view and a list of accounts, select those
* accounts in the tree view.
*/
void
gnc_tree_view_account_set_selected_accounts (GncTreeViewAccount *view,
GList *account_list,
gboolean show_last)
{
GtkTreeModel *model, *f_model, *s_model;
GtkTreePath *path, *f_path, *s_path, *parent_path;
GtkTreeSelection *selection;
GList *element;
Account *account;
g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view));
s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
f_model = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(s_model));
model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(f_model));
/* Clear any existing selection. */
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
gtk_tree_selection_unselect_all (selection);
gtk_tree_view_collapse_all (GTK_TREE_VIEW(view));
/* Now go select what the user requested. */
for (element = account_list; element; )
{
account = element->data;
element = g_list_next(element);
if (account == NULL)
{
/*
* Oops. Someone must have deleted this account and not cleaned
* up all references to it.
*/
continue;
}
path = gnc_tree_model_account_get_path_from_account (GNC_TREE_MODEL_ACCOUNT(model), account);
if (path == NULL)
{
/*
* Oops. Someone must have deleted this account and not cleaned
* up all references to it.
*/
continue;
}
f_path = gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (f_model),
path);
gtk_tree_path_free(path);
if (f_path == NULL)
continue;
s_path = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (s_model),
f_path);
gtk_tree_path_free(f_path);
if (s_path == NULL)
continue;
/* gtk_tree_view requires that a row be visible before it can be selected */
parent_path = gtk_tree_path_copy (s_path);
if (gtk_tree_path_up (parent_path))
{
/* This function is misnamed. It expands the actual item
* specified, not the path to the item specified. I.E. It
* expands one level too many, thus the get of the parent. */
gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), parent_path);
}
gtk_tree_path_free(parent_path);
gtk_tree_selection_select_path (selection, s_path);
if (show_last && (element == NULL))
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(view), s_path, NULL, FALSE, 0.0, 0.0);
gtk_tree_path_free(s_path);
}
}
/*
* Selects all sub-accounts of an acccount.
*/
void
gnc_tree_view_account_select_subaccounts (GncTreeViewAccount *view,
Account *account)
{
GtkTreeModel *s_model;
GtkTreeSelection *selection;
GtkTreePath *sp_account, *sp_start, *sp_end;
GtkTreeIter si_account, si_start, si_end;
gboolean have_start, have_end;
gint num_children;
ENTER("view %p, account %p (%s)", view, account, xaccAccountGetName(account));
g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view));
if (account == NULL)
{
LEAVE("no account");
return;
}
if (!gnc_tree_view_account_get_iter_from_account (view, account, &si_account))
{
LEAVE("view_get_iter_from_account failed");
return;
}
/* Any children? */
s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
num_children = gtk_tree_model_iter_n_children(s_model, &si_account);
if (num_children == 0)
{
LEAVE("no children");
return;
}
/* Expand the tree. Required for selection to work */
sp_account = gtk_tree_model_get_path (s_model, &si_account);
gtk_tree_view_expand_row (GTK_TREE_VIEW(view), sp_account, TRUE);
/* compute start/end paths */
have_start = gtk_tree_model_iter_nth_child(s_model, &si_start, &si_account, 0);
si_end = si_account;
while (num_children)
{
GtkTreeIter tmp_iter = si_end;
have_end = gtk_tree_model_iter_nth_child(s_model, &si_end, &tmp_iter,
num_children - 1);
if (have_end)
num_children = gtk_tree_model_iter_n_children(s_model, &si_end);
else
num_children = 0;
}
if (have_start && have_end)
{
sp_start = gtk_tree_model_get_path (s_model, &si_start);
sp_end = gtk_tree_model_get_path (s_model, &si_end);
/* select everything between */
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
gtk_tree_selection_select_range (selection, sp_start, sp_end);
/* clean up */
gtk_tree_path_free(sp_start);
gtk_tree_path_free(sp_end);
}
gtk_tree_path_free(sp_account);
LEAVE(" ");
return;
}
void
gnc_tree_view_account_expand_to_account (GncTreeViewAccount *view,
Account *account)
{
GtkTreePath *path;
g_return_if_fail(view != NULL);
g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view));
ENTER("view %p, account %p", view, account);
path = gnc_tree_view_account_get_path_from_account(view, account);
if (path)
{
gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), path);
gtk_tree_path_free(path);
}
LEAVE(" ");
}
/*
* Retrieve the account currently under the cursor.
*/
Account *
gnc_tree_view_account_get_cursor_account (GncTreeViewAccount *view)
{
GtkTreePath *s_path;
Account *account;
ENTER("view %p", view);
g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
gtk_tree_view_get_cursor (GTK_TREE_VIEW(view), &s_path, NULL);
if (!s_path)
{
LEAVE("no account");
return NULL;
}
account = gnc_tree_view_account_get_account_from_path (view, s_path);
gtk_tree_path_free(s_path);
LEAVE("account %p (%s)", account, xaccAccountGetName (account));
return account;
}
/************************************************************/
/* Account Tree View Add Column Functions */
/************************************************************/
static void
gtva_update_column_name (GtkTreeViewColumn *column,
const gchar *fmt,
const gchar *mnemonic)
{
gchar *name;
g_return_if_fail(column);
name = g_strdup_printf(fmt, mnemonic);
gtk_tree_view_column_set_title(column, name);
g_free(name);
}
static void
gtva_update_column_names (GncTreeView *view)
{
GncTreeViewAccountPrivate *priv;
const gchar *mnemonic;
priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
mnemonic = gnc_commodity_get_mnemonic(gnc_default_report_currency());
gtva_update_column_name(priv->present_report_column,
/* Translators: %s is a currency mnemonic.*/
_("Present (%s)"), mnemonic);
gtva_update_column_name(priv->balance_report_column,
/* Translators: %s is a currency mnemonic.*/
_("Balance (%s)"), mnemonic);
gtva_update_column_name(priv->cleared_report_column,
/* Translators: %s is a currency mnemonic.*/
_("Cleared (%s)"), mnemonic);
gtva_update_column_name(priv->reconciled_report_column,
/* Translators: %s is a currency mnemonic.*/
_("Reconciled (%s)"), mnemonic);
gtva_update_column_name(priv->future_min_report_column,
/* Translators: %s is a currency mnemonic.*/
_("Future Minimum (%s)"), mnemonic);
gtva_update_column_name(priv->total_report_column,
/* Translators: %s is a currency mnemonic.*/
_("Total (%s)"), mnemonic);
gnc_tree_view_set_show_column_menu(view, FALSE);
gnc_tree_view_set_show_column_menu(view, TRUE);
}
static void
gtva_currency_changed_cb (void)
{
const GList *views, *ptr;
views = gnc_gobject_tracking_get_list (GNC_TREE_VIEW_ACCOUNT_NAME);
for (ptr = views; ptr; ptr = g_list_next(ptr))
{
gtva_update_column_names (ptr->data);
}
}
/* Retrieve a specified account string property and put the result
* into the tree column's text property.
*/
static void
account_cell_property_data_func (GtkTreeViewColumn *tree_column,
GtkCellRenderer *cell,
GtkTreeModel *s_model,
GtkTreeIter *s_iter,
gpointer key)
{
GncTreeViewAccount *view;
Account *account;
gchar *string = NULL;
g_return_if_fail (GTK_IS_TREE_MODEL_SORT (s_model));
account = gnc_tree_view_account_get_account_from_iter(s_model, s_iter);
qof_instance_get (QOF_INSTANCE (account), key, &string, NULL);
if (string == NULL)
string = "";
g_object_set (G_OBJECT (cell), "text", string, "xalign", 0.0, NULL);
view = g_object_get_data(G_OBJECT(tree_column), "tree-view");
if (GNC_IS_TREE_VIEW_ACCOUNT (view))
acc_color_data_func (tree_column, cell, s_model, s_iter, view);
}
GtkTreeViewColumn *
gnc_tree_view_account_add_property_column (GncTreeViewAccount *view,
const gchar *column_title,
const gchar *propname)
{
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL);
g_return_val_if_fail (propname != NULL, NULL);
column = gnc_tree_view_add_text_column(GNC_TREE_VIEW(view), column_title,
propname, NULL, "Sample text",
-1, -1, NULL);
/* This new kvp column has only had one renderer added to it so
* far. Find that renderer. */
renderer = gnc_tree_view_column_get_renderer(column);
g_object_set (G_OBJECT (renderer), "xalign", 1.0, NULL);
// add a pointer to the view to make it easier to access in data_func
g_object_set_data(G_OBJECT(column), "tree-view", (gpointer)view);
gtk_tree_view_column_set_cell_data_func (column, renderer,
account_cell_property_data_func,
g_strdup(propname), g_free);
return column;
}
static void col_edited_helper(GtkCellRendererText *cell, gchar *path_string,
gchar *new_text, gpointer _s_model)
{
Account *account;
GtkTreeModel *s_model;
GtkTreeIter s_iter;
GncTreeViewAccountColumnTextEdited col_edited_cb;
GtkTreeViewColumn *col;
col_edited_cb = g_object_get_data(G_OBJECT(cell),
"column_edited_callback");
col = GTK_TREE_VIEW_COLUMN(g_object_get_data(G_OBJECT(cell),
"column_view"));
s_model = GTK_TREE_MODEL(_s_model);
if (!gtk_tree_model_get_iter_from_string(s_model, &s_iter, path_string))
return;
account = gnc_tree_view_account_get_account_from_iter(s_model, &s_iter);
col_edited_cb(account, col, new_text);
}
static void col_source_helper(GtkTreeViewColumn *col, GtkCellRenderer *cell,
GtkTreeModel *s_model, GtkTreeIter *s_iter,
gpointer _col_source_cb)
{
Account *account;
gchar *text;
GncTreeViewAccountColumnSource col_source_cb;
g_return_if_fail (GTK_IS_TREE_MODEL_SORT (s_model));
col_source_cb = (GncTreeViewAccountColumnSource) _col_source_cb;
account = gnc_tree_view_account_get_account_from_iter(s_model, s_iter);
text = col_source_cb(account, col, cell);
g_object_set (G_OBJECT (cell), "text", text, "xalign", 1.0, NULL);
g_free(text);
}
/**
* If col_edited_cb is null, the editing callback (helper) will be
* effectively disconnected.
**/
void
gtva_setup_column_renderer_edited_cb(GncTreeViewAccount *account_view,
GtkTreeViewColumn *column,
GtkCellRenderer *renderer,
GncTreeViewAccountColumnTextEdited col_edited_cb)
{
GtkTreeModel *s_model;
if (col_edited_cb == NULL)
{
g_object_set(G_OBJECT(renderer), "editable", FALSE, NULL);
g_object_set_data(G_OBJECT(renderer), "column_edited_callback", col_edited_cb);
s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(account_view));
g_signal_handlers_disconnect_by_func(G_OBJECT(renderer), col_edited_cb, s_model);
g_object_set_data(G_OBJECT(renderer), "column_view", column);
}
else
{
g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
g_object_set_data(G_OBJECT(renderer), "column_edited_callback",
col_edited_cb);
s_model = gtk_tree_view_get_model(GTK_TREE_VIEW(account_view));
g_signal_connect(G_OBJECT(renderer), "edited",
(GCallback) col_edited_helper, s_model);
g_object_set_data(G_OBJECT(renderer), "column_view", column);
}
}
GtkTreeViewColumn *
gnc_tree_view_account_add_custom_column(GncTreeViewAccount *account_view,
const gchar *column_title,
GncTreeViewAccountColumnSource
col_source_cb,
GncTreeViewAccountColumnTextEdited
col_edited_cb)
{
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (account_view), NULL);
renderer = gtk_cell_renderer_text_new ();
g_object_set (G_OBJECT (renderer), "xalign", 1.0, NULL);
column = gtk_tree_view_column_new_with_attributes (column_title,
renderer, NULL);
if (col_edited_cb)
{
gtva_setup_column_renderer_edited_cb(account_view, column,
renderer, col_edited_cb);
}
gtk_tree_view_column_set_cell_data_func (column, renderer,
col_source_helper,
col_source_cb, NULL);
gnc_tree_view_append_column (GNC_TREE_VIEW(account_view), column);
return column;
}
/* BEGIN FILTER FUNCTIONS */
#define FILTER_TREE_VIEW "types_tree_view"
/** This function tells the account tree view whether or not to filter
* out a particular account. Accounts may be filtered if the user
* has decided not to display that particular account type, or if the
* user has requested that accounts with a zero total not be shown.
*
* @param account The account that was toggled.
*
* @param user_data A pointer to the AccountFilterDialog struct.
*
* @return TRUE if the account should be visible. FALSE if the
* account should be hidden. */
gboolean
gnc_plugin_page_account_tree_filter_accounts (Account *account,
gpointer user_data)
{
AccountFilterDialog *fd = user_data;
GNCAccountType acct_type;
gnc_numeric total;
gboolean result;
ENTER("account %p:%s", account, xaccAccountGetName(account));
if (g_hash_table_size (fd->filter_override) > 0)
{
Account *test_acc = NULL;
test_acc = g_hash_table_lookup (fd->filter_override, account);
if (test_acc != NULL)
{
LEAVE(" filter: override");
return TRUE;
}
}
if (!fd->show_hidden && xaccAccountIsHidden (account))
{
LEAVE(" hide: hidden");
return FALSE;
}
if (!fd->show_zero_total)
{
total = xaccAccountGetBalanceInCurrency (account, NULL, TRUE);
if (gnc_numeric_zero_p(total))
{
LEAVE(" hide: zero balance");
return FALSE;
}
}
if (!fd->show_unused)
{
if (xaccAccountCountSplits(account, TRUE) == 0)
{
LEAVE(" hide: unused");
return FALSE;
}
}
acct_type = xaccAccountGetType(account);
result = (fd->visible_types & (1 << acct_type)) ? TRUE : FALSE;
LEAVE(" %s", result ? "show" : "hide");
return result;
}
/** The "show hidden" button in the Filter dialog changed state.
* Update the page to reflect these changes.
*
* @param button The GtkCheckButton that was toggled.
*
* @param fd A pointer to the account filter dialog struct.
*/
void
gppat_filter_show_hidden_toggled_cb (GtkToggleButton *button,
AccountFilterDialog *fd)
{
g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
ENTER("button %p", button);
fd->show_hidden = gtk_toggle_button_get_active(button);
gnc_tree_view_account_refilter(fd->tree_view);
LEAVE("show_hidden %d", fd->show_hidden);
}
/** The "show zero totals" button in the Filter dialog changed state.
* Update the page to reflect these changes.
*
* @param button The GtkCheckButton that was toggled.
*
* @param fd A pointer to the account filter dialog struct.
*/
void
gppat_filter_show_zero_toggled_cb (GtkToggleButton *button,
AccountFilterDialog *fd)
{
g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
ENTER("button %p", button);
fd->show_zero_total = gtk_toggle_button_get_active(button);
gnc_tree_view_account_refilter(fd->tree_view);
LEAVE("show_zero %d", fd->show_zero_total);
}
/** The "show unused" button in the Filter dialog changed state.
* Update the page to reflect these changes.
*
* @param button The GtkCheckButton that was toggled.
*
* @param fd A pointer to the account filter dialog struct.
*/
void
gppat_filter_show_unused_toggled_cb (GtkToggleButton *button,
AccountFilterDialog *fd)
{
g_return_if_fail(GTK_IS_TOGGLE_BUTTON(button));
ENTER("button %p", button);
fd->show_unused = gtk_toggle_button_get_active(button);
gnc_tree_view_account_refilter(fd->tree_view);
LEAVE("show_unused %d", fd->show_unused);
}
/** The "clear all account types" button in the Filter dialog was
* clicked. Clear all account types shown, and update the visible
* page.
*
* @param button The button that was clicked.
*
* @param fd A pointer to the account filter dialog struct.
*/
void
gppat_filter_clear_all_cb (GtkWidget *button,
AccountFilterDialog *fd)
{
g_return_if_fail(GTK_IS_BUTTON(button));
ENTER("button %p", button);
fd->visible_types = 0;
gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(fd->model));
gnc_tree_view_account_refilter(fd->tree_view);
LEAVE("types 0x%x", fd->visible_types);
}
/** The "select all account types" button in the Filter dialog was
* clicked. Make all account types visible, and update the page.
*
* @param button The button that was clicked.
*
* @param fd A pointer to the account filter dialog struct. */
void
gppat_filter_select_all_cb (GtkWidget *button,
AccountFilterDialog *fd)
{
g_return_if_fail(GTK_IS_BUTTON(button));
ENTER("button %p", button);
fd->visible_types = -1;
gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(fd->model));
gnc_tree_view_account_refilter(fd->tree_view);
LEAVE("types 0x%x", fd->visible_types);
}
/** The "select default account types" button in the Filter dialog was
* clicked. Set all account types to their default visibility (which
* happens to be visible for all of them), and update the page.
*
* @param button The button that was clicked.
*
* @param fd A pointer to the account filter dialog struct.*/
void
gppat_filter_select_default_cb (GtkWidget *button,
AccountFilterDialog *fd)
{
ENTER("button %p", button);
gppat_filter_select_all_cb(button, fd);
LEAVE(" ");
}
/** Set the renderer's properties.
*
* @param column A GtkTreeColumn
*
* @param renderer The GtkCellRendererToggle being rendered by @column
*
* @param model The GtkTreeModel being rendered
*
* @param iter A GtkTreeIter of the current row rendered
*
* @param data A pointer to the account filter dialog struct. */
static void
gppat_filter_visible_set_func (GtkTreeViewColumn *column,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data)
{
AccountFilterDialog *fd = data;
GNCAccountType type;
gboolean active;
gtk_tree_model_get(model, iter, GNC_TREE_MODEL_ACCOUNT_TYPES_COL_TYPE, &type, -1);
active = (fd->visible_types & (1 << type)) ? TRUE : FALSE;
g_object_set (G_OBJECT (renderer), "active", active, NULL);
}
/** A check box in the tree view was toggled.
*
* @param renderer The GtkCellRendererToggle being toggled.
*
* @param fd A pointer to the account filter dialog struct. */
static void
gppat_filter_visible_toggled_cb (GtkCellRendererToggle *renderer,
gchar *path_str,
AccountFilterDialog *fd)
{
GtkTreeModel *model = fd->model;
GtkTreeIter iter;
GtkTreePath *path;
GNCAccountType type;
ENTER("toggled %p", path_str);
path = gtk_tree_path_new_from_string(path_str);
if (gtk_tree_model_get_iter(model, &iter, path))
{
gtk_tree_model_get(model, &iter, GNC_TREE_MODEL_ACCOUNT_TYPES_COL_TYPE, &type, -1);
fd->visible_types ^= (1 << type);
gnc_tree_view_account_refilter(fd->tree_view);
}
gtk_tree_path_free(path);
LEAVE("types 0x%x", fd->visible_types);
}
/** The Filter dialog was closed. Check to see if this was done via
* the OK button. If so, make the changes permanent. If not, revert
* any changes.
*
* @param dialog A pointer to the "Filter By" dialog.
*
* @param response The response code from closing the dialog.
*
* @param fd A pointer to the account filter dialog struct.
*/
void
gppat_filter_response_cb (GtkWidget *dialog,
gint response,
AccountFilterDialog *fd)
{
gpointer gptemp;
g_return_if_fail(GTK_IS_DIALOG(dialog));
ENTER("dialog %p, response %d", dialog, response);
if (response != GTK_RESPONSE_OK)
{
fd->visible_types = fd->original_visible_types;
fd->show_hidden = fd->original_show_hidden;
fd->show_zero_total = fd->original_show_zero_total;
fd->show_unused = fd->original_show_unused;
gnc_tree_view_account_refilter(fd->tree_view);
}
/* Clean up and delete dialog */
gptemp = (gpointer *)fd->dialog;
g_atomic_pointer_compare_and_exchange(&gptemp,
dialog, NULL);
fd->dialog = gptemp;
gtk_widget_destroy(dialog);
LEAVE("types 0x%x", fd->visible_types);
}
void
account_filter_dialog_create(AccountFilterDialog *fd, GncPluginPage *page)
{
GtkWidget *dialog, *button;
GtkTreeView *view;
GtkCellRenderer *renderer;
GtkBuilder *builder;
gchar *title;
ENTER("(fd %p, page %p)", fd, page);
if (fd->dialog)
{
gtk_window_present(GTK_WINDOW(fd->dialog));
LEAVE("existing dialog");
return;
}
/* Create the dialog */
builder = gtk_builder_new();
gnc_builder_add_from_file (builder, "dialog-account.glade", "account_filter_by_dialog");
dialog = GTK_WIDGET(gtk_builder_get_object (builder, "account_filter_by_dialog"));
fd->dialog = dialog;
gtk_window_set_transient_for(GTK_WINDOW(dialog),
GTK_WINDOW(GNC_PLUGIN_PAGE(page)->window));
/* Translators: The %s is the name of the plugin page */
title = g_strdup_printf(_("Filter %s by..."),
gnc_plugin_page_get_page_name(GNC_PLUGIN_PAGE(page)));
gtk_window_set_title(GTK_WINDOW(dialog), title);
g_free(title);
/* Remember current state */
fd->original_visible_types = fd->visible_types;
fd->original_show_hidden = fd->show_hidden;
fd->original_show_zero_total = fd->show_zero_total;
fd->original_show_unused = fd->show_unused;
/* Update the dialog widgets for the current state */
button = GTK_WIDGET(gtk_builder_get_object (builder, "show_hidden"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
fd->show_hidden);
button = GTK_WIDGET(gtk_builder_get_object (builder, "show_zero"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
fd->show_zero_total);
button = GTK_WIDGET(gtk_builder_get_object (builder, "show_unused"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
fd->show_unused);
/* Set up the tree view and model */
view = GTK_TREE_VIEW(gtk_builder_get_object (builder, FILTER_TREE_VIEW));
fd->model = gnc_tree_model_account_types_filter_using_mask
(~(1 << ACCT_TYPE_ROOT));
gtk_tree_view_set_model(view, fd->model);
g_object_unref (fd->model);
renderer = gtk_cell_renderer_toggle_new();
g_signal_connect(renderer, "toggled",
G_CALLBACK(gppat_filter_visible_toggled_cb), fd);
gtk_tree_view_insert_column_with_data_func (view, -1, NULL, renderer,
gppat_filter_visible_set_func, fd, NULL);
gtk_tree_view_insert_column_with_attributes (view,
-1, _("Account Types"), gtk_cell_renderer_text_new(),
"text", GNC_TREE_MODEL_ACCOUNT_TYPES_COL_NAME, NULL);
/* Wire up the rest of the callbacks */
gtk_builder_connect_signals(builder, fd);
g_object_unref(G_OBJECT(builder));
/* Show it */
gtk_widget_show_all(dialog);
LEAVE(" ");
}
// page state section
#define ACCT_COUNT "NumberOfOpenAccounts"
#define ACCT_OPEN "OpenAccount%d"
#define ACCT_SELECTED "SelectedAccount"
#define SHOW_HIDDEN "ShowHidden"
#define SHOW_ZERO "ShowZeroTotal"
#define SHOW_UNUSED "ShowUnused"
#define ACCT_TYPES "AccountTypes"
// account/budget state section
// versions less than 3.2 would crash if key did not have an "_"
#define SHOW_HIDDEN_ACCOUNTS "Show_Hidden"
#define SHOW_ZERO_TOTALS "Show_ZeroTotal"
#define SHOW_UNUSED_ACCOUNTS "Show_Unused"
#define ACCOUNT_TYPES "Account_Types"
typedef struct foo
{
GKeyFile *key_file;
const gchar *group_name;
int count;
} bar_t;
/** Save information about an expanded row. This function is called
* via a gtk_tree_view_map_expanded_rows, which calls it once per
* expanded row. Its job is to write the full account name of the
* row out to the state file.
*
* @param view A pointer to the GncTreeViewAccount
*
* @param path A pointer to a particular entry in the tree.
*
* @param data A pointer to a data structure holding the information
* related to the state file. */
static void
tree_save_expanded_row (GncTreeViewAccount *view,
GtkTreePath *path,
gpointer user_data)
{
Account *account;
bar_t *bar = user_data;
gchar *key;
gchar *account_name;
account = gnc_tree_view_account_get_account_from_path (view, path);
if (account == NULL)
return;
account_name = gnc_account_get_full_name(account);
if (account_name == NULL)
return;
key = g_strdup_printf(ACCT_OPEN, ++bar->count);
g_key_file_set_string(bar->key_file, bar->group_name, key, account_name);
g_free(key);
g_free(account_name);
}
/** Save information about the selected row. Its job is to write the
* full account name of the row out to the state file.
*
* @param view A pointer to the GtkTreeView embedded in an
* account tree page.
*
* @param path A pointer to a particular entry in the tree.
*
* @param data A pointer to a data structure holding the information
* related to the state file. */
static void
tree_save_selected_row (GncTreeViewAccount *view,
gpointer user_data)
{
Account *account;
bar_t *bar = user_data;
gchar *account_name;
account = gnc_tree_view_account_get_selected_account(view);
if (account == NULL)
return;
account_name = gnc_account_get_full_name (account);
if (account_name == NULL)
return;
g_key_file_set_string(bar->key_file, bar->group_name, ACCT_SELECTED,
account_name);
g_free(account_name);
}
void
gnc_tree_view_account_save(GncTreeViewAccount *view,
AccountFilterDialog *fd,
GKeyFile *key_file, const gchar *group_name)
{
bar_t bar;
g_return_if_fail (key_file != NULL);
g_return_if_fail (group_name != NULL);
ENTER("view %p, key_file %p, group_name %s", view, key_file,
group_name);
g_key_file_set_integer(key_file, group_name, ACCT_TYPES,
fd->visible_types);
g_key_file_set_boolean(key_file, group_name, SHOW_HIDDEN,
fd->show_hidden);
g_key_file_set_boolean(key_file, group_name, SHOW_ZERO,
fd->show_zero_total);
g_key_file_set_boolean(key_file, group_name, SHOW_UNUSED,
fd->show_unused);
bar.key_file = key_file;
bar.group_name = group_name;
bar.count = 0;
tree_save_selected_row(view, &bar);
gtk_tree_view_map_expanded_rows(
GTK_TREE_VIEW(view), (GtkTreeViewMappingFunc) tree_save_expanded_row,
&bar);
g_key_file_set_integer(key_file, group_name, ACCT_COUNT, bar.count);
LEAVE(" ");
}
void
gnc_tree_view_account_save_filter (GncTreeViewAccount *view,
AccountFilterDialog *fd,
GKeyFile *key_file,
const gchar *group_name)
{
g_return_if_fail (key_file != NULL);
g_return_if_fail (group_name != NULL);
ENTER("view %p, key_file %p, group_name %s", view, key_file,
group_name);
g_key_file_set_integer (key_file, group_name, ACCOUNT_TYPES,
fd->visible_types);
g_key_file_set_boolean (key_file, group_name, SHOW_HIDDEN_ACCOUNTS,
fd->show_hidden);
g_key_file_set_boolean (key_file, group_name, SHOW_ZERO_TOTALS,
fd->show_zero_total);
g_key_file_set_boolean (key_file, group_name, SHOW_UNUSED_ACCOUNTS,
fd->show_unused);
g_key_file_set_comment (key_file, group_name, ACCOUNT_TYPES,
"Account Filter Section below, four lines", NULL);
LEAVE("");
}
/** Expand a row in the tree that was expanded when the user last quit
* gnucash. Its job is to map from account name to tree row and
* expand the row.
*
* @param view A pointer to the GncTreeViewAccount.
*
* @param account_name A pointer to the full account name. */
static void
tree_restore_expanded_row (GncTreeViewAccount *view,
const gchar *account_name)
{
Account *account;
QofBook *book;
book = qof_session_get_book(gnc_get_current_session());
account = gnc_account_lookup_by_full_name(gnc_book_get_root_account(book),
account_name);
if (account)
gnc_tree_view_account_expand_to_account(view, account);
}
/** Select the row in the tree that was selected when the user last
* quit gnucash. Its job is to map from account name to tree row and
* select the row.
*
* @param tree A pointer to the GncTreeViewAccount embedded.
*
* @param account_name A pointer to the full account name. */
static void
tree_restore_selected_row (GncTreeViewAccount *view,
const gchar *account_name)
{
Account *account;
QofBook *book;
book = qof_session_get_book(gnc_get_current_session());
account = gnc_account_lookup_by_full_name(gnc_book_get_root_account(book),
account_name);
if (account)
gnc_tree_view_account_set_selected_account(view, account);
}
void
gnc_tree_view_account_restore(GncTreeViewAccount *view,
AccountFilterDialog *fd,
GKeyFile *key_file, const gchar *group_name)
{
GError *error = NULL;
gchar *key, *value;
gint i, count;
gboolean show;
/* Filter information. Ignore missing keys. */
show = g_key_file_get_boolean(key_file, group_name, SHOW_HIDDEN, &error);
if (error)
{
g_warning("error reading group %s key %s: %s",
group_name, SHOW_HIDDEN, error->message);
g_error_free(error);
error = NULL;
show = TRUE;
}
fd->show_hidden = show;
show = g_key_file_get_boolean(key_file, group_name, SHOW_ZERO, &error);
if (error)
{
g_warning("error reading group %s key %s: %s",
group_name, SHOW_ZERO, error->message);
g_error_free(error);
error = NULL;
show = TRUE;
}
fd->show_zero_total = show;
show = g_key_file_get_boolean(key_file, group_name, SHOW_UNUSED, &error);
if (error)
{
g_warning("error reading group %s key %s: %s",
group_name, SHOW_UNUSED, error->message);
g_error_free(error);
error = NULL;
show = TRUE;
}
fd->show_unused = show;
i = g_key_file_get_integer(key_file, group_name, ACCT_TYPES, &error);
if (error)
{
g_warning("error reading group %s key %s: %s",
group_name, ACCT_TYPES, error->message);
g_error_free(error);
error = NULL;
i = -1;
}
fd->visible_types = i;
/* Expanded accounts. Skip if count key missing. */
count = g_key_file_get_integer(key_file, group_name, ACCT_COUNT, &error);
if (error == NULL)
{
for (i = 1; i <= count; i++)
{
key = g_strdup_printf(ACCT_OPEN, i);
value = g_key_file_get_string(key_file, group_name, key, &error);
if (error)
{
g_warning("error reading group %s key %s: %s",
group_name, key, error->message);
g_error_free(error);
error = NULL;
}
else
{
tree_restore_expanded_row(view, value);
g_free(value);
}
g_free(key);
}
}
else
{
g_warning("error reading group %s key %s: %s",
group_name, ACCT_COUNT, error->message);
g_error_free(error);
}
/* Selected account (if any) */
value = g_key_file_get_string(key_file, group_name, ACCT_SELECTED, NULL);
if (value)
{
tree_restore_selected_row(view, value);
g_free(value);
}
/* Update tree view for any changes */
gnc_tree_view_account_refilter(view);
}
void
gnc_tree_view_account_restore_filter (GncTreeViewAccount *view,
AccountFilterDialog *fd,
GKeyFile *key_file,
const gchar *group_name)
{
GError *error = NULL;
gint i;
gboolean show;
g_return_if_fail (key_file != NULL);
g_return_if_fail (group_name != NULL);
/* if entry not found, filter will use the default setting */
/* Filter information. Ignore missing keys. */
show = g_key_file_get_boolean (key_file, group_name, SHOW_HIDDEN_ACCOUNTS, &error);
if (error)
{
g_error_free (error);
error = NULL;
}
else
fd->show_hidden = show;
show = g_key_file_get_boolean(key_file, group_name, SHOW_ZERO_TOTALS, &error);
if (error)
{
g_error_free (error);
error = NULL;
}
else
fd->show_zero_total = show;
show = g_key_file_get_boolean(key_file, group_name, SHOW_UNUSED_ACCOUNTS, &error);
if (error)
{
g_error_free (error);
error = NULL;
}
else
fd->show_unused = show;
i = g_key_file_get_integer(key_file, group_name, ACCOUNT_TYPES, &error);
if (error)
{
g_error_free (error);
error = NULL;
}
else
fd->visible_types = i;
}
// @@fixme -- factor this app-not-gui-specific-logic out.
void
gnc_tree_view_account_name_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_name)
{
// check for accounts with the same name among our parent's children.
// should probably factor this consistency check out to the account
// itself....
{
Account *parent = gnc_account_get_parent(account);
Account *existing = gnc_account_lookup_by_name(parent, new_name);
if (existing != NULL && existing != account)
{
PERR("account with the same name [%s] already exists.", new_name);
return;
}
}
xaccAccountSetName(account, new_name);
}
void
gnc_tree_view_account_code_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_code)
{
if (g_strcmp0(xaccAccountGetCode(account), new_code) == 0)
return;
xaccAccountSetCode(account, new_code);
}
void
gnc_tree_view_account_description_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_desc)
{
if (g_strcmp0(xaccAccountGetDescription(account), new_desc) == 0)
return;
xaccAccountSetDescription(account, new_desc);
}
void
gnc_tree_view_account_notes_edited_cb(Account *account, GtkTreeViewColumn *col, const gchar *new_notes)
{
if (g_strcmp0(xaccAccountGetNotes(account), new_notes) == 0)
return;
xaccAccountSetNotes(account, new_notes);
}
static void
gtva_set_column_editor(GncTreeViewAccount *view,
GtkTreeViewColumn *column,
GncTreeViewAccountColumnTextEdited edited_cb)
{
GList *renderers_orig, *renderers;
GtkCellRenderer *renderer = NULL;
// look for the first text-renderer; on the 0th column of the account tree,
// there are two renderers: pixbuf and text. So find the text one.
for (renderers_orig = renderers = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
renderers && !GTK_IS_CELL_RENDERER_TEXT(renderers->data);
renderers = renderers->next);
if (renderers)
renderer = GTK_CELL_RENDERER(renderers->data);
g_list_free(renderers_orig);
g_return_if_fail(renderer != NULL);
gtva_setup_column_renderer_edited_cb(GNC_TREE_VIEW_ACCOUNT(view), column, renderer, edited_cb);
}
void
gnc_tree_view_account_set_name_edited(GncTreeViewAccount *view,
GncTreeViewAccountColumnTextEdited edited_cb)
{
GncTreeViewAccountPrivate *priv;
priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
gtva_set_column_editor(view, priv->name_column, edited_cb);
}
void
gnc_tree_view_account_set_code_edited(GncTreeViewAccount *view,
GncTreeViewAccountColumnTextEdited edited_cb)
{
GncTreeViewAccountPrivate *priv;
priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
gtva_set_column_editor(view, priv->code_column, edited_cb);
}
void
gnc_tree_view_account_set_description_edited(GncTreeViewAccount *view,
GncTreeViewAccountColumnTextEdited edited_cb)
{
GncTreeViewAccountPrivate *priv;
priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
gtva_set_column_editor(view, priv->desc_column, edited_cb);
}
void
gnc_tree_view_account_set_notes_edited(GncTreeViewAccount *view,
GncTreeViewAccountColumnTextEdited edited_cb)
{
GncTreeViewAccountPrivate *priv;
priv = GNC_TREE_VIEW_ACCOUNT_GET_PRIVATE(view);
gtva_set_column_editor(view, priv->notes_column, edited_cb);
}
static
gboolean gnc_tree_view_search_compare (GtkTreeModel *model, gint column,
const gchar *key, GtkTreeIter *iter, gpointer search_data)
{
gchar *normalized_key;
gchar *case_normalized_key = NULL;
gboolean match = FALSE;
normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
if (normalized_key)
case_normalized_key = g_utf8_casefold (normalized_key, -1);
if (case_normalized_key)
{
int i;
for (i=0;i<3;i++)
{
gchar *normalized_string;
gchar *case_normalized_string = NULL;
gchar *str = NULL;
switch (i)
{
case 0:
gtk_tree_model_get(model,iter,GNC_TREE_MODEL_ACCOUNT_COL_NAME,&str,-1);
break;
case 1:
gtk_tree_model_get(model,iter,GNC_TREE_MODEL_ACCOUNT_COL_CODE,&str,-1);
break;
case 2:
gtk_tree_model_get(model,iter,GNC_TREE_MODEL_ACCOUNT_COL_DESCRIPTION,&str,-1);
break;
}
if (!str)
continue;
normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
if (normalized_string)
case_normalized_string = g_utf8_casefold (normalized_string, -1);
if (case_normalized_string&&NULL!=strstr(case_normalized_string,case_normalized_key))
match=TRUE;
g_free (str);
g_free (normalized_string);
g_free (case_normalized_string);
if (match)
break;
}
}
g_free (normalized_key);
g_free (case_normalized_key);
// inverted return (FALSE means a match)
return !match;
}
void gnc_tree_view_account_set_editing_started_cb(GncTreeViewAccount *view,
GFunc editing_started_cb, gpointer editing_cb_data)
{
gnc_tree_view_set_editing_started_cb (GNC_TREE_VIEW(view),
editing_started_cb, editing_cb_data);
}
void gnc_tree_view_account_set_editing_finished_cb(GncTreeViewAccount *view,
GFunc editing_finished_cb, gpointer editing_cb_data)
{
gnc_tree_view_set_editing_finished_cb (GNC_TREE_VIEW(view),
editing_finished_cb, editing_cb_data);
}