]> Dogcows Code - chaz/homebank/blob - src/rep-budget.c
import homebank-5.2.6
[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 hbtk_combo_box_set_active_id(GTK_COMBO_BOX_TEXT(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 = hbtk_combo_box_get_active_id(GTK_COMBO_BOX_TEXT(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 gdouble dtlamt = ope->amount;
539 guint pos = 0;
540 gboolean insert = FALSE;
541
542 //DB( g_print(" get %s\n", ope->ope_Word) );
543
544 acc = da_acc_get(ope->kacc);
545 if(acc != NULL)
546 {
547 if((acc->flags & AF_NOBUDGET)) goto next1;
548 }
549
550 //filter here
551 if( ope->flags & OF_SPLIT )
552 {
553 guint nbsplit = da_splits_length(ope->splits);
554 Split *split;
555 guint i;
556
557 dtlamt = 0.0;
558 for(i=0;i<nbsplit;i++)
559 {
560 split = da_splits_get(ope->splits, i);
561 switch(tmpfor)
562 {
563 case BUDG_CATEGORY:
564 {
565 Category *catentry = da_cat_get(split->kcat);
566 if(catentry)
567 pos = (catentry->flags & GF_SUB) ? catentry->parent : catentry->key;
568 }
569 break;
570 case BUDG_SUBCATEGORY:
571 pos = split->kcat;
572 break;
573 }
574
575 if( pos == active )
576 {
577 insert = TRUE;
578 dtlamt += split->amount;
579 // no more break here as we need to compute split 4 cat
580 //break;
581 }
582 }
583 }
584 else
585 {
586 switch(tmpfor)
587 {
588 case BUDG_CATEGORY:
589 {
590 Category *catentry = da_cat_get(ope->kcat);
591 if(catentry)
592 pos = (catentry->flags & GF_SUB) ? catentry->parent : catentry->key;
593 }
594 break;
595 case BUDG_SUBCATEGORY:
596 pos = ope->kcat;
597 break;
598 }
599
600 if( pos == active )
601 insert = TRUE;
602 }
603
604 //insert
605 if( insert == TRUE )
606 {
607
608 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
609 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
610 MODEL_TXN_POINTER, ope,
611 MODEL_TXN_SPLITAMT, dtlamt,
612 -1);
613 }
614
615
616 next1:
617 list = g_list_next(list);
618 }
619
620 /* Re-attach model to view */
621 gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_detail), model);
622 g_object_unref(model);
623
624 gtk_tree_view_columns_autosize( GTK_TREE_VIEW(data->LV_detail) );
625
626 }
627
628 }
629
630
631 static void repbudget_compute_cat_spent(guint32 key, gdouble amount, gdouble *tmp_spent, gdouble *tmp_budget)
632 {
633 Category *cat;
634
635 cat = da_cat_get(key);
636 if(cat)
637 {
638 DB( g_print(" cat %02d:%02d (sub=%d), bud=%.2f\n", cat->parent, cat->key, (cat->flags & GF_SUB), tmp_budget[cat->key]) );
639
640 if( (cat->flags & GF_FORCED) || (cat->flags & GF_BUDGET) )
641 {
642 DB( g_print(" + spend %.2f to cat %d\n", amount, cat->key) );
643 tmp_spent[cat->key] += amount;
644 }
645
646 //#1825653 subcat without budget must be computed
647 if( (cat->flags & GF_SUB) )
648 {
649 Category *pcat = da_cat_get(cat->parent);
650
651 if(pcat)
652 {
653 if( (cat->flags & GF_FORCED) || (cat->flags & GF_BUDGET) || (pcat->flags & GF_FORCED) || (pcat->flags & GF_BUDGET) )
654 {
655 DB( g_print(" + spend %.2f to parent %d\n", amount, cat->parent) );
656 tmp_spent[pcat->key] += amount;
657 }
658
659 }
660 }
661 }
662 }
663
664
665 static void repbudget_compute(GtkWidget *widget, gpointer user_data)
666 {
667 struct repbudget_data *data;
668
669 gint tmpfor, tmpkind, tmponlyout;
670 guint32 mindate, maxdate;
671
672 GtkTreeModel *model;
673 GtkTreeIter iter;
674 GList *list;
675 guint n_result, id;
676 gdouble *tmp_spent, *tmp_budget;
677 gboolean *tmp_hassub;
678 gint nbmonth = 1;
679 gchar *title;
680
681 DB( g_print("\n[repbudget] compute\n") );
682
683 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
684
685 tmpfor = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_for));
686 tmpkind = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_kind));
687 tmponlyout = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_onlyout));
688
689 mindate = data->filter->mindate;
690 maxdate = data->filter->maxdate;
691 if(maxdate < mindate) return;
692
693 g_queue_free (data->txn_queue);
694 data->txn_queue = hbfile_transaction_get_partial_budget(data->filter->mindate, data->filter->maxdate);
695
696 DB( g_print(" for=%d, kind=%d\n", tmpfor, tmpkind) );
697
698 nbmonth = countmonth(mindate, maxdate);
699 DB( g_print(" date: min=%d max=%d nbmonth=%d\n", mindate, maxdate, nbmonth) );
700
701 n_result = da_cat_get_max_key();
702 DB( g_print(" nbcat=%d\n", n_result) );
703
704 /* allocate some memory */
705 tmp_spent = g_malloc0((n_result+1) * sizeof(gdouble));
706 tmp_budget = g_malloc0((n_result+1) * sizeof(gdouble));
707 tmp_hassub = g_malloc0((n_result+1) * sizeof(gboolean));
708
709 if(tmp_spent && tmp_budget && tmp_hassub)
710 {
711 guint i = 0;
712 /* compute the results */
713 data->total_spent = 0.0;
714 data->total_budget = 0.0;
715
716 /* compute budget for each category */
717 DB( g_print("\n+ compute budget\n") );
718
719 //fixed #328034: here <=n_result
720 for(i=1; i<=n_result; i++)
721 {
722 Category *entry;
723
724 entry = da_cat_get(i);
725 if( entry == NULL)
726 continue;
727
728 DB( g_print(" %d:'%s' issub=%d hasbudget=%d custom=%d\n",
729 entry->key, entry->name, (entry->flags & GF_SUB), (entry->flags & GF_BUDGET), (entry->flags & GF_CUSTOM)) );
730
731 //debug
732 /*#if MYDEBUG == 1
733 gint k;
734 g_print(" budget vector: ");
735 for(k=0;k<13;k++)
736 g_print( " %d:[%.2f]", k, entry->budget[k]);
737 g_print("\n");
738 #endif*/
739
740 // same value each month ?
741 if(!(entry->flags & GF_CUSTOM))
742 {
743 DB( g_print(" - monthly %.2f\n", entry->budget[0]) );
744 tmp_budget[entry->key] += entry->budget[0]*nbmonth;
745 if( entry->flags & GF_SUB )
746 {
747 tmp_budget[entry->parent] += entry->budget[0]*nbmonth;
748 tmp_hassub[entry->parent] = TRUE;
749 }
750 }
751 //otherwise sum each month from mindate month
752 else
753 {
754 gint month = getmonth(mindate);
755 gint j;
756
757 DB( g_print(" - custom each month for %d months\n", nbmonth) );
758 for(j=0;j<nbmonth;j++) {
759 DB( g_print(" j=%d month=%d budg=%.2f\n", j, month, entry->budget[month]) );
760 tmp_budget[entry->key] += entry->budget[month];
761 if( entry->flags & GF_SUB )
762 {
763 tmp_budget[entry->parent] += entry->budget[month];
764 tmp_hassub[entry->parent] = TRUE;
765 }
766 month++;
767 if(month > 12) { month = 1; }
768 }
769 }
770 }
771
772
773 // compute spent for each transaction */
774 DB( g_print("\n+ compute spent from transactions\n") );
775
776 list = g_queue_peek_head_link(data->txn_queue);
777 while (list != NULL)
778 {
779 Transaction *ope = list->data;
780
781 DB( g_print("ope: %s :: acc=%d, cat=%d, mnt=%.2f\n", ope->memo, ope->kacc, ope->kcat, ope->amount) );
782
783 if( ope->flags & OF_SPLIT )
784 {
785 guint nbsplit = da_splits_length(ope->splits);
786 Split *split;
787
788 for(i=0;i<nbsplit;i++)
789 {
790 split = da_splits_get(ope->splits, i);
791 repbudget_compute_cat_spent(split->kcat, hb_amount_base(split->amount, ope->kcur), tmp_spent, tmp_budget);
792 }
793 }
794 else
795 {
796 repbudget_compute_cat_spent(ope->kcat, hb_amount_base(ope->amount, ope->kcur), tmp_spent, tmp_budget);
797 }
798
799 list = g_list_next(list);
800 i++;
801 }
802
803 DB( g_print("\nclear and detach model\n") );
804
805 /* clear and detach our model */
806 model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report));
807 gtk_list_store_clear (GTK_LIST_STORE(model));
808 g_object_ref(model); /* Make sure the model stays with us after the tree view unrefs it */
809 gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_report), NULL); /* Detach model from view */
810
811 for(i=1, id=0; i<=n_result; i++)
812 {
813 gchar *name;
814 gboolean outofbudget;
815 Category *entry;
816
817 entry = da_cat_get(i);
818 if( entry == NULL)
819 continue;
820
821 name = entry->key == 0 ? "(None)" : entry->fullname;
822
823 //#1553862
824 //if( (tmpfor == BUDG_CATEGORY && !(entry->flags & GF_SUB)) || (tmpfor == BUDG_SUBCATEGORY) )
825 if( (tmpfor == BUDG_CATEGORY && !(entry->flags & GF_SUB)) ||
826 (tmpfor == BUDG_SUBCATEGORY && ((entry->flags & GF_SUB) || !tmp_hassub[i]) ) )
827 {
828 guint pos;
829
830 pos = 0;
831 switch(tmpfor)
832 {
833 case BUDG_CATEGORY:
834 {
835 Category *catentry = da_cat_get(i);
836 if(catentry)
837 pos = (catentry->flags & GF_SUB) ? catentry->parent : catentry->key;
838 }
839 break;
840 case BUDG_SUBCATEGORY:
841 pos = i;
842 break;
843 }
844
845 // display expense or income (filter on amount and not category hypothetical flag
846 if( tmpkind == 1 && tmp_budget[pos] > 0)
847 continue;
848
849 if( tmpkind == 2 && tmp_budget[pos] < 0)
850 continue;
851
852 //DB( g_print(" eval %d '%s' : spen=%.2f bud=%.2f \n", i, name, tmp_spent[pos], tmp_budget[pos] ) );
853
854 if((entry->flags & (GF_BUDGET|GF_FORCED)) || tmp_budget[pos] /*|| tmp_spent[pos]*/)
855 {
856 gdouble result, rawrate;
857 gchar *status;
858
859 result = budget_compute_result(tmp_budget[pos], tmp_spent[pos]);
860 rawrate = 0.0;
861 if(ABS(tmp_budget[pos]) > 0)
862 {
863 rawrate = tmp_spent[pos] / tmp_budget[pos];
864 }
865 else if(tmp_budget[pos] == 0.0)
866 rawrate = ABS(tmp_spent[pos]);
867
868 status = "";
869 outofbudget = FALSE;
870 if( result )
871 {
872 if(rawrate > 1.0)
873 {
874 status = _(" over");
875 outofbudget = TRUE;
876 }
877 else
878 {
879 if(tmp_budget[pos] < 0.0)
880 status = _(" left");
881 else if(tmp_budget[pos] > 0.0)
882 {
883 status = _(" under");
884 outofbudget = TRUE;
885 }
886 }
887 }
888
889 if(tmponlyout == TRUE && outofbudget == FALSE)
890 goto nextins;
891
892 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 ) );
893
894 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
895 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
896 LST_BUDGET_POS, id++,
897 LST_BUDGET_KEY, pos,
898 LST_BUDGET_NAME, name,
899 LST_BUDGET_SPENT, tmp_spent[pos],
900 LST_BUDGET_BUDGET, tmp_budget[pos],
901 LST_BUDGET_RESULT, result,
902 LST_BUDGET_STATUS, status,
903 -1);
904
905 nextins:
906 data->total_spent += tmp_spent[pos];
907 data->total_budget += tmp_budget[pos];
908 }
909 }
910 }
911
912 /* update column 0 title */
913 GtkTreeViewColumn *column = gtk_tree_view_get_column( GTK_TREE_VIEW(data->LV_report), 0);
914 gtk_tree_view_column_set_title(column, _(CYA_CATSUBCAT[tmpfor]));
915
916 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_report));
917
918 /* Re-attach model to view */
919 gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_report), model);
920 g_object_unref(model);
921
922
923 repbudget_update_total(widget, NULL);
924
925 /* update stack chart */
926 title = g_strdup_printf(_("Budget for %s"), _(CYA_CATSUBCAT[tmpfor]) );
927
928 ui_chart_progress_set_currency(GTK_CHARTPROGRESS(data->RE_stack), GLOBALS->kcur);
929
930 /* set chart color scheme */
931 ui_chart_progress_set_color_scheme(GTK_CHARTPROGRESS(data->RE_stack), PREFS->report_color_scheme);
932 ui_chart_progress_set_dualdatas(GTK_CHARTPROGRESS(data->RE_stack), model, _("Budget"), _("Result"), title, NULL);
933
934 g_free(title);
935 }
936
937 //DB( g_print(" inserting %i, %f %f\n", i, total_expense, total_income) );
938
939 /* free our memory */
940 g_free(tmp_hassub);
941 g_free(tmp_spent);
942 g_free(tmp_budget);
943
944 }
945
946
947 static void repbudget_update_total(GtkWidget *widget, gpointer user_data)
948 {
949 struct repbudget_data *data;
950
951 DB( g_print("\n[repbudget] update total\n") );
952
953 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
954
955 GLOBALS->minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
956
957 hb_label_set_colvalue(GTK_LABEL(data->TX_total[0]), data->total_spent, GLOBALS->kcur, GLOBALS->minor);
958 hb_label_set_colvalue(GTK_LABEL(data->TX_total[1]), data->total_budget, GLOBALS->kcur, GLOBALS->minor);
959 hb_label_set_colvalue(GTK_LABEL(data->TX_total[2]), budget_compute_result(data->total_budget, data->total_spent), GLOBALS->kcur, GLOBALS->minor);
960
961
962 }
963
964
965 /*
966 ** update sensitivity
967 */
968 static void repbudget_sensitive(GtkWidget *widget, gpointer user_data)
969 {
970 struct repbudget_data *data;
971 GtkAction *action;
972 gboolean visible, sensitive;
973 gint page;
974
975 DB( g_print("\n[repbudget] sensitive\n") );
976
977 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
978
979 page = gtk_notebook_get_current_page(GTK_NOTEBOOK(data->GR_result));
980
981 visible = page == 0 ? TRUE : FALSE;
982 action = gtk_ui_manager_get_action(data->ui, "/ToolBar/Detail");
983 gtk_action_set_visible (action, visible);
984 //sensitive = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_report)), NULL, NULL);
985 //gtk_action_set_sensitive(action, sensitive);
986 //action = gtk_ui_manager_get_action(data->ui, "/ToolBar/Export");
987 //gtk_action_set_visible (action, visible);
988 hb_widget_visible (data->BT_export, visible);
989
990 sensitive = gtk_tree_model_iter_n_children(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail)), NULL) > 0 ? TRUE : FALSE;
991 gtk_widget_set_sensitive(data->MI_detailtoclip, sensitive);
992 gtk_widget_set_sensitive(data->MI_detailtocsv, sensitive);
993
994 }
995
996
997 static void repbudget_toggle(GtkWidget *widget, gpointer user_data)
998 {
999 struct repbudget_data *data;
1000 gboolean minor;
1001
1002 DB( g_print("\n[repbudget] toggle\n") );
1003
1004 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1005
1006 repbudget_update_total(widget, NULL);
1007
1008 //hbfile_update(data->LV_acc, (gpointer)4);
1009 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_report));
1010
1011 minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
1012 ui_chart_progress_show_minor(GTK_CHARTPROGRESS(data->RE_stack), minor);
1013
1014 }
1015
1016 static void repbudget_setup(struct repbudget_data *data)
1017 {
1018 DB( g_print("\n[repbudget] setup\n") );
1019
1020 data->txn_queue = g_queue_new ();
1021
1022 data->filter = da_flt_malloc();
1023 filter_reset(data->filter);
1024
1025 data->detail = PREFS->budg_showdetail;
1026 data->legend = 1;
1027
1028 /* 3.4 : make int transfer out of stats */
1029 data->filter->option[FILTER_PAYMODE] = 1;
1030 data->filter->paymode[PAYMODE_INTXFER] = FALSE;
1031
1032 filter_preset_daterange_set(data->filter, PREFS->date_range_rep, 0);
1033
1034 g_signal_handler_block(data->PO_mindate, data->handler_id[HID_REPBUDGET_MINDATE]);
1035 g_signal_handler_block(data->PO_maxdate, data->handler_id[HID_REPBUDGET_MAXDATE]);
1036
1037 gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_mindate), data->filter->mindate);
1038 gtk_date_entry_set_date(GTK_DATE_ENTRY(data->PO_maxdate), data->filter->maxdate);
1039
1040 g_signal_handler_unblock(data->PO_mindate, data->handler_id[HID_REPBUDGET_MINDATE]);
1041 g_signal_handler_unblock(data->PO_maxdate, data->handler_id[HID_REPBUDGET_MAXDATE]);
1042
1043 }
1044
1045
1046 static void repbudget_selection(GtkTreeSelection *treeselection, gpointer user_data)
1047 {
1048 GtkTreeModel *model;
1049 GtkTreeIter iter;
1050 guint key = -1;
1051
1052 DB( g_print("\n[repbudget] selection\n") );
1053
1054 if (gtk_tree_selection_get_selected(treeselection, &model, &iter))
1055 {
1056 gtk_tree_model_get(model, &iter, LST_BUDGET_KEY, &key, -1);
1057 }
1058
1059 DB( g_print(" - active is %d\n", key) );
1060
1061 repbudget_detail(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(key));
1062 repbudget_sensitive(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), NULL);
1063 }
1064
1065
1066 /*
1067 **
1068 */
1069 static gboolean repbudget_window_dispose(GtkWidget *widget, GdkEvent *event, gpointer user_data)
1070 {
1071 struct repbudget_data *data = user_data;
1072 struct WinGeometry *wg;
1073
1074 DB( g_print("\n[repbudget] start dispose\n") );
1075
1076 g_queue_free (data->txn_queue);
1077
1078 da_flt_free(data->filter);
1079
1080 g_free(data);
1081
1082 //store position and size
1083 wg = &PREFS->bud_wg;
1084 gtk_window_get_position(GTK_WINDOW(widget), &wg->l, &wg->t);
1085 gtk_window_get_size(GTK_WINDOW(widget), &wg->w, &wg->h);
1086
1087 DB( g_print(" window: l=%d, t=%d, w=%d, h=%d\n", wg->l, wg->t, wg->w, wg->h) );
1088
1089 //enable define windows
1090 GLOBALS->define_off--;
1091 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
1092
1093 DB( g_print("\n[repbudget] end dispose\n") );
1094
1095 return FALSE;
1096 }
1097
1098
1099 // the window creation
1100 GtkWidget *repbudget_window_new(void)
1101 {
1102 struct repbudget_data *data;
1103 struct WinGeometry *wg;
1104 GtkWidget *window, *mainvbox, *hbox, *vbox, *notebook, *treeview;
1105 GtkWidget *label, *widget, *table, *entry;
1106 gint row;
1107 GtkUIManager *ui;
1108 GtkActionGroup *actions;
1109 GtkAction *action;
1110 GError *error = NULL;
1111
1112 data = g_malloc0(sizeof(struct repbudget_data));
1113 if(!data) return NULL;
1114
1115 DB( g_print("\n[repbudget] new\n") );
1116
1117 //disable define windows
1118 GLOBALS->define_off++;
1119 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
1120
1121 /* create window, etc */
1122 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1123 data->window = window;
1124
1125 //store our window private data
1126 g_object_set_data(G_OBJECT(window), "inst_data", (gpointer)data);
1127 DB( g_print(" - new window=%p, inst_data=%p\n", window, data) );
1128
1129 gtk_window_set_title (GTK_WINDOW (window), _("Budget report"));
1130
1131 //set the window icon
1132 gtk_window_set_icon_name(GTK_WINDOW (window), ICONNAME_HB_REP_BUDGET);
1133
1134
1135 //window contents
1136 mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1137 gtk_container_add (GTK_CONTAINER (window), mainvbox);
1138
1139 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1140 gtk_box_pack_start (GTK_BOX (mainvbox), hbox, TRUE, TRUE, 0);
1141
1142 //control part
1143 table = gtk_grid_new ();
1144 // gtk_alignment_new(xalign, yalign, xscale, yscale)
1145 //alignment = gtk_alignment_new(0.0, 0.0, 0.0, 0.0);
1146 //gtk_container_add(GTK_CONTAINER(alignment), table);
1147 gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, FALSE, 0);
1148
1149 gtk_container_set_border_width (GTK_CONTAINER (table), SPACING_SMALL);
1150 gtk_grid_set_row_spacing (GTK_GRID (table), SPACING_SMALL);
1151 gtk_grid_set_column_spacing (GTK_GRID (table), SPACING_MEDIUM);
1152
1153 row = 0;
1154 label = make_label_group(_("Display"));
1155 gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
1156
1157 row++;
1158 label = make_label_widget(_("_View by:"));
1159 gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
1160 widget = make_cycle(label, CYA_CATSUBCAT);
1161 data->CY_for = widget;
1162 gtk_grid_attach (GTK_GRID (table), data->CY_for, 2, row, 1, 1);
1163
1164
1165 row++;
1166 label = make_label_widget(_("_Type:"));
1167 gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
1168 widget = make_cycle(label, CYA_KIND);
1169 data->CY_kind = widget;
1170 gtk_grid_attach (GTK_GRID (table), data->CY_kind, 2, row, 1, 1);
1171
1172 row++;
1173 widget = gtk_check_button_new_with_mnemonic (_("Only out of budget"));
1174 data->CM_onlyout = widget;
1175 gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
1176
1177 row++;
1178 widget = gtk_check_button_new_with_mnemonic (_("Euro _minor"));
1179 data->CM_minor = widget;
1180 gtk_grid_attach (GTK_GRID (table), widget, 2, row, 1, 1);
1181
1182 row++;
1183 widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1184 gtk_grid_attach (GTK_GRID (table), widget, 0, row, 3, 1);
1185
1186 row++;
1187 label = make_label_group(_("Date filter"));
1188 gtk_grid_attach (GTK_GRID (table), label, 0, row, 3, 1);
1189
1190 row++;
1191 label = make_label_widget(_("_Range:"));
1192 gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
1193 data->CY_range = make_daterange(label, DATE_RANGE_CUSTOM_DISABLE);
1194 gtk_grid_attach (GTK_GRID (table), data->CY_range, 2, row, 1, 1);
1195
1196 row++;
1197 label = make_label_widget(_("_From:"));
1198 gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
1199 data->PO_mindate = gtk_date_entry_new();
1200 gtk_grid_attach (GTK_GRID (table), data->PO_mindate, 2, row, 1, 1);
1201
1202 row++;
1203 label = make_label_widget(_("_To:"));
1204 gtk_grid_attach (GTK_GRID (table), label, 1, row, 1, 1);
1205 data->PO_maxdate = gtk_date_entry_new();
1206 gtk_grid_attach (GTK_GRID (table), data->PO_maxdate, 2, row, 1, 1);
1207
1208
1209 //part: info + report
1210 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1211 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
1212
1213 //ui manager
1214 actions = gtk_action_group_new ("default");
1215
1216 //as we use gettext
1217 gtk_action_group_set_translation_domain(actions, GETTEXT_PACKAGE);
1218
1219 // data to action callbacks is set here (data)
1220 gtk_action_group_add_radio_actions (actions, radio_entries, n_radio_entries, 0, G_CALLBACK(repbudget_action_mode), data);
1221
1222 gtk_action_group_add_actions (actions, entries, n_entries, data);
1223
1224 gtk_action_group_add_toggle_actions (actions,
1225 toggle_entries, n_toggle_entries,
1226 data);
1227
1228
1229 /* set which action should have priority in the toolbar */
1230 //action = gtk_action_group_get_action(actions, "List");
1231 //g_object_set(action, "is_important", TRUE, NULL);
1232
1233 //action = gtk_action_group_get_action(actions, "Stack");
1234 //g_object_set(action, "is_important", TRUE, NULL);
1235
1236 action = gtk_action_group_get_action(actions, "Detail");
1237 //g_object_set(action, "is_important", TRUE, NULL);
1238 g_object_set(action, "active", PREFS->budg_showdetail, NULL);
1239
1240 //action = gtk_action_group_get_action(actions, "Refresh");
1241 //g_object_set(action, "is_important", TRUE, NULL);
1242
1243
1244 ui = gtk_ui_manager_new ();
1245 gtk_ui_manager_insert_action_group (ui, actions, 0);
1246 gtk_window_add_accel_group (GTK_WINDOW (window), gtk_ui_manager_get_accel_group (ui));
1247
1248 if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error))
1249 {
1250 g_message ("building UI failed: %s", error->message);
1251 g_error_free (error);
1252 }
1253
1254 data->ui = ui;
1255
1256 //toolbar
1257 data->TB_bar = gtk_ui_manager_get_widget (ui, "/ToolBar");
1258 gtk_box_pack_start (GTK_BOX (vbox), data->TB_bar, FALSE, FALSE, 0);
1259
1260 //add export menu button
1261 GtkToolItem *toolitem;
1262 GtkWidget *menu, *menuitem, *image;
1263
1264 menu = gtk_menu_new ();
1265 //gtk_widget_set_halign (menu, GTK_ALIGN_END);
1266
1267 menuitem = gtk_menu_item_new_with_mnemonic (_("_Result to clipboard"));
1268 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
1269 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (repbudget_export_result_clipboard), data);
1270
1271 menuitem = gtk_menu_item_new_with_mnemonic (_("_Result to CSV"));
1272 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
1273 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (repbudget_export_result_csv), data);
1274
1275 menuitem = gtk_menu_item_new_with_mnemonic (_("_Detail to clipboard"));
1276 data->MI_detailtoclip = menuitem;
1277 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
1278 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (repbudget_export_detail_clipboard), data);
1279
1280 menuitem = gtk_menu_item_new_with_mnemonic (_("_Detail to CSV"));
1281 data->MI_detailtocsv = menuitem;
1282 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
1283 g_signal_connect (G_OBJECT (menuitem), "activate", G_CALLBACK (repbudget_export_detail_csv), data);
1284
1285 gtk_widget_show_all (menu);
1286
1287 widget = gtk_menu_button_new();
1288 data->BT_export = widget;
1289 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET(widget)), GTK_STYLE_CLASS_FLAT);
1290
1291 //gtk_menu_button_set_direction (GTK_MENU_BUTTON(widget), GTK_ARROW_DOWN);
1292 //gtk_widget_set_halign (widget, GTK_ALIGN_END);
1293 image = gtk_image_new_from_icon_name (ICONNAME_HB_FILE_EXPORT, GTK_ICON_SIZE_LARGE_TOOLBAR);
1294 g_object_set (widget, "image", image, "popup", GTK_MENU(menu), NULL);
1295
1296 toolitem = gtk_tool_item_new();
1297 gtk_container_add (GTK_CONTAINER(toolitem), widget);
1298 gtk_toolbar_insert(GTK_TOOLBAR(data->TB_bar), GTK_TOOL_ITEM(toolitem), -1);
1299
1300 //infos
1301 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
1302 gtk_container_set_border_width (GTK_CONTAINER(hbox), SPACING_SMALL);
1303 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1304
1305 widget = make_label(NULL, 0.5, 0.5);
1306 gimp_label_set_attributes (GTK_LABEL (widget), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
1307 data->TX_daterange = widget;
1308 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
1309
1310
1311 entry = gtk_label_new(NULL);
1312 data->TX_total[2] = entry;
1313 gtk_box_pack_end (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
1314 label = gtk_label_new(_("Result:"));
1315 gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1316
1317 entry = gtk_label_new(NULL);
1318 data->TX_total[1] = entry;
1319 gtk_box_pack_end (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
1320 label = gtk_label_new(_("Budget:"));
1321 gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1322
1323 entry = gtk_label_new(NULL);
1324 data->TX_total[0] = entry;
1325 gtk_box_pack_end (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
1326 label = gtk_label_new(_("Spent:"));
1327 gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1328
1329
1330 notebook = gtk_notebook_new();
1331 data->GR_result = notebook;
1332 gtk_widget_show(notebook);
1333 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
1334 gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
1335
1336 gtk_box_pack_start (GTK_BOX (vbox), notebook, TRUE, TRUE, 0);
1337
1338 //page: list
1339
1340 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1341 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, NULL);
1342
1343 widget = gtk_scrolled_window_new (NULL, NULL);
1344 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_ETCHED_IN);
1345 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1346 treeview = create_list_budget();
1347 data->LV_report = treeview;
1348 gtk_container_add (GTK_CONTAINER(widget), treeview);
1349 gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
1350
1351 //detail
1352 widget = gtk_scrolled_window_new (NULL, NULL);
1353 data->GR_detail = widget;
1354 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_ETCHED_IN);
1355 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1356 treeview = create_list_transaction(LIST_TXN_TYPE_DETAIL, PREFS->lst_ope_columns);
1357 data->LV_detail = treeview;
1358 gtk_container_add (GTK_CONTAINER(widget), treeview);
1359
1360 gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
1361
1362 //page: 2d bar
1363 //widget = gtk_chart_new(CHART_TYPE_COL);
1364 widget = ui_chart_progress_new();
1365 data->RE_stack = widget;
1366 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), widget, NULL);
1367
1368 //todo:should move this
1369 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->CM_minor),GLOBALS->minor);
1370
1371
1372 /* attach our minor to treeview */
1373 g_object_set_data(G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_report))), "minor", (gpointer)data->CM_minor);
1374 g_object_set_data(G_OBJECT(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_detail))), "minor", (gpointer)data->CM_minor);
1375
1376 /* signal connect */
1377 g_signal_connect (window, "delete-event", G_CALLBACK (repbudget_window_dispose), (gpointer)data);
1378
1379 g_signal_connect (data->CM_onlyout, "toggled", G_CALLBACK (repbudget_compute), NULL);
1380 g_signal_connect (data->CM_minor, "toggled", G_CALLBACK (repbudget_toggle), NULL);
1381
1382
1383 data->handler_id[HID_REPBUDGET_RANGE] = g_signal_connect (data->CY_range, "changed", G_CALLBACK (repbudget_range_change), NULL);
1384
1385 g_signal_connect (data->CY_for , "changed", G_CALLBACK (repbudget_compute), (gpointer)data);
1386 g_signal_connect (data->CY_kind, "changed", G_CALLBACK (repbudget_compute), (gpointer)data);
1387
1388 data->handler_id[HID_REPBUDGET_MINDATE] = g_signal_connect (data->PO_mindate, "changed", G_CALLBACK (repbudget_date_change), (gpointer)data);
1389 data->handler_id[HID_REPBUDGET_MAXDATE] = g_signal_connect (data->PO_maxdate, "changed", G_CALLBACK (repbudget_date_change), (gpointer)data);
1390
1391 g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_report)), "changed", G_CALLBACK (repbudget_selection), NULL);
1392
1393 g_signal_connect (GTK_TREE_VIEW(data->LV_detail), "row-activated", G_CALLBACK (repbudget_detail_onRowActivated), NULL);
1394
1395
1396 //setup, init and show window
1397 repbudget_setup(data);
1398
1399
1400 /* toolbar */
1401 if(PREFS->toolbar_style == 0)
1402 gtk_toolbar_unset_style(GTK_TOOLBAR(data->TB_bar));
1403 else
1404 gtk_toolbar_set_style(GTK_TOOLBAR(data->TB_bar), PREFS->toolbar_style-1);
1405
1406
1407 //setup, init and show window
1408 wg = &PREFS->bud_wg;
1409 gtk_window_move(GTK_WINDOW(window), wg->l, wg->t);
1410 gtk_window_resize(GTK_WINDOW(window), wg->w, wg->h);
1411
1412
1413
1414 gtk_widget_show_all (window);
1415
1416
1417 //minor ?
1418 if( PREFS->euro_active )
1419 gtk_widget_show(data->CM_minor);
1420 else
1421 gtk_widget_hide(data->CM_minor);
1422
1423 //check for any account included into the budget or warn
1424 {
1425 guint count =0;
1426 GList *lacc, *list;
1427
1428 lacc = list = g_hash_table_get_values(GLOBALS->h_acc);
1429
1430 while (list != NULL)
1431 {
1432 Account *acc;
1433 acc = list->data;
1434 //#1674045 ony rely on nosummary
1435 //if((acc->flags & (AF_CLOSED|AF_NOREPORT))) goto next1;
1436 if((acc->flags & (AF_NOREPORT))) goto next1;
1437 if(!(acc->flags & AF_NOBUDGET))
1438 count++;
1439 next1:
1440 list = g_list_next(list);
1441 }
1442 g_list_free(lacc);
1443
1444 if(count <= 0)
1445 {
1446 ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_WARNING,
1447 _("No account is defined to be part of the budget."),
1448 _("You should include some accounts from the account dialog.")
1449 );
1450 }
1451
1452
1453
1454 }
1455
1456
1457
1458 //gtk_widget_hide(data->GR_detail);
1459
1460 repbudget_sensitive(window, NULL);
1461 repbudget_update_detail(window, NULL);
1462
1463 if( PREFS->date_range_rep != 0)
1464 hbtk_combo_box_set_active_id(GTK_COMBO_BOX_TEXT(data->CY_range), PREFS->date_range_rep);
1465 else
1466 repbudget_compute(window, NULL);
1467
1468 return(window);
1469 }
1470
1471
1472 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1473
1474 static GString *ui_list_repbudget_to_string(GtkTreeView *treeview, gboolean clipboard)
1475 {
1476 GString *node;
1477 GtkTreeModel *model;
1478 GtkTreeIter iter;
1479 gboolean valid;
1480 const gchar *format;
1481
1482 node = g_string_new(NULL);
1483
1484 // header
1485 format = (clipboard == TRUE) ? "%s\t%s\t%s\t%s\t\n" : "%s;%s;%s;%s;\n";
1486 g_string_append_printf(node, format, _("Category"), _("Spent"), _("Budget"), _("Result"));
1487
1488 model = gtk_tree_view_get_model(treeview);
1489 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
1490 while (valid)
1491 {
1492 gchar *name, *status;
1493 gdouble spent, budget, result;
1494
1495 gtk_tree_model_get (model, &iter,
1496 //LST_REPDIST_KEY, i,
1497 LST_BUDGET_NAME, &name,
1498 LST_BUDGET_SPENT, &spent,
1499 LST_BUDGET_BUDGET, &budget,
1500 LST_BUDGET_RESULT, &result,
1501 LST_BUDGET_STATUS, &status,
1502 -1);
1503
1504 format = (clipboard == TRUE) ? "%s\t%.2f\t%.2f\t%.2f\t%s\n" : "%s;%.2f;%.2f;%.2f;%s\n";
1505 g_string_append_printf(node, format, name, spent, budget, result, status);
1506
1507 //leak
1508 g_free(name);
1509 g_free(status);
1510
1511 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
1512 }
1513
1514 //DB( g_print("text is:\n%s", node->str) );
1515
1516 return node;
1517 }
1518
1519
1520
1521
1522 /*
1523 **
1524 ** The function should return:
1525 ** a negative integer if the first value comes before the second,
1526 ** 0 if they are equal,
1527 ** or a positive integer if the first value comes after the second.
1528 */
1529 static gint budget_listview_compare_funct (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata)
1530 {
1531 gint retval = 0;
1532 //Category *entry1, *entry2;
1533 gchar *entry1, *entry2;
1534
1535 gtk_tree_model_get(model, a, LST_BUDGET_NAME, &entry1, -1);
1536 gtk_tree_model_get(model, b, LST_BUDGET_NAME, &entry2, -1);
1537
1538 retval = hb_string_utf8_compare(entry1, entry2);
1539
1540
1541 g_free(entry2);
1542 g_free(entry1);
1543
1544 return retval;
1545 }
1546
1547
1548 static void budget_amount_cell_data_function (GtkTreeViewColumn *col,
1549 GtkCellRenderer *renderer,
1550 GtkTreeModel *model,
1551 GtkTreeIter *iter,
1552 gpointer user_data)
1553 {
1554 gdouble value;
1555 gchar *color;
1556 gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
1557 gint column_id = GPOINTER_TO_INT(user_data);
1558
1559 gtk_tree_model_get(model, iter, column_id, &value, -1);
1560
1561 if( value )
1562 {
1563 hb_strfmon(buf, G_ASCII_DTOSTR_BUF_SIZE-1, value, GLOBALS->kcur, GLOBALS->minor);
1564
1565 if( column_id == LST_BUDGET_RESULT)
1566 color = get_minimum_color_amount (value, 0.0);
1567 else
1568 color = get_normal_color_amount(value);
1569
1570 g_object_set(renderer,
1571 "foreground", color,
1572 "text", buf,
1573 NULL);
1574 }
1575 else
1576 {
1577 g_object_set(renderer, "text", "", NULL);
1578 }
1579 }
1580
1581
1582
1583 static void budget_result_cell_data_function (GtkTreeViewColumn *col,
1584 GtkCellRenderer *renderer,
1585 GtkTreeModel *model,
1586 GtkTreeIter *iter,
1587 gpointer user_data)
1588 {
1589 gdouble value;
1590 gchar *color;
1591 gchar *status;
1592 gint column_id = GPOINTER_TO_INT(user_data);
1593
1594 gtk_tree_model_get(model, iter,
1595 column_id, &value,
1596 LST_BUDGET_STATUS, &status,
1597 -1);
1598
1599 if( value )
1600 {
1601 color = get_minimum_color_amount (value, 0.0);
1602 g_object_set(renderer,
1603 "foreground", color,
1604 "text", status,
1605 NULL);
1606 }
1607 else
1608 {
1609 g_object_set(renderer, "text", "", NULL);
1610 }
1611
1612 //leak
1613 g_free(status);
1614 }
1615
1616
1617
1618 static GtkTreeViewColumn *amount_list_budget_column(gchar *name, gint id)
1619 {
1620 GtkTreeViewColumn *column;
1621 GtkCellRenderer *renderer;
1622
1623 column = gtk_tree_view_column_new();
1624 gtk_tree_view_column_set_title(column, name);
1625 renderer = gtk_cell_renderer_text_new ();
1626 g_object_set(renderer, "xalign", 1.0, NULL);
1627 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1628 gtk_tree_view_column_set_cell_data_func(column, renderer, budget_amount_cell_data_function, GINT_TO_POINTER(id), NULL);
1629 gtk_tree_view_column_set_alignment (column, 0.5);
1630 //gtk_tree_view_column_set_sort_column_id (column, id);
1631 return column;
1632 }
1633
1634 /*
1635 ** create our statistic list
1636 */
1637 static GtkWidget *create_list_budget(void)
1638 {
1639 GtkListStore *store;
1640 GtkWidget *view;
1641 GtkCellRenderer *renderer;
1642 GtkTreeViewColumn *column;
1643
1644 DB( g_print("\n[repbudget] create list\n") );
1645
1646
1647 /* create list store */
1648 store = gtk_list_store_new(
1649 NUM_LST_BUDGET,
1650 G_TYPE_INT,
1651 G_TYPE_INT,
1652 G_TYPE_STRING,
1653 G_TYPE_DOUBLE, //spent
1654 G_TYPE_DOUBLE, //budget
1655 G_TYPE_DOUBLE, //result
1656 G_TYPE_STRING //status
1657 );
1658
1659 //treeview
1660 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1661 g_object_unref(store);
1662
1663 gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), PREFS->grid_lines);
1664
1665 /* column: Name */
1666 column = gtk_tree_view_column_new();
1667 gtk_tree_view_column_set_title(column, _("Category"));
1668 renderer = gtk_cell_renderer_text_new ();
1669 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1670 //gtk_tree_view_column_set_cell_data_func(column, renderer, ope_result_cell_data_function, NULL, NULL);
1671 gtk_tree_view_column_add_attribute(column, renderer, "text", LST_BUDGET_NAME);
1672 //gtk_tree_view_column_set_sort_column_id (column, LST_STAT_NAME);
1673 gtk_tree_view_column_set_resizable(column, TRUE);
1674 gtk_tree_view_column_set_alignment (column, 0.5);
1675 gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
1676
1677 /* column: Expense */
1678 column = amount_list_budget_column(_("Spent"), LST_BUDGET_SPENT);
1679 gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
1680
1681 /* column: Income */
1682 column = amount_list_budget_column(_("Budget"), LST_BUDGET_BUDGET);
1683 gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
1684
1685 /* column: Result */
1686 column = amount_list_budget_column(_("Result"), LST_BUDGET_RESULT);
1687 renderer = gtk_cell_renderer_text_new ();
1688 //g_object_set(renderer, "xalign", 0.0, NULL);
1689 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1690 gtk_tree_view_column_set_cell_data_func(column, renderer, budget_result_cell_data_function, GINT_TO_POINTER(LST_BUDGET_RESULT), NULL);
1691 gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
1692
1693 /* column last: empty */
1694 column = gtk_tree_view_column_new();
1695 gtk_tree_view_append_column (GTK_TREE_VIEW(view), column);
1696
1697 /* sort */
1698 gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), budget_listview_compare_funct, NULL, NULL);
1699 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
1700
1701 /*
1702 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_BUDGET_POS , stat_list_compare_func, GINT_TO_POINTER(LST_BUDGET_POS), NULL);
1703 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_BUDGET_SPENT , stat_list_compare_func, GINT_TO_POINTER(LST_BUDGET_SPENT), NULL);
1704 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_BUDGET_BUDGET, stat_list_compare_func, GINT_TO_POINTER(LST_BUDGET_BUDGET), NULL);
1705 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), LST_BUDGET_RESULT , stat_list_compare_func, GINT_TO_POINTER(LST_BUDGET_RESULT), NULL);
1706 */
1707
1708 return(view);
1709 }
1710
This page took 0.104457 seconds and 4 git commands to generate.