]> Dogcows Code - chaz/homebank/blob - src/hub-spending.c
import homebank-5.2.4
[chaz/homebank] / src / hub-spending.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2019 Maxime DOYEN
3 *
4 * This file is part of HomeBank.
5 *
6 * HomeBank is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * HomeBank is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "homebank.h"
21
22 #include "dsp-mainwindow.h"
23
24 #include "hub-spending.h"
25 #include "gtk-chart.h"
26
27
28 /****************************************************************************/
29 /* Debug macros */
30 /****************************************************************************/
31 #define MYDEBUG 0
32
33 #if MYDEBUG
34 #define DB(x) (x);
35 #else
36 #define DB(x);
37 #endif
38
39 /* our global datas */
40 extern struct HomeBank *GLOBALS;
41 extern struct Preferences *PREFS;
42
43
44 extern gchar *CYA_CATSUBCAT[];
45
46
47 static GtkWidget *create_list_topspending(void)
48 {
49 GtkListStore *store;
50 GtkWidget *view;
51
52 /* create list store */
53 store = gtk_list_store_new(
54 NUM_LST_TOPSPEND,
55 G_TYPE_INT,
56 G_TYPE_INT,
57 G_TYPE_STRING, //category
58 G_TYPE_DOUBLE, //amount
59 G_TYPE_INT //rate
60 );
61
62 //treeview
63 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
64 g_object_unref(store);
65
66 return(view);
67 }
68
69
70
71
72 static gint tmptop_compare_func(struct tmptop *tt1, struct tmptop *tt2)
73 {
74 return tt1->value > tt2->value ? 1 : -1;
75 }
76
77
78 void ui_hub_spending_update(GtkWidget *widget, gpointer user_data)
79 {
80 struct hbfile_data *data;
81 GtkTreeModel *model;
82 gchar *title;
83 gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
84
85 DB( g_print("\n[hub-spendings] update\n") );
86
87 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
88
89 hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, data->toptotal, GLOBALS->kcur, GLOBALS->minor);
90 //hb_label_set_amount(GTK_LABEL(data->TX_topamount), total, GLOBALS->kcur, GLOBALS->minor);
91 title = g_strdup_printf("%s %s", _("Top spending"), strbuffer);
92
93 model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_top));
94
95 gtk_chart_set_color_scheme(GTK_CHART(data->RE_pie), PREFS->report_color_scheme);
96 gtk_chart_set_currency(GTK_CHART(data->RE_pie), GLOBALS->kcur);
97 gtk_chart_set_datas(GTK_CHART(data->RE_pie), model, LST_TOPSPEND_AMOUNT, title, NULL);
98
99 g_free(title);
100
101 //future usage
102 gchar *fu = _("Top %d spending"); title = fu;
103 }
104
105
106 void ui_hub_spending_populate(GtkWidget *widget, gpointer user_data)
107 {
108 struct hbfile_data *data;
109 GtkTreeModel *model;
110 GtkTreeIter iter;
111 GList *list;
112 gint type, range;
113 guint n_result, i, n_items;
114 GArray *garray;
115 gdouble total, other;
116 Account *acc;
117
118 DB( g_print("\n[hub-spendings] populate\n") );
119
120 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
121
122 type = hbtk_radio_get_active(GTK_CONTAINER(data->RA_type));
123 range = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_range));
124
125 DB( g_print(" - type=%d, range=%d\n", type, range) );
126 DB( g_print(" - pref range=%d\n", PREFS->date_range_wal) );
127
128 if(range == FLT_RANGE_OTHER)
129 return;
130
131 filter_preset_daterange_set(data->filter, range, 0);
132
133
134 n_result = da_cat_get_max_key() + 1;
135 total = 0.0;
136
137 DB( g_print(" - max key is %d\n", n_result) );
138
139 /* allocate some memory */
140 garray = g_array_sized_new(FALSE, FALSE, sizeof(struct tmptop), n_result);
141
142 if(garray)
143 {
144 struct tmptop zero = { .key=0, .value=0.0 };
145 GQueue *txn_queue;
146
147 //DB( g_print(" - array length=%d\n", garray->len) );
148
149 for(i=0 ; i<n_result ; i++)
150 {
151 g_array_append_vals(garray, &zero, 1);
152 //g_array_insert_vals(garray, i, &zero, 1);
153
154 //struct tmptop *tt = &g_array_index (garray, struct tmptop, i);
155 //DB( g_print("%4d, %4d %f\n", i, tt->key, tt->value) );
156 }
157
158 //DB( g_print("\n - end array length=%d\n", garray->len) );
159
160 //todo: not ideal, has ot force to get_acc for each txn below
161 txn_queue = hbfile_transaction_get_partial(data->filter->mindate, data->filter->maxdate);
162
163 /* compute the results */
164 list = g_queue_peek_head_link(txn_queue);
165 while (list != NULL)
166 {
167 Transaction *ope = list->data;
168
169 //DB( g_print(" - eval txn: '%s', cat=%d ==> flt-test=%d\n", ope->memo, ope->kcat, filter_txn_match(data->filter, ope)) );
170
171 if( !(ope->paymode == PAYMODE_INTXFER) )
172 {
173 guint32 pos = 0;
174 gdouble trn_amount;
175
176 //todo: optimize here
177 trn_amount = ope->amount;
178 acc = da_acc_get(ope->kacc);
179 if(acc)
180 trn_amount = hb_amount_base(ope->amount, acc->kcur);
181
182 if( ope->flags & OF_SPLIT )
183 {
184 guint nbsplit = da_splits_length(ope->splits);
185 Split *split;
186 struct tmptop *item;
187
188 for(i=0;i<nbsplit;i++)
189 {
190 split = da_splits_get(ope->splits, i);
191 pos = category_report_id(split->kcat, type);
192 if( pos <= garray->len )
193 {
194 trn_amount = hb_amount_base(split->amount, acc->kcur);
195 //trn_amount = split->amount;
196 //#1297054 if( trn_amount < 0 ) {
197 item = &g_array_index (garray, struct tmptop, pos);
198 item->key = pos;
199 item->value += trn_amount;
200 //DB( g_print(" - stored %.2f to item %d\n", trn_amount, pos) );
201 //}
202 }
203 }
204 }
205 else
206 {
207 struct tmptop *item;
208
209 pos = category_report_id(ope->kcat, type);
210 if( pos <= garray->len )
211 {
212 //#1297054 if( trn_amount < 0 ) {
213 item = &g_array_index (garray, struct tmptop, pos);
214 item->key = pos;
215 item->value += trn_amount;
216 //DB( g_print(" - stored %.2f to item %d\n", trn_amount, pos) );
217 //}
218 }
219 }
220
221 }
222
223 list = g_list_next(list);
224 }
225
226 g_queue_free (txn_queue);
227
228 // we need to sort this and limit before
229 g_array_sort(garray, (GCompareFunc)tmptop_compare_func);
230
231 n_items = MIN(garray->len,MAX_TOPSPENDING);
232 other = 0;
233 for(i=0 ; i<garray->len ; i++)
234 {
235 struct tmptop *item;
236
237 item = &g_array_index (garray, struct tmptop, i);
238 if(item->value < 0)
239 {
240 total += item->value;
241
242 if(i >= n_items)
243 other += item->value;
244
245 DB( g_print(" - %d : k='%d' v='%f' t='%f'\n", i, item->key, item->value, total) );
246
247 }
248 }
249
250 model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_top));
251 gtk_list_store_clear (GTK_LIST_STORE(model));
252 g_object_ref(model); /* Make sure the model stays with us after the tree view unrefs it */
253 gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_top), NULL); /* Detach model from view */
254
255 /* insert into the treeview */
256 for(i=0 ; i<MIN(garray->len,MAX_TOPSPENDING) ; i++)
257 {
258 gchar *name;
259 Category *entry;
260 struct tmptop *item;
261 gdouble value;
262
263 item = &g_array_index (garray, struct tmptop, i);
264
265 if(!item->value) continue;
266 //#1767659 top spending should restrict to... spending
267 if(item->value < 0)
268 {
269 value = hb_amount_round(item->value, 2);
270 entry = da_cat_get(item->key);
271 if(entry == NULL) continue;
272
273 name = da_cat_get_name (entry);
274
275 // append test
276 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
277 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
278 LST_TOPSPEND_ID, i,
279 LST_TOPSPEND_KEY, 0,
280 LST_TOPSPEND_NAME, name,
281 LST_TOPSPEND_AMOUNT, value,
282 //LST_TOPSPEND_RATE, (gint)(((ABS(value)*100)/ABS(total)) + 0.5),
283 -1);
284 }
285 }
286
287 // append test
288 if(ABS(other) > 0)
289 {
290 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
291 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
292 LST_TOPSPEND_ID, n_items,
293 LST_TOPSPEND_KEY, 0,
294 LST_TOPSPEND_NAME, _("Other"),
295 LST_TOPSPEND_AMOUNT, other,
296 //LST_TOPSPEND_RATE, (gint)(((ABS(other)*100)/ABS(total)) + 0.5),
297 -1);
298 }
299
300 /* Re-attach model to view */
301 gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_top), model);
302 g_object_unref(model);
303
304
305 // update chart and widgets
306 {
307 gchar *daterange;
308
309 data->toptotal = total;
310 ui_hub_spending_update(widget, data);
311
312 daterange = filter_daterange_text_get(data->filter);
313 gtk_widget_set_tooltip_markup(GTK_WIDGET(data->CY_range), daterange);
314 g_free(daterange);
315 }
316 }
317
318 /* free our memory */
319 g_array_free (garray, TRUE);
320
321 }
322
323
324
325 GtkWidget *ui_hub_spending_create(struct hbfile_data *data)
326 {
327 GtkWidget *hub, *hbox, *tbar;
328 GtkWidget *label, *widget;
329 GtkToolItem *toolitem;
330
331 DB( g_print("\n[hub-spendings] create\n") );
332
333 widget = (GtkWidget *)create_list_topspending();
334 data->LV_top = widget;
335
336 hub = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
337 gtk_container_set_border_width(GTK_CONTAINER(hub), SPACING_SMALL);
338 data->GR_top = hub;
339
340 /* chart + listview */
341 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
342 gtk_box_pack_start (GTK_BOX (hub), hbox, TRUE, TRUE, 0);
343
344 widget = gtk_chart_new(CHART_TYPE_PIE);
345 data->RE_pie = widget;
346 gtk_chart_set_minor_prefs(GTK_CHART(widget), PREFS->euro_value, PREFS->minor_cur.symbol);
347 gtk_chart_show_legend(GTK_CHART(data->RE_pie), TRUE, TRUE);
348 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
349
350 //list toolbar
351 tbar = gtk_toolbar_new();
352 gtk_toolbar_set_icon_size (GTK_TOOLBAR(tbar), GTK_ICON_SIZE_MENU);
353 gtk_toolbar_set_style(GTK_TOOLBAR(tbar), GTK_TOOLBAR_ICONS);
354 gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
355 gtk_box_pack_start (GTK_BOX (hub), tbar, FALSE, FALSE, 0);
356
357 label = make_label_group(_("Where your money goes"));
358 toolitem = gtk_tool_item_new();
359 gtk_container_add (GTK_CONTAINER(toolitem), label);
360 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
361
362 toolitem = gtk_separator_tool_item_new ();
363 gtk_tool_item_set_expand (toolitem, TRUE);
364 gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
365 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
366
367 /* total + date range */
368 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
369 toolitem = gtk_tool_item_new();
370 gtk_container_add (GTK_CONTAINER(toolitem), hbox);
371 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
372
373 data->CY_range = make_daterange(label, FALSE);
374 gtk_box_pack_end (GTK_BOX (hbox), data->CY_range, FALSE, FALSE, 0);
375
376 widget = hbtk_radio_new(CYA_CATSUBCAT, TRUE);
377 data->RA_type = widget;
378 gtk_box_pack_end (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
379
380 hbtk_radio_connect (GTK_CONTAINER(data->RA_type), "toggled", G_CALLBACK (ui_hub_spending_populate), &data);
381
382 g_signal_connect (data->CY_range, "changed", G_CALLBACK (ui_hub_spending_populate), NULL);
383
384
385
386 return hub;
387 }
This page took 0.049942 seconds and 4 git commands to generate.