/* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2023 Maxime DOYEN
*
* This file is part of HomeBank.
*
* HomeBank 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.
*
* HomeBank 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, see .
*/
#include "homebank.h"
#include "list-operation.h"
/****************************************************************************/
/* Debug macros */
/****************************************************************************/
#define MYDEBUG 0
#if MYDEBUG
#define DB(x) (x);
#else
#define DB(x);
#endif
/* our global datas */
extern struct HomeBank *GLOBALS;
extern struct Preferences *PREFS;
//debug
//extern gboolean minor_active;
/* This is not pretty. Of course you can also use a
* separate compare function for each sort ID value */
static gint
list_txn_sort_iter_compare_strings(gchar *s1, gchar *s2)
{
return hb_string_utf8_compare(s1, s2);
}
static gint
list_txn_sort_iter_compare_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
{
gint sortcol = GPOINTER_TO_INT(userdata);
gint retval = 0;
Transaction *ope1, *ope2;
gdouble tmpval = 0;
gtk_tree_model_get(model, a, MODEL_TXN_POINTER, &ope1, -1);
gtk_tree_model_get(model, b, MODEL_TXN_POINTER, &ope2, -1);
switch (sortcol)
{
case LST_DSPOPE_STATUS:
if(!(retval = (ope1->flags & OF_ADDED) - (ope2->flags & OF_ADDED) ) )
{
retval = (ope1->flags & OF_CHANGED) - (ope2->flags & OF_CHANGED);
}
break;
case LST_DSPOPE_DATE:
if(! (retval = ope1->date - ope2->date) )
{
//g_print("sort on balance d1=%d, d2=%d %f %f\n", ope1->date, ope2->date, ope1->balance , ope2->balance);
tmpval = ope1->pos - ope2->pos;
retval = tmpval > 0 ? 1 : -1;
}
//g_print("ret=%d\n", ret);
break;
case LST_DSPOPE_ACCOUNT:
{
Account *a1, *a2;
a1 = da_acc_get(ope1->kacc);
a2 = da_acc_get(ope2->kacc);
retval = list_txn_sort_iter_compare_strings((a1 != NULL) ? a1->name : NULL, (a2 != NULL) ? a2->name : NULL);
}
break;
case LST_DSPOPE_INFO:
if(!(retval = ope1->paymode - ope2->paymode))
{
retval = list_txn_sort_iter_compare_strings(ope1->info, ope2->info);
}
break;
case LST_DSPOPE_PAYEE:
{
//#1721980
gchar *name1 = NULL;
gchar *name2 = NULL;
if( ope1->flags & OF_INTXFER )
{
Account *acc = da_acc_get(ope1->kxferacc);
name1 = (acc != NULL) ? acc->name : NULL;
}
else
{
Payee *pay = da_pay_get(ope1->kpay);
name1 = (pay != NULL) ? pay->name : NULL;
}
if( ope2->flags & OF_INTXFER )
{
Account *acc = da_acc_get(ope2->kxferacc);
name2 = (acc != NULL) ? acc->name : NULL;
}
else
{
Payee *pay = da_pay_get(ope2->kpay);
name2 = (pay != NULL) ? pay->name : NULL;
}
retval = list_txn_sort_iter_compare_strings(name1, name2);
}
break;
case LST_DSPOPE_MEMO:
retval = list_txn_sort_iter_compare_strings(ope1->memo, ope2->memo);
break;
case LST_DSPOPE_CLR:
retval = ope1->status - ope2->status;
break;
case LST_DSPOPE_AMOUNT:
case LST_DSPOPE_EXPENSE:
case LST_DSPOPE_INCOME:
//tmpval = ope1->amount - ope2->amount;
//#1945636 amount sort in base currency
tmpval = hb_amount_base(ope1->amount, ope1->kcur) - hb_amount_base(ope2->amount, ope2->kcur);
retval = tmpval > 0 ? 1 : -1;
break;
case LST_DSPOPE_CATEGORY:
{
Category *c1, *c2;
c1 = da_cat_get(ope1->kcat);
c2 = da_cat_get(ope2->kcat);
if( c1 != NULL && c2 != NULL )
{
retval = list_txn_sort_iter_compare_strings(c1->fullname, c2->fullname);
}
}
break;
case LST_DSPOPE_TAGS:
{
gchar *t1, *t2;
t1 = tags_tostring(ope1->tags);
t2 = tags_tostring(ope2->tags);
retval = list_txn_sort_iter_compare_strings(t1, t2);
g_free(t2);
g_free(t1);
}
break;
case LST_DSPOPE_MATCH:
{
tmpval = ope1->matchrate - ope2->matchrate;
retval = tmpval > 0 ? 1 : -1;
if(!retval)
{
retval = ope1->date - ope2->date;
}
}
break;
default:
g_return_val_if_reached(0);
}
return retval;
}
static void
list_txn_eval_future(GtkCellRenderer *renderer, Transaction *txn)
{
//it seems we are not able to get a valid GdkRGBA
//nor to set/use the alpha
/*GdkRGBA *rgba;
g_object_get(renderer, "foreground-rgba", &rgba, NULL);
//g_print("forcol: %p %f %f %f %f\n", rgba, rgba->red, rgba->green, rgba->blue, rgba->alpha);
//rgba->red = 1.0;
rgba->alpha = 0.5;
g_object_set(renderer, "foreground-set", TRUE,
"foreground-rgba", rgba,
NULL);
gdk_rgba_free(rgba);*/
/*GdkRGBA rgba = {1, 0, 0, 0.15};
GdkRGBA *rgba2;
g_print("forcol: %p %f %f %f %f\n", rgba, rgba.red, rgba.green, rgba.blue, rgba.alpha);
g_object_get(renderer, "background-rgba", &rgba2, NULL);
g_print("forcol: %p %f %f %f %f\n", rgba2, rgba2->red, rgba2->green, rgba2->blue, rgba2->alpha);
//rgba->red = 1.0;
//rgba->alpha = 0.5;
g_object_set(renderer, "background-set", TRUE,
"background-rgba", &rgba,
NULL);
*/
if(txn->date > GLOBALS->today)
{
g_object_set(renderer,
// "scale-set", TRUE,
//5.4.3 scale disabled
// "scale", 0.8,
// "style-set", TRUE,
"style", PANGO_STYLE_OBLIQUE,
NULL);
}
else
{
//5.4.3 scale disabled
g_object_set(renderer, "style-set", FALSE,
//g_object_set(renderer, "scale-set", FALSE, "style-set", FALSE,
NULL);
}
//if( txn->marker == TXN_MARK_DUPDST )
if( txn->dspflags & TXN_DSPFLG_DUPDST )
{
g_object_set(renderer,
// "strikethrough-set", TRUE,
"strikethrough", TRUE,
NULL);
}
else
{
g_object_set(renderer, "strikethrough-set", FALSE,
NULL);
}
//if( txn->marker == TXN_MARK_DUPSRC )
if( txn->dspflags & TXN_DSPFLG_DUPSRC )
{
g_object_set(renderer,
// "weight-set", TRUE,
"weight", PANGO_WEIGHT_BOLD,
NULL);
}
else
{
g_object_set(renderer, "weight-set", FALSE,
NULL);
}
}
static void
list_txn_cell_data_func_status (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
gchar *iconname = NULL;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
switch(GPOINTER_TO_INT(user_data))
{
case 1:
iconname = ( ope->flags & OF_AUTO ) ? ICONNAME_HB_OPE_AUTO : ( ope->flags & OF_ADDED ) ? ICONNAME_HB_OPE_NEW : NULL;
break;
case 2:
iconname = ( ope->flags & OF_CHANGED ) ? ICONNAME_HB_OPE_EDIT : NULL;
break;
case 3:
//iconname = ( ope->marker == TXN_MARK_DUPDST ) ? ICONNAME_HB_OPE_SIMILAR : NULL;
iconname = ( ope->dspflags & TXN_DSPFLG_DUPDST ) ? ICONNAME_HB_OPE_SIMILAR : NULL;
break;
/*case 3:
if( entry->flags & OF_VALID )
iconname = ICONNAME_HB_OPE_VALID;
else
{
if( entry->flags & OF_REMIND )
iconname = ICONNAME_HB_OPE_REMIND;
}
break;*/
}
}
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void
list_txn_cell_data_func_account (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
gchar *text = NULL;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
Account *acc = da_acc_get(ope->kacc);
if( acc )
{
text = acc->name;
}
}
g_object_set(renderer, "text", text, NULL);
}
static void
list_txn_cell_data_func_match_rate (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
gchar buffer[8];
gtk_tree_model_get(model, iter, MODEL_TXN_POINTER, &ope, -1);
g_snprintf(buffer, 8-1, "%d %%", ope->matchrate);
g_object_set(renderer, "text", buffer, NULL);
}
static void
list_txn_cell_data_func_date (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
gchar buffer[256];
GDate date;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split != NULL )
{
g_object_set(renderer, "text", NULL, NULL);
}
else
{
list_txn_eval_future(renderer, ope);
if(ope->date > 0)
{
g_date_set_julian (&date, ope->date);
g_date_strftime (buffer, 256-1, PREFS->date_format, &date);
#if MYDEBUG
gchar *ds = g_strdup_printf ("%s [%02d]", buffer, ope->pos );
g_object_set(renderer, "text", ds, NULL);
g_free(ds);
#else
g_object_set(renderer, "text", buffer, NULL);
#endif
}
else
g_object_set(renderer, "text", "????", NULL);
}
}
static void
list_txn_cell_data_func_info (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
gchar *iconname = NULL;
gchar *text = NULL;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
switch(GPOINTER_TO_INT(user_data))
{
case 1:
if( split == NULL )
{
iconname = (ope->flags & OF_INTXFER) ? ICONNAME_HB_PM_INTXFER : (gchar *)get_paymode_icon_name(ope->paymode);
}
g_object_set(renderer, "icon-name", iconname, NULL);
break;
case 2:
if( split == NULL )
{
list_txn_eval_future(renderer, ope);
text = ope->info;
}
g_object_set(renderer, "text", text, NULL);
break;
}
}
static void
list_txn_cell_data_func_payee (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
gchar *text = NULL;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
list_txn_eval_future(renderer, ope);
//#926782
if(ope->flags & OF_INTXFER)
{
Account *acc = da_acc_get(ope->kxferacc);
//5.6 use acc strings for 5.3 add > < for internal xfer
if( acc )
text = ( ope->flags & OF_INCOME ) ? acc->xferincname : acc->xferexpname;
}
else
{
Payee *pay = da_pay_get(ope->kpay);
text = (pay != NULL) ? pay->name : NULL;
}
}
g_object_set(renderer, "text", text, NULL);
}
static void
list_txn_cell_data_func_tags (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Split *split;
Transaction *ope;
gchar *str;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
list_txn_eval_future(renderer, ope);
if(ope->tags != NULL)
{
str = tags_tostring(ope->tags);
g_object_set(renderer, "text", str, NULL);
g_free(str);
}
else
g_object_set(renderer, "text", "", NULL);
}
else
g_object_set(renderer, "text", NULL, NULL);
}
static void
list_txn_cell_data_func_memo (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
list_txn_eval_future(renderer, ope);
g_object_set(renderer, "text", ope->memo, NULL);
}
else if( split != NULL )
{
g_object_set(renderer, "text", split->memo, NULL);
}
}
static void
list_txn_cell_data_func_account_icon (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
Transaction *ope;
Split *split;
Account *acc;
gchar *iconname = NULL;
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
acc = da_acc_get(ope->kacc);
if( acc && (acc->flags & AF_CLOSED) )
{
iconname = ICONNAME_CHANGES_PREVENT;
}
}
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void
list_txn_cell_data_func_clr (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
struct list_txn_data *data = NULL;
GtkWidget *widget;
Transaction *ope;
Split *split;
gchar *iconname = NULL;
//const gchar *c = "";
widget = gtk_tree_view_column_get_tree_view(col);
if( widget )
data = g_object_get_data(G_OBJECT(widget), "inst_data");
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
switch(GPOINTER_TO_INT(user_data))
{
case 1:
if( (data->lockreconciled == TRUE) && (ope->status == TXN_STATUS_RECONCILED) )
iconname = ICONNAME_CHANGES_PREVENT;
break;
case 2:
{
if( split == NULL )
{
switch(ope->status)
{
/*case TXN_STATUS_CLEARED: c = "c"; break;
case TXN_STATUS_RECONCILED: c = "R"; break;
case TXN_STATUS_REMIND: c = "!"; break;*/
case TXN_STATUS_CLEARED: iconname = ICONNAME_HB_OPE_CLEARED; break;
case TXN_STATUS_RECONCILED: iconname = ICONNAME_HB_OPE_RECONCILED; break;
case TXN_STATUS_REMIND: iconname = ICONNAME_HB_OPE_REMIND; break;
case TXN_STATUS_VOID: iconname = ICONNAME_HB_OPE_VOID; break;
}
}
break;
}
}
//g_object_set(renderer, "text", c, NULL);
g_object_set(renderer, "icon-name", iconname, NULL);
}
static void
list_txn_cell_data_func_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
struct list_txn_data *data = NULL;
GtkWidget *widget;
Transaction *ope;
Split *split;
gint column = GPOINTER_TO_INT(user_data);
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
gint type;
gdouble amount, samount;
gchar *color;
widget = gtk_tree_view_column_get_tree_view(col);
if( widget )
data = g_object_get_data(G_OBJECT(widget), "inst_data");
// get the transaction
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_SPLITAMT, &samount,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
list_txn_eval_future(renderer, ope);
if(column == LST_DSPOPE_BALANCE)
amount = ope->balance;
else
amount = ope->amount;
if(column == LST_DSPOPE_INCOME || column == LST_DSPOPE_EXPENSE)
{
type = (ope->flags & OF_INCOME) ? LST_DSPOPE_INCOME : LST_DSPOPE_EXPENSE;
if( type != column)
{
g_object_set(renderer, "markup", NULL, NULL);
return;
}
}
//for detail display the split part (if any)
if( data && (data->list_type == LIST_TXN_TYPE_DETAIL) )
amount = samount;
}
else if( split != NULL )
{
amount = split->amount;
}
//if(amount != 0)
//{
hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, amount, ope->kcur, GLOBALS->minor);
color = get_normal_color_amount(amount);
//if( (column == LST_DSPOPE_BALANCE) && (ope->overdraft == TRUE) && (PREFS->custom_colors == TRUE) )
if( (column == LST_DSPOPE_BALANCE) && (PREFS->custom_colors == TRUE) && (ope->dspflags & TXN_DSPFLG_OVER) )
{
color = PREFS->color_warn;
}
g_object_set(renderer,
"foreground", color,
"text", buf,
NULL);
//}
//test
if( (column == LST_DSPOPE_BALANCE) && (ope->dspflags & TXN_DSPFLG_LOWBAL))
g_object_set(renderer, "weight", PANGO_WEIGHT_BOLD, NULL);
else
g_object_set(renderer, "weight", PANGO_WEIGHT_NORMAL, NULL);
}
static void
list_txn_cell_data_func_category (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
{
struct list_txn_data *data = NULL;
GtkWidget *widget;
Transaction *ope;
Split *split;
Category *cat;
gchar *color = NULL;
gchar *text = NULL;
widget = gtk_tree_view_column_get_tree_view(col);
if( widget )
data = g_object_get_data(G_OBJECT(widget), "inst_data");
gtk_tree_model_get(model, iter,
MODEL_TXN_SPLITPTR, &split,
MODEL_TXN_POINTER, &ope,
-1);
if( split == NULL )
{
list_txn_eval_future(renderer, ope);
if(ope->flags & OF_SPLIT)
{
text = _("- split -");
}
else
{
if( ope->kcat > 0 )
{
cat = da_cat_get(ope->kcat);
text = (cat != NULL) ? cat->fullname : "";
}
else
{
//#1673902 add a visual marker for uncategorized txn
//#1844881 but not for internal xfer
if( (data->warnnocategory == TRUE) && !(ope->flags & OF_INTXFER) )
{
color = PREFS->color_warn;
text = _("- this needs a category -");
}
}
}
}
else
if( split != NULL )
{
if( split->kcat > 0 )
{
cat = da_cat_get(split->kcat);
text = (cat != NULL) ? cat->fullname : "";
}
}
g_object_set(renderer,
"foreground", color,
"text", text,
NULL);
}
/* = = = = = = = = = = = = = = = = */
//#1967708 encode csv string: add string delimiter ", and doubled if inside
static void list_txn_to_string_csv_text(GString *node, gchar *sep, gchar *text)
{
if( text == NULL )
{
g_string_append (node, "");
}
else
{
//sep into string ?
if( g_strstr_len(text, -1, sep) == NULL )
{
//no: put native text
g_string_append (node, text);
}
else
{
//yes: encode with string delimiter
g_string_append_c (node, '"' );
// " not inside inside ?
if( g_strstr_len(text, -1, "\"") == NULL )
{
//no: put native text
g_string_append (node, text);
}
else
{
//yes: double the text delimiter
GString *dtext = g_string_new(text);
g_string_replace(dtext, "\"", "\"\"", 0);
g_string_append (node, dtext->str);
g_string_free(dtext, TRUE);
}
g_string_append_c (node, '"' );
}
}
}
static void list_txn_to_string_line(GString *node, gchar sep, Transaction *ope, guint32 kcat, gchar *memo, gdouble amount, gboolean hasstatus, gboolean hasacc)
{
Payee *payee;
Category *category;
gchar *tags;
char strbuf[G_ASCII_DTOSTR_BUF_SIZE];
//account
if( hasacc )
{
Account *acc = da_acc_get(ope->kacc);
g_string_append (node, (acc != NULL) ? acc->name : "");
g_string_append_c (node, sep );
}
//date
hb_sprint_date(strbuf, ope->date);
g_string_append (node, strbuf );
g_string_append_c (node, sep );
//paymode
g_snprintf(strbuf, sizeof (strbuf), "%d", ope->paymode);
g_string_append (node, strbuf );
g_string_append_c (node, sep );
//info
//g_string_append (node, (ope->info != NULL) ? ope->info : "" );
list_txn_to_string_csv_text(node, &sep, ope->info);
g_string_append_c (node, sep );
//payee
payee = da_pay_get(ope->kpay);
//g_string_append (node, (payee->name != NULL) ? payee->name : "");
list_txn_to_string_csv_text(node, &sep, payee->name);
g_string_append_c (node, sep );
//memo
//g_string_append (node, (memo != NULL) ? memo : "" );
list_txn_to_string_csv_text(node, &sep, memo);
g_string_append_c (node, sep );
//amount
//#793719
//g_ascii_dtostr (amountbuf, sizeof (amountbuf), ope->amount);
//#1750257 use locale numdigit
//g_ascii_formatd (amountbuf, sizeof (amountbuf), "%.2f", ope->amount);
//TODO: or not we should use the currency fmt here
g_snprintf(strbuf, sizeof (strbuf), "%.2f", amount);
DB( g_print("amount = %f '%s'\n", amount, strbuf) );
g_string_append (node, strbuf );
g_string_append_c (node, sep );
//#1847907 v 5.3.2 add reconcile as c column like in pdf export
//status
if( hasstatus )
{
g_string_append (node, transaction_get_status_string(ope) );
g_string_append_c (node, sep );
}
//category
category = da_cat_get(kcat);
//g_string_append (node, (category->fullname != NULL) ? category->fullname : "" );
list_txn_to_string_csv_text(node, &sep, category->fullname);
g_string_append_c (node, sep );
//tags
tags = tags_tostring(ope->tags);
g_string_append (node, tags != NULL ? tags : "");
g_free(tags);
//eol
g_string_append (node, "\n" );
}
GString *list_txn_to_string(GtkTreeView *treeview, gboolean isclipboard, gboolean hassplit, gboolean hasstatus, gboolean hasacc)
{
struct list_txn_data *data = NULL;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
GString *node;
Transaction *ope;
gdouble amount, samount;
gchar sep;
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
node = g_string_new(NULL);
sep = (isclipboard == TRUE) ? '\t' : ';';
// header line
if( hasacc )
{
g_string_append (node, "account" );
g_string_append_c (node, sep );
}
g_string_append (node, "date" );
g_string_append_c (node, sep );
g_string_append (node, "paymode" );
g_string_append_c (node, sep );
g_string_append (node, "info" );
g_string_append_c (node, sep );
g_string_append (node, "payee" );
g_string_append_c (node, sep );
g_string_append (node, "memo" );
g_string_append_c (node, sep );
g_string_append (node, "amount" );
g_string_append_c (node, sep );
//#1847907 v 5.3.2 add reconcile as c column like in pdf export
if( hasstatus )
{
g_string_append (node, "c" );
g_string_append_c (node, sep );
}
g_string_append (node, "category" );
g_string_append_c (node, sep );
g_string_append (node, "tags");
g_string_append (node, "\n" );
// each txn
model = gtk_tree_view_get_model(treeview);
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
gtk_tree_model_get (model, &iter,
MODEL_TXN_POINTER, &ope,
MODEL_TXN_SPLITAMT, &samount,
-1);
//normal export: if we don't want split or the txn has no split
if( (hassplit == FALSE) )
{
amount = ope->amount;
//for detail display the split part (if any)
if( data && (data->list_type == LIST_TXN_TYPE_DETAIL) )
amount = samount;
list_txn_to_string_line(node, sep, ope, ope->kcat, ope->memo, amount, hasstatus, hasacc);
}
else
{
if( (ope->splits == NULL) )
{
list_txn_to_string_line(node, sep, ope, ope->kcat, ope->memo, ope->amount, hasstatus, hasacc);
}
else
{
guint i, nbsplit = da_splits_length(ope->splits);
for(i=0;isplits, i);
list_txn_to_string_line(node, sep, ope, split->kcat, split->memo, split->amount, hasstatus, hasacc);
}
}
}
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
//DB( g_print("text is:\n%s", node->str) );
return node;
}
gboolean list_txn_column_id_isvisible(GtkTreeView *treeview, gint sort_id)
{
GtkTreeViewColumn *column;
gint n, id;
for(n=0; n < NUM_LST_DSPOPE-1 ; n++ ) // -1 cause account not to be processed
{
column = gtk_tree_view_get_column (treeview, n);
if(column == NULL)
continue;
if( gtk_tree_view_column_get_visible(column) )
{
id = gtk_tree_view_column_get_sort_column_id (column);
if( sort_id == id )
return TRUE;
}
}
return FALSE;
}
static GtkTreeViewColumn *list_txn_get_column(GList *list, gint search_id)
{
GtkTreeViewColumn *column = NULL;
GList *tmp;
gint id;
tmp = g_list_first(list);
while (tmp != NULL)
{
id = gtk_tree_view_column_get_sort_column_id(tmp->data);
if( search_id == id )
{
column = tmp->data;
break;
}
tmp = g_list_next(tmp);
}
return column;
}
guint list_txn_get_quicksearch_column_mask(GtkTreeView *treeview)
{
GtkTreeViewColumn *column;
guint n, mask;
gint id;
mask = 0;
for(n=0; n < NUM_LST_DSPOPE-1 ; n++ ) // -1 cause account not to be processed
{
column = gtk_tree_view_get_column (treeview, n);
if(column == NULL)
continue;
if( gtk_tree_view_column_get_visible(column) )
{
id = gtk_tree_view_column_get_sort_column_id (column);
switch(id)
{
case LST_DSPOPE_MEMO: mask |= FLT_QSEARCH_MEMO; break;
case LST_DSPOPE_INFO: mask |= FLT_QSEARCH_INFO; break;
case LST_DSPOPE_PAYEE: mask |= FLT_QSEARCH_PAYEE; break;
case LST_DSPOPE_CATEGORY: mask |= FLT_QSEARCH_CATEGORY; break;
case LST_DSPOPE_TAGS: mask |= FLT_QSEARCH_TAGS; break;
case LST_DSPOPE_AMOUNT:
case LST_DSPOPE_EXPENSE:
case LST_DSPOPE_INCOME: mask |= FLT_QSEARCH_AMOUNT; break;
}
}
}
return mask;
}
void list_txn_set_save_column_width(GtkTreeView *treeview, gboolean save_column_width)
{
struct list_txn_data *data;
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
if( data )
{
data->save_column_width = save_column_width;
}
}
void list_txn_set_lockreconciled(GtkTreeView *treeview, gboolean lockreconciled)
{
struct list_txn_data *data;
DB( g_print("\n[list_txn] set lock reconciled\n") );
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
data->lockreconciled = lockreconciled;
DB( g_print(" lockrecon=%d\n", data->lockreconciled) );
}
void list_txn_set_warn_nocategory(GtkTreeView *treeview, gboolean warn)
{
struct list_txn_data *data;
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
data->warnnocategory = warn;
}
void list_txn_set_column_acc_visible(GtkTreeView *treeview, gboolean visible)
{
struct list_txn_data *data;
GList *list;
GtkTreeViewColumn *column;
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
data->showall = visible;
list = gtk_tree_view_get_columns( treeview );
//if acc visible: balance must be invisible
column = list_txn_get_column(list, LST_DSPOPE_ACCOUNT);
if(column)
gtk_tree_view_column_set_visible (column, visible);
column = list_txn_get_column(list, LST_DSPOPE_BALANCE);
if(column)
gtk_tree_view_column_set_visible (column, !visible);
g_list_free(list);
}
void list_txn_sort_force(GtkTreeSortable *sortable, gpointer user_data)
{
gint sort_column_id;
GtkSortType order;
DB( g_print("list_txn_sort_force\n") );
gtk_tree_sortable_get_sort_column_id(sortable, &sort_column_id, &order);
DB( g_print(" - id %d order %d\n", sort_column_id, order) );
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortable), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, order);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortable), sort_column_id, order);
}
static void list_txn_get_columns(GtkTreeView *treeview)
{
struct list_txn_data *data;
GtkTreeViewColumn *column;
gint i, col_id;
gint *col_id_ptr;
gint *col_width_ptr;
DB( g_print("\n[list_txn] get columns position/width\n") );
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
//default for LIST_TXN_TYPE_BOOK
col_id_ptr = PREFS->lst_ope_columns;
col_width_ptr = PREFS->lst_ope_col_width;
if( data->list_type == LIST_TXN_TYPE_DETAIL )
{
col_id_ptr = PREFS->lst_det_columns;
col_width_ptr = PREFS->lst_det_col_width;
}
DB( g_print(" nbcol=%d, nbsortid=%d\n", NUM_LST_DSPOPE, gtk_tree_view_get_n_columns (treeview)) );
for(i=0 ; i < NUM_LST_DSPOPE-1 ; i++ ) // -1 'caus: account and blank column
{
column = gtk_tree_view_get_column(treeview, i);
if(column != NULL)
{
col_id = gtk_tree_view_column_get_sort_column_id (column);
if( col_id >= 0 )
{
gboolean visible;
visible = gtk_tree_view_column_get_visible (column);
if( col_id == LST_DSPOPE_BALANCE) //keep initial state of balance
visible = data->tvc_is_visible;
if( visible )
{
col_id_ptr[i] = col_id;
//5.2 moved here to keep old width in case not visible
col_width_ptr[col_id-1] = gtk_tree_view_column_get_width(column);
}
else
col_id_ptr[i] = -col_id;
DB( g_print(" col-%2d => %2d '%s' w=%d\n", i, col_id, gtk_tree_view_column_get_title(column), PREFS->lst_ope_col_width[col_id-1] ) );
}
else //should not occurs
col_id_ptr[i] = 0;
}
}
}
static void list_txn_set_columns(GtkTreeView *treeview, gint *col_id)
{
struct list_txn_data *data;
GtkTreeViewColumn *column, *base;
gboolean visible;
GList *list;
gint i = 0;
gint id;
gint *col_width_ptr;
DB( g_print("\n[list_txn] set columns order/width\n") );
data = g_object_get_data(G_OBJECT(treeview), "inst_data");
#if MYDEBUG == 1
DB( g_print("\n debug column sortid\n") );
for(i=0; i < NUM_LST_DSPOPE-1 ; i++ ) // -1 cause account not to be processed
{
DB( g_print(" pos:%2d sortid:%2d\n", i, col_id[i]) );
}
#endif
DB( g_print("\n apply column prefs\n") );
list = gtk_tree_view_get_columns( treeview );
base = NULL;
for(i=0; i < NUM_LST_DSPOPE-1 ; i++ ) // -1 cause account not to be processed
{
/* hidden are stored as col_id negative */
id = ABS(col_id[i]);
column = list_txn_get_column(list, id);
//DB( g_print(" - get column %d %p\n", id, column) );
if( column != NULL )
{
DB( g_print(" - pos:%2d sortid:%2d (%s)\n", i, col_id[i], gtk_tree_view_column_get_title(column)) );
gtk_tree_view_move_column_after(treeview, column, base);
base = column;
visible = col_id[i] < 0 ? FALSE : TRUE;
/* display exception for detail/import list */
if(data->list_type != LIST_TXN_TYPE_BOOK)
{
if( id == LST_DSPOPE_AMOUNT )
visible = TRUE;
if( id == LST_DSPOPE_STATUS || id == LST_DSPOPE_EXPENSE || id == LST_DSPOPE_INCOME )
visible = FALSE;
}
if(data->list_type == LIST_TXN_TYPE_DETAIL)
{
if( id == LST_DSPOPE_STATUS )
visible = TRUE;
}
if( id == LST_DSPOPE_BALANCE )
{
data->tvc_is_visible = visible;
}
gtk_tree_view_column_set_visible (column, visible);
//5.6 do not apply to allow autosize on XFER dialog
if( (data->list_type != LIST_TXN_TYPE_XFERSOURCE) && (data->list_type != LIST_TXN_TYPE_XFERTARGET) )
{
col_width_ptr = PREFS->lst_ope_col_width;
if( data->list_type == LIST_TXN_TYPE_DETAIL )
col_width_ptr = PREFS->lst_det_col_width;
if( id == LST_DSPOPE_INFO
|| id == LST_DSPOPE_PAYEE
|| id == LST_DSPOPE_MEMO
|| id == LST_DSPOPE_CATEGORY
|| id == LST_DSPOPE_TAGS
|| id == LST_DSPOPE_ACCOUNT )
{
gtk_tree_view_column_set_fixed_width( column, col_width_ptr[id - 1]);
}
}
}
}
g_list_free(list );
}
static void list_txn_sort_column_changed(GtkTreeSortable *sortable, gpointer user_data)
{
struct list_txn_data *data = user_data;
gint id;
GtkSortType order;
gboolean showBalance;
gtk_tree_sortable_get_sort_column_id(sortable, &id, &order);
DB( g_print("list_txn_columns_changed %d %d\n", id, order) );
//here save the transaction list columnid and sort order
PREFS->lst_ope_sort_id = id;
PREFS->lst_ope_sort_order = order;
//manage visibility of balance column
//showBalance = (id == LST_DSPOPE_DATE && order == GTK_SORT_ASCENDING) ? data->tvc_is_visible : FALSE;
showBalance = (id == LST_DSPOPE_DATE) ? data->tvc_is_visible : FALSE;
if(data->showall == TRUE) showBalance = FALSE;
gtk_tree_view_column_set_visible (data->tvc_balance, showBalance);
}
static void
list_txn_column_popup_menuitem_on_activate (GtkCheckMenuItem *checkmenuitem, gpointer user_data)
{
GtkTreeViewColumn *column = user_data;
DB( g_print("toggled\n") );
gtk_tree_view_column_set_visible(column, gtk_check_menu_item_get_active(checkmenuitem) );
}
//beta
static void
list_txn_popmenu_destroy(GtkTreeView *treeview, gpointer user_data)
{
DB( g_print ("\n[list_txn] menu destroy\n") );
}
static gboolean
list_txn_column_popup_callback ( GtkWidget *button,
GdkEventButton *ev,
gpointer user_data )
{
struct list_txn_data *data = user_data;
GtkWidget *menu, *menuitem;
GtkTreeViewColumn *column;
gint i, col_id;
if (ev->type == GDK_BUTTON_PRESS && ev->button == 3)
{
DB( g_print("should popup\n") );
menu = gtk_menu_new ();
//beta
g_signal_connect (menu, "destroy", G_CALLBACK (list_txn_popmenu_destroy), NULL);
//note: deactive this disable any menuitem action
g_signal_connect (menu, "selection-done", G_CALLBACK (gtk_widget_destroy), NULL);
for(i=0 ; i < NUM_LST_DSPOPE-1 ; i++ ) // -1 'caus: account and blank column
{
column = gtk_tree_view_get_column(GTK_TREE_VIEW(data->treeview), i);
if( column != NULL )
{
col_id = gtk_tree_view_column_get_sort_column_id (column);
if( (col_id == -1)
|| (col_id == LST_DSPOPE_STATUS)
|| (col_id == LST_DSPOPE_ACCOUNT)
|| (col_id == LST_DSPOPE_DATE)
|| (col_id == LST_DSPOPE_BALANCE)
)
continue;
//if( (data->tvc_is_visible == FALSE) && (col_id == LST_DSPOPE_BALANCE) )
// continue;
if( (data->list_type == LIST_TXN_TYPE_DETAIL) &&
( (col_id == LST_DSPOPE_AMOUNT)
|| (col_id == LST_DSPOPE_EXPENSE)
|| (col_id == LST_DSPOPE_INCOME)
)
)
continue;
menuitem = gtk_check_menu_item_new_with_label ( gtk_tree_view_column_get_title (column) );
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), gtk_tree_view_column_get_visible (column) );
gtk_widget_show (menuitem);
g_signal_connect (menuitem, "activate",
G_CALLBACK (list_txn_column_popup_menuitem_on_activate), column);
}
}
gtk_menu_attach_to_widget (GTK_MENU (menu), button, NULL);
#if( (GTK_MAJOR_VERSION == 3) && (GTK_MINOR_VERSION >= 22) )
gtk_menu_popup_at_pointer(GTK_MENU (menu), NULL);
#else
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, ev->button, ev->time);
#endif
}
return FALSE;
}
static GtkTreeViewColumn *
list_txn_column_amount_create(gint list_type, gchar *title, gint sortcolumnid, GtkTreeCellDataFunc func)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "xalign", 1.0, NULL);
column = gtk_tree_view_column_new_with_attributes(title, renderer, NULL);
gtk_tree_view_column_set_alignment (column, 0.5);
//gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_column_set_sort_column_id (column, sortcolumnid);
if(list_type == LIST_TXN_TYPE_BOOK)
{
gtk_tree_view_column_set_reorderable(column, TRUE);
}
gtk_tree_view_column_set_cell_data_func(column, renderer, func, GINT_TO_POINTER(sortcolumnid), NULL);
return column;
}
static GtkTreeViewColumn *
list_txn_column_text_create(gint list_type, gchar *title, gint sortcolumnid, GtkTreeCellDataFunc func, gpointer user_data)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, title);
//#1861337 add icon for closed account
if( sortcolumnid == LST_DSPOPE_ACCOUNT )
{
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_account_icon, NULL, NULL);
}
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
gtk_tree_view_column_pack_start(column, renderer, FALSE);
gtk_tree_view_column_set_cell_data_func(column, renderer, func, user_data, NULL);
gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_column_set_sort_column_id (column, sortcolumnid);
gtk_tree_view_column_set_min_width (column, HB_MINWIDTH_COLUMN);
if(list_type == LIST_TXN_TYPE_BOOK || list_type == LIST_TXN_TYPE_DETAIL)
{
if(list_type == LIST_TXN_TYPE_BOOK)
gtk_tree_view_column_set_reorderable(column, TRUE);
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
}
return column;
}
static GtkTreeViewColumn *
list_txn_column_info_create(gint list_type)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Info"));
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start(column, renderer, FALSE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_info, GINT_TO_POINTER(1), NULL);
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_END,
"ellipsize-set", TRUE,
//taken from nemo, not exactly a resize to content, but good compromise
"width-chars", 40,
NULL);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_info, GINT_TO_POINTER(2), NULL);
gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_column_set_sort_column_id (column, LST_DSPOPE_INFO);
gtk_tree_view_column_set_min_width (column, HB_MINWIDTH_COLUMN);
if(list_type == LIST_TXN_TYPE_BOOK || list_type == LIST_TXN_TYPE_DETAIL)
{
if(list_type == LIST_TXN_TYPE_BOOK)
gtk_tree_view_column_set_reorderable(column, TRUE);
gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
}
return column;
}
static void list_txn_destroy( GtkWidget *widget, gpointer user_data )
{
struct list_txn_data *data;
data = g_object_get_data(G_OBJECT(widget), "inst_data");
DB( g_print ("\n[list_transaction] destroy event occurred\n") );
if( data->save_column_width )
{
list_txn_get_columns(GTK_TREE_VIEW(data->treeview));
}
DB( g_print(" - view=%p, inst_data=%p\n", widget, data) );
g_free(data);
}
Transaction *list_txn_get_surround_transaction(GtkTreeView *treeview, Transaction **prev, Transaction **next)
{
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeIter *previter, *nextiter;
GList *list;
Transaction *ope;
ope = NULL;
model = gtk_tree_view_get_model(treeview);
list = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(treeview), &model);
if(list != NULL)
{
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &ope, -1);
if(prev != NULL)
{
previter = gtk_tree_iter_copy(&iter);
if( gtk_tree_model_iter_previous(model, previter) )
{
gtk_tree_model_get(model, previter, MODEL_TXN_POINTER, prev, -1);
}
gtk_tree_iter_free(previter);
}
if(next != NULL)
{
nextiter = gtk_tree_iter_copy(&iter);
if( gtk_tree_model_iter_next(model, nextiter) )
{
gtk_tree_model_get(model, nextiter, MODEL_TXN_POINTER, next, -1);
}
gtk_tree_iter_free(nextiter);
}
}
g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
g_list_free(list);
return ope;
}
Transaction *list_txn_get_active_transaction(GtkTreeView *treeview)
{
GtkTreeModel *model;
GList *list;
Transaction *ope;
ope = NULL;
model = gtk_tree_view_get_model(treeview);
list = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(treeview), &model);
if(list != NULL)
{
GtkTreeIter iter;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, MODEL_TXN_POINTER, &ope, -1);
}
g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
g_list_free(list);
return ope;
}
/*
** create our transaction list
** 1 line: Status, Date, Info, Payee, Category, Tags, CLR, (Amount), Expense, Income, Balance, Memo, (Account)
**
*/
GtkWidget *create_list_transaction(gint list_type, gboolean *pref_columns)
{
struct list_txn_data *data;
GtkTreeStore *store;
GtkWidget *treeview;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column, *col_acc = NULL, *col_status = NULL, *col_match = NULL;
data = g_malloc0(sizeof(struct list_txn_data));
if(!data) return NULL;
data->list_type = list_type;
data->warnnocategory = FALSE;
data->save_column_width = FALSE;
/* create list store */
store = gtk_tree_store_new(
3, G_TYPE_POINTER, // MODEL_TXN_POINTER
G_TYPE_DOUBLE, // MODEL_TXN_SPLITAMT amount part of split for detail only
G_TYPE_POINTER // MODEL_TXN_SPLITPTR
);
//treeview
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
data->treeview = treeview;
g_object_unref(store);
//store our window private data
g_object_set_data(G_OBJECT(treeview), "inst_data", (gpointer)data);
DB( g_print(" - treeview=%p, inst_data=%p\n", treeview, data) );
// connect our dispose function
g_signal_connect (treeview, "destroy", G_CALLBACK (list_txn_destroy), (gpointer)data);
gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (treeview), PREFS->grid_lines);
//gtk_tree_view_set_search_column (GTK_TREE_VIEW (treeview),
// COLUMN_DESCRIPTION);
if(list_type == LIST_TXN_TYPE_BOOK)
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), GTK_SELECTION_MULTIPLE);
/* column 1: Changes */
column = gtk_tree_view_column_new();
//gtk_tree_view_column_set_title(column, _("Status"));
col_status = column;
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_status, GINT_TO_POINTER(1), NULL);
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_status, GINT_TO_POINTER(2), NULL);
renderer = gtk_cell_renderer_pixbuf_new ();
//gtk_cell_renderer_set_fixed_size(renderer, GLOBALS->lst_pixbuf_maxwidth, -1);
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_status, GINT_TO_POINTER(3), NULL);
gtk_tree_view_column_set_sort_column_id (column, LST_DSPOPE_STATUS);
//gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//add system icon to 1st column
GtkWidget *img = gtk_image_new_from_icon_name (ICONNAME_EMBLEM_SYSTEM, GTK_ICON_SIZE_BUTTON);
gtk_widget_show(img);
gtk_tree_view_column_set_widget(column, img);
if( list_type == LIST_TXN_TYPE_BOOK || list_type == LIST_TXN_TYPE_DETAIL )
g_signal_connect ( G_OBJECT (gtk_tree_view_column_get_button (column)),
"button-press-event",
G_CALLBACK ( list_txn_column_popup_callback ),
data );
//5.2 Account is always created but not visible for BOOK
column = list_txn_column_text_create(list_type, _("Account"), LST_DSPOPE_ACCOUNT, list_txn_cell_data_func_account, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
col_acc = column;
//5.5 Match Rate
if(list_type == LIST_TXN_TYPE_XFERTARGET)
{
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Match"));
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
g_object_set(renderer, "xalign", 0.5, NULL);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_match_rate, NULL, NULL);
gtk_tree_view_column_set_sort_column_id (column, LST_DSPOPE_MATCH);
//gtk_tree_view_column_set_resizable(column, TRUE);
col_match = column;
gtk_tree_view_column_set_clickable(column, FALSE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
}
/* column 2: Date */
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Date"));
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
g_object_set(renderer, "xalign", 1.0, NULL);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_date, NULL, NULL);
gtk_tree_view_column_set_sort_column_id (column, LST_DSPOPE_DATE);
//gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = list_txn_column_info_create(list_type);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = list_txn_column_text_create(list_type, _("Payee"), LST_DSPOPE_PAYEE, list_txn_cell_data_func_payee, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = list_txn_column_text_create(list_type, _("Memo"), LST_DSPOPE_MEMO, list_txn_cell_data_func_memo, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* column status CLR */
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, _("Status"));
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_clr, GINT_TO_POINTER(1), NULL);
renderer = gtk_cell_renderer_pixbuf_new();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_set_cell_data_func(column, renderer, list_txn_cell_data_func_clr, GINT_TO_POINTER(2), NULL);
gtk_tree_view_column_set_reorderable(column, TRUE);
gtk_tree_view_column_set_sort_column_id (column, LST_DSPOPE_CLR);
//gtk_tree_view_column_set_sort_indicator (column, FALSE);
//gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_column_set_alignment (column, 0.5);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = list_txn_column_amount_create(list_type, _("Amount"), LST_DSPOPE_AMOUNT, list_txn_cell_data_func_amount);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = list_txn_column_amount_create(list_type, _("Expense"), LST_DSPOPE_EXPENSE, list_txn_cell_data_func_amount);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = list_txn_column_amount_create(list_type, _("Income"), LST_DSPOPE_INCOME, list_txn_cell_data_func_amount);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
column = list_txn_column_text_create(list_type, _("Category"), LST_DSPOPE_CATEGORY, list_txn_cell_data_func_category, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
//set expander column for LIST_TXN_TYPE_DETAIL
if(list_type == LIST_TXN_TYPE_DETAIL)
gtk_tree_view_set_expander_column(GTK_TREE_VIEW(treeview), column);
column = list_txn_column_text_create(list_type, _("Tags"), LST_DSPOPE_TAGS, list_txn_cell_data_func_tags, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
if(list_type == LIST_TXN_TYPE_BOOK)
{
column = list_txn_column_amount_create(list_type, _("Balance"), LST_DSPOPE_BALANCE, list_txn_cell_data_func_amount);
data->tvc_balance = column;
gtk_tree_view_column_set_clickable(column, FALSE);
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
}
/* column 9: empty */
column = gtk_tree_view_column_new();
gtk_tree_view_append_column (GTK_TREE_VIEW(treeview), column);
/* sort */
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_STATUS , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_STATUS), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_DATE , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_DATE), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_INFO , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_INFO), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_PAYEE , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_PAYEE), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_MEMO , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_MEMO), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_AMOUNT , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_AMOUNT), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_EXPENSE , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_EXPENSE), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_INCOME , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_INCOME), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_CATEGORY, list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_CATEGORY), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_TAGS , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_TAGS), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_CLR , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_CLR), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_ACCOUNT , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_ACCOUNT), NULL);
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_DSPOPE_MATCH , list_txn_sort_iter_compare_func, GINT_TO_POINTER(LST_DSPOPE_MATCH), NULL);
/* apply user preference for columns */
list_txn_set_columns(GTK_TREE_VIEW(treeview), pref_columns);
/* force account column for detail treeview */
gtk_tree_view_move_column_after(GTK_TREE_VIEW(treeview), col_acc, col_status);
/* move match column */
if(list_type == LIST_TXN_TYPE_XFERTARGET)
{
gtk_tree_view_move_column_after(GTK_TREE_VIEW(treeview), col_match, col_status);
}
/* by default book don't display acc column, except showall */
//#1821850 detail account column visible
gboolean visible = (list_type == LIST_TXN_TYPE_BOOK) ? FALSE: TRUE;
gtk_tree_view_column_set_visible (col_acc, visible);
/* set initial sort order */
DB( g_print("set sort to %d %d\n", PREFS->lst_ope_sort_id, PREFS->lst_ope_sort_order) );
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), PREFS->lst_ope_sort_id, PREFS->lst_ope_sort_order);
/* signals */
if(list_type == LIST_TXN_TYPE_BOOK)
g_signal_connect (GTK_TREE_SORTABLE(store), "sort-column-changed", G_CALLBACK (list_txn_sort_column_changed), data);
return(treeview);
}