]> Dogcows Code - chaz/homebank/blob - src/rep-budget.c
Merge branch 'upstream'
[chaz/homebank] / src / rep-budget.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
21 #include "homebank.h"
22
23 #include "rep-budget.h"
24
25 #include "list-operation.h"
26 #include "gtk-chart-progress.h"
27 #include "gtk-dateentry.h"
28
29 #include "dsp-mainwindow.h"
30 #include "ui-transaction.h"
31
32
33 /****************************************************************************/
34 /* Debug macros */
35 /****************************************************************************/
36 #define MYDEBUG 0
37
38 #if MYDEBUG
39 #define DB(x) (x);
40 #else
41 #define DB(x);
42 #endif
43
44
45
46 /* our global datas */
47 extern struct HomeBank *GLOBALS;
48 extern struct Preferences *PREFS;
49
50
51 static void repbudget_action_viewlist(GtkAction *action, gpointer user_data);
52 static void repbudget_action_viewstack(GtkAction *action, gpointer user_data);
53 static void repbudget_action_detail(GtkAction *action, gpointer user_data);
54 static void repbudget_action_refresh(GtkAction *action, gpointer user_data);
55
56 static void repbudget_date_change(GtkWidget *widget, gpointer user_data);
57 static void repbudget_range_change(GtkWidget *widget, gpointer user_data);
58 static void repbudget_toggle_detail(GtkWidget *widget, gpointer user_data);
59 static void repbudget_detail(GtkWidget *widget, gpointer user_data);
60 static void repbudget_compute(GtkWidget *widget, gpointer user_data);
61 static void repbudget_update_total(GtkWidget *widget, gpointer user_data);
62 static void repbudget_sensitive(GtkWidget *widget, gpointer user_data);
63 static void repbudget_toggle(GtkWidget *widget, gpointer user_data);
64 static GtkWidget *create_list_budget(void);
65 static void repbudget_update_detail(GtkWidget *widget, gpointer user_data);
66 static void repbudget_update_daterange(GtkWidget *widget, gpointer user_data);
67
68 static GString *ui_list_repbudget_to_string(GtkTreeView *treeview, gboolean clipboard);
69
70
71 extern gchar *CYA_CATSUBCAT[];
72 extern gchar *CYA_KIND[];
73
74
75 static GtkRadioActionEntry radio_entries[] = {
76 { "List" , ICONNAME_HB_VIEW_LIST , N_("List") , NULL, N_("View results as list") , 0 },
77 { "Stack" , ICONNAME_HB_VIEW_STACK, N_("Stack") , NULL, N_("View results as stack bars"), 1 },
78 };
79 static guint n_radio_entries = G_N_ELEMENTS (radio_entries);
80
81
82 static GtkActionEntry entries[] = {
83 { "Refresh" , ICONNAME_HB_REFRESH, N_("Refresh"), NULL, N_("Refresh results"), G_CALLBACK (repbudget_action_refresh) },
84
85 // { "Export" , ICONNAME_HB_FILE_EXPORT , N_("Export") , NULL, N_("Export as CSV"), G_CALLBACK (repbudget_action_export) },
86 };
87 static guint n_entries = G_N_ELEMENTS (entries);
88
89
90 static GtkToggleActionEntry toggle_entries[] = {
91 { "Detail", ICONNAME_HB_OPE_SHOW, /* name, icon-name */
92 N_("Detail"), NULL, /* label, accelerator */
93 N_("Toggle detail"), /* tooltip */
94 G_CALLBACK (repbudget_action_detail),
95 FALSE }, /* is_active */
96
97 };
98 static guint n_toggle_entries = G_N_ELEMENTS (toggle_entries);
99
100
101 static const gchar *ui_info =
102 "<ui>"
103 " <toolbar name='ToolBar'>"
104 " <toolitem action='List'/>"
105 " <toolitem action='Stack'/>"
106 " <separator/>"
107 " <toolitem action='Detail'/>"
108 " <separator/>"
109 " <toolitem action='Refresh'/>"
110 " <separator/>"
111 //" <toolitem action='Export'/>"
112 // replaced by a menubutton
113 " </toolbar>"
114 "</ui>";
115
116
117
118 /* action functions -------------------- */
119 static void repbudget_action_viewlist(GtkAction *action, gpointer user_data)
120 {
121 struct repbudget_data *data = user_data;
122
123 gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_result), 0);
124 repbudget_sensitive(data->window, NULL);
125 }
126
127 static void repbudget_action_viewstack(GtkAction *action, gpointer user_data)
128 {
129 struct repbudget_data *data = user_data;
130
131 gtk_notebook_set_current_page(GTK_NOTEBOOK(data->GR_result), 1);
132 repbudget_sensitive(data->window, NULL);
133 }
134
135
136 static void repbudget_action_mode (GtkRadioAction *action, GtkRadioAction *current, gpointer user_data)
137 {
138 gint value;
139
140 value = gtk_radio_action_get_current_value(GTK_RADIO_ACTION(action));
141 switch( value )
142 {
143 case 0:
144 repbudget_action_viewlist(GTK_ACTION(action), user_data);
145 break;
146 case 1:
147 repbudget_action_viewstack(GTK_ACTION(action), user_data);
148 break;
149 }
150 }
151
152
153 static void repbudget_action_detail(GtkAction *action, gpointer user_data)
154 {
155 struct repbudget_data *data = user_data;
156
157 repbudget_toggle_detail(data->window, NULL);
158 }
159
160
161 static void repbudget_action_refresh(GtkAction *action, gpointer user_data)
162 {
163 struct repbudget_data *data = user_data;
164
165 repbudget_compute(data->window, NULL);
166 }
167
168 /*static void repbudget_action_export(GtkAction *action, gpointer user_data)
169 {
170 struct repbudget_data *data = user_data;
171
172 repbudget_export_csv(data->window, NULL);
173 }*/
174
175 /* ======================== */
176
177
178 static gint getmonth(guint date)
179 {
180 GDate *date1;
181 gint month;
182
183 date1 = g_date_new_julian(date);
184 month = g_date_get_month(date1);
185
186 /*#if MYDEBUG == 1
187 g_print("\n[repbudget] getmonth\n");
188 gchar buffer1[128];
189 g_date_strftime (buffer1, 128-1, "%x", date1);
190 g_print(" date is '%s', month=%d\n", buffer1, month);
191 #endif*/
192
193 g_date_free(date1);
194
195 return(month);
196 }
197
198 static gint countmonth(guint32 mindate, guint32 maxdate)
199 {
200 GDate *date1, *date2;
201 gint nbmonth;
202
203 date1 = g_date_new_julian(mindate);
204 date2 = g_date_new_julian(maxdate);
205
206 nbmonth = 0;
207 while(g_date_compare(date1, date2) < 0)
208 {
209 nbmonth++;
210 g_date_add_months(date1, 1);
211 }
212
213 g_date_free(date2);
214 g_date_free(date1);
215
216 return(nbmonth);
217 }
218
219 static gdouble budget_compute_result(gdouble budget, gdouble spent)
220 {
221 gdouble retval;
222
223 //original formula
224 //result = ABS(budget) - ABS(spent);
225
226 retval = spent - budget;
227
228 return retval;
229 }
230
231
232
233
234
235 static void repbudget_date_change(GtkWidget *widget, gpointer user_data)
236 {
237 struct repbudget_data *data;
238
239 DB( g_print("\n[repbudget] date change\n") );
240
241 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
242
243 data->filter->mindate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_mindate));
244 data->filter->maxdate = gtk_date_entry_get_date(GTK_DATE_ENTRY(data->PO_maxdate));
245
246 // set min/max date for both widget
247 gtk_date_entry_set_maxdate(GTK_DATE_ENTRY(data->PO_mindate), data->filter->maxdate);
248 gtk_date_entry_set_mindate(GTK_DATE_ENTRY(data->PO_maxdate), data->filter->mindate);
249
250 g_signal_handler_block(data->CY_range, data->handler_id[HID_REPBUDGET_RANGE]);
251 gtk_combo_box_set_active(GTK_COMBO_BOX(data->CY_range), FLT_RANGE_OTHER);
252 g_signal_handler_unblock(data->CY_range, data->handler_id[HID_REPBUDGET_RANGE]);
253
254
255 repbudget_compute(widget, NULL);
256 repbudget_update_daterange(widget, NULL);
257
258 }
259
260
261 static void repbudget_range_change(GtkWidget *widget, gpointer user_data)
262 {
263 struct repbudget_data *data;
264 gint range;
265
266 DB( g_print("\n[repbudget] range change\n") );
267
268 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
269
270 range = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_range));
271
272
273 if(range != FLT_RANGE_OTHER)
274 {
275 filter_preset_daterange_set(data->filter, range, 0);
276
277 g_signal_handler_block(data->PO_mindate, data->handler_id[HID_REPBUDGET_MINDATE]);
278 g_signal_handler_block(data->PO_maxdate, data->handler_id[HID_REPBUDGET_MAXDATE]);
279
280 gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_mindate), data->filter->mindate);
281 gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_maxdate), data->filter->maxdate);
282
283 g_signal_handler_unblock(data->PO_mindate, data->handler_id[HID_REPBUDGET_MINDATE]);
284 g_signal_handler_unblock(data->PO_maxdate, data->handler_id[HID_REPBUDGET_MAXDATE]);
285
286 repbudget_compute(widget, NULL);
287 repbudget_update_daterange(widget, NULL);
288 }
289 }
290
291
292 static void repbudget_update_daterange(GtkWidget *widget, gpointer user_data)
293 {
294 struct repbudget_data *data;
295 gchar *daterange;
296
297 DB( g_print("\n[repbudget] update daterange\n") );
298
299 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
300
301 daterange = filter_daterange_text_get(data->filter);
302 gtk_label_set_markup(GTK_LABEL(data->TX_daterange), daterange);
303 g_free(daterange);
304 }
305
306
307 static void repbudget_toggle_detail(GtkWidget *widget, gpointer user_data)
308 {
309 struct repbudget_data *data;
310
311 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
312
313 DB( g_print("\n[repbudget] toggle detail\n") );
314
315 data->detail ^= 1;
316
317 repbudget_update_detail(widget, user_data);
318
319 }
320
321
322 static void repbudget_detail_onRowActivated (GtkTreeView *treeview,
323 GtkTreePath *path,
324 GtkTreeViewColumn *col,
325 gpointer userdata)
326 {
327 struct repbudget_data *data;
328 Transaction *active_txn;
329 gboolean result;
330
331 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(GTK_WIDGET(treeview), GTK_TYPE_WINDOW)), "inst_data");
332
333 DB( g_print ("\n[repbudget] A detail row has been double-clicked!\n") );
334
335 active_txn = list_txn_get_active_transaction(GTK_TREE_VIEW(data->LV_detail));
336 if(active_txn)
337 {
338 Transaction *old_txn, *new_txn;
339
340 old_txn = da_transaction_clone (active_txn);
341 new_txn = active_txn;
342 result = deftransaction_external_edit(GTK_WINDOW(data->window), old_txn, new_txn);
343
344 if(result == GTK_RESPONSE_ACCEPT)
345 {
346 //#1640885
347 GLOBALS->changes_count++;
348 repbudget_compute(data->window, NULL);
349 }
350
351 da_transaction_free (old_txn);
352 }
353 }
354
355
356 static void repbudget_update_detail(GtkWidget *widget, gpointer user_data)
357 {
358 struct repbudget_data *data;
359
360 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
361
362 DB( g_print("\n[repbudget] update detail\n") );
363
364 if(GTK_IS_TREE_VIEW(data->LV_report))
365 {
366 if(data->detail)
367 {
368 GtkTreeSelection *treeselection;
369 GtkTreeModel *model;
370 GtkTreeIter iter;
371 guint key;
372
373 treeselection = gtk_tree_view_get_selection (GTK_TREE_VIEW(data->LV_report));
374
375 if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
376 {
377 gtk_tree_model_get(model, &iter, LST_BUDGET_KEY, &key, -1);
378
379 //DB( g_print(" - active is %d\n", pos) );
380
381 repbudget_detail(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(key));
382 }
383
384
385 gtk_widget_show(data->GR_detail);
386 }
387 else
388 gtk_widget_hide(data->GR_detail);
389
390
391 }
392 }
393
394
395 static void repbudget_export_result_clipboard(GtkWidget *widget, gpointer user_data)
396 {
397 struct repbudget_data *data;
398 GtkClipboard *clipboard;
399 GString *node;
400
401 DB( g_print("\n[repbudget] export result clipboard\n") );
402
403 data = user_data;
404 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
405
406 node = ui_list_repbudget_to_string(GTK_TREE_VIEW(data->LV_report), TRUE);
407
408 clipboard = gtk_clipboard_get_default(gdk_display_get_default());
409 gtk_clipboard_set_text(clipboard, node->str, node->len);
410
411 g_string_free(node, TRUE);
412 }
413
414
415 static void repbudget_export_result_csv(GtkWidget *widget, gpointer user_data)
416 {
417 struct repbudget_data *data;
418 gchar *filename = NULL;
419 GString *node;
420 GIOChannel *io;
421 gchar *name;
422 gint tmpfor;
423
424 DB( g_print("\n[repbudget] export result csv\n") );
425
426 data = user_data;
427 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
428
429 tmpfor = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_for));
430 name = g_strdup_printf("hb-repbudget_%s.csv", CYA_CATSUBCAT[tmpfor]);
431
432 if( ui_file_chooser_csv(GTK_WINDOW(data->window), GTK_FILE_CHOOSER_ACTION_SAVE, &filename, name) == TRUE )
433 {
434 DB( g_print(" + filename is %s\n", filename) );
435 io = g_io_channel_new_file(filename, "w", NULL);
436 if(io != NULL)
437 {
438 node = ui_list_repbudget_to_string(GTK_TREE_VIEW(data->LV_report), FALSE);
439 g_io_channel_write_chars(io, node->str, -1, NULL, NULL);
440 g_io_channel_unref (io);
441 g_string_free(node, TRUE);
442 }
443 g_free( filename );
444 }
445 g_free(name);
446 }
447
448
449 static void repbudget_export_detail_clipboard(GtkWidget *widget, gpointer user_data)
450 {
451 struct repbudget_data *data;
452 GtkClipboard *clipboard;
453 GString *node;
454
455 DB( g_print("\n[repbudget] export detail clipboard\n") );
456
457 data = user_data;
458 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
459
460 node = list_txn_to_string(GTK_TREE_VIEW(data->LV_detail), TRUE);
461
462 clipboard = gtk_clipboard_get_default(gdk_display_get_default());
463 gtk_clipboard_set_text(clipboard, node->str, node->len);
464
465 g_string_free(node, TRUE);
466 }
467
468
469 static void repbudget_export_detail_csv(GtkWidget *widget, gpointer user_data)
470 {
471 struct repbudget_data *data;
472 gchar *filename = NULL;
473 GString *node;
474 GIOChannel *io;
475 gchar *name;
476 gint tmpfor;
477
478 DB( g_print("\n[repbudget] export detail csv\n") );
479
480 data = user_data;
481 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
482
483 tmpfor = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_for));
484 name = g_strdup_printf("hb-repstat-detail_%s.csv", CYA_CATSUBCAT[tmpfor]);
485
486 if( ui_file_chooser_csv(GTK_WINDOW(data->window), GTK_FILE_CHOOSER_ACTION_SAVE, &filename, name) == TRUE )
487 {
488 DB( g_print(" + filename is %s\n", filename) );
489
490 io = g_io_channel_new_file(filename, "w", NULL);
491 if(io != NULL)
492 {
493 node = list_txn_to_string(GTK_TREE_VIEW(data->LV_detail), FALSE);
494 g_io_channel_write_chars(io, node->str, -1, NULL, NULL);
495
496 g_io_channel_unref (io);
497 g_string_free(node, TRUE);
498 }
499
500 g_free( filename );
501 }
502
503 g_free(name);
504 }
505
506
507
508
509 static void repbudget_detail(GtkWidget *widget, gpointer user_data)
510 {
511 struct repbudget_data *data;
512 guint active = GPOINTER_TO_INT(user_data);
513 GList *list;
514 guint tmpfor;
515 GtkTreeModel *model;
516 GtkTreeIter iter;
517
518 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
519
520 DB( g_print("\n[repbudget] detail\n") );
521
522 if(data->detail)
523 {
524 /* clear and detach our model */
525 model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail));
526 gtk_list_store_clear (GTK_LIST_STORE(model));
527 g_object_ref(model); /* Make sure the model stays with us after the tree view unrefs it */
528 gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_detail), NULL); /* Detach model from view */
529
530 tmpfor = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_for));
531
532 /* fill in the model */
533 list = g_queue_peek_head_link(data->txn_queue);
534 while (list != NULL)
535 {
536 Account *acc;
537 Transaction *ope = list->data;
538 guint pos = 0;
539 gboolean insert = FALSE;
540
541 //DB( g_print(" get %s\n", ope->ope_Word) );
542
543 acc = da_acc_get(ope->kacc);
544 if(acc != NULL)
545 {
546 if((acc->flags & AF_NOBUDGET)) goto next1;
547 }
548
549 //filter here
550 if( ope->flags & OF_SPLIT )
551 {
552 guint nbsplit = da_splits_length(ope->splits);
553 Split *split;
554 guint i;
555
556 for(i=0;i<nbsplit;i++)
557 {
558 split = da_splits_get(ope->splits, i);
559 switch(tmpfor)
560 {
561 case BUDG_CATEGORY:
562 {
563 Category *catentry = da_cat_get(split->kcat);
564 if(catentry)
565 pos = (catentry->flags & GF_SUB) ? catentry->parent : catentry->key;
566 }
567 break;
568 case BUDG_SUBCATEGORY:
569 pos = split->kcat;
570 break;
571 }
572
573 if( pos == active )
574 { insert = TRUE; break; }
575 }
576 }
577 else
578 {
579 switch(tmpfor)
580 {
581 case BUDG_CATEGORY:
582 {
583 Category *catentry = da_cat_get(ope->kcat);
584 if(catentry)
585 pos = (catentry->flags & GF_SUB) ? catentry->parent : catentry->key;
586 }
587 break;
588 case BUDG_SUBCATEGORY:
589 pos = ope->kcat;
590 break;
591 }
592
593 if( pos == active )
594 insert = TRUE;
595 }
596
597 //insert
598 if( insert == TRUE )
599 {
600
601 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
602 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
603 LST_DSPOPE_DATAS, ope,
604 -1);
605 }
606
607
608 next1:
609 list = g_list_next(list);
610 }
611
612 /* Re-attach model to view */
613 gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_detail), model);
614 g_object_unref(model);
615
616 gtk_tree_view_columns_autosize( GTK_TREE_VIEW(data->LV_detail) );
617
618 }
619
620 }
621
622
623 static void repbudget_compute_cat_spent(guint32 key, gdouble amount, gdouble *tmp_spent, gdouble *tmp_budget)
624 {
625 Category *cat;
626
627 cat = da_cat_get(key);
628 if(cat)
629 {
630 DB( g_print(" -> affecting %.2f to cat %d sub=%d, bud=%.2f\n", amount, cat->key, (cat->flags & GF_SUB), tmp_budget[cat->key]) );
631 if( (cat->flags & GF_FORCED) || (cat->flags & GF_BUDGET) )
632 tmp_spent[cat->key] += amount;
633 //#1814213 only count subcat with budget
634 //if( (cat->flags & GF_SUB) )
635 if( (cat->flags & GF_FORCED) || ((cat->flags & GF_SUB) && (cat->flags & GF_BUDGET)) )
636 {
637 DB( g_print(" -> affecting %.2f to parent %d, bud=%.2f\n", amount, cat->parent, tmp_budget[cat->parent]) );
638 tmp_spent[cat->parent] += amount;
639 }
640 }
641 }
642
643
644 static void repbudget_compute(GtkWidget *widget, gpointer user_data)
645 {
646 struct repbudget_data *data;
647
648 gint tmpfor, tmpkind, tmponlyout;
649 guint32 mindate, maxdate;
650
651 GtkTreeModel *model;
652 GtkTreeIter iter;
653 GList *list;
654 guint n_result, id;
655 gdouble *tmp_spent, *tmp_budget;
656 gboolean *tmp_hassub;
657 gint nbmonth = 1;
658 gchar *title;
659
660 DB( g_print("\n[repbudget] compute\n") );
661
662 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
663
664 tmpfor = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_for));
665 tmpkind = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_kind));
666 tmponlyout = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_onlyout));
667
668 mindate = data->filter->mindate;
669 maxdate = data->filter->maxdate;
670 if(maxdate < mindate) return;
671
672 g_queue_free (data->txn_queue);
673 data->txn_queue = hbfile_transaction_get_partial_budget(data->filter->mindate, data->filter->maxdate);
674
675 DB( g_print(" for=%d, kind=%d\n", tmpfor, tmpkind) );
676
677 nbmonth = countmonth(mindate, maxdate);
678 DB( g_print(" date: min=%d max=%d nbmonth=%d\n", mindate, maxdate, nbmonth) );
679
680 n_result = da_cat_get_max_key();
681 DB( g_print(" nbcat=%d\n", n_result) );
682
683 /* allocate some memory */
684 tmp_spent = g_malloc0((n_result+1) * sizeof(gdouble));
685 tmp_budget = g_malloc0((n_result+1) * sizeof(gdouble));
686 tmp_hassub = g_malloc0((n_result+1) * sizeof(gboolean));
687
688 if(tmp_spent && tmp_budget && tmp_hassub)
689 {
690 guint i = 0;
691 /* compute the results */
692 data->total_spent = 0.0;
693 data->total_budget = 0.0;
694
695 /* compute budget for each category */
696 DB( g_print("\n+ compute budget\n") );
697
698 //fixed #328034: here <=n_result
699 for(i=1; i<=n_result; i++)
700 {
701 Category *entry;
702
703 entry = da_cat_get(i);
704 if( entry == NULL)
705 continue;
706
707 DB( g_print(" category %d:'%s' issub=%d hasbudget=%d custom=%d\n",
708 entry->key, entry->name, (entry->flags & GF_SUB), (entry->flags & GF_BUDGET), (entry->flags & GF_CUSTOM)) );
709
710 //debug
711 /*#if MYDEBUG == 1
712 gint k;
713 g_print(" budget vector: ");
714 for(k=0;k<13;k++)
715 g_print( " %d:[%.2f]", k, entry->budget[k]);
716 g_print("\n");
717 #endif*/
718
719 // same value each month ?
720 if(!(entry->flags & GF_CUSTOM))
721 {
722 DB( g_print(" - monthly %.2f\n", entry->budget[0]) );
723 tmp_budget[entry->key] += entry->budget[0]*nbmonth;
724 if( entry->flags & GF_SUB )
725 {
726 tmp_budget[entry->parent] += entry->budget[0]*nbmonth;
727 tmp_hassub[entry->parent] = TRUE;
728 }
729 }
730 //otherwise sum each month from mindate month
731 else
732 {
733 gint month = getmonth(mindate);
734 gint j;
735
736 DB( g_print(" - custom each month for %d months\n", nbmonth) );
737 for(j=0;j<nbmonth;j++) {
738 DB( g_print(" j=%d month=%d budg=%.2f\n", j, month, entry->budget[month]) );
739 tmp_budget[entry->key] += entry->budget[month];
740 if( entry->flags & GF_SUB )
741 {
742 tmp_budget[entry->parent] += entry->budget[month];
743 tmp_hassub[entry->parent] = TRUE;
744 }
745 month++;
746 if(month > 12) { month = 1; }
747 }
748 }
749 }
750
751
752 // compute spent for each transaction */
753 DB( g_print("\n+ compute spent from transactions\n") );
754
755 list = g_queue_peek_head_link(data->txn_queue);
756 while (list != NULL)
757 {
758 Transaction *ope = list->data;
759
760 DB( g_print("%d, get ope: %s :: acc=%d, cat=%d, mnt=%.2f\n", i, ope->memo, ope->kacc, ope->kcat, ope->amount) );
761
762 if( ope->flags & OF_SPLIT )
763 {
764 guint nbsplit = da_splits_length(ope->splits);
765 Split *split;
766
767 for(i=0;i<nbsplit;i++)
768 {
769 split = da_splits_get(ope->splits, i);
770 repbudget_compute_cat_spent(split->kcat, hb_amount_base(split->amount, ope->kcur), tmp_spent, tmp_budget);
771 }
772 }
773 else
774 {
775 repbudget_compute_cat_spent(ope->kcat, hb_amount_base(ope->amount, ope->kcur), tmp_spent, tmp_budget);
776 }
777
778 list = g_list_next(list);
779 i++;
780 }
781
782 DB( g_print("\nclear and detach model\n") );
783
784 /* clear and detach our model */
785 model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report));
786 gtk_list_store_clear (GTK_LIST_STORE(model));
787 g_object_ref(model); /* Make sure the model stays with us after the tree view unrefs it */
788 gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_report), NULL); /* Detach model from view */
789
790 for(i=1, id=0; i<=n_result; i++)
791 {
792 gchar *name;
793 gboolean outofbudget;
794 Category *entry;
795
796 entry = da_cat_get(i);
797 if( entry == NULL)
798 continue;
799
800 name = entry->key == 0 ? "(None)" : entry->fullname;
801
802 //#1553862
803 //if( (tmpfor == BUDG_CATEGORY && !(entry->flags & GF_SUB)) || (tmpfor == BUDG_SUBCATEGORY) )
804 if( (tmpfor == BUDG_CATEGORY && !(entry->flags & GF_SUB)) ||
805 (tmpfor == BUDG_SUBCATEGORY && ((entry->flags & GF_SUB) || !tmp_hassub[i]) ) )
806 {
807 guint pos;
808
809 pos = 0;
810 switch(tmpfor)
811 {
812 case BUDG_CATEGORY:
813 {
814 Category *catentry = da_cat_get(i);
815 if(catentry)
816 pos = (catentry->flags & GF_SUB) ? catentry->parent : catentry->key;
817 }
818 break;
819 case BUDG_SUBCATEGORY:
820 pos = i;
821 break;
822 }
823
824 // display expense or income (filter on amount and not category hypothetical flag
825 if( tmpkind == 1 && tmp_budget[pos] > 0)
826 continue;
827
828 if( tmpkind == 2 && tmp_budget[pos] < 0)
829 continue;
830
831 //DB( g_print(" eval %d '%s' : spen=%.2f bud=%.2f \n", i, name, tmp_spent[pos], tmp_budget[pos] ) );
832
833 if((entry->flags & (GF_BUDGET|GF_FORCED)) || tmp_budget[pos] /*|| tmp_spent[pos]*/)
834 {
835 gdouble result, rawrate;
836 gchar *status;
837
838 result = budget_compute_result(tmp_budget[pos], tmp_spent[pos]);
839 rawrate = 0.0;
840 if(ABS(tmp_budget[pos]) > 0)
841 {
842 rawrate = tmp_spent[pos] / tmp_budget[pos];
843 }
844 else if(tmp_budget[pos] == 0.0)
845 rawrate = ABS(tmp_spent[pos]);
846
847 status = "";
848 outofbudget = FALSE;
849 if( result )
850 {
851 if(rawrate > 1.0)
852 {
853 status = _(" over");
854 outofbudget = TRUE;
855 }
856 else
857 {
858 if(tmp_budget[pos] < 0.0)
859 status = _(" left");
860 else if(tmp_budget[pos] > 0.0)
861 {
862 status = _(" under");
863 outofbudget = TRUE;
864 }
865 }
866 }
867
868 if(tmponlyout == TRUE && outofbudget == FALSE)
869 goto nextins;
870
871 DB( g_print(" => insert '%s' s:%.2f b:%.2f r:%.2f (%%%.2f) '%s' '%d'\n\n", name, tmp_spent[pos], tmp_budget[pos], result, rawrate, status, outofbudget ) );
872
873 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
874 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
875 LST_BUDGET_POS, id++,
876 LST_BUDGET_KEY, pos,
877 LST_BUDGET_NAME, name,
878 LST_BUDGET_SPENT, tmp_spent[pos],
879 LST_BUDGET_BUDGET, tmp_budget[pos],
880 LST_BUDGET_RESULT, result,
881 LST_BUDGET_STATUS, status,
882 -1);
883
884 nextins:
885 data->total_spent += tmp_spent[pos];
886 data->total_budget += tmp_budget[pos];
887 }
888 }
889 }
890
891 /* update column 0 title */
892 GtkTreeViewColumn *column = gtk_tree_view_get_column( GTK_TREE_VIEW(data->LV_report), 0);
893 gtk_tree_view_column_set_title(column, _(CYA_CATSUBCAT[tmpfor]));
894
895 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_report));
896
897 /* Re-attach model to view */
898 gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_report), model);
899 g_object_unref(model);
900
901
902 repbudget_update_total(widget, NULL);
903
904 /* update stack chart */
905 title = g_strdup_printf(_("Budget for %s"), _(CYA_CATSUBCAT[tmpfor]) );
906
907 ui_chart_progress_set_currency(GTK_CHARTPROGRESS(data->RE_stack), GLOBALS->kcur);
908
909 /* set chart color scheme */
910 ui_chart_progress_set_color_scheme(GTK_CHARTPROGRESS(data->RE_stack), PREFS->report_color_scheme);
911 ui_chart_progress_set_dualdatas(GTK_CHARTPROGRESS(data->RE_stack), model, _("Budget"), _("Result"), title, NULL);
912
913 g_free(title);
914 }
915
916 //DB( g_print(" inserting %i, %f %f\n", i, total_expense, total_income) );
917
918 /* free our memory */
919 g_free(tmp_hassub);
920 g_free(tmp_spent);
921 g_free(tmp_budget);
922
923 }
924
925
926 static void repbudget_update_total(GtkWidget *widget, gpointer user_data)
927 {
928 struct repbudget_data *data;
929
930 DB( g_print("\n[repbudget] update total\n") );
931
932 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
933
934 GLOBALS->minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
935
936 hb_label_set_colvalue(GTK_LABEL(data->TX_total[0]), data->total_spent, GLOBALS->kcur, GLOBALS->minor);
937 hb_label_set_colvalue(GTK_LABEL(data->TX_total[1]), data->total_budget, GLOBALS->kcur, GLOBALS->minor);
938 hb_label_set_colvalue(GTK_LABEL(data->TX_total[2]), budget_compute_result(data->total_budget, data->total_spent), GLOBALS->kcur, GLOBALS->minor);
939
940
941 }
942
943
944 /*
945 ** update sensitivity
946 */
947 static void repbudget_sensitive(GtkWidget *widget, gpointer user_data)
948 {
949 struct repbudget_data *data;
950 GtkAction *action;
951 gboolean visible, sensitive;
952 gint page;
953
954 DB( g_print("\n[repbudget] sensitive\n") );
955
956 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
957
958 page = gtk_notebook_get_current_page(GTK_NOTEBOOK(data->GR_result));
959
960 visible = page == 0 ? TRUE : FALSE;
961 action = gtk_ui_manager_get_action(data->ui, "/ToolBar/Detail");
962 gtk_action_set_visible (action, visible);
963 //sensitive = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_report)), NULL, NULL);
964 //gtk_action_set_sensitive(action, sensitive);
965 //action = gtk_ui_manager_get_action(data->ui, "/ToolBar/Export");
966 //gtk_action_set_visible (action, visible);
967 hb_widget_visible (data->BT_export, visible);
968
969 sensitive = gtk_tree_model_iter_n_children(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail)), NULL) > 0 ? TRUE : FALSE;
970 gtk_widget_set_sensitive(data->MI_detailtoclip, sensitive);
971 gtk_widget_set_sensitive(data->MI_detailtocsv, sensitive);
972
973 }
974
975
976 static void repbudget_toggle(GtkWidget *widget, gpointer user_data)
977 {
978 struct repbudget_data *data;
979 gboolean minor;
980
981 DB( g_print("\n[repbudget] toggle\n") );
982
983 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
984
985 repbudget_update_total(widget, NULL);
986
987 //hbfile_update(data->LV_acc, (gpointer)4);
988 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_report));
989
990 minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
991 ui_chart_progress_show_minor(GTK_CHARTPROGRESS(data->RE_stack), minor);
992
993 }
994
995 static void repbudget_setup(struct repbudget_data *data)
996 {
997 DB( g_print("\n[repbudget] setup\n") );
998
999 data->txn_queue = g_queue_new ();
1000
1001 data->filter = da_flt_malloc();
1002 filter_reset(data->filter);
1003
1004 data->detail = PREFS->budg_showdetail;
1005 data->legend = 1;
1006
1007 /* 3.4 : make int transfer out of stats */
1008 data->filter->option[FILTER_PAYMODE] = 1;
1009 data->filter->paymode[PAYMODE_INTXFER] = FALSE;
1010
1011 filter_preset_daterange_set(data->filter, PREFS->date_range_rep, 0);
1012
1013 g_signal_handler_block(data->PO_mindate, data->handler_id[HID_REPBUDGET_MINDATE]);
1014 g_signal_handler_block(data->PO_maxdate, data->handler_id[HID_REPBUDGET_MAXDATE]);
1015
1016 gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_mindate), data->filter->mindate);
1017 gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_maxdate), data->filter->maxdate);
1018
1019 g_signal_handler_unblock(data->PO_mindate, data->handler_id[HID_REPBUDGET_MINDATE]);
1020 g_signal_handler_unblock(data->PO_maxdate, data->handler_id[HID_REPBUDGET_MAXDATE]);
1021
1022 }
1023
1024
1025 static void repbudget_selection(GtkTreeSelection *treeselection, gpointer user_data)
1026 {
1027 GtkTreeModel *model;
1028 GtkTreeIter iter;
1029 guint key = -1;
1030
1031 DB( g_print("\n[repbudget] selection\n") );
1032
1033 if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
1034 {
1035 gtk_tree_model_get(model, &iter, LST_BUDGET_KEY, &key, -1);
1036 }
1037
1038 DB( g_print(" - active is %d\n", key) );
1039
1040 repbudget_detail(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(key));
1041 repbudget_sensitive(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
1042 }
1043
1044
1045 /*
1046 **
1047 */
1048 static gboolean repbudget_window_dispose(GtkWidget *widget, GdkEvent *event, gpointer user_data)
1049 {
1050 struct repbudget_data *data = user_data;
1051 struct WinGeometry *wg;
1052
1053 DB( g_print("\n[repbudget] start dispose\n") );
1054
1055 g_queue_free (data->txn_queue);
1056
1057 da_flt_free(data->filter);
1058
1059 g_free(data);
1060
1061 //store position and size
1062 wg = &PREFS->bud_wg;
1063 gtk_window_get_position(GTK_WINDOW(widget), &wg->l, &wg->t);
1064 gtk_window_get_size(GTK_WINDOW(widget), &wg->w, &wg->h);
1065
1066 DB( g_print(" window: l=%d, t=%d, w=%d, h=%d\n", wg->l, wg->t, wg->w, wg->h) );
1067
1068 //enable define windows
1069 GLOBALS->define_off--;
1070 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
1071
1072 DB( g_print("\n[repbudget] end dispose\n") );
1073
1074 return FALSE;
1075 }
1076
1077
1078 // the window creation
1079 GtkWidget *repbudget_window_new(void)
1080 {
1081 struct repbudget_data *data;
1082 struct WinGeometry *wg;
1083 GtkWidget *window, *mainvbox, *hbox, *vbox, *notebook, *treeview;
1084 GtkWidget *label, *widget, *table, *entry;
1085 gint row;
1086 GtkUIManager *ui;
1087 GtkActionGroup *actions;
1088 GtkAction *action;
1089 GError *error = NULL;
1090
1091 data = g_malloc0(sizeof(struct repbudget_data));
1092 if(!data) return NULL;
1093
1094 DB( g_print("\n[repbudget] new\n") );
1095
1096 //disable define windows
1097 GLOBALS->define_off++;
1098 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
1099
1100 /* create window, etc */
1101 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1102 data->window = window;
1103
1104 //store our window private data
1105 g_object_set_data(G_OBJECT(window), "inst_data", (gpointer)data);
1106 DB( g_print(" - new window=%p, inst_data=%p\n", window, data) );
1107
1108 gtk_window_set_title (GTK_WINDOW (window), _("Budget report"));
1109
1110 //set the window icon
1111 gtk_window_set_icon_name(GTK_WINDOW (window), ICONNAME_HB_REP_BUDGET);
1112
1113
1114 //window contents
1115 mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1116 gtk_container_add (GTK_CONTAINER (window), mainvbox);
1117
1118 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1119 gtk_box_pack_start (GTK_BOX (mainvbox), hbox, TRUE, TRUE, 0);
1120
1121 //control part
1122 table = gtk_grid_new ();
1123 // gtk_alignment_new(xalign, yalign, xscale, yscale)
1124 //alignment = gtk_alignment_new(0.0, 0.0, 0.0, 0.0);
1125 //gtk_container_add(GTK_CONTAINER(alignment), table);
1126 gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, FALSE, 0);
1127
1128 gtk_container_set_border_width (GTK_CONTAINER (table), SPACING_SMALL);
1129 gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_SMALL);
1130 gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM);
1131
1132 row = 0;
1133 label = make_label_group(_("Display"));
1134 gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
1135
1136 row++;
1137 label = make_label_widget(_("_View by:"));
1138 gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
1139 widget = make_cycle(label, CYA_CATSUBCAT);
1140 data->CY_for = widget;
1141 gtk_grid_attach (GTK_GRID (table), data->CY_for, 2, row, 1, 1);
1142
1143
1144 row++;
1145 label = make_label_widget(_("_Type:"));
1146 gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
1147 widget = make_cycle(label, CYA_KIND);
1148 data->CY_kind = widget;
1149 gtk_grid_attach (GTK_GRID (table), data->CY_kind, 2, row, 1, 1);
1150
1151 row++;
1152 widget = gtk_check_button_new_with_mnemonic (_("Only out of budget"));
1153 data->CM_onlyout = widget;
1154 gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
1155
1156 row++;
1157 widget = gtk_check_button_new_with_mnemonic (_("Euro _minor"));
1158 data->CM_minor = widget;
1159 gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
1160
1161 row++;
1162 widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1163 gtk_grid_attach (GTK_GRID (table), widget, 0, row, 3, 1);
1164
1165 row++;
1166 label = make_label_group(_("Date filter"));
1167 gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
1168
1169 row++;
1170 label = make_label_widget(_("_Range:"));
1171 gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
1172 data->CY_range = make_daterange(label, FALSE);
1173 gtk_grid_attach (GTK_GRID (table), data->CY_range, 2, row, 1, 1);
1174
1175 row++;
1176 label = make_label_widget(_("_From:"));
1177 gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
1178 data->PO_mindate = gtk_date_entry_new();
1179 gtk_grid_attach (GTK_GRID (table), data->PO_mindate, 2, row, 1, 1);
1180
1181 row++;
1182 label = make_label_widget(_("_To:"));
1183 gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
1184 data->PO_maxdate = gtk_date_entry_new();
1185 gtk_grid_attach (GTK_GRID (table), data->PO_maxdate, 2, row, 1, 1);
1186
1187
1188 //part: info + report
1189 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1190 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1191
1192 //ui manager
1193 actions = gtk_action_group_new ("default");
1194
1195 //as we use gettext
1196 gtk_action_group_set_translation_domain(actions, GETTEXT_PACKAGE);
1197
1198 // data to action callbacks is set here (data)
1199 gtk_action_group_add_radio_actions (actions, radio_entries, n_radio_entries, 0, G_CALLBACK(repbudget_action_mode), data);
1200
1201 gtk_action_group_add_actions (actions, entries, n_entries, data);
1202
1203 gtk_action_group_add_toggle_actions (actions,
1204 toggle_entries, n_toggle_entries,
1205 data);
1206
1207
1208 /* set which action should have priority in the toolbar */
1209 //action = gtk_action_group_get_action(actions, "List");
1210 //g_object_set(action, "is_important", TRUE, NULL);
1211
1212 //action = gtk_action_group_get_action(actions, "Stack");
1213 //g_object_set(action, "is_important", TRUE, NULL);
1214
1215 action = gtk_action_group_get_action(actions, "Detail");
1216 //g_object_set(action, "is_important", TRUE, NULL);
1217 g_object_set(action, "active", PREFS->budg_showdetail, NULL);
1218
1219 //action = gtk_action_group_get_action(actions, "Refresh");
1220 //g_object_set(action, "is_important", TRUE, NULL);
1221
1222
1223 ui = gtk_ui_manager_new ();
1224 gtk_ui_manager_insert_action_group (ui, actions, 0);
1225 gtk_window_add_accel_group (GTK_WINDOW (window), gtk_ui_manager_get_accel_group (ui));
1226
1227 if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error))
1228 {
1229 g_message ("building UI failed: %s", error->message);
1230 g_error_free (error);
1231 }
1232
1233 data->ui = ui;
1234
1235 //toolbar
1236 data->TB_bar = gtk_ui_manager_get_widget (ui, "/ToolBar");
1237 gtk_box_pack_start (GTK_BOX (vbox), data->TB_bar, FALSE, FALSE, 0);
1238
1239 //add export menu button
1240 GtkToolItem *toolitem;
1241 GtkWidget *menu, *menuitem, *image;
1242
1243 menu = gtk_menu_new ();
1244 //gtk_widget_set_halign (menu, GTK_ALIGN_END);
1245
1246 menuitem = gtk_menu_item_new_with_mnemonic (_("_Result to clipboard"));
1247 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
1248 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (repbudget_export_result_clipboard), data);
1249
1250 menuitem = gtk_menu_item_new_with_mnemonic (_("_Result to CSV"));
1251 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
1252 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (repbudget_export_result_csv), data);
1253
1254 menuitem = gtk_menu_item_new_with_mnemonic (_("_Detail to clipboard"));
1255 data->MI_detailtoclip = menuitem;
1256 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
1257 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (repbudget_export_detail_clipboard), data);
1258
1259 menuitem = gtk_menu_item_new_with_mnemonic (_("_Detail to CSV"));
1260 data->MI_detailtocsv = menuitem;
1261 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
1262 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (repbudget_export_detail_csv), data);
1263
1264 gtk_widget_show_all (menu);
1265
1266 widget = gtk_menu_button_new();
1267 data->BT_export = widget;
1268 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(widget)), GTK_STYLE_CLASS_FLAT);
1269
1270 //gtk_menu_button_set_direction (GTK_MENU_BUTTON(widget), GTK_ARROW_DOWN);
1271 //gtk_widget_set_halign (widget, GTK_ALIGN_END);
1272 image = gtk_image_new_from_icon_name (ICONNAME_HB_FILE_EXPORT, GTK_ICON_SIZE_LARGE_TOOLBAR);
1273 g_object_set (widget, "image", image, "popup", GTK_MENU(menu), NULL);
1274
1275 toolitem = gtk_tool_item_new();
1276 gtk_container_add (GTK_CONTAINER(toolitem), widget);
1277 gtk_toolbar_insert(GTK_TOOLBAR(data->TB_bar), GTK_TOOL_ITEM(toolitem), -1);
1278
1279 //infos
1280 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
1281 gtk_container_set_border_width (GTK_CONTAINER(hbox), SPACING_SMALL);
1282 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1283
1284 widget = make_label(NULL, 0.5, 0.5);
1285 gimp_label_set_attributes (GTK_LABEL (widget), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
1286 data->TX_daterange = widget;
1287 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
1288
1289
1290 entry = gtk_label_new(NULL);
1291 data->TX_total[2] = entry;
1292 gtk_box_pack_end (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
1293 label = gtk_label_new(_("Result:"));
1294 gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1295
1296 entry = gtk_label_new(NULL);
1297 data->TX_total[1] = entry;
1298 gtk_box_pack_end (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
1299 label = gtk_label_new(_("Budget:"));
1300 gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1301
1302 entry = gtk_label_new(NULL);
1303 data->TX_total[0] = entry;
1304 gtk_box_pack_end (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
1305 label = gtk_label_new(_("Spent:"));
1306 gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1307
1308
1309 notebook = gtk_notebook_new();
1310 data->GR_result = notebook;
1311 gtk_widget_show(notebook);
1312 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
1313 gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
1314
1315 gtk_box_pack_start (GTK_BOX (vbox), notebook, TRUE, TRUE, 0);
1316
1317 //page: list
1318
1319 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1320 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, NULL);
1321
1322 widget = gtk_scrolled_window_new (NULL, NULL);
1323 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_ETCHED_IN);
1324 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1325 treeview = create_list_budget();
1326 data->LV_report = treeview;
1327 gtk_container_add (GTK_CONTAINER(widget), treeview);
1328 gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
1329
1330 //detail
1331 widget = gtk_scrolled_window_new (NULL, NULL);
1332 data->GR_detail = widget;
1333 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_ETCHED_IN);
1334 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1335 treeview = create_list_transaction(LIST_TXN_TYPE_DETAIL, PREFS->lst_ope_columns);
1336 data->LV_detail = treeview;
1337 gtk_container_add (GTK_CONTAINER(widget), treeview);
1338
1339 gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
1340
1341 //page: 2d bar
1342 //widget = gtk_chart_new(CHART_TYPE_COL);
1343 widget = ui_chart_progress_new();
1344 data->RE_stack = widget;
1345 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), widget, NULL);
1346
1347 //todo:should move this
1348 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_minor),GLOBALS->minor);
1349
1350
1351 /* attach our minor to treeview */
1352 g_object_set_data(G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report))), "minor", (gpointer)data->CM_minor);
1353 g_object_set_data(G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail))), "minor", (gpointer)data->CM_minor);
1354
1355 /* signal connect */
1356 g_signal_connect (window, "delete-event", G_CALLBACK (repbudget_window_dispose), (gpointer)data);
1357
1358 g_signal_connect (data->CM_onlyout, "toggled", G_CALLBACK (repbudget_compute), NULL);
1359 g_signal_connect (data->CM_minor, "toggled", G_CALLBACK (repbudget_toggle), NULL);
1360
1361
1362 data->handler_id[HID_REPBUDGET_RANGE] = g_signal_connect (data->CY_range, "changed", G_CALLBACK (repbudget_range_change), NULL);
1363
1364 g_signal_connect (data->CY_for , "changed", G_CALLBACK (repbudget_compute), (gpointer)data);
1365 g_signal_connect (data->CY_kind, "changed", G_CALLBACK (repbudget_compute), (gpointer)data);
1366
1367 data->handler_id[HID_REPBUDGET_MINDATE] = g_signal_connect (data->PO_mindate, "changed", G_CALLBACK (repbudget_date_change), (gpointer)data);
1368 data->handler_id[HID_REPBUDGET_MAXDATE] = g_signal_connect (data->PO_maxdate, "changed", G_CALLBACK (repbudget_date_change), (gpointer)data);
1369
1370 g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_report)), "changed", G_CALLBACK (repbudget_selection), NULL);
1371
1372 g_signal_connect (GTK_TREE_VIEW(data->LV_detail), "row-activated", G_CALLBACK (repbudget_detail_onRowActivated), NULL);
1373
1374
1375 //setup, init and show window
1376 repbudget_setup(data);
1377
1378
1379 /* toolbar */
1380 if(PREFS->toolbar_style == 0)
1381 gtk_toolbar_unset_style(GTK_TOOLBAR(data->TB_bar));
1382 else
1383 gtk_toolbar_set_style(GTK_TOOLBAR(data->TB_bar), PREFS->toolbar_style-1);
1384
1385
1386 //setup, init and show window
1387 wg = &PREFS->bud_wg;
1388 gtk_window_move(GTK_WINDOW(window), wg->l, wg->t);
1389 gtk_window_resize(GTK_WINDOW(window), wg->w, wg->h);
1390
1391
1392
1393 gtk_widget_show_all (window);
1394
1395
1396 //minor ?
1397 if( PREFS->euro_active )
1398 gtk_widget_show(data->CM_minor);
1399 else
1400 gtk_widget_hide(data->CM_minor);
1401
1402 //check for any account included into the budget or warn
1403 {
1404 guint count =0;
1405 GList *lacc, *list;
1406
1407 lacc = list = g_hash_table_get_values(GLOBALS->h_acc);
1408
1409 while (list != NULL)
1410 {
1411 Account *acc;
1412 acc = list->data;
1413 //#1674045 ony rely on nosummary
1414 //if((acc->flags & (AF_CLOSED|AF_NOREPORT))) goto next1;
1415 if((acc->flags & (AF_NOREPORT))) goto next1;
1416 if(!(acc->flags & AF_NOBUDGET))
1417 count++;
1418 next1:
1419 list = g_list_next(list);
1420 }
1421 g_list_free(lacc);
1422
1423 if(count <= 0)
1424 {
1425 ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_WARNING,
1426 _("No account is defined to be part of the budget."),
1427 _("You should include some accounts from the account dialog.")
1428 );
1429 }
1430
1431
1432
1433 }
1434
1435
1436
1437 //gtk_widget_hide(data->GR_detail);
1438
1439 repbudget_sensitive(window, NULL);
1440 repbudget_update_detail(window, NULL);
1441
1442 if( PREFS->date_range_rep != 0)
1443 gtk_combo_box_set_active(GTK_COMBO_BOX(data->CY_range), PREFS->date_range_rep);
1444 else
1445 repbudget_compute(window, NULL);
1446
1447 return(window);
1448 }
1449
1450
1451 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1452
1453 static GString *ui_list_repbudget_to_string(GtkTreeView *treeview, gboolean clipboard)
1454 {
1455 GString *node;
1456 GtkTreeModel *model;
1457 GtkTreeIter iter;
1458 gboolean valid;
1459 const gchar *format;
1460
1461 node = g_string_new(NULL);
1462
1463 // header
1464 format = (clipboard == TRUE) ? "%s\t%s\t%s\t%s\t\n" : "%s;%s;%s;%s;\n";
1465 g_string_append_printf(node, format, _("Category"), _("Spent"), _("Budget"), _("Result"));
1466
1467 model = gtk_tree_view_get_model(treeview);
1468 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
1469 while (valid)
1470 {
1471 gchar *name, *status;
1472 gdouble spent, budget, result;
1473
1474 gtk_tree_model_get (model, &iter,
1475 //LST_REPDIST_KEY, i,
1476 LST_BUDGET_NAME, &name,
1477 LST_BUDGET_SPENT, &spent,
1478 LST_BUDGET_BUDGET, &budget,
1479 LST_BUDGET_RESULT, &result,
1480 LST_BUDGET_STATUS, &status,
1481 -1);
1482
1483 format = (clipboard == TRUE) ? "%s\t%.2f\t%.2f\t%.2f\t%s\n" : "%s;%.2f;%.2f;%.2f;%s\n";
1484 g_string_append_printf(node, format, name, spent, budget, result, status);
1485
1486 //leak
1487 g_free(name);
1488 g_free(status);
1489
1490 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
1491 }
1492
1493 //DB( g_print("text is:\n%s", node->str) );
1494
1495 return node;
1496 }
1497
1498
1499
1500
1501 /*
1502 **
1503 ** The function should return:
1504 ** a negative integer if the first value comes before the second,
1505 ** 0 if they are equal,
1506 ** or a positive integer if the first value comes after the second.
1507 */
1508 static gint budget_listview_compare_funct (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
1509 {
1510 gint retval = 0;
1511 //Category *entry1, *entry2;
1512 gchar *entry1, *entry2;
1513
1514 gtk_tree_model_get(model, a, LST_BUDGET_NAME, &entry1, -1);
1515 gtk_tree_model_get(model, b, LST_BUDGET_NAME, &entry2, -1);
1516
1517 retval = hb_string_utf8_compare(entry1, entry2);
1518
1519
1520 g_free(entry2);
1521 g_free(entry1);
1522
1523 return retval;
1524 }
1525
1526
1527 static void budget_amount_cell_data_function (GtkTreeViewColumn *col,
1528 GtkCellRenderer *renderer,
1529 GtkTreeModel *model,
1530 GtkTreeIter *iter,
1531 gpointer user_data)
1532 {
1533 gdouble value;
1534 gchar *color;
1535 gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
1536 gint column_id = GPOINTER_TO_INT(user_data);
1537
1538 gtk_tree_model_get(model, iter, column_id, &value, -1);
1539
1540 if( value )
1541 {
1542 hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, value, GLOBALS->kcur, GLOBALS->minor);
1543
1544 if( column_id == LST_BUDGET_RESULT)
1545 color = get_minimum_color_amount (value, 0.0);
1546 else
1547 color = get_normal_color_amount(value);
1548
1549 g_object_set(renderer,
1550 "foreground", color,
1551 "text", buf,
1552 NULL);
1553 }
1554 else
1555 {
1556 g_object_set(renderer, "text", "", NULL);
1557 }
1558 }
1559
1560
1561
1562 static void budget_result_cell_data_function (GtkTreeViewColumn *col,
1563 GtkCellRenderer *renderer,
1564 GtkTreeModel *model,
1565 GtkTreeIter *iter,
1566 gpointer user_data)
1567 {
1568 gdouble value;
1569 gchar *color;
1570 gchar *status;
1571 gint column_id = GPOINTER_TO_INT(user_data);
1572
1573 gtk_tree_model_get(model, iter,
1574 column_id, &value,
1575 LST_BUDGET_STATUS, &status,
1576 -1);
1577
1578 if( value )
1579 {
1580 color = get_minimum_color_amount (value, 0.0);
1581 g_object_set(renderer,
1582 "foreground", color,
1583 "text", status,
1584 NULL);
1585 }
1586 else
1587 {
1588 g_object_set(renderer, "text", "", NULL);
1589 }
1590
1591 //leak
1592 g_free(status);
1593 }
1594
1595
1596
1597 static GtkTreeViewColumn *amount_list_budget_column(gchar *name, gint id)
1598 {
1599 GtkTreeViewColumn *column;
1600 GtkCellRenderer *renderer;
1601
1602 column = gtk_tree_view_column_new();
1603 gtk_tree_view_column_set_title(column, name);
1604 renderer = gtk_cell_renderer_text_new ();
1605 g_object_set(renderer, "xalign", 1.0, NULL);
1606 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1607 gtk_tree_view_column_set_cell_data_func(column, renderer, budget_amount_cell_data_function, GINT_TO_POINTER(id), NULL);
1608 gtk_tree_view_column_set_alignment (column, 0.5);
1609 //gtk_tree_view_column_set_sort_column_id (column, id);
1610 return column;
1611 }
1612
1613 /*
1614 ** create our statistic list
1615 */
1616 static GtkWidget *create_list_budget(void)
1617 {
1618 GtkListStore *store;
1619 GtkWidget *view;
1620 GtkCellRenderer *renderer;
1621 GtkTreeViewColumn *column;
1622
1623 DB( g_print("\n[repbudget] create list\n") );
1624
1625
1626 /* create list store */
1627 store = gtk_list_store_new(
1628 NUM_LST_BUDGET,
1629 G_TYPE_INT,
1630 G_TYPE_INT,
1631 G_TYPE_STRING,
1632 G_TYPE_DOUBLE, //spent
1633 G_TYPE_DOUBLE, //budget
1634 G_TYPE_DOUBLE, //result
1635 G_TYPE_STRING //status
1636 );
1637
1638 //treeview
1639 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1640 g_object_unref(store);
1641
1642 gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), PREFS->grid_lines);
1643
1644 /* column: Name */
1645 column = gtk_tree_view_column_new();
1646 gtk_tree_view_column_set_title(column, _("Category"));
1647 renderer = gtk_cell_renderer_text_new ();
1648 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1649 //gtk_tree_view_column_set_cell_data_func(column, renderer, ope_result_cell_data_function, NULL, NULL);
1650 gtk_tree_view_column_add_attribute(column, renderer, "text", LST_BUDGET_NAME);
1651 //gtk_tree_view_column_set_sort_column_id (column, LST_STAT_NAME);
1652 gtk_tree_view_column_set_resizable(column, TRUE);
1653 gtk_tree_view_column_set_alignment (column, 0.5);
1654 gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
1655
1656 /* column: Expense */
1657 column = amount_list_budget_column(_("Spent"), LST_BUDGET_SPENT);
1658 gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
1659
1660 /* column: Income */
1661 column = amount_list_budget_column(_("Budget"), LST_BUDGET_BUDGET);
1662 gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
1663
1664 /* column: Result */
1665 column = amount_list_budget_column(_("Result"), LST_BUDGET_RESULT);
1666 renderer = gtk_cell_renderer_text_new ();
1667 //g_object_set(renderer, "xalign", 0.0, NULL);
1668 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1669 gtk_tree_view_column_set_cell_data_func(column, renderer, budget_result_cell_data_function, GINT_TO_POINTER(LST_BUDGET_RESULT), NULL);
1670 gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
1671
1672 /* column last: empty */
1673 column = gtk_tree_view_column_new();
1674 gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
1675
1676 /* sort */
1677 gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), budget_listview_compare_funct, NULL, NULL);
1678 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
1679
1680 /*
1681 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_BUDGET_POS , stat_list_compare_func, GINT_TO_POINTER(LST_BUDGET_POS), NULL);
1682 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_BUDGET_SPENT , stat_list_compare_func, GINT_TO_POINTER(LST_BUDGET_SPENT), NULL);
1683 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_BUDGET_BUDGET, stat_list_compare_func, GINT_TO_POINTER(LST_BUDGET_BUDGET), NULL);
1684 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_BUDGET_RESULT , stat_list_compare_func, GINT_TO_POINTER(LST_BUDGET_RESULT), NULL);
1685 */
1686
1687 return(view);
1688 }
1689
This page took 0.117799 seconds and 4 git commands to generate.