/* HomeBank -- Free, easy, personal accounting for everyone.
* Copyright (C) 1995-2019 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 "hub-account.h"
#include "dsp-mainwindow.h"
#include "list-account.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;
extern HbKvData CYA_ACC_TYPE[];
static void ui_hub_account_expand_all(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_acc));
}
static void ui_hub_account_collapse_all(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
gtk_tree_view_collapse_all(GTK_TREE_VIEW(data->LV_acc));
}
static void ui_hub_account_groups_free(GHashTable *h_group)
{
GHashTableIter grp_iter;
gpointer key, value;
DB( g_print("\n[hub-account] groups free\n") );
g_hash_table_iter_init (&grp_iter, h_group);
while (g_hash_table_iter_next (&grp_iter, &key, &value))
{
PnlAccGrp *group = value;
g_ptr_array_free (group->acclist, TRUE);
g_free(group);
}
g_hash_table_destroy (h_group);
}
static GHashTable *ui_hub_account_groups_get(GtkTreeView *treeview, gint groupby, gboolean showall)
{
GHashTable *h_group;
GList *lacc, *elt;
gchar *groupname;
gint nballoc;
DB( g_print("\n[hub-account] groups get\n") );
nballoc = da_acc_length ();
DB( g_print(" %d accounts\n", nballoc) );
h_group = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
lacc = g_hash_table_get_values(GLOBALS->h_acc);
elt = g_list_first(lacc);
while (elt != NULL)
{
Account *acc = elt->data;
PnlAccGrp *group;
//#1674045 ony rely on nosummary
//if( showall || !(acc->flags & (AF_CLOSED|AF_NOSUMMARY)) )
if( showall || !(acc->flags & AF_NOSUMMARY) )
{
switch( groupby )
{
case DSPACC_GROUP_BY_BANK:
{
groupname = _("(no institution)");
if( (acc->bankname != NULL) && strlen(acc->bankname) > 0 )
groupname = acc->bankname;
}
break;
default:
//pre 5.1.3 historical by type display
groupname = hbtk_get_label(CYA_ACC_TYPE, acc->type);
break;
}
//#1820853 groupname could be NULL
if( groupname != NULL )
{
if( g_hash_table_contains(h_group, groupname) == FALSE )
{
group = g_malloc0(sizeof(PnlAccGrp));
if( group )
{
group->acclist = g_ptr_array_sized_new(nballoc);
group->expanded = list_account_level1_expanded(treeview, groupname);
g_hash_table_insert(h_group, g_strdup(groupname), group );
DB( g_print(" grp '%s' exp:%d\n", groupname, group->expanded) );
}
}
group = g_hash_table_lookup(h_group, groupname);
if( group != NULL )
{
g_ptr_array_add(group->acclist, (gpointer)acc);
DB( g_print(" + acc '%s'\n", acc->name) );
}
}
}
elt = g_list_next(elt);
}
g_list_free(lacc);
return h_group;
}
void ui_hub_account_populate(GtkWidget *widget, gpointer user_data)
{
struct hbfile_data *data;
GtkTreeModel *model;
GtkTreeIter iter1, child_iter;
Account *acc;
guint j, nbtype;
gdouble gtbank, gttoday, gtfuture;
GHashTable *h_group;
GHashTableIter grp_iter;
gpointer key, value;
DB( g_print("\n[hub-account] populate\n") );
data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
h_group = ui_hub_account_groups_get(GTK_TREE_VIEW(data->LV_acc), PREFS->pnl_acc_show_by, data->showall);
DB( g_print("\n\n populate listview, %d group(s)\n", g_hash_table_size(h_group)) );
nbtype = 0;
gtbank = gttoday = gtfuture = 0;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_acc));
gtk_tree_store_clear (GTK_TREE_STORE(model));
g_hash_table_iter_init (&grp_iter, h_group);
while (g_hash_table_iter_next (&grp_iter, &key, &value))
{
PnlAccGrp *group = value;
gdouble tbank, ttoday, tfuture;
gint position;
if(group != NULL)
{
nbtype++;
//1: Header: Bank, Cash, ...
DB( g_print(" g '%s'\n", (gchar *)key) );
//#1663399 keep type position like in dropdown
position = 0;
if( PREFS->pnl_acc_show_by == DSPACC_GROUP_BY_TYPE )
{
gint t = 0;
while(CYA_ACC_TYPE[t].name != NULL && t < 32)
{
if( !strcmp(CYA_ACC_TYPE[t].name, key) )
break;
t++;
}
position = t;
}
gtk_tree_store_append (GTK_TREE_STORE(model), &iter1, NULL);
gtk_tree_store_set (GTK_TREE_STORE(model), &iter1,
LST_DSPACC_POS, position,
LST_DSPACC_DATATYPE, DSPACC_TYPE_HEADER,
LST_DSPACC_NAME, key,
-1);
//2: Accounts for real
tbank = ttoday = tfuture = 0;
for(j=0;jacclist->len;j++)
{
acc = g_ptr_array_index(group->acclist, j);
//tbank += acc->bal_bank;
//ttoday += acc->bal_today;
//tfuture += acc->bal_future;
tbank += hb_amount_base(acc->bal_bank, acc->kcur);
ttoday += hb_amount_base(acc->bal_today, acc->kcur);
tfuture += hb_amount_base(acc->bal_future, acc->kcur);
DB( g_print(" + '%s' :: %.2f %.2f %.2f\n", acc->name, acc->bal_bank, acc->bal_today, acc->bal_future) );
gtk_tree_store_append (GTK_TREE_STORE(model), &child_iter, &iter1);
gtk_tree_store_set (GTK_TREE_STORE(model), &child_iter,
LST_DSPACC_DATAS, acc,
LST_DSPACC_DATATYPE, DSPACC_TYPE_NORMAL,
LST_DSPACC_BANK, acc->bal_bank,
LST_DSPACC_TODAY, acc->bal_today,
LST_DSPACC_FUTURE, acc->bal_future,
-1);
}
if(group->acclist->len > 1)
{
DB( g_print(" + total :: %.2f %.2f %.2f\n", tbank, ttoday, tfuture) );
// insert the total line
gtk_tree_store_append (GTK_TREE_STORE(model), &child_iter, &iter1);
gtk_tree_store_set (GTK_TREE_STORE(model), &child_iter,
LST_DSPACC_DATATYPE, DSPACC_TYPE_SUBTOTAL,
LST_DSPACC_NAME, _("Total"),
LST_DSPACC_BANK, tbank,
LST_DSPACC_TODAY, ttoday,
LST_DSPACC_FUTURE, tfuture,
-1);
}
/* set balance to header to display when collasped */
DB( g_print(" (enrich group total header) :: %.2f %.2f %.2f\n", tbank, ttoday, tfuture) );
gtk_tree_store_set (GTK_TREE_STORE(model), &iter1,
LST_DSPACC_BANK, tbank,
LST_DSPACC_TODAY, ttoday,
LST_DSPACC_FUTURE, tfuture,
-1);
if( group->expanded == TRUE )
{
GtkTreePath *tmppath = gtk_tree_model_get_path(model, &iter1);
DB( g_print(" expanding '%s'\n", (gchar *)key) );
gtk_tree_view_expand_row(GTK_TREE_VIEW(data->LV_acc), tmppath, TRUE);
gtk_tree_path_free(tmppath);
}
/* add to grand total */
gtbank += tbank;
gttoday += ttoday;
gtfuture += tfuture;
}
}
DB( g_print(" + grand total :: %.2f %.2f %.2f\n", gtbank, gttoday, gtfuture) );
// Grand total
if( nbtype > 1 )
{
gtk_tree_store_append (GTK_TREE_STORE(model), &iter1, NULL);
gtk_tree_store_set (GTK_TREE_STORE(model), &iter1,
LST_DSPACC_DATATYPE, DSPACC_TYPE_SUBTOTAL,
LST_DSPACC_NAME, _("Grand total"),
LST_DSPACC_BANK, gtbank,
LST_DSPACC_TODAY, gttoday,
LST_DSPACC_FUTURE, gtfuture,
-1);
}
//gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_acc));
ui_hub_account_groups_free(h_group);
}
/* Callback function for the undo action */
/*static void
activate_action (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
g_print ("Action %s activated\n", g_action_get_name (G_ACTION (action)));
}*/
static void
ui_hub_account_activate_toggle (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
struct hbfile_data *data = user_data;
GVariant *old_state, *new_state;
old_state = g_action_get_state (G_ACTION (action));
new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));
DB( g_print ("Toggle action %s activated, state changes from %d to %d\n",
g_action_get_name (G_ACTION (action)),
g_variant_get_boolean (old_state),
g_variant_get_boolean (new_state)) );
data->showall = g_variant_get_boolean (new_state);
ui_hub_account_populate(GLOBALS->mainwindow, NULL);
g_simple_action_set_state (action, new_state);
g_variant_unref (old_state);
}
static void
ui_hub_account_activate_radio (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
//struct hbfile_data *data = user_data;
GVariant *old_state, *new_state;
old_state = g_action_get_state (G_ACTION (action));
new_state = g_variant_new_string (g_variant_get_string (parameter, NULL));
DB( g_print ("Radio action %s activated, state changes from %s to %s\n",
g_action_get_name (G_ACTION (action)),
g_variant_get_string (old_state, NULL),
g_variant_get_string (new_state, NULL)) );
PREFS->pnl_acc_show_by = DSPACC_GROUP_BY_TYPE;
if( !strcmp("bank", g_variant_get_string(new_state, NULL)) )
PREFS->pnl_acc_show_by = DSPACC_GROUP_BY_BANK;
ui_hub_account_populate(GLOBALS->mainwindow, NULL);
g_simple_action_set_state (action, new_state);
g_variant_unref (old_state);
}
static const GActionEntry actions[] = {
// name, function(), type, state,
// { "paste", activate_action, NULL, NULL, NULL, {0,0,0} },
{ "showall", ui_hub_account_activate_toggle, NULL, "false" , NULL, {0,0,0} },
{ "groupby", ui_hub_account_activate_radio , "s", "'type'", NULL, {0,0,0} }
};
void ui_hub_account_setup(struct hbfile_data *data)
{
GAction *action;
GVariant *new_state;
if( !G_IS_SIMPLE_ACTION_GROUP(data->action_group_acc) )
return;
action = g_action_map_lookup_action (G_ACTION_MAP (data->action_group_acc), "showall");
if( action )
{
new_state = g_variant_new_boolean (data->showall);
g_simple_action_set_state (G_SIMPLE_ACTION(action), new_state);
}
action = g_action_map_lookup_action (G_ACTION_MAP (data->action_group_acc), "groupby");
if( action )
{
const gchar *value = "type";
if( PREFS->pnl_acc_show_by == DSPACC_GROUP_BY_BANK )
value = "bank";
new_state = g_variant_new_string (value);
g_simple_action_set_state (G_SIMPLE_ACTION (action), new_state);
}
}
GtkWidget *ui_hub_account_create(struct hbfile_data *data)
{
GtkWidget *hub, *label, *widget, *sw, *tbar, *hbox, *image;
GtkToolItem *toolitem;
DB( g_print("\n[hub-account] create\n") );
hub = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_set_border_width(GTK_CONTAINER(hub), SPACING_SMALL);
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_AUTOMATIC);
gtk_box_pack_start (GTK_BOX (hub), sw, TRUE, TRUE, 0);
widget = (GtkWidget *)create_list_account();
data->LV_acc = widget;
gtk_container_add (GTK_CONTAINER (sw), widget);
//list toolbar
tbar = gtk_toolbar_new();
gtk_toolbar_set_icon_size (GTK_TOOLBAR(tbar), GTK_ICON_SIZE_MENU);
gtk_toolbar_set_style(GTK_TOOLBAR(tbar), GTK_TOOLBAR_ICONS);
gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
gtk_box_pack_start (GTK_BOX (hub), tbar, FALSE, FALSE, 0);
label = make_label_group(_("Your accounts"));
toolitem = gtk_tool_item_new();
gtk_container_add (GTK_CONTAINER(toolitem), label);
gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
toolitem = gtk_separator_tool_item_new ();
gtk_tool_item_set_expand (toolitem, TRUE);
gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
toolitem = gtk_tool_item_new();
gtk_container_add (GTK_CONTAINER(toolitem), hbox);
gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
widget = make_image_button(ICONNAME_HB_BUTTON_EXPAND, _("Expand all"));
data->BT_expandall = widget;
gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
widget = make_image_button(ICONNAME_HB_BUTTON_COLLAPSE, _("Collapse all"));
data->BT_collapseall = widget;
gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
toolitem = gtk_separator_tool_item_new ();
gtk_tool_item_set_expand (toolitem, FALSE);
gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
//gmenu test (see test folder into gtk)
GMenu *menu, *section;
menu = g_menu_new ();
//g_menu_append (menumodel, "About", "actions.undo");
//g_menu_append (menumodel, "Test", "actions.redo");
section = g_menu_new ();
g_menu_append (section, _("Show all"), "actions.showall");
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_object_unref (section);
section = g_menu_new ();
g_menu_append (section, _("By type"), "actions.groupby::type");
g_menu_append (section, _("By institution"), "actions.groupby::bank");
g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
g_object_unref (section);
GSimpleActionGroup *group = g_simple_action_group_new ();
data->action_group_acc = group;
g_action_map_add_action_entries (G_ACTION_MAP (group), actions, G_N_ELEMENTS (actions), data);
widget = gtk_menu_button_new();
gtk_menu_button_set_direction (GTK_MENU_BUTTON(widget), GTK_ARROW_UP);
gtk_widget_set_halign (widget, GTK_ALIGN_END);
image = gtk_image_new_from_icon_name (ICONNAME_EMBLEM_SYSTEM, GTK_ICON_SIZE_MENU);
g_object_set (widget, "image", image, NULL);
toolitem = gtk_tool_item_new();
gtk_container_add (GTK_CONTAINER(toolitem), widget);
gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
gtk_widget_insert_action_group (widget, "actions", G_ACTION_GROUP(group));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (widget), G_MENU_MODEL (menu));
g_signal_connect (G_OBJECT (data->BT_expandall ), "clicked" , G_CALLBACK (ui_hub_account_expand_all), NULL);
g_signal_connect (G_OBJECT (data->BT_collapseall), "clicked" , G_CALLBACK (ui_hub_account_collapse_all), NULL);
return hub;
}