]> Dogcows Code - chaz/homebank/blobdiff - src/hub-spending.c
import homebank-5.2.4
[chaz/homebank] / src / hub-spending.c
diff --git a/src/hub-spending.c b/src/hub-spending.c
new file mode 100644 (file)
index 0000000..60edb47
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 ; i<n_result ; i++)
+               {
+                       g_array_append_vals(garray, &zero, 1);
+                       //g_array_insert_vals(garray, i, &zero, 1);
+
+                       //struct tmptop *tt = &g_array_index (garray, struct tmptop, i);
+                       //DB( g_print("%4d, %4d %f\n", i, tt->key, 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;i<nbsplit;i++)
+                                       {
+                                               split = da_splits_get(ope->splits, 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 ; i<garray->len ; 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 ; i<MIN(garray->len,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;
+}
This page took 0.028012 seconds and 4 git commands to generate.