/* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2014 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 "dsp_account.h"
#include "dsp_mainwindow.h"
#include "list_operation.h"
#include "ui-widgets.h"
#include "ui-filter.h"
#include "ui-transaction.h"
#include "gtk-dateentry.h"
#include "ui-payee.h"
#include "ui-category.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
#define UI 1
//extern gchar *CYA_FLT_SELECT[];
extern gchar *CYA_FLT_TYPE[];
extern gchar *CYA_FLT_STATUS[];
static void register_panel_collect_filtered_txn(GtkWidget *view);
static void register_panel_listview_populate(GtkWidget *view);
static void register_panel_action(GtkWidget *widget, gpointer user_data);
static void register_panel_update(GtkWidget *widget, gpointer user_data);
static void register_panel_export_csv(GtkWidget *widget, gpointer user_data);
static void register_panel_make_archive(GtkWidget *widget, gpointer user_data);
static void reconcile_selected_foreach_func (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer userdata);
Transaction *get_active_transaction(GtkTreeView *treeview);
static void register_panel_selection(GtkTreeSelection *treeselection, gpointer user_data);
static void register_panel_onRowActivated (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer userdata);
/* account action functions -------------------- */
static void register_panel_action_editfilter(GtkAction *action, gpointer user_data)
{
struct register_panel_data *data = user_data;
register_panel_action(data->window, GINT_TO_POINTER(ACTION_ACCOUNT_FILTER));
}
static void register_panel_action_add(GtkAction *action, gpointer user_data)
{
struct register_panel_data *data = user_data;
register_panel_action(data->window, GINT_TO_POINTER(ACTION_ACCOUNT_ADD));
}
static void register_panel_action_inherit(GtkAction *action, gpointer user_data)
{
struct register_panel_data *data = user_data;
register_panel_action(data->window, GINT_TO_POINTER(ACTION_ACCOUNT_INHERIT));
}
static void register_panel_action_edit(GtkAction *action, gpointer user_data)
{
struct register_panel_data *data = user_data;
register_panel_action(data->window, GINT_TO_POINTER(ACTION_ACCOUNT_EDIT));
}
static void register_panel_action_reconcile(GtkAction *action, gpointer user_data)
{
struct register_panel_data *data = user_data;
register_panel_action(data->window, GINT_TO_POINTER(ACTION_ACCOUNT_RECONCILE));
}
static void register_panel_action_remove(GtkAction *action, gpointer user_data)
{
struct register_panel_data *data = user_data;
register_panel_action(data->window, GINT_TO_POINTER(ACTION_ACCOUNT_REMOVE));
}
static void register_panel_action_createtemplate(GtkAction *action, gpointer user_data)
{
struct register_panel_data *data = user_data;
register_panel_make_archive(data->window, NULL);
}
static void register_panel_action_exportcsv(GtkAction *action, gpointer user_data)
{
struct register_panel_data *data = user_data;
register_panel_export_csv(data->window, NULL);
}
static void register_panel_action_exportqif(GtkAction *action, gpointer user_data)
{
struct register_panel_data *data = user_data;
gchar *filename;
DB( g_print("(qif) test qif export\n\n") );
if( ui_file_chooser_qif(GTK_WINDOW(data->window), &filename) == TRUE )
{
hb_export_qif_account_single(filename, data->acc);
g_free( filename );
}
}
static void register_panel_action_assign(GtkAction *action, gpointer user_data)
{
struct register_panel_data *data = user_data;
gint count;
gboolean usermode = TRUE;
count = transaction_auto_assign(GLOBALS->ope_list, data->accnum);
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_ope));
GLOBALS->changes_count += count;
//inform the user
if(usermode == TRUE)
{
gchar *txt;
if(count == 0)
txt = _("No transaction changed");
else
txt = _("transaction auto assigned: %d");
ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_INFO,
_("Auto assigment result"),
txt,
count);
}
}
static void register_panel_action_close(GtkAction *action, gpointer user_data)
{
struct register_panel_data *data = user_data;
DB( g_print("action close\n") );
DB( g_print("window %p\n", data->window) );
gtk_widget_destroy (GTK_WIDGET (data->window));
//g_signal_emit_by_name(data->window, "delete-event", NULL, &result);
}
/* these 5 functions are independant from account window */
/* account functions -------------------- */
static void register_panel_export_csv(GtkWidget *widget, gpointer user_data)
{
struct register_panel_data *data;
gchar *filename = NULL;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean valid;
GIOChannel *io;
DB( g_print("\n[account] export csv\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
if( ui_file_chooser_csv(GTK_WINDOW(data->window), GTK_FILE_CHOOSER_ACTION_SAVE, &filename, NULL) == TRUE )
{
DB( g_print(" + filename is %s\n", filename) );
io = g_io_channel_new_file(filename, "w", NULL);
if(io != NULL)
{
//title line
g_io_channel_write_chars(io, "date;paymode;info;payee;wording;amount;category;tags\n", -1, NULL, NULL);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
while (valid)
{
Transaction *ope;
gchar *outstr;
GDate *date;
gchar datebuf[256];
gchar *info, *payeename, *categoryname;
Payee *payee;
Category *category;
gchar *tags;
char amountbuf[G_ASCII_DTOSTR_BUF_SIZE];
gtk_tree_model_get (model, &iter,
LST_DSPOPE_DATAS, &ope,
-1);
//date
date = g_date_new_julian (ope->date);
if( PREFS->dtex_datefmt == PRF_DATEFMT_MDY )
{
g_sprintf(datebuf, "%02d/%02d/%04d",
g_date_get_month(date),
g_date_get_day(date),
g_date_get_year(date)
);
}
else
{
g_sprintf(datebuf, "%02d/%02d/%04d",
g_date_get_day(date),
g_date_get_month(date),
g_date_get_year(date)
);
}
g_date_free(date);
info = ope->info;
if(info == NULL) info = "";
payee = da_pay_get(ope->kpay);
payeename = (payee->name == NULL) ? "" : payee->name;
category = da_cat_get(ope->kcat);
categoryname = (category->name == NULL) ? "" : da_cat_get_fullname(category);
tags = transaction_tags_tostring(ope);
//#793719
//g_ascii_dtostr (amountbuf, sizeof (amountbuf), ope->amount);
g_ascii_formatd (amountbuf, sizeof (amountbuf), "%.2f", ope->amount);
DB( g_print("amount = %f '%s'\n", ope->amount, amountbuf) );
outstr = g_strdup_printf("%s;%d;%s;%s;%s;%s;%s;%s\n",
datebuf,
ope->paymode,
info,
payeename,
ope->wording,
amountbuf,
categoryname,
tags != NULL ? tags : ""
);
DB( g_print("%s", outstr) );
g_io_channel_write_chars(io, outstr, -1, NULL, NULL);
g_free(outstr);
g_free(tags);
valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
}
g_io_channel_unref (io);
}
g_free( filename );
}
}
/*
** make an archive with the active transaction
*/
static void register_panel_make_archive(GtkWidget *widget, gpointer user_data)
{
struct register_panel_data *data;
DB( g_print("\n[account] make archive\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
GtkWidget *p_dialog = NULL;
GtkTreeModel *model;
GList *selection, *list;
gint result, count;
count = gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope)));
if( count > 0 )
{
p_dialog = gtk_message_dialog_new
(
NULL,
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_YES_NO,
_("Do you want to create a template with\neach of the selected transaction ?")
);
/*
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
_("%d archives will be created"),
GLOBALS->changes_count
);
*/
result = gtk_dialog_run( GTK_DIALOG( p_dialog ) );
gtk_widget_destroy( p_dialog );
if(result == GTK_RESPONSE_YES)
{
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope)), &model);
list = g_list_first(selection);
while(list != NULL)
{
Archive *item;
Transaction *ope;
GtkTreeIter iter;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, LST_DSPOPE_DATAS, &ope, -1);
DB( g_print(" create archive %s %.2f\n", ope->wording, ope->amount) );
item = da_archive_malloc();
//fill it
item->amount = ope->amount;
item->kacc = ope->kacc;
item->kxferacc = ope->kxferacc;
item->paymode = ope->paymode;
item->flags = ope->flags & (OF_INCOME);
item->kpay = ope->kpay;
item->kcat = ope->kcat;
if(ope->wording != NULL)
item->wording = g_strdup(ope->wording);
else
item->wording = g_strdup(_("(new archive)"));
GLOBALS->arc_list = g_list_append(GLOBALS->arc_list, item);
GLOBALS->changes_count++;
list = g_list_next(list);
}
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
}
}
}
static void register_panel_cb_filter_daterange(GtkWidget *widget, gpointer user_data)
{
struct register_panel_data *data;
gint range;
DB( g_print("\n[account] filter_daterange\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
range = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_range));
if(range != FLT_RANGE_OTHER)
{
filter_preset_daterange_set(data->filter, range);
register_panel_collect_filtered_txn(data->LV_ope);
register_panel_listview_populate(data->LV_ope);
}
else
{
if(ui_flt_manage_dialog_new(data->filter, FALSE) != GTK_RESPONSE_REJECT)
{
register_panel_collect_filtered_txn(data->LV_ope);
register_panel_listview_populate(data->LV_ope);
register_panel_update(data->LV_ope, GINT_TO_POINTER(UF_SENSITIVE+UF_BALANCE));
}
}
}
static void register_panel_cb_filter_type(GtkWidget *widget, gpointer user_data)
{
struct register_panel_data *data;
gint type;
DB( g_print("\n[account] filter_type\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
type = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_type));
filter_preset_type_set(data->filter, type);
register_panel_collect_filtered_txn(data->LV_ope);
register_panel_listview_populate(data->LV_ope);
}
static void register_panel_cb_filter_status(GtkWidget *widget, gpointer user_data)
{
struct register_panel_data *data;
gint status;
DB( g_print("\n[account] filter_status\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
status = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_status));
filter_preset_status_set(data->filter, status);
register_panel_collect_filtered_txn(data->LV_ope);
register_panel_listview_populate(data->LV_ope);
}
static void register_panel_cb_filter_reset(GtkWidget *widget, gpointer user_data)
{
struct register_panel_data *data;
DB( g_print("\n[account] filter_reset\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
filter_default_all_set(data->filter);
filter_preset_daterange_set (data->filter, PREFS->date_range_txn);
if(PREFS->hidereconciled)
filter_preset_status_set (data->filter, 1);
register_panel_collect_filtered_txn(data->LV_ope);
register_panel_listview_populate(data->LV_ope);
g_signal_handler_block(data->CY_range, data->handler_id[HID_RANGE]);
g_signal_handler_block(data->CY_type, data->handler_id[HID_TYPE]);
g_signal_handler_block(data->CY_status, data->handler_id[HID_STATUS]);
gtk_combo_box_set_active(GTK_COMBO_BOX(data->CY_range), data->filter->range);
gtk_combo_box_set_active(GTK_COMBO_BOX(data->CY_type), data->filter->type);
gtk_combo_box_set_active(GTK_COMBO_BOX(data->CY_status), data->filter->status);
g_signal_handler_unblock(data->CY_status, data->handler_id[HID_STATUS]);
g_signal_handler_unblock(data->CY_type, data->handler_id[HID_TYPE]);
g_signal_handler_unblock(data->CY_range, data->handler_id[HID_RANGE]);
}
static void register_panel_balance_refresh(GtkWidget *view)
{
struct register_panel_data *data;
GList *list;
gdouble balance;
GtkTreeModel *model;
guint32 ldate = 0;
gushort lpos = 1;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(view, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[account] balance refresh\n") );
balance = data->acc->initial;
//#1270687: sort if date changed
if(data->do_sort)
{
DB( g_print(" - complete txn sort\n") );
GLOBALS->ope_list = da_transaction_sort(GLOBALS->ope_list);
data->do_sort = FALSE;
}
list = g_list_first(GLOBALS->ope_list);
while (list != NULL)
{
Transaction *ope;
ope = list->data;
if(ope->kacc == data->accnum)
{
//#1267344
if(!(ope->flags & OF_REMIND))
balance += ope->amount;
ope->balance = balance;
}
if(ope->date == ldate)
{
ope->pos = ++lpos;
}
else
{
ope->pos = lpos = 1;
}
ldate = ope->date;
list = g_list_next(list);
}
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
list_transaction_sort_force(GTK_TREE_SORTABLE(model), NULL);
}
static void register_panel_collect_filtered_txn(GtkWidget *view)
{
struct register_panel_data *data;
guint i;
GList *list;
DB( g_print("\n[register_panel] collect_filtered_txn\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(view, GTK_TYPE_WINDOW)), "inst_data");
if(data->gpatxn != NULL)
g_ptr_array_free (data->gpatxn, TRUE);
#if MYDEBUG == 1
guint nbtxn = g_list_length (GLOBALS->ope_list);
g_print(" - nb txn %d\n", nbtxn);
#endif
data->gpatxn = g_ptr_array_sized_new(64);
//data->hidden = 0;
list = g_list_first(GLOBALS->ope_list); i=0;
while (list != NULL)
{
Transaction *ope = list->data;
if(ope->kacc == data->accnum)
{
if(filter_test(data->filter, ope) == 1)
{
g_ptr_array_add(data->gpatxn, (gpointer)ope);
}
/*else
{
data->hidden++;
}*/
}
#if MYDEBUG == 1
if( !(i % 1000) ) { g_print(" - progress %d/%d\n", i, nbtxn); }
#endif
i++;
list = g_list_next(list);
}
g_signal_handler_block(data->ST_search, data->handler_id[HID_SEARCH]);
gtk_entry_set_text (GTK_ENTRY(data->ST_search), "");
g_signal_handler_unblock(data->ST_search, data->handler_id[HID_SEARCH]);
}
static void register_panel_listview_populate(GtkWidget *widget)
{
struct register_panel_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
gboolean hastext;
gchar *needle;
gint sort_column_id;
GtkSortType order;
guint i, qs_flag;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
DB( g_print("\n[register_panel] listview_populate\n") );
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
gtk_list_store_clear (GTK_LIST_STORE(model));
// ref model to keep it
g_object_ref(model);
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_ope), NULL);
// perf: if you leave the sort, insert is damned slow
gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE(GTK_LIST_STORE(model)), &sort_column_id, &order);
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(GTK_LIST_STORE(model)), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, PREFS->lst_ope_sort_order);
hastext = gtk_entry_get_text_length (GTK_ENTRY(data->ST_search)) >= 2;
needle = (gchar *)gtk_entry_get_text(GTK_ENTRY(data->ST_search));
//build the mask flag for quick search
qs_flag = 0;
if(hastext)
{
qs_flag = list_transaction_get_quicksearch_column_mask(GTK_TREE_VIEW(data->LV_ope));
}
data->total = 0;
data->totalsum = 0.0;
for(i=0;igpatxn->len;i++)
{
Transaction *txn = g_ptr_array_index(data->gpatxn, i);
gboolean insert = TRUE;
if(hastext)
{
insert = filter_txn_search_match(needle, txn, qs_flag);
}
if(insert)
{
gtk_list_store_append (GTK_LIST_STORE(model), &iter);
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
LST_DSPOPE_DATAS, txn,
-1);
data->totalsum += txn->amount;
data->total++;
}
}
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_ope), model); /* Re-attach model to view */
g_object_unref(model);
// push back the sort id
gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(GTK_LIST_STORE(model)), sort_column_id, order);
/* update info range text */
{
gchar *daterange;
daterange = filter_daterange_text_get(data->filter);
gtk_widget_set_tooltip_markup(GTK_WIDGET(data->CY_range), daterange);
g_free(daterange);
}
register_panel_update(data->LV_ope, GINT_TO_POINTER(UF_SENSITIVE+UF_BALANCE));
}
static void reconcile_selected_foreach_func (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer userdata)
{
Transaction *trn;
gtk_tree_model_get(model, iter, LST_DSPOPE_DATAS, &trn, -1);
account_balances_sub(trn);
trn->flags ^= OF_VALID;
//#1308745 remind flags is exclusive with reconciled
if(trn->flags & OF_VALID)
trn->flags &= ~(OF_REMIND);
trn->flags |= OF_CHANGED;
account_balances_add(trn);
/* #492755 let the chld transfer unchnaged */
}
Transaction *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, LST_DSPOPE_DATAS, &ope, -1);
}
g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
g_list_free(list);
return ope;
}
static void remove_active_transaction(GtkTreeView *treeview)
{
GtkTreeModel *model;
GList *list;
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_list_store_remove(GTK_LIST_STORE(model), &iter);
}
g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
g_list_free(list);
}
static void register_panel_action(GtkWidget *widget, gpointer user_data)
{
struct register_panel_data *data;
gint action = GPOINTER_TO_INT(user_data);
gboolean result;
DB( g_print("\n[account] action\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//data = INST_DATA(widget);
DB( g_print(" - action=%d\n", action) );
switch(action)
{
//add
case ACTION_ACCOUNT_ADD:
//inherit
case ACTION_ACCOUNT_INHERIT:
{
GtkWidget *dialog;
Transaction *src_txn;
gint type = 0;
homebank_app_date_get_julian();
if(action == ACTION_ACCOUNT_ADD)
{
DB( g_print("(transaction) add multiple\n") );
src_txn = da_transaction_malloc();
src_txn->date = GLOBALS->today;
src_txn->kacc = data->accnum;
type = TRANSACTION_EDIT_ADD;
}
else
{
DB( g_print("(transaction) inherit multiple\n") );
src_txn = da_transaction_clone(get_active_transaction(GTK_TREE_VIEW(data->LV_ope)));
type = TRANSACTION_EDIT_INHERIT;
}
dialog = create_deftransaction_window(GTK_WINDOW(data->window), type);
result = GTK_RESPONSE_ADD;
while(result == GTK_RESPONSE_ADD)
{
/* clone source transaction */
data->cur_ope = da_transaction_clone (src_txn);
if( PREFS->heritdate == FALSE ) //fix: 318733 / 1335285
data->cur_ope->date = GLOBALS->today;
deftransaction_set_transaction(dialog, data->cur_ope);
result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ADD || result == GTK_RESPONSE_ACCEPT)
{
deftransaction_get(dialog, NULL);
transaction_add(data->cur_ope, data->LV_ope, data->accnum);
register_panel_update(widget, GINT_TO_POINTER(UF_BALANCE));
data->acc->flags |= AF_ADDED;
GLOBALS->changes_count++;
//store last date
src_txn->date = data->cur_ope->date;
}
da_transaction_free (data->cur_ope);
}
deftransaction_dispose(dialog, NULL);
da_transaction_free (src_txn);
gtk_widget_destroy (dialog);
}
break;
case ACTION_ACCOUNT_EDIT:
{
Transaction *old_txn;
GtkWidget *dialog;
old_txn = get_active_transaction(GTK_TREE_VIEW(data->LV_ope));
if(old_txn)
{
dialog = create_deftransaction_window(GTK_WINDOW(data->window), TRANSACTION_EDIT_MODIFY);
data->cur_ope = da_transaction_clone (old_txn); // to keep old datas, just in case
deftransaction_set_transaction(dialog, data->cur_ope);
result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
deftransaction_get(dialog, NULL);
account_balances_sub(old_txn);
account_balances_add(data->cur_ope);
// different accoutn : remove from the display
if( data->cur_ope->kacc != data->accnum )
{
remove_active_transaction(GTK_TREE_VIEW(data->LV_ope));
}
if( data->cur_ope->paymode == PAYMODE_INTXFER )
{
//nota: if kxfer is 0, the user may have just changed the paymode to xfer
DB( g_print(" - kxfer = %d\n", data->cur_ope->kxfer) );
if(data->cur_ope->kxfer > 0) //1) search a strong linked child
{
Transaction *ltxn;
DB( g_print(" - old_txn: kacc=%d kxferacc=%d\n", old_txn->kacc, old_txn->kxferacc) );
ltxn = transaction_strong_get_child_transfer(old_txn);
if(ltxn != NULL) //should never be the case
{
DB( g_print(" - strong link found, do sync\n") );
transaction_xfer_sync_child(data->cur_ope, ltxn);
}
else
{
DB( g_print(" - no, somethin' went wrong here...\n") );
}
}
else
{
//2) any standard transaction that match ?
transaction_xfer_search_or_add_child(data->cur_ope, data->LV_ope);
}
}
//#1250061 : manage ability to break an internal xfer
if(old_txn->paymode == PAYMODE_INTXFER && data->cur_ope->paymode != PAYMODE_INTXFER)
{
GtkWidget *p_dialog;
DB( g_print(" - should break internal xfer\n") );
p_dialog = gtk_message_dialog_new
(
NULL,
GTK_DIALOG_MODAL,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_YES_NO,
_("Do you want to break the internal transfer ?\n\n"
"Proceeding will delete the target transaction.")
);
result = gtk_dialog_run( GTK_DIALOG( p_dialog ) );
gtk_widget_destroy( p_dialog );
if(result == GTK_RESPONSE_YES)
{
transaction_xfer_delete_child(data->cur_ope);
}
else //force paymode to internal xfer
{
data->cur_ope->paymode = PAYMODE_INTXFER;
}
}
//#1270687: sort if date changed
if(old_txn->date != data->cur_ope->date)
data->do_sort = TRUE;
da_transaction_copy(data->cur_ope, old_txn);
register_panel_update(widget, GINT_TO_POINTER(UF_BALANCE));
data->acc->flags |= AF_CHANGED;
GLOBALS->changes_count++;
}
da_transaction_free (data->cur_ope);
deftransaction_dispose(dialog, NULL);
gtk_widget_destroy (dialog);
}
}
break;
case ACTION_ACCOUNT_REMOVE:
{
GtkWidget *p_dialog = NULL;
GtkTreeModel *model;
GList *selection, *list;
gint result;
//gint count;
DB( g_print(" - remove\n") );
//count = gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope)));
//todo: replace with a call to ui_dialog_msg_question ?
p_dialog = gtk_message_dialog_new
(
NULL,
GTK_DIALOG_MODAL,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_YES_NO,
_("Do you want to delete\neach of the selected transaction ?")
);
/*
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
_("%d transactions will be definitively lost.\n"),
GLOBALS->changes_count
);
*/
result = gtk_dialog_run( GTK_DIALOG( p_dialog ) );
gtk_widget_destroy( p_dialog );
if(result == GTK_RESPONSE_YES)
{
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope)), &model);
g_object_ref(model); /* Make sure the model stays with us after the tree view unrefs it */
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_ope), NULL); /* Detach model from view */
DB( g_print(" remove %d line\n", g_list_length(selection)) );
list = g_list_last(selection);
while(list != NULL)
{
Transaction *entry;
GtkTreeIter iter;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, LST_DSPOPE_DATAS, &entry, -1);
DB( g_print(" delete %s %.2f\n", entry->wording, entry->amount) );
account_balances_sub(entry);
/* v3.4: also remove child transfer */
if( entry->paymode == PAYMODE_INTXFER )
{
transaction_xfer_delete_child( entry );
}
gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
GLOBALS->ope_list = g_list_remove(GLOBALS->ope_list, entry);
da_transaction_free(entry);
GLOBALS->changes_count++;
list = g_list_previous(list);
}
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_ope), model); /* Re-attach model to view */
g_object_unref(model);
register_panel_update(widget, GINT_TO_POINTER(UF_BALANCE));
data->acc->flags |= AF_CHANGED;
}
}
break;
//reconcile
case ACTION_ACCOUNT_RECONCILE:
{
GtkTreeSelection *selection;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope));
gtk_tree_selection_selected_foreach(selection, (GtkTreeSelectionForeachFunc)reconcile_selected_foreach_func, data);
DB( g_print(" - reconcile\n") );
gtk_widget_queue_draw (data->LV_ope);
//gtk_widget_queue_resize (data->LV_acc);
register_panel_update(widget, GINT_TO_POINTER(UF_BALANCE));
data->acc->flags |= AF_CHANGED;
GLOBALS->changes_count++;
}
break;
case ACTION_ACCOUNT_FILTER:
{
if(ui_flt_manage_dialog_new(data->filter, FALSE) != GTK_RESPONSE_REJECT)
{
register_panel_collect_filtered_txn(data->LV_ope);
register_panel_listview_populate(data->LV_ope);
register_panel_update(data->LV_ope, GINT_TO_POINTER(UF_SENSITIVE+UF_BALANCE));
g_signal_handler_block(data->CY_range, data->handler_id[HID_RANGE]);
gtk_combo_box_set_active(GTK_COMBO_BOX(data->CY_range), FLT_RANGE_OTHER);
g_signal_handler_unblock(data->CY_range, data->handler_id[HID_RANGE]);
}
}
break;
}
}
static void register_panel_toggle_minor(GtkWidget *widget, gpointer user_data)
{
struct register_panel_data *data;
DB( g_print("\n[account] toggle\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
register_panel_update(data->LV_ope, GINT_TO_POINTER(UF_BALANCE));
gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_ope));
}
static void register_panel_selection(GtkTreeSelection *treeselection, gpointer user_data)
{
DB( g_print("\n[account] selection changed cb\n") );
register_panel_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(UF_SENSITIVE));
}
static void register_panel_update(GtkWidget *widget, gpointer user_data)
{
struct register_panel_data *data;
GtkTreeSelection *selection;
gint flags = GPOINTER_TO_INT(user_data);
gint count = 0;
DB( g_print("\n[account] update\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
//data = INST_DATA(widget);
GLOBALS->minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
/* set window title */
if(flags & UF_TITLE)
{
DB( printf(" - UF_TITLE\n") );
}
/* update disabled things */
if(flags & UF_SENSITIVE)
{
gboolean sensitive;
DB( printf(" - UF_SENSITIVE\n") );
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope));
count = gtk_tree_selection_count_selected_rows(selection);
DB( printf(" - count = %d\n", count) );
/*
if (active = gtk_tree_selection_get_selected(selection, &model, &iter))
{
gint *indices;
path = gtk_tree_model_get_path(model, &iter);
indices = gtk_tree_path_get_indices(path);
data->accnum = indices[0];
DB( printf(" active is %d, sel=%d\n", indices[0], active) );
}
*/
// multiple: disable inherit, edit
sensitive = (count != 1 ) ? FALSE : TRUE;
gtk_action_set_sensitive(gtk_ui_manager_get_action(data->ui, "/TxnBar/Inherit"), sensitive);
gtk_action_set_sensitive(gtk_ui_manager_get_action(data->ui, "/TxnBar/Edit"), sensitive);
sensitive = (count > 0 ) ? TRUE : FALSE;
// no selection: disable reconcile, remove
gtk_action_set_sensitive(gtk_ui_manager_get_action(data->ui, "/MenuBar/TransactionMenu/Reconcile"), sensitive);
gtk_action_set_sensitive(gtk_ui_manager_get_action(data->ui, "/MenuBar/TransactionMenu/Remove"), sensitive);
gtk_action_set_sensitive(gtk_ui_manager_get_action(data->ui, "/MenuBar/TransactionMenu/Template"), sensitive);
gtk_action_set_sensitive(gtk_ui_manager_get_action(data->ui, "/TxnBar/Remove"), sensitive);
gtk_action_set_sensitive(gtk_ui_manager_get_action(data->ui, "/TxnBar/Reconcile"), sensitive);
gtk_action_set_sensitive(gtk_ui_manager_get_action(data->ui, "/TxnBar/Template"), sensitive);
// multiple: disable inherit, edit
sensitive = (count != 1 ) ? FALSE : TRUE;
gtk_action_set_sensitive(gtk_ui_manager_get_action(data->ui, "/MenuBar/TransactionMenu/Inherit"), sensitive);
gtk_action_set_sensitive(gtk_ui_manager_get_action(data->ui, "/MenuBar/TransactionMenu/Edit"), sensitive);
}
/* update toolbar & list */
if(flags & UF_VISUAL)
{
DB( printf(" - UF_VISUAL\n") );
if(PREFS->toolbar_style == 0)
gtk_toolbar_unset_style(GTK_TOOLBAR(data->TB_bar));
else
gtk_toolbar_set_style(GTK_TOOLBAR(data->TB_bar), PREFS->toolbar_style-1);
//minor ?
if( PREFS->euro_active )
{
gtk_widget_show(data->CM_minor);
}
else
{
gtk_widget_hide(data->CM_minor);
}
}
/* update balances */
if(flags & UF_BALANCE)
{
DB( printf(" - UF_BALANCE\n") );
register_panel_balance_refresh(widget);
/*
hb_label_set_colvaluecurr(GTK_LABEL(data->TX_balance[0]), data->acc->bal_bank, data->acc->kcur);
hb_label_set_colvaluecurr(GTK_LABEL(data->TX_balance[1]), data->acc->bal_today, data->acc->kcur);
hb_label_set_colvaluecurr(GTK_LABEL(data->TX_balance[2]), data->acc->bal_future, data->acc->kcur);
*/
hb_label_set_colvalue(GTK_LABEL(data->TX_balance[0]), data->acc->bal_bank, GLOBALS->minor);
hb_label_set_colvalue(GTK_LABEL(data->TX_balance[1]), data->acc->bal_today, GLOBALS->minor);
hb_label_set_colvalue(GTK_LABEL(data->TX_balance[2]), data->acc->bal_future, GLOBALS->minor);
ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
}
/* update fltinfo */
DB( printf(" - statusbar\n") );
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_ope));
count = gtk_tree_selection_count_selected_rows(selection);
DB( printf(" - nb selected = %d\n", count) );
/* if more than one ope selected, we make a sum to display to the user */
gdouble opeexp = 0.0;
gdouble opeinc = 0.0;
gchar buf1[64];
gchar buf2[64];
gchar buf3[64];
if( count >= 1 )
{
GList *list, *tmplist;
GtkTreeModel *model;
GtkTreeIter iter;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope));
list = gtk_tree_selection_get_selected_rows(selection, &model);
tmplist = g_list_first(list);
while (tmplist != NULL)
{
Transaction *item;
gtk_tree_model_get_iter(model, &iter, tmplist->data);
gtk_tree_model_get(model, &iter, LST_DSPOPE_DATAS, &item, -1);
if( item->flags & OF_INCOME )
opeinc += item->amount;
else
opeexp += item->amount;
DB( printf(" - %s, %.2f\n", item->wording, item->amount ) );
tmplist = g_list_next(tmplist);
}
g_list_free(list);
DB( printf(" %f - %f = %f\n", opeinc, opeexp, opeinc + opeexp) );
/*
hb_strfmon(buf1, 64-1, opeinc, data->acc->kcur);
hb_strfmon(buf2, 64-1, -opeexp, data->acc->kcur);
hb_strfmon(buf3, 64-1, opeinc + opeexp, data->acc->kcur);
*/
mystrfmon(buf1, 64-1, opeinc, GLOBALS->minor);
mystrfmon(buf2, 64-1, -opeexp, GLOBALS->minor);
mystrfmon(buf3, 64-1, opeinc + opeexp, GLOBALS->minor);
}
gchar *msg;
if( count <= 0 )
{
//msg = g_strdup_printf (_("transaction selected: %d, hidden: %d"), count, data->hidden);
mystrfmon(buf3, 64-1, data->totalsum, GLOBALS->minor);
msg = g_strdup_printf(_("%d items (%s)"), data->total, buf3);
}
else
//TRANSLATORS: detail of the 3 %s which are some amount of selected transaction, 1=total 2=income, 3=expense
//msg = g_strdup_printf (_("transaction selected: %d, hidden: %d / %s ( %s - %s)"), count, data->hidden, buf3, buf1, buf2);
msg = g_strdup_printf(_("%d items (%d selected %s)"), data->total, count, buf3);
gtk_label_set_markup(GTK_LABEL(data->TX_selection), msg);
g_free (msg);
}
void register_panel_onRowActivated (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer userdata)
{
struct register_panel_data *data;
GtkTreeModel *model;
GtkTreeIter iter;
gint col_id, count;
GList *selection, *list;
Transaction *ope;
gchar *tagstr, *txt;
gboolean refreshbalance;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
col_id = gtk_tree_view_column_get_sort_column_id (col);
count = gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(treeview));
model = gtk_tree_view_get_model(treeview);
//get transaction double clicked to initiate the widget
gtk_tree_model_get_iter(model, &iter, path);
gtk_tree_model_get(model, &iter, LST_DSPOPE_DATAS, &ope, -1);
DB( g_print ("%d rows been double-clicked on column=%d! ope=%s\n", count, col_id, ope->wording) );
if( count == 1)
{
register_panel_action(GTK_WIDGET(treeview), GINT_TO_POINTER(ACTION_ACCOUNT_EDIT));
}
else
if(col_id >= LST_DSPOPE_DATE && col_id != LST_DSPOPE_BALANCE)
{
GtkWidget *parentwindow, *dialog, *mainbox, *widget1, *widget2, *content;
parentwindow = gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW);
dialog = gtk_dialog_new_with_buttons (NULL,
GTK_WINDOW (parentwindow),
0,
GTK_STOCK_CANCEL,
GTK_RESPONSE_REJECT,
GTK_STOCK_OK,
GTK_RESPONSE_ACCEPT,
NULL);
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
gint w;
w = gtk_tree_view_column_get_width(GTK_TREE_VIEW_COLUMN(col));
gtk_window_set_default_size (GTK_WINDOW(dialog), w + 40, 0);
content = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
mainbox = gtk_hbox_new (FALSE, 0);
gtk_container_set_border_width (GTK_CONTAINER (mainbox), HB_BOX_SPACING);
gtk_box_pack_start (GTK_BOX (content), mainbox, FALSE, FALSE, 0);
widget1 = widget2 = NULL;
switch( col_id )
{
case LST_DSPOPE_DATE:
gtk_window_set_title (GTK_WINDOW (dialog), _("Modify date..."));
widget1 = gtk_dateentry_new();
gtk_dateentry_set_date(GTK_DATE_ENTRY(widget1), (guint)ope->date);
break;
case LST_DSPOPE_INFO:
gtk_window_set_title (GTK_WINDOW (dialog), _("Modify info..."));
widget1 = make_paymode(NULL);
widget2 = make_string(NULL);
gtk_combo_box_set_active(GTK_COMBO_BOX(widget1), ope->paymode);
gtk_entry_set_text(GTK_ENTRY(widget2), ope->info);
break;
case LST_DSPOPE_PAYEE:
gtk_window_set_title (GTK_WINDOW (dialog), _("Modify payee..."));
widget1 = ui_pay_comboboxentry_new(NULL);
ui_pay_comboboxentry_populate(GTK_COMBO_BOX(widget1), GLOBALS->h_pay);
ui_pay_comboboxentry_set_active(GTK_COMBO_BOX(widget1), ope->kpay);
break;
case LST_DSPOPE_WORDING:
gtk_window_set_title (GTK_WINDOW (dialog), _("Modify description..."));
widget1 = make_string(NULL);
gtk_entry_set_text(GTK_ENTRY(widget1), ope->wording);
break;
case LST_DSPOPE_EXPENSE:
case LST_DSPOPE_INCOME:
case LST_DSPOPE_AMOUNT:
gtk_window_set_title (GTK_WINDOW (dialog), _("Modify amount..."));
widget1 = make_amount(NULL);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget1), ope->amount);
break;
case LST_DSPOPE_CATEGORY:
gtk_window_set_title (GTK_WINDOW (dialog), _("Modify category..."));
widget1 = ui_cat_comboboxentry_new(FALSE);
ui_cat_comboboxentry_populate(GTK_COMBO_BOX(widget1), GLOBALS->h_cat);
ui_cat_comboboxentry_set_active(GTK_COMBO_BOX(widget1), ope->kcat);
break;
case LST_DSPOPE_TAGS:
gtk_window_set_title (GTK_WINDOW (dialog), _("Modify tags..."));
widget1 = make_string(NULL);
tagstr = transaction_tags_tostring(ope);
txt = (tagstr != NULL) ? tagstr : "";
gtk_entry_set_text(GTK_ENTRY(widget1), txt);
g_free(tagstr);
break;
}
if(widget1 != NULL) gtk_box_pack_start (GTK_BOX (mainbox), widget1, TRUE, TRUE, 0);
if(widget2 != NULL) gtk_box_pack_start (GTK_BOX (mainbox), widget2, TRUE, TRUE, 0);
gtk_widget_show_all(mainbox);
//wait for the user
gint result = gtk_dialog_run (GTK_DIALOG (dialog));
if(result == GTK_RESPONSE_ACCEPT)
{
selection = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(treeview), &model);
refreshbalance = FALSE;
list = g_list_first(selection);
while(list != NULL)
{
GtkTreeIter iter;
const gchar *txt;
gtk_tree_model_get_iter(model, &iter, list->data);
gtk_tree_model_get(model, &iter, LST_DSPOPE_DATAS, &ope, -1);
DB( g_print(" modifying %s %.2f\n", ope->wording, ope->amount) );
switch( col_id )
{
case LST_DSPOPE_DATE:
ope->date = gtk_dateentry_get_date(GTK_DATE_ENTRY(widget1));
data->do_sort = TRUE;
refreshbalance = TRUE;
break;
case LST_DSPOPE_INFO:
ope->paymode = gtk_combo_box_get_active(GTK_COMBO_BOX(widget1));
txt = gtk_entry_get_text(GTK_ENTRY(widget2));
if (txt && *txt)
{
g_free(ope->info);
ope->info = g_strdup(txt);
}
break;
case LST_DSPOPE_PAYEE:
ope->kpay = ui_pay_comboboxentry_get_key_add_new(GTK_COMBO_BOX(widget1));
DB( g_print(" -> payee: '%d'\n", ope->kpay) );
break;
case LST_DSPOPE_WORDING:
txt = gtk_entry_get_text(GTK_ENTRY(widget1));
if (txt && *txt)
{
g_free(ope->wording);
ope->wording = g_strdup(txt);
}
break;
case LST_DSPOPE_EXPENSE:
case LST_DSPOPE_INCOME:
case LST_DSPOPE_AMOUNT:
ope->flags &= ~(OF_INCOME); //remove flag
ope->amount = gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget1));
if(ope->amount > 0) ope->flags |= OF_INCOME;
refreshbalance = TRUE;
break;
case LST_DSPOPE_CATEGORY:
if(!(ope->flags & OF_SPLIT))
{
ope->kcat = ui_cat_comboboxentry_get_key_add_new(GTK_COMBO_BOX(widget1));
//bad .... ope->category = gtk_combo_box_get_active(GTK_COMBO_BOX(widget1));
DB( g_print(" -> category: '%d'\n", ope->kcat) );
}
break;
case LST_DSPOPE_TAGS:
txt = (gchar *)gtk_entry_get_text(GTK_ENTRY(widget1));
if (txt && *txt)
{
DB( g_print(" -> tags: '%s'\n", txt) );
transaction_tags_parse(ope, txt);
}
break;
}
ope->flags |= OF_CHANGED;
GLOBALS->changes_count++;
list = g_list_next(list);
}
if(refreshbalance)
register_panel_update(GTK_WIDGET(treeview), GINT_TO_POINTER(UF_BALANCE));
g_list_foreach(selection, (GFunc)gtk_tree_path_free, NULL);
g_list_free(selection);
}
// cleanup and destroy
gtk_widget_destroy (dialog);
}
}
/*
static gint listview_context_cb (GtkWidget *widget, GdkEventButton *event, GtkWidget *menu)
{
if (event->button == 3)
{
if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (treeview),
(gint) event->x, (gint) event->y, &path, NULL, NULL, NULL))
{
gtk_tree_view_set_cursor (GTK_TREE_VIEW (treeview), path, NULL, FALSE);
gtk_tree_path_free (path);
}
gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL,
event->button, event->time);
// On indique à l'appelant que l'on a géré cet événement.
return TRUE;
}
// On indique à l'appelant que l'on n'a pas géré cet événement.
return FALSE;
}
*/
/*
** populate the account window
*/
void register_panel_window_init(GtkWidget *widget, gpointer user_data)
{
DB( g_print("\n[account] init window\n") );
DB( g_print(" - sort transactions\n") );
GLOBALS->ope_list = da_transaction_sort(GLOBALS->ope_list);
//DB( g_print(" mindate=%d, maxdate=%d %x\n", data->filter->mindate,data->filter->maxdate) );
DB( g_print(" - call update visual\n") );
register_panel_update(widget, GINT_TO_POINTER(UF_VISUAL));
DB( g_print(" - set range or populate+update sensitive+balance\n") );
register_panel_cb_filter_reset(widget, user_data);
}
/*
**
*/
static gboolean
register_panel_getgeometry(GtkWidget *widget, GdkEventConfigure *event, gpointer user_data)
{
//struct register_panel_data *data = user_data;
struct WinGeometry *wg;
DB( g_print("\n[account] get geometry\n") );
//store position and size
wg = &PREFS->acc_wg;
gtk_window_get_position(GTK_WINDOW(widget), &wg->l, &wg->t);
gtk_window_get_size(GTK_WINDOW(widget), &wg->w, &wg->h);
GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(widget));
GdkWindowState state = gdk_window_get_state(gdk_window);
wg->s = (state & GDK_WINDOW_STATE_MAXIMIZED) ? 1 : 0;
DB( g_print(" window: l=%d, t=%d, w=%d, h=%d s=%d, state=%d\n", wg->l, wg->t, wg->w, wg->h, wg->s, state & GDK_WINDOW_STATE_MAXIMIZED) );
return FALSE;
}
/*
**
*/
static gboolean register_panel_dispose(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
//struct register_panel_data *data = user_data;
DB( g_print("\n[account] delete-event\n") );
return FALSE;
}
/* Another callback */
static gboolean register_panel_destroy( GtkWidget *widget,
gpointer user_data )
{
struct register_panel_data *data;
data = g_object_get_data(G_OBJECT(widget), "inst_data");
DB( g_print ("\n[account] destroy event occurred\n") );
//enable define windows
GLOBALS->define_off--;
/* unset transaction edit mutex */
if(data->acc)
data->acc->window = NULL;
/* free title and filter */
DB( g_print(" user_data=%p to be free\n", user_data) );
g_free(data->wintitle);
if(data->gpatxn != NULL)
g_ptr_array_free (data->gpatxn, TRUE);
da_filter_free(data->filter);
g_free(data);
//our global list has changed, so update the treeview
ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_BALANCE+UF_REFRESHALL));
return FALSE;
}
static void
quick_search_activate_cb (GtkEntry *entry, gpointer user_data)
{
struct register_panel_data *data = user_data;
register_panel_listview_populate (data->LV_ope);
}
static gint quick_search_text_changed_timeout (gpointer user_data)
{
struct register_panel_data *data = user_data;
DB( g_print("quick search timed out !\n") );
register_panel_listview_populate (data->window);
data->timer_tag = 0;
return FALSE;
}
static void
quick_search_text_changed_cb (GtkEntry *entry,
GParamSpec *pspec,
gpointer user_data)
{
struct register_panel_data *data = user_data;
gboolean has_text;
has_text = gtk_entry_get_text_length (entry) > 0;
gtk_entry_set_icon_sensitive (entry,
GTK_ENTRY_ICON_SECONDARY,
has_text);
if(data->timer_tag == 0 )
data->timer_tag = g_timeout_add( DEFAULT_DELAY, quick_search_text_changed_timeout, (gpointer)user_data);
}
static void
quick_search_icon_press_cb (GtkEntry *entry,
gint position,
GdkEventButton *event,
gpointer data)
{
if (position == GTK_ENTRY_ICON_SECONDARY)
gtk_entry_set_text (entry, "");
}
static GtkActionEntry entries[] = {
/* name, stock id, label */
{ "AccountMenu" , NULL, N_("_Account"), NULL, NULL, NULL },
{ "TransactionMenu", NULL, N_("Transacti_on"), NULL, NULL, NULL },
{ "ActionsMenu" , NULL, N_("_Actions"), NULL, NULL, NULL },
{ "ToolsMenu" , NULL, N_("_Tools"), NULL, NULL, NULL },
{ "Close" , GTK_STOCK_CLOSE , N_("_Close") , NULL, N_("Close the current account"), G_CALLBACK (register_panel_action_close) },
/* name, stock id, label, accelerator, tooltip */
{ "Filter" , "hb-filter" , N_("_Filter..."), NULL, N_("Open the list filter"), G_CALLBACK (register_panel_action_editfilter) },
{ "Add" , HB_STOCK_OPE_ADD , N_("_Add..."), NULL, N_("Add a new transaction"), G_CALLBACK (register_panel_action_add) },
{ "Inherit" , HB_STOCK_OPE_HERIT , N_("_Inherit..."), NULL, N_("Inherit from the active transaction"), G_CALLBACK (register_panel_action_inherit) },
{ "Edit" , HB_STOCK_OPE_EDIT , N_("_Edit..."), NULL, N_("Edit the active transaction"), G_CALLBACK (register_panel_action_edit) },
{ "Reconcile" , "hb-ope-valid" , N_("_Reconciled"), "r", N_("Toggle reconciled status of active transactions"), G_CALLBACK (register_panel_action_reconcile) },
{ "Remove" , HB_STOCK_OPE_DELETE , N_("_Remove..."), NULL, N_("Remove the active transactions"), G_CALLBACK (register_panel_action_remove) },
{ "Template" , GTK_STOCK_CONVERT , N_("Create template..."), NULL, N_("Create template"), G_CALLBACK (register_panel_action_createtemplate) },
{ "Assign" , "hb-assign-run" , N_("Auto. Assignments"), NULL, N_("Run auto assignments"), G_CALLBACK (register_panel_action_assign) },
{ "ExportQIF" , "hb-file-export" , N_("Export QIF..."), NULL, N_("Export as QIF"), G_CALLBACK (register_panel_action_exportqif) },
{ "ExportCSV" , "hb-file-export" , N_("Export CSV..."), NULL, N_("Export as CSV"), G_CALLBACK (register_panel_action_exportcsv) },
};
static guint n_entries = G_N_ELEMENTS (entries);
static const gchar *ui_info =
""
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
"";
GtkWidget *register_panel_window_new(guint32 accnum, Account *acc)
{
struct register_panel_data *data;
struct WinGeometry *wg;
GtkWidget *window, *mainbox, *hbox, *hbox2, *align, *sw;
GtkWidget *treeview, *label, *widget;
GtkUIManager *ui;
GtkActionGroup *actions;
GtkAction *action;
GError *error = NULL;
DB( g_print("\n[account] create_register_panel_window\n") );
data = g_malloc0(sizeof(struct register_panel_data));
if(!data) return NULL;
//disable define windows
GLOBALS->define_off++;
ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
/* create window, etc */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
data->window = window;
//debug
data->wintitle = NULL;
data->accnum = accnum;
data->acc = acc;
/* set transaction edit mutex */
if(data->acc)
data->acc->window = GTK_WINDOW(window);
//g_free(data->wintitle);
data->wintitle = g_strdup_printf("%s - HomeBank", data->acc->name);
gtk_window_set_title (GTK_WINDOW (window), data->wintitle);
// connect our dispose function
g_signal_connect (window, "delete_event",
G_CALLBACK (register_panel_dispose), (gpointer)data);
// connect our dispose function
g_signal_connect (window, "destroy",
G_CALLBACK (register_panel_destroy), (gpointer)data);
// connect our dispose function
g_signal_connect (window, "configure-event",
G_CALLBACK (register_panel_getgeometry), (gpointer)data);
//gtk_window_set_icon_from_file(GTK_WINDOW (WI_account), "./pixmaps/.png", NULL);
//store our window private data
g_object_set_data(G_OBJECT(window), "inst_data", (gpointer)data);
DB( g_print(" - new window=%p, inst_data=%p\n", window, data) );
//set the window icon
//homebank_window_set_icon_from_file(GTK_WINDOW (window), "ope_show.svg");
gtk_window_set_icon_name(GTK_WINDOW (window), HB_STOCK_OPE_SHOW );
#if UI == 1
//start test uimanager
actions = gtk_action_group_new ("Account");
//as we use gettext
gtk_action_group_set_translation_domain(actions, GETTEXT_PACKAGE);
DB( g_print(" - add actions: %p user_data: %p\n", actions, data) );
gtk_action_group_add_actions (actions, entries, n_entries, data);
/* set which action should have priority in the toolbar */
action = gtk_action_group_get_action(actions, "Add");
g_object_set(action, "is_important", TRUE, "short_label", _("Add"), NULL);
action = gtk_action_group_get_action(actions, "Inherit");
g_object_set(action, "is_important", TRUE, "short_label", _("Inherit"), NULL);
action = gtk_action_group_get_action(actions, "Edit");
g_object_set(action, "is_important", TRUE, "short_label", _("Edit"), NULL);
action = gtk_action_group_get_action(actions, "Filter");
g_object_set(action, "is_important", TRUE, "short_label", _("Filter"), NULL);
action = gtk_action_group_get_action(actions, "Reconcile");
g_object_set(action, "is_important", TRUE, "short_label", _("Reconcile"), NULL);
ui = gtk_ui_manager_new ();
DB( g_print(" - insert action group:\n") );
gtk_ui_manager_insert_action_group (ui, actions, 0);
GtkAccelGroup *ag = gtk_ui_manager_get_accel_group (ui);
DB( g_print(" - add_accel_group actions=%x, ui=%x, ag=%x\n", (gint)actions, (gint)ui, (gint)ag) );
gtk_window_add_accel_group (GTK_WINDOW (window), ag);
DB( g_print(" - add ui from string:\n") );
if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error))
{
g_message ("building menus failed: %s", error->message);
g_error_free (error);
}
data->ui = ui;
data->actions = actions;
#endif
mainbox = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (window), mainbox);
widget = gtk_ui_manager_get_widget (ui, "/MenuBar");
//data->menu = widget;
gtk_box_pack_start (GTK_BOX (mainbox), widget, FALSE, FALSE, 0);
hbox = gtk_hbox_new (FALSE, 3*HB_BOX_SPACING);
gtk_container_set_border_width (GTK_CONTAINER(hbox), HB_BOX_SPACING);
gtk_box_pack_start (GTK_BOX (mainbox), hbox, FALSE, FALSE, 0);
// account name (+ balance)
align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 0, 4*HB_BOX_SPACING);
gtk_box_pack_start (GTK_BOX (hbox), align, TRUE, TRUE, 0);
label = gtk_label_new(data->acc->name);
gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_LARGE, -1);
gtk_container_add (GTK_CONTAINER (align), label);
/* balances area */
hbox2 = gtk_hbox_new (FALSE, HB_BOX_SPACING);
gtk_box_pack_start (GTK_BOX (hbox), hbox2, FALSE, FALSE, 0);
label = gtk_label_new(NULL);
data->TX_balance[2] = label;
gtk_box_pack_end (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
label = gtk_label_new(_("Future:"));
gtk_box_pack_end (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
label = gtk_label_new(NULL);
data->TX_balance[1] = label;
gtk_box_pack_end (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
label = gtk_label_new(_("Today:"));
gtk_box_pack_end (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
label = gtk_label_new(NULL);
data->TX_balance[0] = label;
gtk_box_pack_end (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
label = gtk_label_new(_("Bank:"));
gtk_box_pack_end (GTK_BOX (hbox2), label, FALSE, FALSE, 0);
//quick search
widget = gtk_entry_new ();
data->ST_search = widget;
gtk_entry_set_icon_from_stock (GTK_ENTRY (widget), GTK_ENTRY_ICON_PRIMARY, GTK_STOCK_FIND);
gtk_entry_set_icon_from_stock (GTK_ENTRY (widget), GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
gtk_widget_set_size_request(widget, 240, -1);
gtk_box_pack_end (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
g_signal_connect (widget, "activate", G_CALLBACK (quick_search_activate_cb), data);
data->handler_id[HID_SEARCH] = g_signal_connect (widget, "notify::text", G_CALLBACK (quick_search_text_changed_cb), data);
g_signal_connect (widget, "icon-press", G_CALLBACK (quick_search_icon_press_cb), data);
// windows interior
hbox = gtk_hbox_new (FALSE, HB_BOX_SPACING);
gtk_container_set_border_width (GTK_CONTAINER(hbox), HB_BOX_SPACING/2);
gtk_box_pack_start (GTK_BOX (mainbox), hbox, FALSE, FALSE, 0);
label = make_label(_("_Range:"), 1.0, 0.5);
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
data->CY_range = make_daterange(label, TRUE);
gtk_box_pack_start (GTK_BOX (hbox), data->CY_range, FALSE, FALSE, 0);
label = make_label(_("_Type:"), 1.0, 0.5);
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
data->CY_type = make_cycle(label, CYA_FLT_TYPE);
gtk_box_pack_start (GTK_BOX (hbox), data->CY_type, FALSE, FALSE, 0);
label = make_label(_("_Status:"), 1.0, 0.5);
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
data->CY_status = make_cycle(label, CYA_FLT_STATUS);
gtk_box_pack_start (GTK_BOX (hbox), data->CY_status, FALSE, FALSE, 0);
widget = gtk_button_new_with_mnemonic (_("Reset _Filter"));
data->BT_reset = widget;
gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
//TRANSLATORS: this is for Euro specific users, a toggle to display in 'Minor' currency
widget = gtk_check_button_new_with_mnemonic (_("_Minor currency"));
data->CM_minor = widget;
gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
label = gtk_label_new(NULL);
data->TX_selection = label;
gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
gtk_box_pack_end (GTK_BOX (hbox), label, TRUE, TRUE, 0);
/*
label = make_label(_("_Month:"), 1.0, 0.5);
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
data->CY_month = make_cycle(label, CYA_SELECT);
gtk_box_pack_start (GTK_BOX (hbox), data->CY_month, FALSE, FALSE, 0);
label = make_label(_("_Year:"), 1.0, 0.5);
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
data->NB_year = make_year(label);
gtk_box_pack_start (GTK_BOX (hbox), data->NB_year, FALSE, FALSE, 0);
*/
//list
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_ETCHED_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
treeview = (GtkWidget *)create_list_transaction(TRN_LIST_TYPE_BOOK, PREFS->lst_ope_columns);
data->LV_ope = treeview;
gtk_container_add (GTK_CONTAINER (sw), treeview);
gtk_box_pack_start (GTK_BOX (mainbox), sw, TRUE, TRUE, 0);
/* toolbars */
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (mainbox), hbox, FALSE, FALSE, 0);
widget = gtk_ui_manager_get_widget (ui, "/TxnBar");
data->TB_bar = widget;
//gtk_widget_set_halign (widget, GTK_ALIGN_START);
gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
widget = gtk_ui_manager_get_widget (ui, "/ToolBar");
data->TB_tools = widget;
gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
#ifdef G_OS_WIN32
if(PREFS->toolbar_style == 0)
{
gtk_toolbar_unset_style(GTK_TOOLBAR(data->TB_bar));
gtk_toolbar_unset_style(GTK_TOOLBAR(data->TB_tools));
}
else
{
gtk_toolbar_set_style(GTK_TOOLBAR(data->TB_bar), PREFS->toolbar_style-1);
gtk_toolbar_set_style(GTK_TOOLBAR(data->TB_tools), PREFS->toolbar_style-1);
}
#endif
//todo: should move this
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_minor),GLOBALS->minor);
g_object_set_data(G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_ope))), "minor", data->CM_minor);
// connect signals
data->handler_id[HID_RANGE] = g_signal_connect (data->CY_range , "changed", G_CALLBACK (register_panel_cb_filter_daterange), NULL);
data->handler_id[HID_TYPE] = g_signal_connect (data->CY_type , "changed", G_CALLBACK (register_panel_cb_filter_type), NULL);
data->handler_id[HID_STATUS] = g_signal_connect (data->CY_status, "changed", G_CALLBACK (register_panel_cb_filter_status), NULL);
g_signal_connect (data->BT_reset , "clicked", G_CALLBACK (register_panel_cb_filter_reset), NULL);
g_signal_connect (data->CM_minor , "toggled", G_CALLBACK (register_panel_toggle_minor), NULL);
//g_signal_connect (GTK_TREE_VIEW(treeview), "cursor-changed", G_CALLBACK (register_panel_update), (gpointer)2);
g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), "changed", G_CALLBACK (register_panel_selection), NULL);
g_signal_connect (GTK_TREE_VIEW(treeview), "row-activated", G_CALLBACK (register_panel_onRowActivated), GINT_TO_POINTER(2));
//todo: test context menu
/*
menu = gtk_menu_new();
menu_items = gtk_ui_manager_get_widget (ui, "/MenuBar/TransactionMenu/Add");
menu_items = gtk_menu_item_new_with_label ("test");
gtk_widget_show(menu_items);
gtk_menu_shell_append (GTK_MENU (menu), menu_items);
//todo: debug test
g_signal_connect (treeview, "button-press-event", G_CALLBACK (listview_context_cb),
// todo: here is not a GtkMenu but GtkImageMenuItem...
menu
//gtk_ui_manager_get_widget (ui, "/MenuBar")
);
*/
//setup, init and show window
wg = &PREFS->acc_wg;
if(wg->s == 0)
{
gtk_window_move(GTK_WINDOW(window), wg->l, wg->t);
gtk_window_resize(GTK_WINDOW(window), wg->w, wg->h);
}
else
gtk_window_maximize(GTK_WINDOW(window));
gtk_widget_show_all (window);
/* make sure splash is up */
while (gtk_events_pending ())
gtk_main_iteration ();
/* setup to moove later */
data->filter = da_filter_malloc();
DB( g_print(" - filter ok %x\n", (gint)data->filter) );
return window;
}