X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fhomebank;a=blobdiff_plain;f=src%2Fhub-spending.c;fp=src%2Fhub-spending.c;h=60edb477e4e68bb85e2fd7af0d7acfa9f370930b;hp=0000000000000000000000000000000000000000;hb=a6c6b0df5492c2160ed97e3a376bdb2fe7c5ebc4;hpb=cd13d9691c46c2b2d6d459e9e6a76bed1c21b7a6 diff --git a/src/hub-spending.c b/src/hub-spending.c new file mode 100644 index 0000000..60edb47 --- /dev/null +++ b/src/hub-spending.c @@ -0,0 +1,387 @@ +/* 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 "dsp-mainwindow.h" + +#include "hub-spending.h" +#include "gtk-chart.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 gchar *CYA_CATSUBCAT[]; + + +static GtkWidget *create_list_topspending(void) +{ +GtkListStore *store; +GtkWidget *view; + + /* create list store */ + store = gtk_list_store_new( + NUM_LST_TOPSPEND, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_STRING, //category + G_TYPE_DOUBLE, //amount + G_TYPE_INT //rate + ); + + //treeview + view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + + return(view); +} + + + + +static gint tmptop_compare_func(struct tmptop *tt1, struct tmptop *tt2) +{ + return tt1->value > tt2->value ? 1 : -1; +} + + +void ui_hub_spending_update(GtkWidget *widget, gpointer user_data) +{ +struct hbfile_data *data; +GtkTreeModel *model; +gchar *title; +gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE]; + + DB( g_print("\n[hub-spendings] update\n") ); + + data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data"); + + hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, data->toptotal, GLOBALS->kcur, GLOBALS->minor); + //hb_label_set_amount(GTK_LABEL(data->TX_topamount), total, GLOBALS->kcur, GLOBALS->minor); + title = g_strdup_printf("%s %s", _("Top spending"), strbuffer); + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_top)); + + gtk_chart_set_color_scheme(GTK_CHART(data->RE_pie), PREFS->report_color_scheme); + gtk_chart_set_currency(GTK_CHART(data->RE_pie), GLOBALS->kcur); + gtk_chart_set_datas(GTK_CHART(data->RE_pie), model, LST_TOPSPEND_AMOUNT, title, NULL); + + g_free(title); + + //future usage + gchar *fu = _("Top %d spending"); title = fu; +} + + +void ui_hub_spending_populate(GtkWidget *widget, gpointer user_data) +{ +struct hbfile_data *data; +GtkTreeModel *model; +GtkTreeIter iter; +GList *list; +gint type, range; +guint n_result, i, n_items; +GArray *garray; +gdouble total, other; +Account *acc; + + DB( g_print("\n[hub-spendings] populate\n") ); + + data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data"); + + type = hbtk_radio_get_active(GTK_CONTAINER(data->RA_type)); + range = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_range)); + + DB( g_print(" - type=%d, range=%d\n", type, range) ); + DB( g_print(" - pref range=%d\n", PREFS->date_range_wal) ); + + if(range == FLT_RANGE_OTHER) + return; + + filter_preset_daterange_set(data->filter, range, 0); + + + n_result = da_cat_get_max_key() + 1; + total = 0.0; + + DB( g_print(" - max key is %d\n", n_result) ); + + /* allocate some memory */ + garray = g_array_sized_new(FALSE, FALSE, sizeof(struct tmptop), n_result); + + if(garray) + { + struct tmptop zero = { .key=0, .value=0.0 }; + GQueue *txn_queue; + + //DB( g_print(" - array length=%d\n", garray->len) ); + + for(i=0 ; ikey, tt->value) ); + } + + //DB( g_print("\n - end array length=%d\n", garray->len) ); + + //todo: not ideal, has ot force to get_acc for each txn below + txn_queue = hbfile_transaction_get_partial(data->filter->mindate, data->filter->maxdate); + + /* compute the results */ + list = g_queue_peek_head_link(txn_queue); + while (list != NULL) + { + Transaction *ope = list->data; + + //DB( g_print(" - eval txn: '%s', cat=%d ==> flt-test=%d\n", ope->memo, ope->kcat, filter_txn_match(data->filter, ope)) ); + + if( !(ope->paymode == PAYMODE_INTXFER) ) + { + guint32 pos = 0; + gdouble trn_amount; + + //todo: optimize here + trn_amount = ope->amount; + acc = da_acc_get(ope->kacc); + if(acc) + trn_amount = hb_amount_base(ope->amount, acc->kcur); + + if( ope->flags & OF_SPLIT ) + { + guint nbsplit = da_splits_length(ope->splits); + Split *split; + struct tmptop *item; + + for(i=0;isplits, i); + pos = category_report_id(split->kcat, type); + if( pos <= garray->len ) + { + trn_amount = hb_amount_base(split->amount, acc->kcur); + //trn_amount = split->amount; + //#1297054 if( trn_amount < 0 ) { + item = &g_array_index (garray, struct tmptop, pos); + item->key = pos; + item->value += trn_amount; + //DB( g_print(" - stored %.2f to item %d\n", trn_amount, pos) ); + //} + } + } + } + else + { + struct tmptop *item; + + pos = category_report_id(ope->kcat, type); + if( pos <= garray->len ) + { + //#1297054 if( trn_amount < 0 ) { + item = &g_array_index (garray, struct tmptop, pos); + item->key = pos; + item->value += trn_amount; + //DB( g_print(" - stored %.2f to item %d\n", trn_amount, pos) ); + //} + } + } + + } + + list = g_list_next(list); + } + + g_queue_free (txn_queue); + + // we need to sort this and limit before + g_array_sort(garray, (GCompareFunc)tmptop_compare_func); + + n_items = MIN(garray->len,MAX_TOPSPENDING); + other = 0; + for(i=0 ; ilen ; i++) + { + struct tmptop *item; + + item = &g_array_index (garray, struct tmptop, i); + if(item->value < 0) + { + total += item->value; + + if(i >= n_items) + other += item->value; + + DB( g_print(" - %d : k='%d' v='%f' t='%f'\n", i, item->key, item->value, total) ); + + } + } + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_top)); + gtk_list_store_clear (GTK_LIST_STORE(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_top), NULL); /* Detach model from view */ + + /* insert into the treeview */ + for(i=0 ; ilen,MAX_TOPSPENDING) ; i++) + { + gchar *name; + Category *entry; + struct tmptop *item; + gdouble value; + + item = &g_array_index (garray, struct tmptop, i); + + if(!item->value) continue; + //#1767659 top spending should restrict to... spending + if(item->value < 0) + { + value = hb_amount_round(item->value, 2); + entry = da_cat_get(item->key); + if(entry == NULL) continue; + + name = da_cat_get_name (entry); + + // append test + gtk_list_store_append (GTK_LIST_STORE(model), &iter); + gtk_list_store_set (GTK_LIST_STORE(model), &iter, + LST_TOPSPEND_ID, i, + LST_TOPSPEND_KEY, 0, + LST_TOPSPEND_NAME, name, + LST_TOPSPEND_AMOUNT, value, + //LST_TOPSPEND_RATE, (gint)(((ABS(value)*100)/ABS(total)) + 0.5), + -1); + } + } + + // append test + if(ABS(other) > 0) + { + gtk_list_store_append (GTK_LIST_STORE(model), &iter); + gtk_list_store_set (GTK_LIST_STORE(model), &iter, + LST_TOPSPEND_ID, n_items, + LST_TOPSPEND_KEY, 0, + LST_TOPSPEND_NAME, _("Other"), + LST_TOPSPEND_AMOUNT, other, + //LST_TOPSPEND_RATE, (gint)(((ABS(other)*100)/ABS(total)) + 0.5), + -1); + } + + /* Re-attach model to view */ + gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_top), model); + g_object_unref(model); + + + // update chart and widgets + { + gchar *daterange; + + data->toptotal = total; + ui_hub_spending_update(widget, data); + + daterange = filter_daterange_text_get(data->filter); + gtk_widget_set_tooltip_markup(GTK_WIDGET(data->CY_range), daterange); + g_free(daterange); + } + } + + /* free our memory */ + g_array_free (garray, TRUE); + +} + + + +GtkWidget *ui_hub_spending_create(struct hbfile_data *data) +{ +GtkWidget *hub, *hbox, *tbar; +GtkWidget *label, *widget; +GtkToolItem *toolitem; + + DB( g_print("\n[hub-spendings] create\n") ); + + widget = (GtkWidget *)create_list_topspending(); + data->LV_top = widget; + + hub = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_set_border_width(GTK_CONTAINER(hub), SPACING_SMALL); + data->GR_top = hub; + + /* chart + listview */ + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_pack_start (GTK_BOX (hub), hbox, TRUE, TRUE, 0); + + widget = gtk_chart_new(CHART_TYPE_PIE); + data->RE_pie = widget; + gtk_chart_set_minor_prefs(GTK_CHART(widget), PREFS->euro_value, PREFS->minor_cur.symbol); + gtk_chart_show_legend(GTK_CHART(data->RE_pie), TRUE, TRUE); + gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0); + + //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(_("Where your money goes")); + 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); + + /* total + date range */ + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL); + toolitem = gtk_tool_item_new(); + gtk_container_add (GTK_CONTAINER(toolitem), hbox); + gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1); + + data->CY_range = make_daterange(label, FALSE); + gtk_box_pack_end (GTK_BOX (hbox), data->CY_range, FALSE, FALSE, 0); + + widget = hbtk_radio_new(CYA_CATSUBCAT, TRUE); + data->RA_type = widget; + gtk_box_pack_end (GTK_BOX (hbox), widget, FALSE, FALSE, 0); + + hbtk_radio_connect (GTK_CONTAINER(data->RA_type), "toggled", G_CALLBACK (ui_hub_spending_populate), &data); + + g_signal_connect (data->CY_range, "changed", G_CALLBACK (ui_hub_spending_populate), NULL); + + + + return hub; +}