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