Rework/clean the code. Add features needed by the code that embeds a

gnc-tree-view-account into a window.  I.E. Filters, pseudo top-level
account, etc.  Add documentation.


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/branches/gnucash-gnome2-dev@9239 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
David Hampton
2003-09-06 07:17:49 +00:00
parent bbdc0d21ce
commit 6b8fd653a7
4 changed files with 1043 additions and 357 deletions

View File

@@ -3,7 +3,8 @@
* display accounts in a GtkTreeView.
*
* Copyright (C) 2003 Jan Arne Petersen
* Author: Jan Arne Petersen <jpetersen@uni-bonn.de>
* Authors: Jan Arne Petersen <jpetersen@uni-bonn.de>
* 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
@@ -24,6 +25,7 @@
*/
#include "config.h"
#include <string.h>
#include "gnc-tree-model-account.h"
@@ -33,6 +35,7 @@
#include "gnc-commodity.h"
#include "gnc-engine-util.h"
#include "gnc-ui-util.h"
#include "messages.h"
#define TREE_MODEL_ACCOUNT_CM_CLASS "tree-model-account"
@@ -77,11 +80,12 @@ static gboolean gnc_tree_model_account_iter_parent (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *child);
static gpointer account_row_inserted (Account *account,
gpointer data);
void gnc_tree_model_account_event_handler (GUID *entity, QofIdType type,
GNCEngineEventType event_type,
gpointer user_data);
static void gnc_tree_model_account_set_toplevel (GncTreeModelAccount *model,
Account *toplevel);
static void gnc_tree_model_account_event_handler (GUID *entity, QofIdType type,
GNCEngineEventType event_type,
gpointer user_data);
struct GncTreeModelAccountPrivate
{
@@ -90,6 +94,11 @@ struct GncTreeModelAccountPrivate
gint event_handler_id;
};
/************************************************************/
/* g_object required functions */
/************************************************************/
static GtkObjectClass *parent_class = NULL;
GType
@@ -200,6 +209,11 @@ gnc_tree_model_account_destroy (GtkObject *object)
LEAVE(" ");
}
/************************************************************/
/* New Model Creation */
/************************************************************/
GtkTreeModel *
gnc_tree_model_account_new (AccountGroup *group)
{
@@ -222,6 +236,13 @@ gnc_tree_model_account_new (AccountGroup *group)
priv = model->priv;
priv->root = group;
{
Account *account;
account = xaccMallocAccount(gnc_get_current_book());
gnc_tree_model_account_set_toplevel (model, account);
}
priv->event_handler_id =
gnc_engine_register_event_handler (gnc_tree_model_account_event_handler, model);
@@ -230,152 +251,44 @@ gnc_tree_model_account_new (AccountGroup *group)
return GTK_TREE_MODEL (model);
}
void
gnc_tree_model_account_set_root (GncTreeModelAccount *model,
AccountGroup *group)
/************************************************************/
/* Gnc Tree Model Debugging Utility Function */
/************************************************************/
#define ITER_STRING_LEN 128
static const gchar *
iter_to_string (GtkTreeIter *iter)
{
GtkTreePath *path;
gint i;
#ifdef G_THREADS_ENABLED
static GStaticPrivate gtmits_buffer_key = G_STATIC_PRIVATE_INIT;
gchar *string;
ENTER("model %p, group %p", model, group);
g_return_if_fail (model != NULL);
g_return_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model));
string = g_static_private_get (&gtmits_buffer_key);
if (string == NULL) {
string = malloc(ITER_STRING_LEN + 1);
g_static_private_set (&gtmits_buffer_key, string, g_free);
}
#else
static char string[ITER_STRING_LEN + 1];
#endif
DEBUG("old root %p", model->priv->root);
if (model->priv->root != NULL) {
path = gtk_tree_path_new_first ();
if (model->priv->toplevel != NULL) {
gtk_tree_path_append_index (path, 0);
}
for (i = 0; i < xaccGroupGetNumAccounts (model->priv->root); i++) {
gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
}
gtk_tree_path_free (path);
}
model->priv->root = group;
if (model->priv->root != NULL) {
xaccGroupForEachAccount (model->priv->root, account_row_inserted, model, TRUE);
}
LEAVE("new root %p", model->priv->root);
if (iter)
snprintf(string, ITER_STRING_LEN,
"[stamp:%x data:%p (%s), %p, %d]",
iter->stamp, iter->user_data,
xaccAccountGetName ((Account *) iter->user_data),
iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
else
strcpy(string, "(null)");
return string;
}
Account *
gnc_tree_model_account_get_account (GncTreeModelAccount *model,
GtkTreeIter *iter)
{
g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model), NULL);
g_return_val_if_fail (iter != NULL, NULL);
g_return_val_if_fail (iter->user_data != NULL, NULL);
g_return_val_if_fail (iter->stamp == model->stamp, NULL);
return (Account *) iter->user_data;
}
void
gnc_tree_model_account_set_toplevel (GncTreeModelAccount *model,
Account *toplevel)
{
GtkTreePath *path;
gint i;
GtkTreeIter iter;
ENTER("model %p, toplevel %p", model, toplevel);
g_return_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model));
DEBUG("old toplevel %p", model->priv->toplevel);
if (model->priv->toplevel != NULL) {
path = gtk_tree_path_new_first ();
gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
gtk_tree_path_free (path);
} else {
path = gtk_tree_path_new_first ();
for (i = 0; i < xaccGroupGetNumAccounts (model->priv->root); i++) {
gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
}
gtk_tree_path_free (path);
}
model->priv->toplevel = toplevel;
if (model->priv->toplevel != NULL) {
path = gtk_tree_path_new_first ();
gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
gtk_tree_path_free (path);
}
if (model->priv->root != NULL) {
xaccGroupForEachAccount (model->priv->root, account_row_inserted, model, TRUE);
}
LEAVE("new toplevel %p", model->priv->root);
}
Account *
gnc_tree_model_account_get_toplevel (GncTreeModelAccount *model)
{
g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model), NULL);
return model->priv->toplevel;
}
void
gnc_tree_model_account_get_iter_from_account (GncTreeModelAccount *model,
Account *account,
GtkTreeIter *iter)
{
AccountGroup *group;
gint i;
ENTER("model %p, account %p, iter %p", model, account, iter);
g_return_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model));
g_return_if_fail (account != NULL);
g_return_if_fail (iter != NULL);
iter->user_data = account;
iter->stamp = model->stamp;
if (account == model->priv->toplevel) {
iter->user_data2 = NULL;
iter->user_data3 = GINT_TO_POINTER (0);
return;
}
group = xaccAccountGetParent (account);
for (i = 0; i < xaccGroupGetNumAccounts (group); i++) {
if (xaccGroupGetAccount (group, i) == account) {
break;
}
}
iter->user_data2 = group;
iter->user_data3 = GINT_TO_POINTER (i);
LEAVE("iter [stamp:%x data:%p, %p, %d]", iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
}
GtkTreePath *
gnc_tree_model_account_get_path_from_account (GncTreeModelAccount *model,
Account *account)
{
GtkTreeIter tree_iter;
GtkTreePath *tree_path;
ENTER("model %p, account %p", model, account);
g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model), NULL);
g_return_val_if_fail (account != NULL, NULL);
gnc_tree_model_account_get_iter_from_account (model, account, &tree_iter);
tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &tree_iter);
if (tree_path) {
gchar *path_string = gtk_tree_path_to_string(tree_path);
LEAVE("path (2) %s", path_string);
g_free(path_string);
}
return tree_path;
}
/************************************************************/
/* Gtk Tree Model Required Interface Functions */
/************************************************************/
static void
gnc_tree_model_account_tree_model_init (GtkTreeModelIface *iface)
@@ -487,7 +400,7 @@ gnc_tree_model_account_get_iter (GtkTreeModel *tree_model,
iter->user_data3 = GINT_TO_POINTER (0);
iter->stamp = model->stamp;
LEAVE("iter (1) [stamp:%x data:%p, %p, %d]", iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
LEAVE("iter (1) %s", iter_to_string(iter));
return TRUE;
}
}
@@ -525,7 +438,7 @@ gnc_tree_model_account_get_iter (GtkTreeModel *tree_model,
iter->user_data2 = group;
iter->user_data3 = GINT_TO_POINTER (indices[i - 1]);
LEAVE("iter (5) [stamp:%x data:%p, %p, %d]", iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
LEAVE("iter (5) %s", iter_to_string(iter));
return TRUE;
}
@@ -540,8 +453,7 @@ gnc_tree_model_account_get_path (GtkTreeModel *tree_model,
gint i;
gboolean found, finished = FALSE;
ENTER("model %p, iter [stamp:%x data:%p, %p, %d]", model,
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
ENTER("model %p, iter %s", model, iter_to_string(iter));
g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model), NULL);
g_return_val_if_fail (iter != NULL, NULL);
g_return_val_if_fail (iter->user_data != NULL, NULL);
@@ -616,8 +528,8 @@ gnc_tree_model_account_get_value (GtkTreeModel *tree_model,
gboolean negative; /* used to set "defecit style" aka red numbers */
gchar *string;
//ENTER("model %p, iter [stamp:%x data:%p, %p, %d], col %d", tree_model,
// iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3), column);
// ENTER("model %p, iter %s, col %d", tree_model,
// iter_to_string(iter), column);
g_return_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model));
g_return_if_fail (iter != NULL);
g_return_if_fail (iter->user_data != NULL);
@@ -628,7 +540,10 @@ gnc_tree_model_account_get_value (GtkTreeModel *tree_model,
switch (column) {
case GNC_TREE_MODEL_ACCOUNT_COL_NAME:
g_value_init (value, G_TYPE_STRING);
g_value_set_string (value, xaccAccountGetName (account));
if (account == model->priv->toplevel)
g_value_set_string (value, _("New top level account"));
else
g_value_set_string (value, xaccAccountGetName (account));
break;
case GNC_TREE_MODEL_ACCOUNT_COL_TYPE:
g_value_init (value, G_TYPE_STRING);
@@ -802,8 +717,7 @@ gnc_tree_model_account_iter_next (GtkTreeModel *tree_model,
AccountGroup *group;
gint i;
ENTER("model %p, iter [stamp:%x data:%p, %p, %d]", tree_model,
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
ENTER("model %p, iter %s", tree_model, iter_to_string(iter));
g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model), FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (iter->user_data != NULL, FALSE);
@@ -836,8 +750,7 @@ gnc_tree_model_account_iter_next (GtkTreeModel *tree_model,
iter->user_data2 = group;
iter->user_data3 = GINT_TO_POINTER (i + 1);
LEAVE("iter [stamp:%x data:%p, %p, %d]",
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
LEAVE("iter %s", iter_to_string(iter));
return TRUE;
}
@@ -851,12 +764,10 @@ gnc_tree_model_account_iter_children (GtkTreeModel *tree_model,
AccountGroup *group;
if (parent) {
ENTER("model %p, iter [stamp:%x data:%p, %p, %d], parent [stamp:%x data:%p, %p, %d]", tree_model,
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3),
parent->stamp, parent->user_data, parent->user_data2, GPOINTER_TO_INT(parent->user_data3));
ENTER("model %p, iter %p (to be filed in), parent %s",
tree_model, iter, iter_to_string(parent));
} else {
ENTER("model %p, iter [stamp:%x data:%p, %p, %d], parent (null)", tree_model,
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
ENTER("model %p, iter %p (to be filed in), parent (null)", tree_model, iter);
}
g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (tree_model), FALSE);
@@ -868,8 +779,7 @@ gnc_tree_model_account_iter_children (GtkTreeModel *tree_model,
iter->user_data2 = NULL;
iter->user_data3 = GINT_TO_POINTER (0);
iter->stamp = model->stamp;
LEAVE("iter (1) [stamp:%x data:%p, %p, %d]",
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
LEAVE("iter (1) %s", iter_to_string(iter));
return TRUE;
} else if (parent->user_data == model->priv->toplevel) {
parent = NULL;
@@ -895,8 +805,7 @@ gnc_tree_model_account_iter_children (GtkTreeModel *tree_model,
iter->user_data2 = model->priv->root;
iter->user_data3 = GINT_TO_POINTER (0);
iter->stamp = model->stamp;
LEAVE("iter (2) [stamp:%x data:%p, %p, %d]",
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
LEAVE("iter (2) %s", iter_to_string(iter));
return TRUE;
}
@@ -924,8 +833,7 @@ gnc_tree_model_account_iter_children (GtkTreeModel *tree_model,
iter->user_data2 = group;
iter->user_data3 = GINT_TO_POINTER (0);
iter->stamp = model->stamp;
LEAVE("iter (3) [stamp:%x data:%p, %p, %d]",
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
LEAVE("iter (3) %s", iter_to_string(iter));
return TRUE;
}
@@ -936,8 +844,7 @@ gnc_tree_model_account_iter_has_child (GtkTreeModel *tree_model,
GncTreeModelAccount *model;
AccountGroup *group;
ENTER("model %p, iter [stamp:%x data:%p, %p, %d]", tree_model,
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
ENTER("model %p, iter %s", tree_model, iter_to_string(iter));
g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (tree_model), FALSE);
model = GNC_TREE_MODEL_ACCOUNT (tree_model);
@@ -968,8 +875,7 @@ gnc_tree_model_account_iter_n_children (GtkTreeModel *tree_model,
GncTreeModelAccount *model;
AccountGroup *group;
ENTER("model %p, iter [stamp:%x data:%p, %p, %d]", tree_model,
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
ENTER("model %p, iter %s", tree_model, iter_to_string(iter));
g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (tree_model), FALSE);
model = GNC_TREE_MODEL_ACCOUNT (tree_model);
@@ -1009,14 +915,16 @@ gnc_tree_model_account_iter_nth_child (GtkTreeModel *tree_model,
AccountGroup *group;
if (parent) {
ENTER("model %p, iter [stamp:%x data:%p, %p, %d], parent [stamp:%x data:%p, %p, %d], n %d",
tree_model,
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3),
parent->stamp, parent->user_data, parent->user_data2, GPOINTER_TO_INT(parent->user_data3), n);
gchar *parent_string;
parent_string = strdup(iter_to_string(parent));
ENTER("model %p, iter %s, parent %s, n %d",
tree_model, iter_to_string(iter),
parent_string, n);
g_free(parent_string);
} else {
ENTER("model %p, iter [stamp:%x data:%p, %p, %d], parent (null), n %d",
tree_model,
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3), n);
ENTER("model %p, iter %s, parent (null), n %d",
tree_model, iter_to_string(iter), n);
}
g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (tree_model), FALSE);
@@ -1033,8 +941,7 @@ gnc_tree_model_account_iter_nth_child (GtkTreeModel *tree_model,
iter->user_data2 = NULL;
iter->user_data3 = GINT_TO_POINTER (0);
iter->stamp = model->stamp;
LEAVE("iter (1) [stamp:%x data:%p, %p, %d]",
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
LEAVE("iter (1) %s", iter_to_string(iter));
return TRUE;
}
}
@@ -1051,8 +958,7 @@ gnc_tree_model_account_iter_nth_child (GtkTreeModel *tree_model,
iter->user_data2 = model->priv->root;
iter->user_data3 = GINT_TO_POINTER (n);
iter->stamp = model->stamp;
LEAVE("iter (2) [stamp:%x data:%p, %p, %d]",
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
LEAVE("iter (2) %s", iter_to_string(iter));
return TRUE;
}
@@ -1083,8 +989,7 @@ gnc_tree_model_account_iter_nth_child (GtkTreeModel *tree_model,
iter->user_data2 = group;
iter->user_data3 = GINT_TO_POINTER (n);
iter->stamp = model->stamp;
LEAVE("iter (3) [stamp:%x data:%p, %p, %d]",
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
LEAVE("iter (3) %s", iter_to_string(iter));
return TRUE;
}
@@ -1099,14 +1004,16 @@ gnc_tree_model_account_iter_parent (GtkTreeModel *tree_model,
gint i;
if (child) {
ENTER("model %p, iter [stamp:%x data:%p, %p, %d], child [stamp:%x data:%p, %p, %d]",
tree_model,
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3),
child->stamp, child->user_data, child->user_data2, GPOINTER_TO_INT(child->user_data3));
gchar *child_string;
child_string = strdup(iter_to_string(child));
ENTER("model %p, iter %s, child %s",
tree_model, iter_to_string(iter),
child_string);
g_free(child_string);
} else {
ENTER("model %p, iter [stamp:%x data:%p, %p, %d], child (null)",
tree_model,
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
ENTER("model %p, iter %s, child (null)",
tree_model, iter_to_string(iter));
}
g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (tree_model), FALSE);
@@ -1133,8 +1040,7 @@ gnc_tree_model_account_iter_parent (GtkTreeModel *tree_model,
iter->user_data2 = NULL;
iter->user_data3 = GINT_TO_POINTER (0);
iter->stamp = model->stamp;
LEAVE("iter (1) [stamp:%x data:%p, %p, %d]",
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
LEAVE("iter (1) %s", iter_to_string(iter));
return TRUE;
} else {
iter->stamp = 0;
@@ -1149,8 +1055,7 @@ gnc_tree_model_account_iter_parent (GtkTreeModel *tree_model,
iter->user_data2 = group;
iter->user_data3 = GINT_TO_POINTER (i);
iter->stamp = model->stamp;
LEAVE("iter (2) [stamp:%x data:%p, %p, %d]",
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
LEAVE("iter (2) %s", iter_to_string(iter));
return TRUE;
}
}
@@ -1160,8 +1065,7 @@ gnc_tree_model_account_iter_parent (GtkTreeModel *tree_model,
iter->user_data2 = NULL;
iter->user_data3 = GINT_TO_POINTER (0);
iter->stamp = model->stamp;
LEAVE("iter (2) [stamp:%x data:%p, %p, %d]",
iter->stamp, iter->user_data, iter->user_data2, GPOINTER_TO_INT(iter->user_data3));
LEAVE("iter (3) %s", iter_to_string(iter));
return TRUE;
}
iter->stamp = 0;
@@ -1169,6 +1073,11 @@ gnc_tree_model_account_iter_parent (GtkTreeModel *tree_model,
return FALSE;
}
/************************************************************/
/* Account Tree View Root Functions */
/************************************************************/
static gpointer
account_row_inserted (Account *account,
gpointer data)
@@ -1176,7 +1085,8 @@ account_row_inserted (Account *account,
GtkTreePath *path;
GtkTreeIter iter;
ENTER("account %p, model %p", account, data);
ENTER("account %p (%s), model %p",
account, xaccAccountGetName(account), data);
gnc_tree_model_account_get_iter_from_account (GNC_TREE_MODEL_ACCOUNT (data), account, &iter);
path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter);
@@ -1189,16 +1099,352 @@ account_row_inserted (Account *account,
return NULL;
}
/*
* Add a new top node to the model. This node is for a pseudo-account
* that lives above the main level accounts in the engine.
*/
Account *
gnc_tree_model_account_get_toplevel (GncTreeModelAccount *model)
{
g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model), NULL);
return model->priv->toplevel;
}
/*
* Add a new top node to the model. This node is for a pseudo-account
* that lives above the main level accounts in the engine.
*/
static void
gnc_tree_model_account_set_toplevel (GncTreeModelAccount *model,
Account *toplevel)
{
GtkTreePath *path;
gint i;
GtkTreeIter iter;
ENTER("model %p, toplevel %p", model, toplevel);
g_return_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model));
DEBUG("old toplevel %p", model->priv->toplevel);
if (model->priv->toplevel != NULL) {
path = gtk_tree_path_new_first ();
gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
gtk_tree_path_free (path);
} else {
path = gtk_tree_path_new_first ();
for (i = 0; i < xaccGroupGetNumAccounts (model->priv->root); i++) {
gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
}
gtk_tree_path_free (path);
}
DEBUG("set new toplevel %p", toplevel);
model->priv->toplevel = toplevel;
if (model->priv->toplevel != NULL) {
path = gtk_tree_path_new_first ();
gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
gtk_tree_path_free (path);
}
if (model->priv->root != NULL) {
xaccGroupForEachAccount (model->priv->root, account_row_inserted, model, TRUE);
}
LEAVE("new toplevel %p", model->priv->root);
}
/************************************************************/
/* Account Tree View Filter Functions */
/************************************************************/
/*
* DRH - THIS FUNCTION SHOULD BE REMOVED.
*
* This function is from before the account tree view existed. This
* functionality should migrate there.
*/
void
gnc_tree_model_account_set_root (GncTreeModelAccount *model,
AccountGroup *group)
{
g_assert_not_reached ();
#if 0
GtkTreePath *path;
gint i;
ENTER("model %p, group %p", model, group);
g_return_if_fail (model != NULL);
g_return_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model));
DEBUG("old root %p", model->priv->root);
if (model->priv->root != NULL) {
path = gtk_tree_path_new_first ();
if (model->priv->toplevel != NULL) {
gtk_tree_path_append_index (path, 0);
}
for (i = 0; i < xaccGroupGetNumAccounts (model->priv->root); i++) {
gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
}
gtk_tree_path_free (path);
}
model->priv->root = group;
if (model->priv->root != NULL) {
xaccGroupForEachAccount (model->priv->root, account_row_inserted, model, TRUE);
}
LEAVE("new root %p", model->priv->root);
#endif
}
/*
* Convert a model/iter pair to a gnucash account. This routine should
* only be called from an account tree view filter function.
*/
Account *
gnc_tree_model_account_get_account (GncTreeModelAccount *model,
GtkTreeIter *iter)
{
g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model), NULL);
g_return_val_if_fail (iter != NULL, NULL);
g_return_val_if_fail (iter->user_data != NULL, NULL);
g_return_val_if_fail (iter->stamp == model->stamp, NULL);
return (Account *) iter->user_data;
}
/*
* Convert a model/account pair into a gtk_tree_model_iter. This
* routine should only be called from the file
* gnc-tree-view-account.c.
*/
void
gnc_tree_model_account_get_iter_from_account (GncTreeModelAccount *model,
Account *account,
GtkTreeIter *iter)
{
AccountGroup *group;
gint i;
ENTER("model %p, account %p, iter %p", model, account, iter);
g_return_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model));
g_return_if_fail (account != NULL);
g_return_if_fail (iter != NULL);
iter->user_data = account;
iter->stamp = model->stamp;
if (account == model->priv->toplevel) {
iter->user_data2 = NULL;
iter->user_data3 = GINT_TO_POINTER (0);
return;
}
group = xaccAccountGetParent (account);
for (i = 0; i < xaccGroupGetNumAccounts (group); i++) {
if (xaccGroupGetAccount (group, i) == account) {
break;
}
}
iter->user_data2 = group;
iter->user_data3 = GINT_TO_POINTER (i);
LEAVE("iter %s", iter_to_string(iter));
}
/*
* Convert a model/account pair into a gtk_tree_model_path. This
* routine should only be called from the file
* gnc-tree-view-account.c.
*/
GtkTreePath *
gnc_tree_model_account_get_path_from_account (GncTreeModelAccount *model,
Account *account)
{
GtkTreeIter tree_iter;
GtkTreePath *tree_path;
ENTER("model %p, account %p", model, account);
g_return_val_if_fail (GNC_IS_TREE_MODEL_ACCOUNT (model), NULL);
g_return_val_if_fail (account != NULL, NULL);
gnc_tree_model_account_get_iter_from_account (model, account, &tree_iter);
tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &tree_iter);
if (tree_path) {
gchar *path_string = gtk_tree_path_to_string(tree_path);
LEAVE("path (2) %s", path_string);
g_free(path_string);
}
return tree_path;
}
/************************************************************/
/* Account Tree Model - Engine Event Handling Functions */
/************************************************************/
typedef struct _remove_data {
GUID guid;
GncTreeModelAccount *model;
GtkTreePath *path;
} remove_data;
static GSList *pending_removals = NULL;
/** This function performs common updating to the model after an
* account has been added or removed. The parent entry needs to be
* tapped on the shoulder so that it can correctly update the
* disclosure triangle (first added child/last removed child) or
* possibly rebuild its child list of that level of accounts is
* visible.
*
* @internal
*
* @param model The account tree model containing the account that
* has been added or deleted.
*
* @param path The path to the newly added item, or the just removed
* item.
*/
static void
gnc_tree_model_account_path_changed (GncTreeModelAccount *model,
GtkTreePath *path)
{
GtkTreeIter iter;
if (gtk_tree_path_up (path)) {
gtk_tree_model_get_iter (GTK_TREE_MODEL(model), &iter, path);
gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL(model), path, &iter);
}
do {
model->stamp++;
} while (model->stamp == 0);
}
/** This function is a helper routine for the following
* gnc_tree_model_account_event_handler() function. It is called
* from an iterator over all the pending account removals, and is
* responsible for selecting the right item(s), deleting it from the
* pending list, and then sending the "row_deleted" signal to any/all
* parent models.
*
* @internal
*
* @param data An item in the pending deletion list.
*
* @param entity The guid value of the destroyed item.
*/
static void
gnc_tree_model_account_delete_event_helper (remove_data *data,
GUID *entity)
{
if (!guid_equal(&data->guid, entity))
return;
pending_removals = g_slist_remove (pending_removals, data);
gtk_tree_model_row_deleted (GTK_TREE_MODEL(data->model), data->path);
gnc_tree_model_account_path_changed (data->model, data->path);
gtk_tree_path_free(data->path);
g_free(data);
}
/** This function is the handler for all event messages from the
* engine. Its purpose is to update the account tree model any time
* an account is added to the engine or deleted from the engine.
* This change to the model is then propagated to any/all overlying
* filters and views. This function listens to the ADD, REMOVE, and
* DESTROY events.
*
* @internal
*
* @warning There is a "Catch 22" situation here.
* gtk_tree_model_row_deleted() can't be called until after the item
* has been deleted from the real model (which is the engine's
* account tree for us), but once the account has been deleted from
* the engine we have no way to determine the path to pass to
* row_deleted(). This is a PITA, but the only ither choice is to
* have this model mirror the engine's accounts instead of
* referencing them directly.
*
* @param entity The guid of the affected item.
*
* @param type The type of the affected item. This function only
* cares about items of type "account".
*
* @param event type The type of the event. This function only cares
* about items of type ADD, REMOVE, and DESTROY.
*
* @param user_data A pointer to the account tree model.
*/
void gnc_tree_model_account_event_handler (GUID *entity, QofIdType type,
GNCEngineEventType event_type,
gpointer user_data)
{
GncTreeModelAccount *model;
GtkTreePath *path;
GtkTreeIter iter;
Account *account;
remove_data *data;
const gchar *account_name;
/* hard failures */
g_return_if_fail(GNC_IS_TREE_MODEL_ACCOUNT(user_data));
/* soft failures */
if (safe_strcmp(type, GNC_ID_ACCOUNT) != 0)
return;
ENTER("entity %p of type %s, event %d, model %p",
entity, type, event_type, user_data);
model = (GncTreeModelAccount *)user_data;
do {
model->stamp++;
} while (model->stamp == 0);
/* Get the account.*/
/* DRH - Put the book in the model private data so this code
* supports multiple simultaneous books. */
account = xaccAccountLookup (entity, gnc_get_current_book ());
account_name = xaccAccountGetName(account);
switch (event_type) {
case GNC_EVENT_ADD:
/* Tell the filters/views where the new account was added. */
DEBUG("create account %p (%s)", account, account_name);
gnc_tree_model_account_get_iter_from_account (model, account, &iter);
path = gtk_tree_model_get_path (GTK_TREE_MODEL(model), &iter);
gtk_tree_model_row_inserted (GTK_TREE_MODEL(model), path, &iter);
gnc_tree_model_account_path_changed (model, path);
gtk_tree_path_free(path);
break;
case GNC_EVENT_REMOVE:
/* Record the path of this account for later use in destruction */
DEBUG("remove account %p (%s)", account, account_name);
data = malloc(sizeof(*data));
data->guid = *entity;
data->model = model;
data->path = gnc_tree_model_account_get_path_from_account (model,
account);
pending_removals = g_slist_append (pending_removals, data);
LEAVE(" ");
return;
case GNC_EVENT_DESTROY:
/* Tell the filters/view the account has been deleted. */
DEBUG("destroy account %p (%s)", account, account_name);
g_slist_foreach (pending_removals,
(GFunc)gnc_tree_model_account_delete_event_helper,
entity);
break;
default:
LEAVE("ignored event for %p (%s)", account, account_name);
return;
}
LEAVE(" new stamp %u", model->stamp);
}

View File

@@ -23,6 +23,14 @@
* Boston, MA 02111-1307, USA gnu@gnu.org
*/
/** @addtogroup Engine
@{ */
/** @file gnc-tree-model-account.h
@brief GtkTreeModel implementation for gnucash account tree.
@author Jan Arne Petersen <jpetersen@uni-bonn.de>
@author David Hampton <hampton@employees.org>
*/
#ifndef __GNC_TREE_MODEL_ACCOUNT_H
#define __GNC_TREE_MODEL_ACCOUNT_H
@@ -91,26 +99,92 @@ typedef struct {
GtkObjectClass parent;
} GncTreeModelAccountClass;
/* function prototypes */
/* Standard g_object type */
GType gnc_tree_model_account_get_type (void);
/** @name Account Tree Model Constructors */
/** @{ */
GtkTreeModel *gnc_tree_model_account_new (AccountGroup *group);
/** @} */
/* OBSOLETE */
void gnc_tree_model_account_set_root (GncTreeModelAccount *model,
AccountGroup *group);
/** This function returns the account associated with the top level
* pseudo-account. The gnucash engine does not have a single top
* level account (it has a list of top level accounts), but this code
* provides one so that it can be used with all parts of the gnucash
* gui.
*
* @internal This function should only be called from gnc-tree-view-account.c.
*
* @param account_view A pointer to an account tree view.
*
* @return The top-level pseudo-account.
*/
Account *gnc_tree_model_account_get_toplevel (GncTreeModelAccount *model);
/** @name Account Tree Model Get/Set Functions */
/** @{ */
/** Convert a model/iter pair to a gnucash account. This routine should
* only be called from an account tree view filter function. The
* model and iter values will be provided as part of the call to the
* filter.
*
* @param model A pointer to the account tree model.
*
* @param iter A gtk_tree_iter corresponding to a single account in
* the model.
*
* @return A pointer to the corresponding account.
*/
Account *gnc_tree_model_account_get_account (GncTreeModelAccount *model,
GtkTreeIter *iter);
void gnc_tree_model_account_set_toplevel (GncTreeModelAccount *model,
Account *toplevel);
Account *gnc_tree_model_account_get_toplevel (GncTreeModelAccount *model);
GtkTreePath *gnc_tree_model_account_get_path_from_account (GncTreeModelAccount *model,
Account *account);
/** Convert a model/account pair into a gtk_tree_model_iter. This
* routine should only be called from the file
* gnc-tree-view-account.c.
*
* @internal
*
* @param model The model that an account belongs to.
*
* @param account The account to convert.
*
* @param iter A pointer to an iter. This iter will be rewritten to
* contain the results of the query.
*/
void gnc_tree_model_account_get_iter_from_account (GncTreeModelAccount *model,
Account *account,
GtkTreeIter *iter);
/** Convert a model/account pair into a gtk_tree_model_path. This
* routine should only be called from the file
* gnc-tree-view-account.c.
*
* @internal
*
* @param model The model that an account belongs to.
*
* @param account The account to convert.
*
* @return A pointer to a path describing the account. It is the
* responsibility of the caller to free this path when done.
*/
GtkTreePath *gnc_tree_model_account_get_path_from_account (GncTreeModelAccount *model,
Account *account);
/** @} */
G_END_DECLS
#endif /* __GNC_TREE_MODEL_ACCOUNT_H */

View File

@@ -39,26 +39,22 @@
#include "gnc-ui-util.h"
#include "messages.h"
#define TREE_VIEW_ACCOUNT_CM_CLASS "tree-view-account"
/** Static Globals *******************************************************/
/* This static indicates the debugging module that this .o belongs to. */
static short module = 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 void gnc_tree_view_account_destroy (GtkObject *object);
#if 0
static void gnc_tree_model_account_refresh_handler (GHashTable *changes,
gpointer data);
static void gnc_tree_model_account_refresh (GncTreeModelAccount *model);
#endif
struct GncTreeViewAccountPrivate
{
AccountViewInfo avi;
gboolean has_filter;
};
typedef struct _gnc_tree_view_account_default {
@@ -92,6 +88,10 @@ static gnc_tree_view_account_default gnc_tree_view_account_defaults[] = {
};
/************************************************************/
/* g_object required functions */
/************************************************************/
static GObjectClass *parent_class = NULL;
GType
@@ -141,11 +141,9 @@ gnc_tree_view_account_class_init (GncTreeViewAccountClass *klass)
static void
gnc_tree_view_account_init (GncTreeViewAccount *view)
{
ENTER(" ");
view->priv = g_new0 (GncTreeViewAccountPrivate, 1);
gnc_init_account_view_info(&view->priv->avi);
LEAVE(" ");
}
static void
@@ -156,13 +154,11 @@ gnc_tree_view_account_finalize (GObject *object)
g_return_if_fail (object != NULL);
g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (object));
ENTER("view %p", object);
account_view = GNC_TREE_VIEW_ACCOUNT (object);
g_free (account_view->priv);
if (G_OBJECT_CLASS (parent_class)->finalize)
(* G_OBJECT_CLASS (parent_class)->finalize) (object);
LEAVE(" ");
}
static void
@@ -173,60 +169,56 @@ gnc_tree_view_account_destroy (GtkObject *object)
g_return_if_fail (object != NULL);
g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (object));
ENTER("view %p", object);
account_view = GNC_TREE_VIEW_ACCOUNT (object);
if (GTK_OBJECT_CLASS (parent_class)->destroy)
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
LEAVE(" ");
}
static void
gnc_tree_view_update_column_visibility_internal (GncTreeViewAccount *account_view)
{
GtkTreeView *tree_view;
GtkTreeViewColumn *column;
gint i;
ENTER(" ");
tree_view = GTK_TREE_VIEW (account_view);
/************************************************************/
/* New View Creation */
/************************************************************/
for (i = 0; i < NUM_ACCOUNT_FIELDS; i++) {
DEBUG("setting column %d to %s", i, account_view->priv->avi.show_field[i] ? "visible" : "invisible");
column = gtk_tree_view_get_column (tree_view, i);
gtk_tree_view_column_set_visible (column, account_view->priv->avi.show_field[i]);
}
LEAVE(" ");
}
static GtkTreeView *
gnc_tree_view_account_new_internal (gboolean filterable)
/*
* 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)
{
GncTreeViewAccount *account_view;
GtkTreeView *tree_view;
GtkTreeModel *model, *filter_model;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
GtkTreePath *virtual_root_path = NULL;
gint i;
ENTER(" ");
/* Create our view */
account_view = g_object_new (GNC_TYPE_TREE_VIEW_ACCOUNT, NULL);
tree_view = GTK_TREE_VIEW (account_view);
/* Create/get a pointer to the existing model for this set of books. */
model = gnc_tree_model_account_new (gnc_book_get_group (gnc_get_current_book ()));
if (filterable) {
filter_model = egg_tree_model_filter_new (model, NULL);
gtk_tree_view_set_model (tree_view, filter_model);
} else {
gtk_tree_view_set_model (tree_view, model);
}
/* Set up the view private filter on the common model. */
if (!show_root)
virtual_root_path = gtk_tree_path_new_first ();
filter_model = egg_tree_model_filter_new (model, virtual_root_path);
gtk_tree_view_set_model (tree_view, filter_model);
gtk_object_sink(GTK_OBJECT(model));
account_view->priv->has_filter = filterable;
if (virtual_root_path)
gtk_tree_path_free(virtual_root_path);
/* Set column visibilities */
gnc_tree_view_init_default_visibility(&account_view->priv->avi);
/* Set default visibilities */
gtk_tree_view_set_headers_visible (tree_view, FALSE);
gnc_tree_view_account_init_view_info(&account_view->priv->avi);
/* Account name */
/* Set up the "account name" column */
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, gettext(gnc_tree_view_account_defaults[0].field_name));
renderer = gtk_cell_renderer_pixbuf_new ();
@@ -241,7 +233,7 @@ gnc_tree_view_account_new_internal (gboolean filterable)
gtk_tree_view_column_set_resizable (column, TRUE);
gtk_tree_view_set_expander_column (tree_view, column);
/* All other columns */
/* Set up all other columns */
for (i = 1; i < NUM_ACCOUNT_FIELDS; i++) {
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes (gettext(gnc_tree_view_account_defaults[i].field_name),
@@ -266,69 +258,19 @@ gnc_tree_view_account_new_internal (gboolean filterable)
gtk_tree_view_column_set_resizable (column, TRUE);
}
#if 0
priv->component_id = gnc_register_gui_component (TREE_MODEL_ACCOUNT_CM_CLASS,
gnc_tree_model_account_refresh_handler,
NULL,
model);
gnc_gui_component_watch_entity_type (priv->component_id,
GNC_ID_ACCOUNT,
GNC_EVENT_CREATE | GNC_EVENT_DESTROY);
#endif
LEAVE("%p", tree_view);
return tree_view;
}
GtkTreeView *
gnc_tree_view_account_new (void)
{
return gnc_tree_view_account_new_internal (FALSE);
}
GtkTreeView *
gnc_tree_view_account_new_filterable (void)
{
return gnc_tree_view_account_new_internal (TRUE);
}
/********************************************************************\
* gnc_init_account_view_info *
* initialize an account view info structure with default values *
* *
* Args: avi - structure to initialize *
* Returns: nothing *
\********************************************************************/
void
gnc_tree_view_init_default_visibility(AccountViewInfo *avi)
{
int i;
for (i = 0; i < NUM_ACCOUNT_FIELDS; i++)
avi->show_field[i] = FALSE;
avi->show_field[ACCOUNT_NAME] = TRUE;
avi->show_field[ACCOUNT_DESCRIPTION] = TRUE;
avi->show_field[ACCOUNT_TOTAL] = TRUE;
}
void
gnc_tree_view_update_column_visibility (GncTreeViewAccount *account_view,
AccountViewInfo *avi)
{
GncTreeViewAccountPrivate *priv;
ENTER("%p", account_view);
g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(account_view));
priv = account_view->priv;
priv->avi = *avi;
gnc_tree_view_update_column_visibility_internal (account_view);
LEAVE(" ");
}
/************************************************************/
/* Account Tree View Filter Functions */
/************************************************************/
/*
* Convert a column name to a numeric identifier. This is a
* helper routine for the following function.
*/
static gint
gnc_tree_view_account_pref_name_to_field (const char *pref_name)
{
@@ -341,8 +283,12 @@ gnc_tree_view_account_pref_name_to_field (const char *pref_name)
return(GNC_TREE_MODEL_ACCOUNT_COL_NAME);
}
/*
* Set the list of columns that will be visible in an account tree view.
*/
void
gnc_tree_view_account_configure_columns (GncTreeViewAccount *account_view, GSList *list)
gnc_tree_view_account_configure_columns (GncTreeViewAccount *account_view,
GSList *column_names)
{
AccountViewInfo new_avi;
AccountFieldCode field;
@@ -351,7 +297,7 @@ gnc_tree_view_account_configure_columns (GncTreeViewAccount *account_view, GSLis
ENTER(" ");
memset (&new_avi, 0, sizeof(new_avi));
for (node = list; node != NULL; node = node->next)
for (node = column_names; node != NULL; node = node->next)
{
field = gnc_tree_view_account_pref_name_to_field(node->data);
if (field < NUM_ACCOUNT_FIELDS)
@@ -360,10 +306,76 @@ gnc_tree_view_account_configure_columns (GncTreeViewAccount *account_view, GSLis
new_avi.show_field[ACCOUNT_NAME] = TRUE;
gnc_tree_view_update_column_visibility (account_view, &new_avi);
gnc_tree_view_account_set_view_info (account_view, &new_avi);
LEAVE(" ");
}
/*
* Initialize an account view info structure with default values.
*/
void
gnc_tree_view_account_init_view_info(AccountViewInfo *avi)
{
int i;
for (i = 0; i < NUM_ACCOUNT_FIELDS; i++)
avi->show_field[i] = FALSE;
avi->show_field[ACCOUNT_NAME] = TRUE;
}
/*
* 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));
priv = account_view->priv;
*avi = priv->avi;
}
/*
* Set the account view info data in use by the specified tree to
* match the callers request.
*
* DRH - COMPATABILITY WARNING
*
* This function does not do anything with the 'include_type' field.
* Should there be a automatic filter for backward compatability
* that uses these flags, or should all uses of this be converted to
* a eggtreemodelfilter?
*/
void
gnc_tree_view_account_set_view_info (GncTreeViewAccount *account_view,
AccountViewInfo *avi)
{
GtkTreeViewColumn *column;
gint i;
ENTER("%p", account_view);
g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(account_view));
account_view->priv->avi = *avi;
for (i = 0; i < NUM_ACCOUNT_FIELDS; i++) {
column = gtk_tree_view_get_column (GTK_TREE_VIEW(account_view), i);
gtk_tree_view_column_set_visible (column, avi->show_field[i]);
}
LEAVE(" ");
}
/*
* Set an eggtreemodel 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.
*/
void
gnc_tree_view_account_set_filter (GncTreeViewAccount *account_view,
EggTreeModelFilterVisibleFunc func,
@@ -372,71 +384,222 @@ gnc_tree_view_account_set_filter (GncTreeViewAccount *account_view,
{
GtkTreeModel *filter_model;
g_return_if_fail (account_view->priv->has_filter == TRUE);
ENTER("view %p, filter func %p, data %p, destroy %p",
account_view, func, data, destroy);
filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (account_view));
egg_tree_model_filter_set_visible_func (EGG_TREE_MODEL_FILTER (filter_model),
func, data, destroy);
LEAVE(" ");
}
gboolean
gnc_tree_view_account_get_selected_account (GncTreeViewAccount *view,
Account **account)
/*
* Forces the entire account tree to be re-evaluated for visibility.
*/
void
gnc_tree_view_account_refilter (GncTreeViewAccount *view)
{
GtkTreeModel *filter_model;
g_return_if_fail(GNC_IS_TREE_VIEW_ACCOUNT(view));
filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW(view));
egg_tree_model_filter_refilter (EGG_TREE_MODEL_FILTER (filter_model));
}
/************************************************************/
/* Account Tree View Get/Set Functions */
/************************************************************/
/*
* Return the account associated with the top level pseudo-account for
* the tree.
*/
Account *
gnc_tree_view_account_get_top_level (GncTreeViewAccount *view)
{
GtkTreeModel *model, *filter_model;
filter_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
model = egg_tree_model_filter_get_model (EGG_TREE_MODEL_FILTER (filter_model));
return gnc_tree_model_account_get_toplevel (GNC_TREE_MODEL_ACCOUNT(model));
}
/*
* 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 *model;
GtkTreeIter iter, child_iter;
Account *account;
g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), FALSE);
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));
if (!gtk_tree_selection_get_selected (selection, &model, &iter))
if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
LEAVE("no account, get_selected failed");
return FALSE;
if (!view->priv->has_filter) {
*account = iter.user_data;
return TRUE;
}
egg_tree_model_filter_convert_iter_to_child_iter (EGG_TREE_MODEL_FILTER (model),
&child_iter, &iter);
account = child_iter.user_data;
return TRUE;
LEAVE("account %p (%s)", account, xaccAccountGetName (account));
return account;
}
#if 0
static void
gnc_tree_view_account_refresh_handler (GHashTable *changes, gpointer user_data)
/*
* 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)
{
ENTER("changes %p, view %p", changes, user_data);
g_return_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (user_data));
GtkTreeModel *model, *filter_model;
GtkTreeSelection *selection;
GtkTreePath *path, *filter_path, *parent_path;
gnc_tree_view_account_refresh (GNC_TREE_VIEW_ACCOUNT (user_data));
LEAVE(" ");
ENTER("view %p, account %p (%s)", view,
account, xaccAccountGetName (account));
/* Clear any existing selection. */
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
gtk_tree_selection_unselect_all (selection);
filter_model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
model = egg_tree_model_filter_get_model (EGG_TREE_MODEL_FILTER (filter_model));
path = gnc_tree_model_account_get_path_from_account (GNC_TREE_MODEL_ACCOUNT(model), account);
if (path == NULL) {
LEAVE("get_path_from_account failed");
return;
}
{
gchar *path_string = gtk_tree_path_to_string(path);
DEBUG("tree path %s", path_string);
g_free(path_string);
}
filter_path =
egg_tree_model_filter_convert_child_path_to_path (EGG_TREE_MODEL_FILTER (filter_model),
path);
gtk_tree_path_free(path);
if (filter_path == NULL) {
LEAVE("convert_child_path_to_path failed");
return;
}
/* gtk_tree_view requires that a row be visible before it can be selected */
parent_path = gtk_tree_path_copy (filter_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, filter_path);
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(view), filter_path, NULL, FALSE, 0.0, 0.0);
{
gchar *path_string = gtk_tree_path_to_string(filter_path);
LEAVE("filter path %s", path_string);
g_free(path_string);
}
gtk_tree_path_free(filter_path);
}
/*
* This helper function is called once for each row in the tree view
* that is currently selected. Its task is to an the corresponding
* account to the end of a glist.
*/
static void
gnc_tree_view_account_refresh (GncTreeViewAccount *view)
get_selected_accounts_helper (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer data)
{
GtkTreePath *path;
gint i;
ENTER("view %p", view);
if (view->priv->root == NULL) {
return;
}
GList **return_list = data;
Account *account;
path = gtk_tree_path_new_first ();
if (view->priv->toplevel != NULL) {
gtk_tree_path_append_index (path, 0);
}
for (i = 0; i < xaccGroupGetNumAccounts (view->priv->root); i++) {
gtk_tree_view_row_deleted (GTK_TREE_VIEW (view), path);
}
gtk_tree_path_free (path);
xaccGroupForEachAccount (view->priv->root, account_row_inserted, view, TRUE);
LEAVE(" ");
account = iter->user_data;
*return_list = g_list_append(*return_list, account);
}
#endif
/*
* 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;
GList *return_list = NULL;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view));
gtk_tree_selection_selected_foreach(selection, get_selected_accounts_helper, &return_list);
return 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;
GtkTreePath *path, *parent_path;
GtkTreeSelection *selection;
GList *element;
Account *account;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
/* Clear any existing selection. */
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);
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;
}
/* gtk_tree_view requires that a row be visible before it can be selected */
parent_path = gtk_tree_path_copy (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, path);
if (show_last && (element == NULL))
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(view), path, NULL, FALSE, 0.0, 0.0);
gtk_tree_path_free(path);
}
}

View File

@@ -22,6 +22,13 @@
* *
\********************************************************************/
/** @addtogroup Engine
@{ */
/** @file gnc-tree-view-account.h
@brief GtkTreeView implementation for gnucash account tree.
@author David Hampton <hampton@employees.org>
*/
#ifndef __GNC_TREE_VIEW_ACCOUNT_H
#define __GNC_TREE_VIEW_ACCOUNT_H
@@ -57,22 +64,218 @@ typedef struct {
GtkTreeViewClass parent;
} GncTreeViewAccountClass;
/* function prototypes */
/* Standard g_object type */
GType gnc_tree_view_account_get_type (void);
GtkTreeView *gnc_tree_view_account_new (void);
GtkTreeView *gnc_tree_view_account_new_filterable (void);
void gnc_tree_view_update_column_visibility (GncTreeViewAccount *account_view,
/** @name Account Tree View Constructors */
/** @{ */
/** Create a new account tree view. This view may or may not show a
* pseudo top-level account. The gnucash engine does not have a
* single top level account (it has a list of top level accounts),
* but this code provides one so that it can be used with all parts
* of the gnucash gui.
*
* @param show_root Show the pseudo top-level account in this view.
*
* @return A pointer to a new account tree view.
*/
GtkTreeView *gnc_tree_view_account_new (gboolean show_root);
/** @} */
/** @name Account Tree View Configuration */
/** @{ */
/** Configure (by name) the set of visible columns in an account tree
* view. By default, only the account name column is show. The
* avalible list of columns can be found in the file
* gnc-tree-view-account.c
*
* @param account_view A pointer to an account tree view.
*
* @param column_names A list of column names to make visible.
*/
void gnc_tree_view_account_configure_columns (GncTreeViewAccount *account_view,
GSList *column_names);
/** @} */
/** @name Account Tree View Filtering */
/** @{ */
/** Given pointers to an account tree and old style filter block, this
* function will copy the current configuration of the account tree
* widget into the data block. This may be used in conjunction with
* the gnc_tree_view_account_set_view_info function to modify the
* filters on an existing account tree.
*
* @param account_view A pointer to an account tree view.
*
* @param avi A pointer to an old style filter block to fill in.
*/
void gnc_tree_view_account_get_view_info (GncTreeViewAccount *account_view,
AccountViewInfo *avi);
void gnc_tree_view_init_default_visibility (AccountViewInfo *avi);
void gnc_tree_view_account_configure_columns (GncTreeViewAccount *account_view, GSList *list);
/** Given pointers to an account tree and old style filter block, this
* function will applies the settings specified to the current
* configuration of the account tree widget. This may be used in
* conjunction with the gnc_tree_view_account_get_view_info function
* to modify the filters on an existing account tree.
*
* @param account_view A pointer to an account tree view.
*
* @param avi A pointer to an old style filter block to apply to the
* view..
*/
void gnc_tree_view_account_set_view_info (GncTreeViewAccount *account_view,
AccountViewInfo *avi);
/** Given a pointer to an old style filter block, initialize it to the
* default values for an account tree. The defaults are to show all
* types of accounts, and show only the account name column.
*
* @param avi A pointer to an old style filter block.
*/
void gnc_tree_view_account_init_view_info (AccountViewInfo *avi);
/** This function attaches a filter function to the given account
* tree. This function will be called for each account that the view
* thinks should possibly show. The filter may perform any actions
* necessary on the account to decide whether it should be shown or
* not. (I.E. Check type, placeholder status, etc.) If the filter
* returns TRUE then the account wil be displayed.
*
* @param account_view A pointer to an account tree view.
*
* @param func A filtration function that is called on individual
* elements in the tree. If this function returns TRUE, the account
* will be displayed.
*
* @param data A data block passed into each instance of the function.
*
* @param destroy A function to destroy the data block. This
* function will be called when the filter is destroyed. may be
* NULL.
*/
void gnc_tree_view_account_set_filter (GncTreeViewAccount *account_view,
EggTreeModelFilterVisibleFunc func,
gpointer data,
GtkDestroyNotify destroy);
gboolean gnc_tree_view_account_get_selected_account (GncTreeViewAccount *view,
Account **account);
/** This function forces the account tree filter to be evaluated. It
* may be necessary to call this function if the initial state of the
* view is incorrect. This appears to only be necessary if the
* filter affects one of the top level accounts in gnucash.
*
* @note This calls a function in libegg that is annotated as being
* slow. You have been warned.
*
* @param account_view A pointer to an account tree view.
*/
void gnc_tree_view_account_refilter (GncTreeViewAccount *view);
/** @} */
/** @name Account Tree View Get/Set Functions */
/** @{ */
/** This function returns the account associated with the top level
* pseudo-account. The gnucash engine does not have a single top
* level account (it has a list of top level accounts), but this code
* provides one so that it can be used with all parts of the gnucash
* gui.
*
* @note It only makes sense to call this function when the account
* tree is created such that the "top level account" is visible. At
* the time this was written, only the "New/Edit Account" dialog does
* that.
*
* @param account_view A pointer to an account tree view.
*
* @return The top-level pseudo-account.
*/
Account * gnc_tree_view_account_get_top_level (GncTreeViewAccount *view);
/** This function returns the account associated with the selected
* item in the account tree view.
*
* @note It only makes sense to call this function when the account
* tree is set to select a single item. There is a different
* function to use when the tree supports multiple selections.
*
* @param account_view A pointer to an account tree view.
*
* @return The selected account, or NULL if no account was selected.
*/
Account * gnc_tree_view_account_get_selected_account (GncTreeViewAccount *view);
/** This function selects an account in the account tree view. All
* other accounts will be unselected. In addition, this function
* collapses the entitre tree and then expands only the path to the
* selected account, making the item easy to find. In general, this
* routine only need be called when initially putting up a window
* containing an account tree view widget.
*
* @note It only makes sense to call this function when the account
* tree is set to select a single item. There is a different
* function to use when the tree supports multiple selections.
*
* @param account_view A pointer to an account tree view.
*
* @param account A pointer to the account to select.
*/
void gnc_tree_view_account_set_selected_account (GncTreeViewAccount *view,
Account *account);
/** This function returns a list of the accounts associated with the
* selected items in the account tree view.
*
* @note It only makes sense to call this function when the account
* tree is set to select multiple items. There is a different
* function to use when the tree supports single selection.
*
* @param account_view A pointer to an account tree view.
*
* @return A list of accounts, or NULL if no account was selected.
*/
GList * gnc_tree_view_account_get_selected_accounts (GncTreeViewAccount *view);
/** This function selects a set of accounts in the account tree view.
* All other accounts will be unselected. In addition, this function
* collapses the entitre tree and then expands only the path to the
* selected accounts, making them easy to find. In general, this
* routine only need be called when initially putting up a window
* containing an account tree view widget.
*
* @note It only makes sense to call this function when the account
* tree is set to select a single item. There is a different
* function to use when the tree supports multiple selections.
*
* @note It is the responsibility of the caller to free the returned
* list.
*
* @param account_view A pointer to an account tree view.
*
* @param account A pointer to the account to select.
*
* @param show_last Force the window to scroll to the last account
* selected.
*/
void gnc_tree_view_account_set_selected_accounts (GncTreeViewAccount *view,
GList *account_list,
gboolean show_last);
/** @} */
G_END_DECLS