Merge lp:~adorsaz/homebank/adv-budget-with-categories into lp:homebank/5.2.x

Proposed by Adrien Dorsaz
Status: Merged
Approved by: Maxime DOYEN
Approved revision: 128
Merge reported by: Maxime DOYEN
Merged at revision: not available
Proposed branch: lp:~adorsaz/homebank/adv-budget-with-categories
Merge into: lp:homebank/5.2.x
Diff against target: 6508 lines (+6445/-0) (has conflicts)
5 files modified
src/Makefile.am (+2/-0)
src/Makefile.in (+70/-0)
src/dsp_mainwindow.c.OTHER (+3388/-0)
src/ui-budget-tabview.c (+2916/-0)
src/ui-budget-tabview.h (+69/-0)
Text conflict in src/Makefile.in
Contents conflict in src/dsp_mainwindow.c
To merge this branch: bzr merge lp:~adorsaz/homebank/adv-budget-with-categories
Reviewer Review Type Date Requested Status
Maxime DOYEN Needs Fixing
Review via email: mp+362864@code.launchpad.net

Description of the change

Hello,

As I've said, I've worked again last month on the advanced budget manager for Home Bank last month.

This new merge propose is rebased on Home Bank 5.2.1 and includes already the first merge propose.

This one add the following features:

1. Categories can be managed directly from the view (rename category from the view, add, remove, merge categories from toolbar)
2. The view sorts by name categories inside the same hierarchy level
3. The view allows to expand and collapse lines as other Home Bank dialogs with a toolbar
4. A search widget is available to look fast for a category inside the view
5. The code has been refactored to correctly uses GTK objects (filter, sort...): the main model is built only once on dialog open and never rebuilt.
6. The "Monthly" columns now directly contains a toggle button to enable "same amount" feature and the entry to set the monthly amount (instead of two columns of first implementation)
7. The "Category Name" column now displays a toggle button in "Income" and "Expense" display mode to force the display in Balance mode
8. The view has vertical and horizontal scrollbars enabled (before, the horizontal one was disabled, but it did too big window when amounts were large)

To implement category management, I've based my work on the ui-category.c file (I needed to rewrite the UI part, but I was able to use same logic for the non-UI part).

Regards,
Adrien

To post a comment you must log in.
Revision history for this message
Maxime DOYEN (mdoyen) wrote :

merged this code with my local trunk for review

Revision history for this message
Maxime DOYEN (mdoyen) wrote :

Hi Adrien,

I finally took time this morning to review this code, I still would like to add this to 5.3 as promise. For now I am still working on some refinement for many things in 5.3, and will release a 5.3 RC in a few days (I hope).

Anyway I would suggest some changes in your proposal before including it, no hurry for you, as I plan to release a final 5.3 on September, after a quite long RC period, due to the very deep changes I have made.

Here is a list:

bug2fix:
- the selection remains on a wrong line, sometimes
  to reproduce with example file
 under expense, click on income tax, goes to balance: OK the selection remains
 under expense, click on income tax, goes to income: total is selected (default) ?
 under expense, click on any category with no budget (Car/Fuel), goes to balance: KO, invoice is selected

changes2make:
- global: rename file/func name, menu title, dialog title to something like 'Budget (table view)'; ui_bud_tabview_xxx, because the axis of the dialog is only visual (semantic)
- dialog: disable edition (add/remove/merge) with HB_BUD_TABVIEW_EDIT_ENABLE, for now I prefer keep budget to input budget only, and category management separately, with pragmas you will be able to compile with that if you want
- dialog: top radio button should be Summary(or Synthesis)/Expense/Income, to follow semantic and other Hb dialog
- dialog: move the searchbar to topright

- list: remove the '(check forced display)' into the title + checkbox in profit of a single toggle button at bottom (I added a pin icon in the budget list fro 5.3, and will also for your list), this with lighten the display
- list: center title column
- list: add an empty column as last column, to avoid ugly right justify on last column
- list: total column should be displayed in 2nd position to ease reading
- list: display category with budget in bold to ease visual anchor
- list: display subcategories in italic

add2do:
- a way (button below the list to enable clear a budget line (similar to clear input in my budget dialog)

things2consider:
- list: add an 'Average' (by month) column just after/before total ?
- list: hide 0.00 when not usefull (to focus reading) ?

review: Needs Fixing
Revision history for this message
Adrien Dorsaz (adorsaz) wrote :

Hi Maxime,

Thanks for this review, your suggestions are nice and well explained.

I've began to rename the dialog (and symbols), to build read-only by default and to re-order buttons on top.

For the bug of selection, I think I'll simply clear the current selection on view change, because there's a lot difference between them in the lines (especially between Expense and Credit which have only the total lines in common).

For the moment, the first lines in the tree are the "Incomes". Should I first display "Expense" to follow the order of top buttons ? (same question for the "Income" and "Expense" line of the "Totals" group ?)

Thanks again and see you !

105. By Adrien Dorsaz

rename ui-adv-budget to ui-budget-tabview

106. By Adrien Dorsaz

replace all `adv_budget` (and similar) prefix in symbols with `bud_tabview`

107. By Adrien Dorsaz

ui-budget-tabview: by default build tabview in read-only mode

108. By Adrien Dorsaz

ui-budget-tabview: reorder and rename top radio buttons

109. By Adrien Dorsaz

ui-budget-tabview: add empty last column to follow the window expension

110. By Adrien Dorsaz

ui-budget-tabview: on model toggle, reset scroll to top and unselect rows

111. By Adrien Dorsaz

budget-tabview: center title columns and hide tree lines

That's to be coherent with others tree view style

112. By Adrien Dorsaz

budget-tabview: place total column on second position

113. By Adrien Dorsaz

ui-budget-tabview: display category name with bold / italic

If it has budget, the font weight is bold.
If it is a subcategory, the style is italic.

For edition: adjust the GF_BUDGET flag according to GF_FORCED
as done in the ui-category source code.

114. By Adrien Dorsaz

ui-budget-tabview: add monthly average column

115. By Adrien Dorsaz

ui-budget-tabview: display category name with normal weight within Summary

116. By Adrien Dorsaz

ui-budget-tabview: edit mode is able to clear inputs

117. By Adrien Dorsaz

ui-budget-tabview: general redesign interface

Move the search entry to top right of UI.

Remove the "Category with checkbox" column.
Add a check button to (un)force monitoring Category.

Add some space between buttons to add/remove/merge category and the
clear input button.

Refactor Category search code to be reusable for delete, clear inputs
and toggle monitoring.

118. By Adrien Dorsaz

ui-budget-tabview: adjust documentation, adjust symbols with `_`

119. By Adrien Dorsaz

ui-budget-tabview: refactor merge dialog and avoid select merge source as target

120. By Adrien Dorsaz

ui-budget-tabview: refactor category_add to use the get_selected_category

121. By Adrien Dorsaz

ui-budget-tabview: hide amount if equals 0.0 (which allow to remove sensitive workaround)

122. By Adrien Dorsaz

ui-budget-tabview: use fg colors defined by user for debit/credit

123. By Adrien Dorsaz

ui-budget-tabview: monthly average uses homebank round helper

124. By Adrien Dorsaz

ui-budget-tabview: remove the useless right margin to align with the search entry above

125. By Adrien Dorsaz

ui-budget-tabview: remove "fill" property missly added to GtkBox (that's a child property, not a property of GtkBox)

Revision history for this message
Adrien Dorsaz (adorsaz) wrote :

Ok, I've fixed the bug and applied all suggested changes.

Note that to move the GtkSearchEntry to top right, I've added a GtkBox which contains it and the radio buttons.

I've decided to not add both widgets directly to the same cell of the grid (as made in ui-budget.c), as the Gtk documentation clearly says in such case the behavior is not defined. With my solution, the GtkSearchEntry remain visible even if the window become very small (the minimal window width is the sum of both widget width).

For the clear input button, I've used a GtkCheckButton for the moment, as I wasn't sure what you've made in 5.3. Although, that's pretty easy to move it to GtkToggleButton: you just have to change the function building the widget.

Finally, the branch seems clean on my side, but here (on the merge request web page), launchpad show a strange file added named "src/dsp_mainwindow.c.OTHER". I suppose that's because the 5.2.x branch has evolved since January and there's conflict to solve. I've tried to reproduce conflicts with Bazaar Explorer, but I failed, I hope you'll be able to manage them :-)

126. By Adrien Dorsaz

ui-budget-tabview: edit mode: on add, the default value can be the currently selected row if it's a root

127. By Adrien Dorsaz

ui-budget-tabview: edit mode: add dialog show available parents according to current view mode (Summary, Expense, Income)

128. By Adrien Dorsaz

ui-budget-tabview: fix typo in comments

Revision history for this message
Adrien Dorsaz (adorsaz) wrote :

Hi Maxime,

FYI, I've just added two revisions for the "add category" dialog for the edit mode:

* the parent default value can be "Debit" or "Credit" roots of the tree view if they are selected
* the parent category list is limited to "Income" when only the "Incomes" are viewed in the budget tabview (the same for "Expense"

Revision history for this message
Maxime DOYEN (mdoyen) wrote :

Hi Adrien,

Thanks for your time and work on this.
I have just merged this to my local trunk for a last "quick" review before including this into the I hope close release of 5.3-rc.

Max.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/Makefile.am'
--- src/Makefile.am 2019-04-11 20:36:11 +0000
+++ src/Makefile.am 2019-09-17 21:50:17 +0000
@@ -106,6 +106,8 @@
106 ui-assist-start.h \106 ui-assist-start.h \
107 ui-budget.c \107 ui-budget.c \
108 ui-budget.h \108 ui-budget.h \
109 ui-budget-tabview.c \
110 ui-budget-tabview.h \
109 ui-category.c \111 ui-category.c \
110 ui-category.h \112 ui-category.h \
111 ui-currency.c \113 ui-currency.c \
112114
=== modified file 'src/Makefile.in'
--- src/Makefile.in 2019-04-11 20:36:11 +0000
+++ src/Makefile.in 2019-09-17 21:50:17 +0000
@@ -119,12 +119,21 @@
119 rep-vehicle.$(OBJEXT) ui-account.$(OBJEXT) \119 rep-vehicle.$(OBJEXT) ui-account.$(OBJEXT) \
120 ui-archive.$(OBJEXT) ui-assign.$(OBJEXT) \120 ui-archive.$(OBJEXT) ui-assign.$(OBJEXT) \
121 ui-assist-import.$(OBJEXT) ui-assist-start.$(OBJEXT) \121 ui-assist-import.$(OBJEXT) ui-assist-start.$(OBJEXT) \
122<<<<<<< TREE
122 ui-budget.$(OBJEXT) ui-category.$(OBJEXT) \123 ui-budget.$(OBJEXT) ui-category.$(OBJEXT) \
123 ui-currency.$(OBJEXT) ui-dialogs.$(OBJEXT) ui-filter.$(OBJEXT) \124 ui-currency.$(OBJEXT) ui-dialogs.$(OBJEXT) ui-filter.$(OBJEXT) \
124 ui-hbfile.$(OBJEXT) ui-payee.$(OBJEXT) ui-pref.$(OBJEXT) \125 ui-hbfile.$(OBJEXT) ui-payee.$(OBJEXT) ui-pref.$(OBJEXT) \
125 ui-split.$(OBJEXT) ui-tag.$(OBJEXT) ui-transaction.$(OBJEXT) \126 ui-split.$(OBJEXT) ui-tag.$(OBJEXT) ui-transaction.$(OBJEXT) \
126 ui-txn-multi.$(OBJEXT) ui-widgets-data.$(OBJEXT) \127 ui-txn-multi.$(OBJEXT) ui-widgets-data.$(OBJEXT) \
127 ui-widgets.$(OBJEXT)128 ui-widgets.$(OBJEXT)
129=======
130 ui-budget.$(OBJEXT) ui-budget-tabview.$(OBJEXT) \
131 ui-category.$(OBJEXT) ui-currency.$(OBJEXT) \
132 ui-dialogs.$(OBJEXT) ui-filter.$(OBJEXT) ui-hbfile.$(OBJEXT) \
133 ui-payee.$(OBJEXT) ui-pref.$(OBJEXT) ui-split.$(OBJEXT) \
134 ui-transaction.$(OBJEXT) ui-txn-multi.$(OBJEXT) \
135 ui-widgets.$(OBJEXT)
136>>>>>>> MERGE-SOURCE
128homebank_OBJECTS = $(am_homebank_OBJECTS)137homebank_OBJECTS = $(am_homebank_OBJECTS)
129am__DEPENDENCIES_1 =138am__DEPENDENCIES_1 =
130homebank_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)139homebank_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
@@ -441,6 +450,8 @@
441 ui-assist-start.h \450 ui-assist-start.h \
442 ui-budget.c \451 ui-budget.c \
443 ui-budget.h \452 ui-budget.h \
453 ui-budget-tabview.c \
454 ui-budget-tabview.h \
444 ui-category.c \455 ui-category.c \
445 ui-category.h \456 ui-category.h \
446 ui-currency.c \457 ui-currency.c \
@@ -561,6 +572,7 @@
561distclean-compile:572distclean-compile:
562 -rm -f *.tab.c573 -rm -f *.tab.c
563574
575<<<<<<< TREE
564@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp-account.Po@am__quote@ # am--include-marker576@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp-account.Po@am__quote@ # am--include-marker
565@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp-mainwindow.Po@am__quote@ # am--include-marker577@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp-mainwindow.Po@am__quote@ # am--include-marker
566@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-chart-colors.Po@am__quote@ # am--include-marker578@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-chart-colors.Po@am__quote@ # am--include-marker
@@ -627,6 +639,64 @@
627 @echo '# dummy' >$@-t && $(am__mv) $@-t $@639 @echo '# dummy' >$@-t && $(am__mv) $@-t $@
628640
629am--depfiles: $(am__depfiles_remade)641am--depfiles: $(am__depfiles_remade)
642=======
643@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp_account.Po@am__quote@
644@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp_mainwindow.Po@am__quote@
645@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-chart-colors.Po@am__quote@
646@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-chart-stack.Po@am__quote@
647@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-chart.Po@am__quote@
648@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-dateentry.Po@am__quote@
649@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-account.Po@am__quote@
650@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-archive.Po@am__quote@
651@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-assign.Po@am__quote@
652@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-category.Po@am__quote@
653@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-currency.Po@am__quote@
654@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-encoding.Po@am__quote@
655@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-export.Po@am__quote@
656@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-filter.Po@am__quote@
657@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-hbfile.Po@am__quote@
658@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-import-csv.Po@am__quote@
659@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-import-ofx.Po@am__quote@
660@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-import-qif.Po@am__quote@
661@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-import.Po@am__quote@
662@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-misc.Po@am__quote@
663@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-payee.Po@am__quote@
664@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-preferences.Po@am__quote@
665@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-report.Po@am__quote@
666@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-split.Po@am__quote@
667@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-tag.Po@am__quote@
668@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-transaction.Po@am__quote@
669@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-xml.Po@am__quote@
670@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/homebank.Po@am__quote@
671@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/language.Po@am__quote@
672@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list_account.Po@am__quote@
673@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list_operation.Po@am__quote@
674@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list_topspending.Po@am__quote@
675@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list_upcoming.Po@am__quote@
676@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep_balance.Po@am__quote@
677@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep_budget.Po@am__quote@
678@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep_stats.Po@am__quote@
679@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep_time.Po@am__quote@
680@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep_vehicle.Po@am__quote@
681@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-account.Po@am__quote@
682@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-archive.Po@am__quote@
683@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-assign.Po@am__quote@
684@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-assist-import.Po@am__quote@
685@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-assist-start.Po@am__quote@
686@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-budget-tabview.Po@am__quote@
687@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-budget.Po@am__quote@
688@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-category.Po@am__quote@
689@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-currency.Po@am__quote@
690@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-dialogs.Po@am__quote@
691@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-filter.Po@am__quote@
692@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-hbfile.Po@am__quote@
693@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-payee.Po@am__quote@
694@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-pref.Po@am__quote@
695@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-split.Po@am__quote@
696@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-transaction.Po@am__quote@
697@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-txn-multi.Po@am__quote@
698@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-widgets.Po@am__quote@
699>>>>>>> MERGE-SOURCE
630700
631.c.o:701.c.o:
632@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<702@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
633703
=== added file 'src/dsp_mainwindow.c.OTHER'
--- src/dsp_mainwindow.c.OTHER 1970-01-01 00:00:00 +0000
+++ src/dsp_mainwindow.c.OTHER 2019-09-17 21:50:17 +0000
@@ -0,0 +1,3388 @@
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 "dsp_mainwindow.h"
24
25#include "list_account.h"
26#include "list_upcoming.h"
27#include "list_topspending.h"
28
29#include "dsp_account.h"
30#include "ui-assist-import.h"
31#include "ui-assist-start.h"
32#include "ui-account.h"
33#include "ui-currency.h"
34#include "ui-payee.h"
35#include "ui-category.h"
36#include "ui-archive.h"
37#include "ui-assign.h"
38#include "ui-budget.h"
39#include "ui-budget-tabview.h"
40#include "ui-pref.h"
41#include "ui-hbfile.h"
42#include "ui-transaction.h"
43
44#include "rep_balance.h"
45#include "rep_budget.h"
46#include "rep_stats.h"
47#include "rep_time.h"
48#include "rep_vehicle.h"
49
50#include "gtk-chart.h"
51
52//old url prior 2018
53//#define HOMEBANK_URL_HELP "http://homebank.free.fr/help/"
54//#define HOMEBANK_URL_HELP_ONLINE "https://launchpad.net/homebank/+addquestion"
55//#define HOMEBANK_URL_HELP_PROBLEM "https://launchpad.net/homebank/+filebug"
56//#define HOMEBANK_URL_HELP_TRANSLATE "https://launchpad.net/homebank/+translations"
57
58#define HOMEBANK_URL_HELP "index.html"
59#define HOMEBANK_URL_HELP_ONLINE "http://homebank.free.fr/support.php"
60#define HOMEBANK_URL_HELP_UPDATES "http://homebank.free.fr/downloads.php"
61#define HOMEBANK_URL_HELP_PROBLEM "http://homebank.free.fr/development.php#bug"
62#define HOMEBANK_URL_HELP_TRANSLATE "http://homebank.free.fr/development.php#translate"
63
64
65/****************************************************************************/
66/* Debug macros */
67/****************************************************************************/
68#define MYDEBUG 0
69
70#if MYDEBUG
71#define DB(x) (x);
72#else
73#define DB(x);
74#endif
75
76/* our global datas */
77extern struct HomeBank *GLOBALS;
78extern struct Preferences *PREFS;
79extern gchar *homebank_pixmaps_dir;
80
81
82/* our functions prototype */
83static void ui_mainwindow_action_new(void);
84static void ui_mainwindow_action_open(void);
85static void ui_mainwindow_action_save(void);
86static void ui_mainwindow_action_saveas(void);
87static void ui_mainwindow_action_revert(void);
88static void ui_mainwindow_action_openbak(void);
89static void ui_mainwindow_action_properties(void);
90static void ui_mainwindow_action_close(void);
91static void ui_mainwindow_action_quit(void);
92
93static void ui_mainwindow_action_defcurrency(void);
94static void ui_mainwindow_action_defaccount(void);
95static void ui_mainwindow_action_defpayee(void);
96static void ui_mainwindow_action_defcategory(void);
97static void ui_mainwindow_action_defarchive(void);
98static void ui_mainwindow_action_defbudget(void);
99static void ui_mainwindow_action_defbudget_tabview(void);
100static void ui_mainwindow_action_defassign(void);
101static void ui_mainwindow_action_preferences(void);
102
103static void ui_mainwindow_action_toggle_toolbar(GtkToggleAction *action);
104static void ui_mainwindow_action_toggle_upcoming(GtkToggleAction *action);
105static void ui_mainwindow_action_toggle_topspending(GtkToggleAction *action);
106static void ui_mainwindow_action_toggle_minor(GtkToggleAction *action);
107
108static void ui_mainwindow_action_showtransactions(void);
109static void ui_mainwindow_action_showalltransactions(void);
110
111static void ui_mainwindow_action_addtransactions(void);
112static void ui_mainwindow_action_checkscheduled(void);
113
114static void ui_mainwindow_action_statistic(void);
115static void ui_mainwindow_action_trendtime(void);
116static void ui_mainwindow_action_budget(void);
117static void ui_mainwindow_action_balance(void);
118static void ui_mainwindow_action_vehiclecost(void);
119
120static void ui_mainwindow_action_import(GtkAction *action);
121static void ui_mainwindow_action_export(void);
122static void ui_mainwindow_action_anonymize(void);
123static void ui_mainwindow_action_file_statistics(void);
124
125static void ui_mainwindow_action_help(void);
126void ui_mainwindow_action_help_welcome(void);
127static void ui_mainwindow_action_help_online(void);
128static void ui_mainwindow_action_help_updates(void);
129static void ui_mainwindow_action_help_releasenotes(void);
130static void ui_mainwindow_action_help_translate(void);
131static void ui_mainwindow_action_help_problem(void);
132static void ui_mainwindow_action_about(void);
133
134
135static GtkWidget *ui_mainwindow_create_recent_chooser_menu (GtkRecentManager *manager);
136
137static void ui_mainwindow_populate_topspending(GtkWidget *widget, gpointer user_data);
138
139void ui_mainwindow_open(GtkWidget *widget, gpointer user_data);
140
141void ui_mainwindow_save(GtkWidget *widget, gpointer user_data);
142void ui_mainwindow_revert(GtkWidget *widget, gpointer user_data);
143void ui_mainwindow_action(GtkWidget *widget, gpointer user_data);
144void ui_mainwindow_toggle_minor(GtkWidget *widget, gpointer user_data);
145void ui_mainwindow_clear(GtkWidget *widget, gpointer user_data);
146
147gboolean ui_dialog_msg_savechanges(GtkWidget *widget, gpointer user_data);
148
149void ui_mainwindow_update(GtkWidget *widget, gpointer user_data);
150void ui_mainwindow_addtransactions(GtkWidget *widget, gpointer user_data);
151void ui_mainwindow_recent_add (struct hbfile_data *data, const gchar *path);
152
153static void ui_panel_topspending_update(GtkWidget *widget, gpointer user_data);
154
155static void ui_mainwindow_scheduled_populate(GtkWidget *widget, gpointer user_data);
156void ui_mainwindow_scheduled_postall(GtkWidget *widget, gpointer user_data);
157
158void ui_mainwindow_recent_add (struct hbfile_data *data, const gchar *path);
159
160static void ui_panel_accounts_setup(struct hbfile_data *data);
161
162extern gchar *CYA_ACC_TYPE[];
163
164gchar *CYA_CATSUBCAT[] = {
165 N_("Category"),
166 N_("Subcategory"),
167 NULL
168};
169
170
171static GtkActionEntry entries[] = {
172
173 /* name, icon-name, label */
174
175 { "FileMenu" , NULL, N_("_File"), NULL, NULL, NULL },
176 //{ "ImportMenu" , NULL, N_("_Import"), NULL, NULL, NULL },
177 { "RecentMenu" , NULL, N_("Open _Recent"), NULL, NULL, NULL },
178 { "EditMenu" , NULL, N_("_Edit"), NULL, NULL, NULL },
179 { "ViewMenu" , NULL, N_("_View"), NULL, NULL, NULL },
180 { "ManageMenu" , NULL, N_("_Manage"), NULL, NULL, NULL },
181 { "TxnMenu" , NULL, N_("_Transactions"), NULL, NULL, NULL },
182 { "ReportMenu" , NULL, N_("_Reports"), NULL, NULL, NULL },
183 { "ToolsMenu" , NULL, N_("_Tools"), NULL, NULL, NULL },
184 { "HelpMenu" , NULL, N_("_Help"), NULL, NULL, NULL },
185
186// { "Import" , NULL, N_("Import") },
187// { "Export" , NULL, N_("Export to") },
188 /* name, icon-name, label, accelerator, tooltip */
189
190 /* FileMenu */
191 { "New" , ICONNAME_HB_FILE_NEW , N_("_New") , "<control>N", N_("Create a new file"), G_CALLBACK (ui_mainwindow_action_new) },
192 { "Open" , ICONNAME_HB_FILE_OPEN , N_("_Open...") , "<control>O", N_("Open a file"), G_CALLBACK (ui_mainwindow_action_open) },
193 { "Save" , ICONNAME_HB_FILE_SAVE , N_("_Save") , "<control>S", N_("Save the current file"), G_CALLBACK (ui_mainwindow_action_save) },
194 { "SaveAs" , ICONNAME_SAVE_AS , N_("Save _As...") , "<shift><control>S", N_("Save the current file with a different name"), G_CALLBACK (ui_mainwindow_action_saveas) },
195
196 { "Revert" , ICONNAME_REVERT , N_("Revert") , NULL, N_("Revert to a saved version of this file"), G_CALLBACK (ui_mainwindow_action_revert) },
197 { "OpenBak" , NULL , N_("Restore backup") , NULL, N_("Restore from a backup file"), G_CALLBACK (ui_mainwindow_action_openbak) },
198
199 { "Properties" , ICONNAME_PROPERTIES , N_("Properties..."), NULL, N_("Configure the file"), G_CALLBACK (ui_mainwindow_action_properties) },
200 { "Close" , ICONNAME_CLOSE , N_("_Close") , "<control>W", N_("Close the current file"), G_CALLBACK (ui_mainwindow_action_close) },
201 { "Quit" , ICONNAME_QUIT , N_("_Quit") , "<control>Q", N_("Quit HomeBank"), G_CALLBACK (ui_mainwindow_action_quit) },
202
203 /* Exchange */
204 { "Import" , ICONNAME_HB_FILE_IMPORT , N_("Import...") , NULL, N_("Open the import assistant"), G_CALLBACK (ui_mainwindow_action_import) },
205 //{ "ImportQIF" , ICONNAME_HB_FILE_IMPORT , N_("QIF file...") , NULL, N_("Open the import assistant"), G_CALLBACK (ui_mainwindow_action_import) },
206 //{ "ImportOFX" , ICONNAME_HB_FILE_IMPORT , N_("OFX/QFX file...") , NULL, N_("Open the import assistant"), G_CALLBACK (ui_mainwindow_action_import) },
207 //{ "ImportCSV" , ICONNAME_HB_FILE_IMPORT , N_("CSV file...") , NULL, N_("Open the import assistant"), G_CALLBACK (ui_mainwindow_action_import) },
208
209 { "ExportQIF" , ICONNAME_HB_FILE_EXPORT , N_("Export as QIF...") , NULL, N_("Export all account in a QIF file"), G_CALLBACK (ui_mainwindow_action_export) },
210
211 /* EditMenu */
212 { "Preferences", ICONNAME_PREFERENCES , N_("Preferences..."), NULL, N_("Configure HomeBank"), G_CALLBACK (ui_mainwindow_action_preferences) },
213
214 /* ManageMenu */
215 { "Currency" , ICONNAME_HB_CURRENCY , N_("Currencies...") , NULL, N_("Configure the currencies"), G_CALLBACK (ui_mainwindow_action_defcurrency) },
216 { "Account" , ICONNAME_HB_ACCOUNT , N_("Acc_ounts...") , NULL, N_("Configure the accounts"), G_CALLBACK (ui_mainwindow_action_defaccount) },
217 { "Payee" , ICONNAME_HB_PAYEE , N_("_Payees...") , NULL, N_("Configure the payees"), G_CALLBACK (ui_mainwindow_action_defpayee) },
218 { "Category" , ICONNAME_HB_CATEGORY , N_("Categories...") , NULL, N_("Configure the categories"), G_CALLBACK (ui_mainwindow_action_defcategory) },
219 { "Archive" , ICONNAME_HB_ARCHIVE , N_("Scheduled/Template...") , NULL, N_("Configure the scheduled/template transactions"), G_CALLBACK (ui_mainwindow_action_defarchive) },
220 { "Budget" , ICONNAME_HB_BUDGET , N_("Budget...") , NULL, N_("Configure the budget"), G_CALLBACK (ui_mainwindow_action_defbudget) },
221 { "BudgetTabview", ICONNAME_HB_BUDGET , N_("Budget (tab view)...") , NULL, N_("View the budget"), G_CALLBACK (ui_mainwindow_action_defbudget_tabview) },
222 { "Assign" , ICONNAME_HB_ASSIGN , N_("Assignments..."), NULL, N_("Configure the automatic assignments"), G_CALLBACK (ui_mainwindow_action_defassign) },
223
224 /* TxnMenu */
225 { "AddTxn" , ICONNAME_HB_OPE_ADD , N_("Add...") , NULL, N_("Add transactions"), G_CALLBACK (ui_mainwindow_action_addtransactions) },
226 { "ShowTxn" , ICONNAME_HB_OPE_SHOW , N_("Show...") , NULL, N_("Shows selected account transactions"), G_CALLBACK (ui_mainwindow_action_showtransactions) },
227 { "ShowAllTxn" , ICONNAME_HB_OPE_SHOW , N_("Show all...") , NULL, N_("Shows all account transactions"), G_CALLBACK (ui_mainwindow_action_showalltransactions) },
228 { "Scheduler" , NULL , N_("Set scheduler...") , NULL, N_("Configure the transaction scheduler"), G_CALLBACK (ui_mainwindow_action_properties) },
229 { "AddScheduled", NULL , N_("Post scheduled"), NULL, N_("Post pending scheduled transactions"), G_CALLBACK (ui_mainwindow_action_checkscheduled) },
230
231 /* ReportMenu */
232 { "RStatistics" , ICONNAME_HB_REP_STATS , N_("_Statistics...") , NULL, N_("Open the Statistics report"), G_CALLBACK (ui_mainwindow_action_statistic) },
233 { "RTrendTime" , ICONNAME_HB_REP_TIME , N_("_Trend Time...") , NULL, N_("Open the Trend Time report"), G_CALLBACK (ui_mainwindow_action_trendtime) },
234 { "RBudget" , ICONNAME_HB_REP_BUDGET , N_("B_udget...") , NULL, N_("Open the Budget report"), G_CALLBACK (ui_mainwindow_action_budget) },
235 { "RBalance" , ICONNAME_HB_REP_BALANCE, N_("Balance...") , NULL, N_("Open the Balance report"), G_CALLBACK (ui_mainwindow_action_balance) },
236 { "RVehiculeCost", ICONNAME_HB_REP_CAR , N_("_Vehicle cost...") , NULL, N_("Open the Vehicle cost report"), G_CALLBACK (ui_mainwindow_action_vehiclecost) },
237
238 /* Tools */
239 { "Welcome" , NULL , N_("Show welcome dialog...") , NULL, NULL, G_CALLBACK (ui_mainwindow_action_help_welcome) },
240 { "FileStats" , NULL , N_("File statistics...") , NULL, NULL, G_CALLBACK (ui_mainwindow_action_file_statistics) },
241 { "Anonymize" , NULL , N_("Anonymize...") , NULL, NULL, G_CALLBACK (ui_mainwindow_action_anonymize) },
242
243 /* HelpMenu */
244 { "Contents" , ICONNAME_HELP , N_("_Contents") , "F1", N_("Documentation about HomeBank"), G_CALLBACK (ui_mainwindow_action_help) },
245 { "Online" , "lpi-help" , N_("Get Help Online...") , NULL, N_("Connect to the LaunchPad website for online help"), G_CALLBACK (ui_mainwindow_action_help_online) },
246
247 { "Updates" , NULL , N_("Check for updates...") , NULL, N_("Visit HomeBank website to check for update"), G_CALLBACK (ui_mainwindow_action_help_updates) },
248 { "ReleaseNotes", NULL , N_("Release Notes") , NULL, N_("Display the release notes"), G_CALLBACK (ui_mainwindow_action_help_releasenotes) },
249 { "Problem" , "lpi-bug" , N_("Report a Problem...") , NULL, N_("Connect to the LaunchPad website to help fix problems"), G_CALLBACK (ui_mainwindow_action_help_problem) },
250 { "Translate" , "lpi-translate" , N_("Translate this Application..."), NULL, N_("Connect to the LaunchPad website to help translate this application"), G_CALLBACK (ui_mainwindow_action_help_translate) },
251
252 { "About" , ICONNAME_ABOUT , N_("_About") , NULL, N_("About HomeBank") ,G_CALLBACK (ui_mainwindow_action_about) },
253
254};
255static guint n_entries = G_N_ELEMENTS (entries);
256
257
258static GtkToggleActionEntry toggle_entries[] = {
259/* name , icon-name, label, accelerator, tooltip, callback, is_active */
260 { "Toolbar" , NULL , N_("_Toolbar") , NULL, NULL, G_CALLBACK (ui_mainwindow_action_toggle_toolbar), TRUE },
261 { "Spending" , NULL , N_("_Top spending") , NULL, NULL, G_CALLBACK (ui_mainwindow_action_toggle_topspending), TRUE },
262 { "Upcoming" , NULL , N_("_Scheduled list") , NULL, NULL, G_CALLBACK (ui_mainwindow_action_toggle_upcoming), TRUE },
263 { "AsMinor" , NULL , N_("Euro minor"), "<control>M", NULL, G_CALLBACK (ui_mainwindow_action_toggle_minor), FALSE },
264};
265
266static guint n_toggle_entries = G_N_ELEMENTS (toggle_entries);
267
268
269static const gchar *ui_info =
270"<ui>"
271
272" <menubar name='MenuBar'>"
273" <menu action='FileMenu'>"
274" <menuitem action='New'/>"
275" <menuitem action='Open'/>"
276" <menuitem action='RecentMenu'/>"
277" <separator/>"
278" <menuitem action='Save'/>"
279" <menuitem action='SaveAs'/>"
280" <separator/>"
281" <menuitem action='Import'/>"
282/*" <menu action='ImportMenu'>"
283" <menuitem action='ImportQIF'/>"
284" <menuitem action='ImportOFX'/>"
285" <menuitem action='ImportCSV'/>"
286" </menu>"*/
287" <menuitem action='ExportQIF'/>"
288// future: print to come here
289" <separator/>"
290" <menuitem action='Revert'/>"
291" <menuitem action='OpenBak'/>"
292" <separator/>"
293" <menuitem action='Properties'/>"
294" <separator/>"
295" <menuitem action='Close'/>"
296" <menuitem action='Quit'/>"
297" </menu>"
298" <menu action='EditMenu'>"
299" <menuitem action='Preferences'/>"
300" </menu>"
301" <menu action='ViewMenu'>"
302" <menuitem action='Toolbar'/>"
303" <separator/>"
304" <menuitem action='Spending'/>"
305" <menuitem action='Upcoming'/>"
306" <separator/>"
307" <menuitem action='AsMinor'/>"
308" </menu>"
309" <menu action='ManageMenu'>"
310" <menuitem action='Account'/>"
311" <menuitem action='Payee'/>"
312" <menuitem action='Category'/>"
313" <menuitem action='Archive'/>"
314" <menuitem action='Budget'/>"
315" <menuitem action='BudgetTabview'/>"
316" <menuitem action='Assign'/>"
317" <menuitem action='Currency'/>"
318" </menu>"
319" <menu action='TxnMenu'>"
320" <menuitem action='AddTxn'/>"
321" <menuitem action='ShowTxn'/>"
322" <menuitem action='ShowAllTxn'/>"
323" <separator/>"
324" <menuitem action='Scheduler'/>"
325" <menuitem action='AddScheduled'/>"
326" </menu>"
327" <menu action='ReportMenu'>"
328" <menuitem action='RStatistics'/>"
329" <menuitem action='RTrendTime'/>"
330" <menuitem action='RBalance'/>"
331" <menuitem action='RBudget'/>"
332" <menuitem action='RVehiculeCost'/>"
333" </menu>"
334" <menu action='ToolsMenu'>"
335" <menuitem action='Welcome'/>"
336" <menuitem action='FileStats'/>"
337" <separator/>"
338" <menuitem action='Anonymize'/>"
339" </menu>"
340" <menu action='HelpMenu'>"
341" <menuitem action='Contents'/>"
342" <menuitem action='Online'/>"
343" <separator/>"
344" <menuitem action='Updates'/>"
345" <menuitem action='ReleaseNotes'/>"
346" <menuitem action='Problem'/>"
347" <menuitem action='Translate'/>"
348" <separator/>"
349" <menuitem action='About'/>"
350" </menu>"
351" </menubar>"
352
353" <toolbar name='ToolBar'>"
354" <toolitem action='New'/>"
355// here Open + recent is coded
356" <toolitem action='Save'/>"
357" <separator/>"
358" <toolitem action='Account'/>"
359" <toolitem action='Payee'/>"
360" <toolitem action='Category'/>"
361" <toolitem action='Archive'/>"
362" <toolitem action='Budget'/>"
363" <toolitem action='Assign'/>"
364" <toolitem action='Currency'/>"
365" <separator/>"
366" <toolitem action='ShowTxn'/>"
367" <toolitem action='AddTxn'/>"
368" <separator/>"
369" <toolitem action='RStatistics'/>"
370" <toolitem action='RTrendTime'/>"
371" <toolitem action='RBalance'/>"
372" <toolitem action='RBudget'/>"
373" <toolitem action='RVehiculeCost'/>"
374" </toolbar>"
375
376"</ui>";
377
378
379
380/* TODO: a bouger */
381
382
383/*
384**
385*/
386void ui_mainwindow_revert(GtkWidget *widget, gpointer user_data)
387{
388//struct hbfile_data *data;
389gchar *basename;
390gchar *title;
391gchar *secondtext;
392gint result;
393
394 DB( g_print("\n[ui-mainwindow] revert\n") );
395
396 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
397
398 basename = g_path_get_basename(GLOBALS->xhb_filepath);
399 title = g_strdup_printf (
400 _("Revert unsaved changes to file '%s'?"), basename);
401
402 secondtext =
403 _("- Changes made to the file will be permanently lost\n"
404 "- File will be reloaded from the last save (.xhb~)");
405
406 result = ui_dialog_msg_confirm_alert(
407 GTK_WINDOW(GLOBALS->mainwindow),
408 title,
409 secondtext,
410 _("_Revert")
411 );
412
413 g_free(title);
414 g_free(basename);
415
416 if( result == GTK_RESPONSE_OK )
417 {
418 DB( g_print(" - should revert\n") );
419
420 hbfile_change_filepath(hb_filename_new_with_extension(GLOBALS->xhb_filepath, "xhb~"));
421 ui_mainwindow_open_internal(widget, NULL);
422 hbfile_change_filepath(hb_filename_new_with_extension(GLOBALS->xhb_filepath, "xhb"));
423 }
424
425}
426
427
428static void
429activate_url (GtkAboutDialog *about,
430 const gchar *link,
431 gpointer data)
432{
433 DB( g_print("activate url %s\n", link) );
434
435 homebank_util_url_show (link);
436}
437
438static void hbfile_about(void)
439{
440GtkWidget *dialog;
441GdkPixbuf *pixbuf;
442gchar *pathfilename;
443gchar *version;
444
445 static const gchar *artists[] = {
446 "Maxime DOYEN",
447 NULL
448 };
449
450 static const gchar *authors[] = {
451 "Lead developer:\n" \
452 "Maxime DOYEN",
453 "\nContributor:\n" \
454 "Ga\xc3\xabtan LORIDANT (Maths formulas for charts)\n",
455 NULL
456 };
457
458/*
459 const gchar *documenters[] = {
460 "Maxime DOYEN",
461 NULL
462 };
463*/
464
465 static const gchar *copyright = "Copyright \xc2\xa9 1995-2018 - Maxime DOYEN";
466
467
468
469 version = g_strdup_printf (PACKAGE_VERSION "\n<small>Running against GTK+ %d.%d.%d</small>",
470 gtk_get_major_version (),
471 gtk_get_minor_version (),
472 gtk_get_micro_version ());
473
474 dialog = gtk_about_dialog_new();
475
476 gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(GLOBALS->mainwindow));
477 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
478
479 gtk_about_dialog_set_program_name (GTK_ABOUT_DIALOG(dialog), g_get_application_name ());
480 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), version);
481 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog), copyright);
482 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog), _("Free, easy, personal accounting for everyone"));
483 gtk_about_dialog_set_license_type (GTK_ABOUT_DIALOG(dialog), GTK_LICENSE_GPL_2_0);
484
485 //gtk_about_dialog_set_wrap_license(GTK_ABOUT_DIALOG(dialog), );
486 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog), "http://homebank.free.fr");
487 gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(dialog), "Visit the HomeBank website");
488
489 gtk_about_dialog_set_logo_icon_name(GTK_ABOUT_DIALOG(dialog), "homebank");
490
491 pathfilename = g_build_filename(homebank_app_get_images_dir(), "splash.png", NULL);
492 pixbuf = gdk_pixbuf_new_from_file(pathfilename, NULL);
493 g_free(pathfilename);
494
495 if( pixbuf )
496 {
497 gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), pixbuf);
498 g_object_unref (pixbuf);
499 }
500
501 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(dialog), authors);
502 gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(dialog), artists);
503 //gtk_about_dialog_set_documenters(GTK_ABOUT_DIALOG(dialog), );
504 //gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(dialog), );
505
506 g_signal_connect (dialog, "activate-link", G_CALLBACK (activate_url), NULL);
507
508 gtk_dialog_run (GTK_DIALOG (dialog));
509
510 gtk_widget_destroy (dialog);
511
512 g_free(version);
513
514}
515
516
517/* hbfile action functions -------------------- */
518static void ui_mainwindow_action_new(void)
519{
520GtkWidget *widget = GLOBALS->mainwindow;
521
522 if( ui_dialog_msg_savechanges(widget,NULL) == TRUE )
523 {
524 //clear all, and init GLOBALS->xhb_filepath to default
525 ui_mainwindow_clear(widget, GINT_TO_POINTER(TRUE)); // GPOINTER_TO_INT(
526 ui_mainwindow_update(widget, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
527
528 ui_start_assistant();
529 //ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
530 //ui_mainwindow_scheduled_populate(GLOBALS->mainwindow, NULL);
531 //ui_mainwindow_populate_topspending(GLOBALS->mainwindow, NULL);
532 }
533}
534
535static void ui_mainwindow_action_open(void)
536{
537 ui_mainwindow_open(GLOBALS->mainwindow, GINT_TO_POINTER(FALSE));
538}
539
540static void ui_mainwindow_action_openbak(void)
541{
542 ui_mainwindow_open(GLOBALS->mainwindow, GINT_TO_POINTER(TRUE));
543}
544
545static void ui_mainwindow_action_save(void)
546{
547 ui_mainwindow_save(GLOBALS->mainwindow, GINT_TO_POINTER(FALSE));
548}
549
550static void ui_mainwindow_action_saveas(void)
551{
552 ui_mainwindow_save(GLOBALS->mainwindow, GINT_TO_POINTER(TRUE));
553}
554
555static void ui_mainwindow_action_revert(void)
556{
557 ui_mainwindow_revert(GLOBALS->mainwindow, NULL);
558}
559
560static void ui_mainwindow_action_close(void)
561{
562GtkWidget *widget = GLOBALS->mainwindow;
563
564 if( ui_dialog_msg_savechanges(widget,NULL) == TRUE )
565 {
566 //clear all, and init GLOBALS->xhb_filepath to default
567 ui_mainwindow_clear(widget, GINT_TO_POINTER(TRUE));
568 ui_mainwindow_update(widget, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_BALANCE+UF_REFRESHALL));
569 }
570
571}
572
573
574static void ui_mainwindow_action_quit(void)
575{
576gboolean result;
577
578 //emulate the wm close button
579 g_signal_emit_by_name(GLOBALS->mainwindow, "delete-event", NULL, &result);
580}
581
582
583static void ui_mainwindow_action_file_statistics(void)
584{
585 ui_dialog_file_statistics();
586}
587
588
589static void ui_mainwindow_action_properties(void)
590{
591 create_defhbfile_dialog();
592 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
593}
594
595static void ui_mainwindow_action_anonymize(void)
596{
597gint result;
598gchar *title;
599gchar *secondtext;
600
601 title = _("Are you sure you want to anonymize the file?");
602
603 secondtext =
604 _("Proceeding will anonymize any text, \n"
605 "like 'account x', 'payee y', 'memo z', ...");
606
607 result = ui_dialog_msg_confirm_alert(
608 GTK_WINDOW(GLOBALS->mainwindow),
609 title,
610 secondtext,
611 _("_Anonymize")
612 );
613
614 //#1707201
615 //if( result == GTK_RESPONSE_CANCEL )
616 // return;
617 if( result == GTK_RESPONSE_OK )
618 {
619 hbfile_anonymize();
620 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
621 }
622}
623
624
625static void ui_mainwindow_action_defcurrency(void)
626{
627 ui_cur_manage_dialog();
628 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
629}
630
631
632static void ui_mainwindow_action_defaccount(void)
633{
634 ui_acc_manage_dialog();
635
636 //our global list has changed, so update the treeview
637 //todo: optimize this, should not call compute balance here
638 account_compute_balances ();
639 ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
640
641 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_BALANCE));
642}
643
644static void ui_mainwindow_action_defpayee(void)
645{
646 ui_pay_manage_dialog();
647 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
648}
649
650static void ui_mainwindow_action_defcategory(void)
651{
652 ui_cat_manage_dialog();
653 //todo:why refresh upcoming here??
654 //ui_mainwindow_populate_upcoming(GLOBALS->mainwindow, NULL);
655 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
656}
657
658
659static void ui_mainwindow_defarchive(Archive *arc)
660{
661struct hbfile_data *data;
662GtkTreeModel *model;
663
664 data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
665
666 // upcoming list have direct pointer to the arc (which may have changed)
667 model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_upc));
668 gtk_list_store_clear (GTK_LIST_STORE(model));
669
670 ui_arc_manage_dialog(arc);
671
672 ui_mainwindow_scheduled_populate(GLOBALS->mainwindow, NULL);
673
674 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
675}
676
677
678static void ui_mainwindow_action_defarchive(void)
679{
680 ui_mainwindow_defarchive(NULL);
681}
682
683
684static void ui_mainwindow_action_defbudget(void)
685{
686 ui_bud_manage_dialog();
687 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
688}
689
690static void ui_mainwindow_action_defbudget_tabview(void)
691{
692 ui_bud_tabview_manage_dialog();
693 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
694}
695
696
697static void ui_mainwindow_action_defassign(void)
698{
699
700 ui_asg_manage_dialog();
701
702 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
703}
704
705
706static void ui_mainwindow_action_preferences(void)
707{
708struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
709
710 defpref_dialog_new();
711 if(!PREFS->euro_active)
712 {
713 GtkToggleAction *action = (GtkToggleAction *)gtk_ui_manager_get_action(data->manager, "/MenuBar/ViewMenu/AsMinor");
714
715 gtk_toggle_action_set_active(action, FALSE);
716 ui_mainwindow_action_toggle_minor(action);
717 }
718 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_VISUAL+UF_REFRESHALL));
719}
720
721/* display action */
722
723static void ui_mainwindow_action_toggle_toolbar(GtkToggleAction *action)
724{
725//struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
726
727 PREFS->wal_toolbar = gtk_toggle_action_get_active(action);
728 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_VISUAL));
729}
730
731static void ui_mainwindow_action_toggle_upcoming(GtkToggleAction *action)
732{
733//struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
734
735 PREFS->wal_upcoming = gtk_toggle_action_get_active(action);
736 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_VISUAL));
737}
738
739static void ui_mainwindow_action_toggle_topspending(GtkToggleAction *action)
740{
741//struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
742
743 PREFS->wal_spending = gtk_toggle_action_get_active(action);
744 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_VISUAL));
745}
746
747static void ui_mainwindow_action_toggle_minor(GtkToggleAction *action)
748{
749struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
750
751 GLOBALS->minor = gtk_toggle_action_get_active(action);
752
753 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_acc));
754 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_upc));
755
756 // top spending
757 gtk_chart_show_minor(GTK_CHART(data->RE_pie), GLOBALS->minor);
758
759 ui_panel_topspending_update(data->window, data);
760
761}
762
763static void ui_mainwindow_action_showtransactions(void)
764{
765struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
766GtkWidget *window;
767
768 //todo:change this
769 if( data->acc )
770 {
771 if( data->acc->window == NULL )
772 {
773 window = register_panel_window_new(data->acc);
774 register_panel_window_init(window, NULL);
775 }
776 else
777 {
778 if(GTK_IS_WINDOW(data->acc->window))
779 gtk_window_present(GTK_WINDOW(data->acc->window));
780
781 }
782 }
783}
784
785
786static void ui_mainwindow_action_showalltransactions(void)
787{
788GtkWidget *window;
789
790 if( GLOBALS->alltxnwindow == NULL )
791 {
792 window = register_panel_window_new(NULL);
793 register_panel_window_init(window, NULL);
794 }
795 else
796 {
797 if(GTK_IS_WINDOW(GLOBALS->alltxnwindow))
798 gtk_window_present(GTK_WINDOW(GLOBALS->alltxnwindow));
799 }
800
801}
802
803
804static void ui_mainwindow_action_addtransactions(void)
805{
806 ui_mainwindow_addtransactions(GLOBALS->mainwindow, NULL);
807}
808
809static void ui_mainwindow_action_checkscheduled(void)
810{
811 ui_mainwindow_scheduled_postall(GLOBALS->mainwindow, GINT_TO_POINTER(TRUE));
812}
813
814static void ui_mainwindow_action_statistic(void)
815{
816 ui_repdist_window_new();
817}
818
819static void ui_mainwindow_action_trendtime(void)
820{
821struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
822
823 ui_reptime_window_new(data->acc != NULL ? data->acc->key : 0);
824}
825
826static void ui_mainwindow_action_budget(void)
827{
828 repbudget_window_new();
829}
830
831static void ui_mainwindow_action_balance(void)
832{
833struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
834
835 repbalance_window_new(data->acc != NULL ? data->acc->key : 0);
836}
837
838static void ui_mainwindow_action_vehiclecost(void)
839{
840 repcost_window_new();
841}
842
843static void ui_mainwindow_action_import(GtkAction *action)
844{
845/*const gchar *name;
846gint filetype = FILETYPE_UNKNOWN;
847
848 name = gtk_action_get_name(action);
849
850 if( g_str_has_suffix (name, "QIF"))
851 filetype= FILETYPE_QIF;
852 else
853 if( g_str_has_suffix (name, "OFX"))
854 filetype= FILETYPE_OFX;
855 else
856 if( g_str_has_suffix (name, "CSV"))
857 filetype= FILETYPE_CSV_HB;*/
858
859 //DB( g_print("action %s type=%d\n", name, filetype) );
860
861 ui_import_assistant_new(NULL);
862
863}
864
865
866static void ui_mainwindow_action_about(void)
867{
868 hbfile_about();
869
870
871}
872
873
874static void ui_mainwindow_action_export(void)
875{
876gchar *filename;
877
878 if( ui_file_chooser_qif(NULL, &filename) == TRUE )
879 {
880 hb_export_qif_account_all(filename);
881 g_free( filename );
882 }
883}
884
885
886static void ui_mainwindow_action_help(void)
887{
888gchar *link;
889
890 link = g_build_filename("file:///", homebank_app_get_help_dir(), HOMEBANK_URL_HELP, NULL );
891 homebank_util_url_show (link);
892 g_free(link);
893}
894
895
896static void ui_mainwindow_action_help_releasenotes(void)
897{
898gchar *link;
899
900 #ifdef G_OS_WIN32
901 link = g_build_filename("file:///", homebank_app_get_datas_dir(), "ChangeLog.txt", NULL );
902 #else
903 link = g_build_filename("file:///", homebank_app_get_datas_dir(), "ChangeLog", NULL );
904 #endif
905 homebank_util_url_show (link);
906 g_free(link);
907}
908
909
910//todo: move this to a ui-assist-welcome.c
911
912static void ui_mainwindow_action_help_welcome1 (GtkButton *button, gpointer user_data)
913{
914 gtk_dialog_response (GTK_DIALOG(user_data), 1);
915}
916
917static void ui_mainwindow_action_help_welcome2 (GtkButton *button, gpointer user_data)
918{
919 gtk_dialog_response (GTK_DIALOG(user_data), 2);
920}
921
922static void ui_mainwindow_action_help_welcome3 (GtkButton *button, gpointer user_data)
923{
924 gtk_dialog_response (GTK_DIALOG(user_data), 3);
925}
926
927static void ui_mainwindow_action_help_welcome4 (GtkButton *button, gpointer user_data)
928{
929 gtk_dialog_response (GTK_DIALOG(user_data), 4);
930}
931
932static void ui_mainwindow_action_help_welcome5 (GtkButton *button, gpointer user_data)
933{
934 gtk_dialog_response (GTK_DIALOG(user_data), 5);
935}
936
937void ui_mainwindow_action_help_welcome(void)
938{
939GtkWidget *dialog, *content_area;
940GtkWidget *mainvbox, *widget, *label;
941
942 dialog = gtk_dialog_new_with_buttons (_("Welcome to HomeBank"),
943 GTK_WINDOW(GLOBALS->mainwindow),
944 0,
945 _("_Close"),
946 GTK_RESPONSE_ACCEPT,
947 NULL);
948
949 content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
950
951 mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
952 gtk_box_pack_start (GTK_BOX (content_area), mainvbox, FALSE, FALSE, 0);
953 gtk_container_set_border_width (GTK_CONTAINER(mainvbox), SPACING_MEDIUM);
954
955 label = make_label (_("HomeBank"), 0, 0);
956 gimp_label_set_attributes(GTK_LABEL(label), PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, -1);
957 gtk_box_pack_start (GTK_BOX (mainvbox), label, FALSE, FALSE, 0);
958
959 label = make_label (_("Free, easy, personal accounting for everyone"), 0, 0);
960 gtk_box_pack_start (GTK_BOX (mainvbox), label, FALSE, FALSE, 0);
961
962 widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
963 gtk_box_pack_start (GTK_BOX (content_area), widget, FALSE, FALSE, 0);
964
965 mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_MEDIUM);
966 gtk_box_pack_start (GTK_BOX (content_area), mainvbox, TRUE, TRUE, 0);
967 gtk_container_set_border_width (GTK_CONTAINER(mainvbox), SPACING_MEDIUM);
968
969 label = make_label (_("What do you want to do:"), 0, 0);
970 gimp_label_set_attributes(GTK_LABEL(label), PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, -1);
971 gtk_box_pack_start (GTK_BOX (mainvbox), label, FALSE, FALSE, 0);
972
973 widget = gtk_button_new_with_mnemonic(_("Read HomeBank _Manual"));
974 gtk_box_pack_start (GTK_BOX (mainvbox), widget, FALSE, FALSE, 0);
975 g_signal_connect (widget, "clicked", G_CALLBACK (ui_mainwindow_action_help_welcome1), dialog);
976
977 widget = gtk_button_new_with_mnemonic(_("Configure _preferences"));
978 gtk_box_pack_start (GTK_BOX (mainvbox), widget, FALSE, FALSE, 0);
979 g_signal_connect (widget, "clicked", G_CALLBACK (ui_mainwindow_action_help_welcome2), dialog);
980
981 widget = gtk_button_new_with_mnemonic(_("Create a _new file"));
982 gtk_box_pack_start (GTK_BOX (mainvbox), widget, FALSE, FALSE, 0);
983 g_signal_connect (widget, "clicked", G_CALLBACK (ui_mainwindow_action_help_welcome3), dialog);
984
985 widget = gtk_button_new_with_mnemonic(_("_Open an existing file"));
986 gtk_box_pack_start (GTK_BOX (mainvbox), widget, FALSE, FALSE, 0);
987 g_signal_connect (widget, "clicked", G_CALLBACK (ui_mainwindow_action_help_welcome4), dialog);
988
989 widget = gtk_button_new_with_mnemonic(_("Open the _example file"));
990 gtk_box_pack_start (GTK_BOX (mainvbox), widget, FALSE, FALSE, 0);
991 g_signal_connect (widget, "clicked", G_CALLBACK (ui_mainwindow_action_help_welcome5), dialog);
992
993 //connect all our signals
994 g_signal_connect (dialog, "destroy", G_CALLBACK (gtk_widget_destroyed), &dialog);
995
996 gtk_widget_show_all (dialog);
997
998 //wait for the user
999 gint result = gtk_dialog_run (GTK_DIALOG (dialog));
1000
1001 // cleanup and destroy
1002 gtk_widget_destroy (dialog);
1003
1004 // do appropriate action
1005 switch(result)
1006 {
1007 case 1:
1008 ui_mainwindow_action_help();
1009 break;
1010 case 2:
1011 ui_mainwindow_action_preferences();
1012 break;
1013 case 3:
1014 ui_mainwindow_action_new();
1015 break;
1016 case 4:
1017 ui_mainwindow_action_open();
1018 break;
1019 case 5:
1020 hbfile_change_filepath(g_build_filename(homebank_app_get_datas_dir(), "example.xhb", NULL));
1021 ui_mainwindow_open_internal(GLOBALS->mainwindow, NULL);
1022 break;
1023 }
1024
1025}
1026
1027
1028static void ui_mainwindow_action_help_updates(void)
1029{
1030const gchar *link = HOMEBANK_URL_HELP_UPDATES;
1031
1032 homebank_util_url_show (link);
1033}
1034
1035
1036static void ui_mainwindow_action_help_online(void)
1037{
1038const gchar *link = HOMEBANK_URL_HELP_ONLINE;
1039
1040 homebank_util_url_show (link);
1041}
1042
1043
1044static void ui_mainwindow_action_help_translate(void)
1045{
1046const gchar *link = HOMEBANK_URL_HELP_TRANSLATE;
1047
1048 homebank_util_url_show (link);
1049}
1050
1051
1052static void ui_mainwindow_action_help_problem(void)
1053{
1054const gchar *link = HOMEBANK_URL_HELP_PROBLEM;
1055
1056 homebank_util_url_show (link);
1057}
1058
1059
1060/* hbfile functions -------------------- */
1061
1062
1063/*
1064**
1065*/
1066static void ui_mainwindow_selection(GtkTreeSelection *treeselection, gpointer user_data)
1067{
1068 ui_mainwindow_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(UF_SENSITIVE));
1069}
1070
1071
1072static void ui_mainwindow_close_openbooks(void)
1073{
1074GList *lacc, *elt;
1075
1076 DB( g_print("\n[ui-mainwindow] close openbooks\n") );
1077
1078 lacc = elt = g_hash_table_get_values(GLOBALS->h_acc);
1079 while (elt != NULL)
1080 {
1081 Account *item = elt->data;
1082
1083 if(item->window)
1084 {
1085 gtk_widget_destroy(GTK_WIDGET(item->window));
1086 item->window = NULL;
1087 }
1088
1089 elt = g_list_next(elt);
1090 }
1091 g_list_free(lacc);
1092
1093}
1094
1095
1096
1097/*
1098**
1099*/
1100void ui_mainwindow_clear(GtkWidget *widget, gpointer user_data)
1101{
1102struct hbfile_data *data;
1103gboolean file_clear = GPOINTER_TO_INT(user_data);
1104
1105 DB( g_print("\n[ui-mainwindow] clear\n") );
1106
1107 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1108
1109 // Close opened account window
1110 // Clear TreeView
1111 ui_mainwindow_close_openbooks();
1112 gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_acc))));
1113 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_upc))));
1114 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_top))));
1115
1116 data->showall = FALSE;
1117 ui_panel_accounts_setup(data);
1118
1119 hbfile_cleanup(file_clear);
1120 hbfile_setup(file_clear);
1121
1122}
1123
1124
1125/*
1126** add some transactions directly
1127*/
1128void ui_mainwindow_addtransactions(GtkWidget *widget, gpointer user_data)
1129{
1130struct hbfile_data *data;
1131GtkWidget *window;
1132gint result = 1;
1133guint32 date;
1134gint account, count;
1135
1136 DB( g_print("\n[ui-mainwindow] add transactions\n") );
1137
1138 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1139
1140 /* init the transaction */
1141 date = homebank_app_date_get_julian();
1142
1143 //#1656531
1144 account = 0;
1145 if(data->acc != NULL)
1146 account = data->acc->key;
1147
1148 window = create_deftransaction_window(GTK_WINDOW(data->window), TRANSACTION_EDIT_ADD, FALSE, account);
1149 count = 0;
1150 while(result == HB_RESPONSE_ADD || result == HB_RESPONSE_ADDKEEP)
1151 {
1152 Transaction *ope;
1153
1154 /* fill in the transaction */
1155 if( result == HB_RESPONSE_ADD )
1156 {
1157 ope = da_transaction_malloc();
1158 ope->date = date;
1159 ope->kacc = account;
1160
1161 if( PREFS->heritdate == FALSE ) //fix: 318733
1162 ope->date = GLOBALS->today;
1163
1164 da_transaction_set_default_template(ope);
1165 }
1166
1167 // normally we can't be in addkeep without initialized ope with add
1168
1169 deftransaction_set_transaction(window, ope);
1170
1171 result = gtk_dialog_run (GTK_DIALOG (window));
1172
1173 DB( g_print(" - dialog result is %d\n", result) );
1174
1175 if(result == HB_RESPONSE_ADD || result == HB_RESPONSE_ADDKEEP || result == GTK_RESPONSE_ACCEPT)
1176 {
1177 deftransaction_get(window, NULL);
1178 transaction_add(GTK_WINDOW(GLOBALS->mainwindow), ope);
1179
1180 DB( g_print(" - added 1 transaction to %d\n", ope->kacc) );
1181
1182 ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
1183
1184 count++;
1185 //todo: still usefull ? store last date
1186 date = ope->date;
1187 }
1188
1189 if( result == HB_RESPONSE_ADD )
1190 {
1191 da_transaction_free(ope);
1192 ope = NULL;
1193 }
1194
1195 }
1196
1197
1198 deftransaction_dispose(window, NULL);
1199 gtk_widget_destroy (window);
1200
1201 /* todo optimize this */
1202 if(count > 0)
1203 {
1204 GLOBALS->changes_count += count;
1205 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_BALANCE+UF_REFRESHALL));
1206 }
1207}
1208
1209struct tmptop
1210{
1211 guint32 key;
1212 gdouble value;
1213};
1214
1215
1216#define MAX_TOPSPENDING 10
1217
1218
1219static gint tmptop_compare_func(struct tmptop *tt1, struct tmptop *tt2)
1220{
1221 return tt1->value > tt2->value ? 1 : -1;
1222}
1223
1224
1225static void ui_panel_topspending_update(GtkWidget *widget, gpointer user_data)
1226{
1227struct hbfile_data *data;
1228GtkTreeModel *model;
1229gchar *title;
1230gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
1231
1232 DB( g_print("\n[ui-mainwindow] topspending_update\n") );
1233
1234 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1235
1236 hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, data->toptotal, GLOBALS->kcur, GLOBALS->minor);
1237 //hb_label_set_amount(GTK_LABEL(data->TX_topamount), total, GLOBALS->kcur, GLOBALS->minor);
1238 title = g_strdup_printf("%s %s", _("Top spending"), strbuffer);
1239
1240 model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_top));
1241
1242 gtk_chart_set_color_scheme(GTK_CHART(data->RE_pie), PREFS->report_color_scheme);
1243 gtk_chart_set_currency(GTK_CHART(data->RE_pie), GLOBALS->kcur);
1244 gtk_chart_set_datas(GTK_CHART(data->RE_pie), model, LST_TOPSPEND_AMOUNT, title, NULL);
1245
1246 g_free(title);
1247
1248 //future usage
1249 gchar *fu = _("Top %d spending"); title = fu;
1250}
1251
1252
1253static void ui_mainwindow_populate_topspending(GtkWidget *widget, gpointer user_data)
1254{
1255struct hbfile_data *data;
1256GtkTreeModel *model;
1257GtkTreeIter iter;
1258GList *list;
1259gint type, range;
1260guint n_result, i, n_items;
1261GArray *garray;
1262gdouble total, other;
1263Account *acc;
1264
1265
1266 DB( g_print("\n[ui-mainwindow] populate_topspending\n") );
1267
1268 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1269
1270 type = radio_get_active(GTK_CONTAINER(data->RA_type));
1271 range = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_range));
1272
1273 DB( g_print(" - type=%d, range=%d\n", type, range) );
1274 DB( g_print(" - pref range=%d\n", PREFS->date_range_wal) );
1275
1276 if(range == FLT_RANGE_OTHER)
1277 return;
1278
1279 filter_preset_daterange_set(data->filter, range, 0);
1280
1281
1282 n_result = da_cat_get_max_key() + 1;
1283 total = 0.0;
1284
1285 DB( g_print(" - max key is %d\n", n_result) );
1286
1287 /* allocate some memory */
1288 garray = g_array_sized_new(FALSE, FALSE, sizeof(struct tmptop), n_result);
1289
1290 if(garray)
1291 {
1292 struct tmptop zero = { .key=0, .value=0.0 };
1293 GQueue *txn_queue;
1294
1295 //DB( g_print(" - array length=%d\n", garray->len) );
1296
1297 for(i=0 ; i<n_result ; i++)
1298 {
1299 g_array_append_vals(garray, &zero, 1);
1300 //g_array_insert_vals(garray, i, &zero, 1);
1301
1302 //struct tmptop *tt = &g_array_index (garray, struct tmptop, i);
1303 //DB( g_print("%4d, %4d %f\n", i, tt->key, tt->value) );
1304 }
1305
1306 //DB( g_print("\n - end array length=%d\n", garray->len) );
1307
1308 //todo: not ideal, has ot force to get_acc for each txn below
1309 txn_queue = hbfile_transaction_get_partial(data->filter->mindate, data->filter->maxdate);
1310
1311 /* compute the results */
1312 list = g_queue_peek_head_link(txn_queue);
1313 while (list != NULL)
1314 {
1315 Transaction *ope = list->data;
1316
1317 //DB( g_print(" - eval txn: '%s', cat=%d ==> flt-test=%d\n", ope->memo, ope->kcat, filter_test(data->filter, ope)) );
1318
1319 if( !(ope->paymode == PAYMODE_INTXFER) )
1320 {
1321 guint32 pos = 0;
1322 gdouble trn_amount;
1323
1324 //todo: optimize here
1325 trn_amount = ope->amount;
1326 acc = da_acc_get(ope->kacc);
1327 if(acc)
1328 trn_amount = hb_amount_base(ope->amount, acc->kcur);
1329
1330 if( ope->flags & OF_SPLIT )
1331 {
1332 guint nbsplit = da_splits_length(ope->splits);
1333 Split *split;
1334 struct tmptop *item;
1335
1336 for(i=0;i<nbsplit;i++)
1337 {
1338 split = da_splits_get(ope->splits, i);
1339 pos = category_report_id(split->kcat, type);
1340 if( pos <= garray->len )
1341 {
1342 trn_amount = hb_amount_base(split->amount, acc->kcur);
1343 //trn_amount = split->amount;
1344 //#1297054 if( trn_amount < 0 ) {
1345 item = &g_array_index (garray, struct tmptop, pos);
1346 item->key = pos;
1347 item->value += trn_amount;
1348 //DB( g_print(" - stored %.2f to item %d\n", trn_amount, pos) );
1349 //}
1350 }
1351 }
1352 }
1353 else
1354 {
1355 struct tmptop *item;
1356
1357 pos = category_report_id(ope->kcat, type);
1358 if( pos <= garray->len )
1359 {
1360 //#1297054 if( trn_amount < 0 ) {
1361 item = &g_array_index (garray, struct tmptop, pos);
1362 item->key = pos;
1363 item->value += trn_amount;
1364 //DB( g_print(" - stored %.2f to item %d\n", trn_amount, pos) );
1365 //}
1366 }
1367 }
1368
1369 }
1370
1371 list = g_list_next(list);
1372 }
1373
1374 g_queue_free (txn_queue);
1375
1376 // we need to sort this and limit before
1377 g_array_sort(garray, (GCompareFunc)tmptop_compare_func);
1378
1379 n_items = MIN(garray->len,MAX_TOPSPENDING);
1380 other = 0;
1381 for(i=0 ; i<garray->len ; i++)
1382 {
1383 struct tmptop *item;
1384
1385 item = &g_array_index (garray, struct tmptop, i);
1386 if(item->value < 0)
1387 {
1388 total += item->value;
1389
1390 if(i >= n_items)
1391 other += item->value;
1392
1393 DB( g_print(" - %d : k='%d' v='%f' t='%f'\n", i, item->key, item->value, total) );
1394
1395 }
1396 }
1397
1398 model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_top));
1399 gtk_list_store_clear (GTK_LIST_STORE(model));
1400 g_object_ref(model); /* Make sure the model stays with us after the tree view unrefs it */
1401 gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_top), NULL); /* Detach model from view */
1402
1403 /* insert into the treeview */
1404 for(i=0 ; i<MIN(garray->len,MAX_TOPSPENDING) ; i++)
1405 {
1406 gchar *name;
1407 Category *entry;
1408 struct tmptop *item;
1409 gdouble value;
1410
1411 item = &g_array_index (garray, struct tmptop, i);
1412
1413 if(!item->value) continue;
1414 //#1767659 top spending should restrict to... spending
1415 if(item->value < 0)
1416 {
1417 value = hb_amount_round(item->value, 2);
1418 entry = da_cat_get(item->key);
1419 if(entry == NULL) continue;
1420
1421 name = entry->key == 0 ? _("(no category)") : entry->fullname;
1422
1423 // append test
1424 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
1425 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
1426 LST_TOPSPEND_ID, i,
1427 LST_TOPSPEND_KEY, 0,
1428 LST_TOPSPEND_NAME, name,
1429 LST_TOPSPEND_AMOUNT, value,
1430 //LST_TOPSPEND_RATE, (gint)(((ABS(value)*100)/ABS(total)) + 0.5),
1431 -1);
1432 }
1433 }
1434
1435 // append test
1436 if(ABS(other) > 0)
1437 {
1438 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
1439 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
1440 LST_TOPSPEND_ID, n_items,
1441 LST_TOPSPEND_KEY, 0,
1442 LST_TOPSPEND_NAME, _("Other"),
1443 LST_TOPSPEND_AMOUNT, other,
1444 //LST_TOPSPEND_RATE, (gint)(((ABS(other)*100)/ABS(total)) + 0.5),
1445 -1);
1446 }
1447
1448 /* Re-attach model to view */
1449 gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_top), model);
1450 g_object_unref(model);
1451
1452
1453 // update chart and widgets
1454 {
1455 gchar *daterange;
1456
1457 data->toptotal = total;
1458 ui_panel_topspending_update(widget, data);
1459
1460 daterange = filter_daterange_text_get(data->filter);
1461 gtk_widget_set_tooltip_markup(GTK_WIDGET(data->CY_range), daterange);
1462 g_free(daterange);
1463 }
1464 }
1465
1466 /* free our memory */
1467 g_array_free (garray, TRUE);
1468
1469}
1470
1471
1472/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1473/* scheduled */
1474/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1475static Archive *
1476ui_mainwindow_scheduled_get_selected_item(GtkTreeView *treeview)
1477{
1478GtkTreeSelection *treeselection;
1479GtkTreeModel *model;
1480GtkTreeIter iter;
1481
1482 treeselection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
1483 if( gtk_tree_selection_get_selected(treeselection, &model, &iter) )
1484 {
1485 Archive *arc;
1486
1487 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, LST_DSPUPC_DATAS, &arc, -1);
1488 return arc;
1489 }
1490
1491 return NULL;
1492}
1493
1494
1495static void ui_mainwindow_scheduled_onRowActivated (GtkTreeView *treeview,
1496 GtkTreePath *path,
1497 GtkTreeViewColumn *col,
1498 gpointer userdata)
1499{
1500//struct hbfile_data *data;
1501Archive *arc;
1502
1503 DB( g_print ("\n[ui-mainwindow] A scheduled row has been double-clicked!\n") );
1504
1505 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(treeview, GTK_TYPE_WINDOW)), "inst_data");
1506
1507 arc = ui_mainwindow_scheduled_get_selected_item(treeview);
1508 ui_mainwindow_defarchive(arc);
1509}
1510
1511
1512static void ui_mainwindow_scheduled_do_post(Archive *arc, gboolean doedit, gpointer user_data)
1513{
1514struct hbfile_data *data = user_data;
1515GtkWidget *window;
1516gint result;
1517Transaction *txn;
1518
1519 window = create_deftransaction_window(GTK_WINDOW(data->window), TRANSACTION_EDIT_ADD, TRUE, 0);
1520
1521 /* fill in the transaction */
1522 txn = da_transaction_malloc();
1523 da_transaction_init_from_template(txn, arc);
1524 txn->date = scheduled_get_postdate(arc, arc->nextdate);
1525
1526 deftransaction_set_transaction(window, txn);
1527
1528 result = gtk_dialog_run (GTK_DIALOG (window));
1529
1530 DB( g_print(" - dialog result is %d\n", result) );
1531
1532 if(result == HB_RESPONSE_ADD || result == GTK_RESPONSE_ACCEPT)
1533 {
1534 deftransaction_get(window, NULL);
1535 transaction_add(GTK_WINDOW(GLOBALS->mainwindow), txn);
1536 GLOBALS->changes_count++;
1537
1538 scheduled_date_advance(arc);
1539
1540 DB( g_print(" - added 1 transaction to %d\n", txn->kacc) );
1541 }
1542
1543 da_transaction_free(txn);
1544
1545 deftransaction_dispose(window, NULL);
1546 gtk_widget_destroy (window);
1547
1548}
1549
1550
1551static void ui_mainwindow_scheduled_editpost_cb(GtkWidget *widget, gpointer user_data)
1552{
1553struct hbfile_data *data = user_data;
1554
1555 Archive *arc = ui_mainwindow_scheduled_get_selected_item(GTK_TREE_VIEW(data->LV_upc));
1556
1557 if( (arc != NULL) )
1558 {
1559 ui_mainwindow_scheduled_do_post(arc, TRUE, data);
1560 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE|UF_REFRESHALL));
1561 }
1562}
1563
1564
1565static void ui_mainwindow_scheduled_post_cb(GtkWidget *widget, gpointer user_data)
1566{
1567struct hbfile_data *data = user_data;
1568
1569 DB( g_print("\n[ui-mainwindow] scheduled post\n") );
1570
1571 Archive *arc = ui_mainwindow_scheduled_get_selected_item(GTK_TREE_VIEW(data->LV_upc));
1572
1573 if( (arc != NULL) )
1574 {
1575 if( scheduled_is_postable(arc) )
1576 {
1577 Transaction *txn = da_transaction_malloc ();
1578
1579 da_transaction_init_from_template(txn, arc);
1580 txn->date = scheduled_get_postdate(arc, arc->nextdate);
1581 transaction_add(GTK_WINDOW(GLOBALS->mainwindow), txn);
1582
1583 GLOBALS->changes_count++;
1584 scheduled_date_advance(arc);
1585
1586 da_transaction_free (txn);
1587 }
1588 else
1589 {
1590 ui_mainwindow_scheduled_do_post(arc, FALSE, data);
1591 }
1592
1593 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE|UF_REFRESHALL));
1594 }
1595}
1596
1597
1598static void ui_mainwindow_scheduled_skip_cb(GtkWidget *widget, gpointer user_data)
1599{
1600struct hbfile_data *data = user_data;
1601
1602 Archive *arc = ui_mainwindow_scheduled_get_selected_item(GTK_TREE_VIEW(data->LV_upc));
1603 if( (arc != NULL) && (arc->flags & OF_AUTO) )
1604 {
1605 GLOBALS->changes_count++;
1606 scheduled_date_advance(arc);
1607
1608 ui_mainwindow_scheduled_populate(GLOBALS->mainwindow, NULL);
1609 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
1610 }
1611}
1612
1613
1614
1615static void ui_mainwindow_scheduled_update(GtkWidget *widget, gpointer user_data)
1616{
1617struct hbfile_data *data;
1618//gint filter;
1619
1620 DB( g_print("\n[ui-mainwindow] scheduled update\n") );
1621
1622 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1623
1624 //filter = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_sched_filter));
1625
1626 Archive *arc = ui_mainwindow_scheduled_get_selected_item(GTK_TREE_VIEW(data->LV_upc));
1627
1628 if(arc)
1629 {
1630 DB( g_print("archive is %s\n", arc->memo) );
1631
1632 gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_skip), TRUE);
1633 gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_post), TRUE);
1634 gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_editpost), TRUE);
1635 }
1636 else
1637 {
1638 gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_skip), FALSE);
1639 gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_post), FALSE);
1640 gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_editpost), FALSE);
1641 }
1642
1643}
1644
1645
1646
1647static void ui_mainwindow_scheduled_selection_cb(GtkTreeSelection *treeselection, gpointer user_data)
1648{
1649
1650
1651 ui_mainwindow_scheduled_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(UF_SENSITIVE));
1652}
1653
1654
1655
1656/*
1657** called after load, importamiga, on demand
1658*/
1659void ui_mainwindow_scheduled_postall(GtkWidget *widget, gpointer user_data)
1660{
1661//struct hbfile_data *data;
1662gint count;
1663gint usermode = GPOINTER_TO_INT(user_data);
1664
1665 DB( g_print("\n[ui-mainwindow] check scheduled\n") );
1666
1667 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1668
1669 count = scheduled_post_all_pending();
1670
1671 //inform the user
1672 if(usermode == TRUE)
1673 {
1674 gchar *txt;
1675
1676 //#125534
1677 if( count > 0 )
1678 {
1679 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_REFRESHALL));
1680 }
1681
1682 if(count == 0)
1683 txt = _("No transaction to add");
1684 else
1685 txt = _("transaction added: %d");
1686
1687 ui_dialog_msg_infoerror(GTK_WINDOW(GLOBALS->mainwindow), GTK_MESSAGE_INFO,
1688 _("Check scheduled transactions result"),
1689 txt,
1690 count);
1691 }
1692
1693}
1694
1695
1696static void ui_mainwindow_scheduled_populate(GtkWidget *widget, gpointer user_data)
1697{
1698struct hbfile_data *data;
1699GtkTreeModel *model;
1700GtkTreeIter iter;
1701GList *list;
1702gdouble totexp = 0;
1703gdouble totinc = 0;
1704gint count = 0;
1705gchar buffer[256];
1706guint32 maxpostdate;
1707GDate *date;
1708//Account *acc;
1709
1710 DB( g_print("\n[ui-mainwindow] scheduled populate list\n") );
1711
1712 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1713
1714 model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_upc));
1715 gtk_list_store_clear (GTK_LIST_STORE(model));
1716
1717 homebank_app_date_get_julian();
1718
1719 maxpostdate = scheduled_date_get_post_max();
1720
1721 date = g_date_new_julian (maxpostdate);
1722 g_date_strftime (buffer, 256-1, PREFS->date_format, date);
1723 g_date_free(date);
1724
1725 gtk_label_set_text(GTK_LABEL(data->LB_maxpostdate), buffer);
1726
1727
1728 list = g_list_first(GLOBALS->arc_list);
1729 while (list != NULL)
1730 {
1731 Archive *arc = list->data;
1732 Account *acc;
1733 gdouble inc, exp;
1734 guint nbdays, nblate;
1735
1736 if((arc->flags & OF_AUTO) ) //&& arc->kacc > 0)
1737 {
1738 count++;
1739 nbdays = arc->nextdate - maxpostdate;
1740 nblate = scheduled_get_latepost_count(arc, GLOBALS->today);
1741
1742 DB( g_print(" - append '%s' : %d\n", arc->memo, nbdays) );
1743
1744 if(arc->flags & OF_INCOME)
1745 {
1746 inc = arc->amount;
1747 exp = 0.0;
1748 }
1749 else
1750 {
1751 exp = arc->amount;
1752 inc = 0.0;
1753 }
1754
1755 /* insert normal txn */
1756 acc = da_acc_get(arc->kacc);
1757 if( acc)
1758 {
1759 totinc += hb_amount_base(inc, acc->kcur);
1760 totexp += hb_amount_base(exp, acc->kcur);
1761 }
1762 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
1763 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
1764 LST_DSPUPC_DATAS, arc,
1765 LST_DSPUPC_ACCOUNT, acc,
1766 LST_DSPUPC_MEMO, arc->memo,
1767 LST_DSPUPC_EXPENSE, exp,
1768 LST_DSPUPC_INCOME, inc,
1769 LST_DSPUPC_REMAINING, nbdays,
1770 LST_DSPUPC_NB_LATE, nblate,
1771 -1);
1772
1773 /* insert internal xfer txn : 1378836 */
1774 if(arc->paymode == PAYMODE_INTXFER)
1775 {
1776 acc = da_acc_get(arc->kxferacc);
1777 if( acc)
1778 {
1779 totinc += hb_amount_base(-inc, acc->kcur);
1780 totexp += hb_amount_base(-exp, acc->kcur);
1781 }
1782 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
1783 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
1784 LST_DSPUPC_DATAS, arc,
1785 LST_DSPUPC_ACCOUNT, acc,
1786 LST_DSPUPC_MEMO, arc->memo,
1787 LST_DSPUPC_EXPENSE, -inc,
1788 LST_DSPUPC_INCOME, -exp,
1789 LST_DSPUPC_REMAINING, nbdays,
1790 LST_DSPUPC_NB_LATE, nblate,
1791 -1);
1792 }
1793
1794 }
1795 list = g_list_next(list);
1796 }
1797
1798 // insert total
1799 if(count > 0 )
1800 {
1801 gtk_list_store_append (GTK_LIST_STORE(model), &iter);
1802 gtk_list_store_set (GTK_LIST_STORE(model), &iter,
1803 LST_DSPUPC_DATAS, NULL,
1804 LST_DSPUPC_ACCOUNT, NULL,
1805 LST_DSPUPC_MEMO, _("Total"),
1806 LST_DSPUPC_EXPENSE, totexp,
1807 LST_DSPUPC_INCOME, totinc,
1808 -1);
1809 }
1810
1811 ui_mainwindow_scheduled_update(widget, NULL);
1812
1813}
1814
1815
1816gboolean ui_mainwindow_open_backup_check_confirm(gchar *filepath)
1817{
1818gboolean retval = FALSE;
1819gchar *basename, *secondtext;
1820gboolean result;
1821
1822 basename = g_path_get_basename(filepath);
1823 secondtext = g_strdup_printf (
1824 _("Your are about to open the backup file '%s'.\n\nAre you sure you want to do this ?"), basename);
1825
1826 result = ui_dialog_msg_confirm_alert(
1827 GTK_WINDOW(GLOBALS->mainwindow),
1828 _("Open the backup file ?"),
1829 secondtext,
1830 _("_Open backup")
1831 );
1832
1833 g_free(secondtext);
1834 g_free(basename);
1835
1836 if( result == GTK_RESPONSE_OK )
1837 retval = TRUE;
1838
1839 return retval;
1840}
1841
1842
1843/*
1844**
1845*/
1846void ui_mainwindow_open(GtkWidget *widget, gpointer user_data)
1847{
1848//struct hbfile_data *data;
1849gboolean bakmode = GPOINTER_TO_INT(user_data);;
1850gboolean doopen = TRUE;
1851gchar *filename = NULL;
1852
1853 DB( g_print("\n[ui-mainwindow] open\n") );
1854
1855 //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1856
1857 //#1791554 do ask for save confirm
1858 if( bakmode != TRUE )
1859 doopen = ui_dialog_msg_savechanges(widget,NULL);
1860
1861 if( doopen == TRUE )
1862 {
1863 if( ui_file_chooser_xhb(GTK_FILE_CHOOSER_ACTION_OPEN, &filename, bakmode) == TRUE )
1864 {
1865 //#1710955 test for backup open
1866 if( hbfile_file_isbackup(filename) )
1867 {
1868 if( ui_mainwindow_open_backup_check_confirm(filename) == TRUE )
1869 {
1870 GLOBALS->hbfile_is_bak = TRUE;
1871 }
1872 else
1873 {
1874 g_free(filename);
1875 return;
1876 }
1877 }
1878
1879 hbfile_change_filepath(filename);
1880 ui_mainwindow_open_internal(widget, NULL);
1881 }
1882 }
1883}
1884
1885
1886/*
1887 * open the file stored in GLOBALS->xhb_filepath
1888 */
1889void ui_mainwindow_open_internal(GtkWidget *widget, gpointer user_data)
1890{
1891struct hbfile_data *data;
1892gint r;
1893
1894 DB( g_print("\n[ui-mainwindow] open internal\n") );
1895
1896 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1897
1898 if( GLOBALS->xhb_filepath != NULL )
1899 {
1900 DB( g_print(" - filename: '%s'\n", GLOBALS->xhb_filepath) );
1901
1902 ui_mainwindow_clear(GLOBALS->mainwindow, GINT_TO_POINTER(FALSE));
1903 GLOBALS->hbfile_is_new = FALSE;
1904
1905 r = homebank_load_xml(GLOBALS->xhb_filepath);
1906 if( r == XML_OK )
1907 {
1908 DB( g_print(" - file loaded ok : rcode=%d\n", r) );
1909
1910 GLOBALS->xhb_timemodified = hbfile_file_get_time_modified(GLOBALS->xhb_filepath);
1911 hbfile_file_hasrevert(GLOBALS->xhb_filepath);
1912
1913 if(PREFS->appendscheduled)
1914 scheduled_post_all_pending();
1915
1916 if(PREFS->do_update_currency)
1917 ui_cur_manage_dialog_update_currencies(GTK_WINDOW(GLOBALS->mainwindow));
1918
1919 homebank_lastopenedfiles_save();
1920
1921 //todo: delete this after computing done at xml read
1922 account_compute_balances();
1923
1924 ui_mainwindow_recent_add(data, GLOBALS->xhb_filepath);
1925 }
1926 else
1927 {
1928 gchar *msg = _("Unknown error");
1929
1930 switch(r)
1931 {
1932 case XML_IO_ERROR:
1933 msg = _("I/O error for file '%s'.");
1934 break;
1935 case XML_FILE_ERROR:
1936 msg = _("The file '%s' is not a valid HomeBank file.");
1937 break;
1938 case XML_VERSION_ERROR:
1939 msg = _("The file '%s' was saved with a higher version of HomeBank\nand cannot be loaded by the current version.");
1940 break;
1941 }
1942
1943 ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_ERROR,
1944 _("File error"),
1945 msg,
1946 GLOBALS->xhb_filepath
1947 );
1948
1949 ui_mainwindow_clear(GLOBALS->mainwindow, GINT_TO_POINTER(TRUE));
1950
1951 }
1952
1953 ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
1954 ui_mainwindow_scheduled_populate(GLOBALS->mainwindow, NULL);
1955 ui_mainwindow_populate_topspending(GLOBALS->mainwindow, NULL);
1956 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_VISUAL));
1957 }
1958
1959
1960}
1961
1962
1963/*
1964**
1965*/
1966void ui_mainwindow_save(GtkWidget *widget, gpointer user_data)
1967{
1968struct hbfile_data *data;
1969gboolean saveas = GPOINTER_TO_INT(user_data);
1970gchar *filename = NULL;
1971gint r = XML_UNSET;
1972
1973 DB( g_print("\n[ui-mainwindow] save\n") );
1974
1975 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1976
1977 if( GLOBALS->hbfile_is_new == TRUE )
1978 saveas = 1;
1979
1980 //#1710955 test for backup open
1981 if( GLOBALS->hbfile_is_bak == TRUE )
1982 {
1983 //todo: later for backup, should also remove datetime and .bak
1984 hbfile_change_filepath(hb_filename_new_with_extension(GLOBALS->xhb_filepath, "xhb"));
1985 saveas = 1;
1986 }
1987
1988 if(saveas == 1)
1989 {
1990 if(ui_file_chooser_xhb(GTK_FILE_CHOOSER_ACTION_SAVE, &filename, FALSE) == TRUE)
1991 {
1992 DB( g_print(" + should save as '%s'\n", filename) );
1993 homebank_file_ensure_xhb(filename);
1994 homebank_backup_current_file();
1995 r = homebank_save_xml(GLOBALS->xhb_filepath);
1996 GLOBALS->hbfile_is_new = FALSE;
1997 GLOBALS->hbfile_is_bak = FALSE;
1998 }
1999 else
2000 return;
2001 }
2002 else
2003 {
2004 guint64 time_modified = hbfile_file_get_time_modified (GLOBALS->xhb_filepath);
2005 gint result = GTK_RESPONSE_OK;
2006
2007 DB( g_print(" + should quick save '%s'\n + time: open=%lu :: now=%lu\n", GLOBALS->xhb_filepath, GLOBALS->xhb_timemodified, time_modified) );
2008
2009 if( GLOBALS->xhb_timemodified != time_modified )
2010 {
2011 result = ui_dialog_msg_confirm_alert(
2012 GTK_WINDOW(GLOBALS->mainwindow),
2013 _("The file has been modified since reading it."),
2014 _("If you save it, all the external changes could be lost. Save it anyway?"),
2015 _("S_ave Anyway")
2016 );
2017
2018 if( result != GTK_RESPONSE_OK )
2019 return;
2020 }
2021
2022 DB( g_print(" + saving...\n") );
2023 homebank_file_ensure_xhb(NULL);
2024 homebank_backup_current_file();
2025 r = homebank_save_xml(GLOBALS->xhb_filepath);
2026 }
2027
2028 if(r == XML_OK)
2029 {
2030 DB( g_print(" + OK...\n") );
2031 GLOBALS->changes_count = 0;
2032 GLOBALS->xhb_timemodified = hbfile_file_get_time_modified (GLOBALS->xhb_filepath);
2033 ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_VISUAL));
2034 }
2035 else
2036 {
2037 gchar *msg = _("I/O error for file '%s'.");
2038
2039 ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_ERROR,
2040 _("File error"),
2041 msg,
2042 GLOBALS->xhb_filepath
2043 );
2044 }
2045}
2046
2047
2048static void ui_panel_accounts_expand_all(GtkWidget *widget, gpointer user_data)
2049{
2050struct hbfile_data *data;
2051
2052 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
2053 gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_acc));
2054}
2055
2056
2057static void ui_panel_accounts_collapse_all(GtkWidget *widget, gpointer user_data)
2058{
2059struct hbfile_data *data;
2060
2061 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
2062 gtk_tree_view_collapse_all(GTK_TREE_VIEW(data->LV_acc));
2063}
2064
2065
2066
2067static GHashTable *ui_panel_accounts_groups_get(GList *lacc, gint groupby, gboolean showall)
2068{
2069GHashTable *hash;
2070GList *elt;
2071gchar *groupname;
2072gint nballoc;
2073
2074 DB( g_print("\n[ui-mainwindow] accounts_groups_get\n") );
2075
2076 nballoc = da_acc_length ();
2077
2078 DB( g_print(" %d accounts\n", nballoc) );
2079
2080 hash = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
2081 elt = g_list_first(lacc);
2082 while (elt != NULL)
2083 {
2084 Account *acc = elt->data;
2085 GPtrArray *group;
2086
2087 //#1674045 ony rely on nosummary
2088 //if( showall || !(acc->flags & (AF_CLOSED|AF_NOSUMMARY)) )
2089 if( showall || !(acc->flags & AF_NOSUMMARY) )
2090 {
2091 if( groupby == DSPACC_GROUP_BY_BANK )
2092 {
2093 groupname = _("(no institution)");
2094 if( (acc->bankname != NULL) && strlen(acc->bankname) > 0 )
2095 groupname = acc->bankname;
2096 }
2097 else
2098 {
2099 //pre 5.1.3 historical by type display
2100 groupname = _(CYA_ACC_TYPE[acc->type]);
2101 }
2102
2103 if( g_hash_table_contains(hash, groupname) == FALSE )
2104 {
2105 g_hash_table_insert(hash, g_strdup(groupname), g_ptr_array_sized_new(nballoc) );
2106 //DB( g_print(" - type hash insert '%s' = %d\n", groupname, inserted) );
2107 }
2108
2109 group = g_hash_table_lookup(hash, groupname);
2110 if( group != NULL )
2111 {
2112 g_ptr_array_add(group, (gpointer)acc);
2113 DB( g_print(" -- add '%s' to group '%s'\n", acc->name, groupname) );
2114 }
2115 }
2116 elt = g_list_next(elt);
2117 }
2118
2119 DB( g_print(" end\n") );
2120
2121 return hash;
2122}
2123
2124
2125
2126
2127
2128
2129void ui_mainwindow_populate_accounts(GtkWidget *widget, gpointer user_data)
2130{
2131struct hbfile_data *data;
2132GtkTreeModel *model;
2133GtkTreeIter iter1, child_iter;
2134GList *lacc, *elt;
2135Account *acc;
2136guint j, nbtype;
2137gdouble gtbank, gttoday, gtfuture;
2138
2139GHashTable *h_group;
2140GHashTableIter grp_iter;
2141gpointer key, value;
2142
2143 DB( g_print("\n[ui-mainwindow] populate accounts\n") );
2144
2145 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
2146
2147 /* here we create a count and a list of every account pointer by type */
2148 lacc = elt = g_hash_table_get_values(GLOBALS->h_acc);
2149
2150 h_group = ui_panel_accounts_groups_get(lacc, PREFS->pnl_acc_show_by, data->showall);
2151 g_list_free(lacc);
2152
2153
2154 gtbank = gttoday = gtfuture = 0;
2155
2156 DB( g_print(" - populate listview, %d group(s)\n", g_hash_table_size(h_group)) );
2157
2158 model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_acc));
2159 gtk_tree_store_clear (GTK_TREE_STORE(model));
2160
2161 nbtype = 0;
2162 g_hash_table_iter_init (&grp_iter, h_group);
2163 while (g_hash_table_iter_next (&grp_iter, &key, &value))
2164 {
2165 GPtrArray *gpa = value;
2166 gdouble tbank, ttoday, tfuture;
2167 gint position;
2168
2169 if(gpa != NULL)
2170 {
2171 nbtype++;
2172 //1: Header: Bank, Cash, ...
2173 DB( g_print(" - add group '%s'\n", (gchar *)key) );
2174
2175 //#1663399 keep type position like in dropdown
2176 position = 0;
2177 if( PREFS->pnl_acc_show_by == DSPACC_GROUP_BY_TYPE )
2178 {
2179 gint t = 0;
2180
2181 while(CYA_ACC_TYPE[t] != NULL && t < 15)
2182 {
2183 if( !strcmp(CYA_ACC_TYPE[t], key) )
2184 break;
2185 t++;
2186 }
2187
2188 position = t;
2189 }
2190
2191 gtk_tree_store_append (GTK_TREE_STORE(model), &iter1, NULL);
2192 gtk_tree_store_set (GTK_TREE_STORE(model), &iter1,
2193 LST_DSPACC_POS, position,
2194 LST_DSPACC_DATATYPE, DSPACC_TYPE_HEADER,
2195 LST_DSPACC_NAME, key,
2196 -1);
2197
2198 tbank = ttoday = tfuture = 0;
2199
2200 //2: Accounts for real
2201 for(j=0;j<gpa->len;j++)
2202 {
2203 acc = g_ptr_array_index(gpa, j);
2204
2205 //tbank += acc->bal_bank;
2206 //ttoday += acc->bal_today;
2207 //tfuture += acc->bal_future;
2208 tbank += hb_amount_base(acc->bal_bank, acc->kcur);
2209 ttoday += hb_amount_base(acc->bal_today, acc->kcur);
2210 tfuture += hb_amount_base(acc->bal_future, acc->kcur);
2211
2212 DB( g_print(" - add account '%s' :: %.2f %.2f %.2f\n", acc->name, acc->bal_bank, acc->bal_today, acc->bal_future) );
2213
2214 gtk_tree_store_append (GTK_TREE_STORE(model), &child_iter, &iter1);
2215 gtk_tree_store_set (GTK_TREE_STORE(model), &child_iter,
2216 LST_DSPACC_DATAS, acc,
2217 LST_DSPACC_DATATYPE, DSPACC_TYPE_NORMAL,
2218 LST_DSPACC_BANK, acc->bal_bank,
2219 LST_DSPACC_TODAY, acc->bal_today,
2220 LST_DSPACC_FUTURE, acc->bal_future,
2221 -1);
2222 }
2223
2224 if(gpa->len > 1)
2225 {
2226 DB( g_print(" - group total :: %.2f %.2f %.2f\n", tbank, ttoday, tfuture) );
2227
2228 // insert the total line
2229 gtk_tree_store_append (GTK_TREE_STORE(model), &child_iter, &iter1);
2230 gtk_tree_store_set (GTK_TREE_STORE(model), &child_iter,
2231 LST_DSPACC_DATATYPE, DSPACC_TYPE_SUBTOTAL,
2232 LST_DSPACC_NAME, _("Total"),
2233 LST_DSPACC_BANK, tbank,
2234 LST_DSPACC_TODAY, ttoday,
2235 LST_DSPACC_FUTURE, tfuture,
2236 -1);
2237 }
2238
2239 /* set balance to header to display when collasped */
2240 DB( g_print(" - enrich group total header :: %.2f %.2f %.2f\n", tbank, ttoday, tfuture) );
2241 gtk_tree_store_set (GTK_TREE_STORE(model), &iter1,
2242 LST_DSPACC_BANK, tbank,
2243 LST_DSPACC_TODAY, ttoday,
2244 LST_DSPACC_FUTURE, tfuture,
2245 -1);
2246
2247 /* add to grand total */
2248 gtbank += tbank;
2249 gttoday += ttoday;
2250 gtfuture += tfuture;
2251
2252 }
2253
2254 }
2255
2256 DB( g_print(" - grand total :: %.2f %.2f %.2f\n", gtbank, gttoday, gtfuture) );
2257
2258 // Grand total
2259 if( nbtype > 1 )
2260 {
2261 gtk_tree_store_append (GTK_TREE_STORE(model), &iter1, NULL);
2262 gtk_tree_store_set (GTK_TREE_STORE(model), &iter1,
2263 LST_DSPACC_DATATYPE, DSPACC_TYPE_SUBTOTAL,
2264 LST_DSPACC_NAME, _("Grand total"),
2265 LST_DSPACC_BANK, gtbank,
2266 LST_DSPACC_TODAY, gttoday,
2267 LST_DSPACC_FUTURE, gtfuture,
2268 -1);
2269 }
2270
2271
2272 gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_acc));
2273
2274 DB( g_print(" - free ressources\n") );
2275
2276 g_hash_table_iter_init (&grp_iter, h_group);
2277 while (g_hash_table_iter_next (&grp_iter, &key, &value))
2278 {
2279 g_ptr_array_free (value, TRUE);
2280 }
2281 g_hash_table_destroy (h_group);
2282
2283}
2284
2285
2286void ui_mainwindow_update(GtkWidget *widget, gpointer user_data)
2287{
2288struct hbfile_data *data;
2289gint flags;
2290
2291 DB( g_print("\n[ui-mainwindow] update %p\n", user_data) );
2292
2293 data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
2294 //data = INST_DATA(widget);
2295
2296 flags = GPOINTER_TO_INT(user_data);
2297
2298 /* set window title */
2299 if(flags & UF_TITLE)
2300 {
2301 gchar *basename;
2302 gchar *changed;
2303
2304 DB( g_print(" 1: wintitle %p\n", data->wintitle) );
2305
2306 basename = g_path_get_basename(GLOBALS->xhb_filepath);
2307
2308 DB( g_print(" global changes: %d\n", GLOBALS->changes_count) );
2309
2310 g_free(data->wintitle);
2311
2312 changed = (GLOBALS->changes_count > 0) ? "*" : "";
2313
2314#if MYDEBUG == 1
2315 data->wintitle = g_strdup_printf("%s%s (%d)- %s - " PROGNAME, changed, basename, GLOBALS->changes_count, GLOBALS->owner);
2316#else
2317 data->wintitle = g_strdup_printf("%s%s - %s - " PROGNAME, changed, basename, GLOBALS->owner);
2318#endif
2319
2320 gtk_window_set_title (GTK_WINDOW (gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), data->wintitle);
2321
2322 g_free(basename);
2323 }
2324
2325 /* update disabled things */
2326 if(flags & UF_SENSITIVE)
2327 {
2328 GtkTreeSelection *selection;
2329 GtkTreeModel *model;
2330 GtkTreeIter iter;
2331 GtkTreePath *path;
2332 gboolean active,sensitive;
2333
2334 DB( g_print(" 2: disabled, opelist count\n") );
2335
2336 //#1656531
2337 data->acc = NULL;
2338
2339 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc));
2340 active = gtk_tree_selection_get_selected(selection, &model, &iter);
2341 if(active)
2342 {
2343 Account *acc;
2344 gint depth;
2345
2346 path = gtk_tree_model_get_path(model, &iter);
2347 depth = gtk_tree_path_get_depth(path);
2348
2349 if( depth > 1 )
2350 {
2351 DB( g_print(" depth is %d\n", depth) );
2352
2353 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, LST_DSPACC_DATAS, &acc, -1);
2354 data->acc = acc;
2355 }
2356 else
2357 active = FALSE;
2358 }
2359
2360 DB( g_print(" changes %d - new %d\n", GLOBALS->changes_count, GLOBALS->hbfile_is_new) );
2361
2362 // save
2363 sensitive = (GLOBALS->changes_count != 0 ) ? TRUE : FALSE;
2364 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/FileMenu/Save"), sensitive);
2365
2366 // backup
2367 sensitive = ( (GLOBALS->changes_count != 0) && GLOBALS->xhb_hasrevert ) ? TRUE : FALSE;
2368 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/FileMenu/Revert"), sensitive);
2369 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/FileMenu/OpenBak"), sensitive);
2370
2371 // define off ?
2372 sensitive = GLOBALS->define_off == 0 ? TRUE : FALSE;
2373 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Account"), sensitive);
2374 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Payee"), sensitive);
2375 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Category"), sensitive);
2376 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Budget"), sensitive);
2377 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/EditMenu/Preferences"), sensitive);
2378
2379 // empty account list: disable Archives, Edit, Filter, Add, Statistics, Overdrawn, Car Cost
2380 sensitive = da_acc_length() > 0 ? TRUE : FALSE;
2381 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/FileMenu/Close"), sensitive);
2382 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Archive"), sensitive);
2383 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/TxnMenu/AddTxn"), sensitive);
2384 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/TxnMenu/ShowTxn"), sensitive);
2385 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ReportMenu/RStatistics"), sensitive);
2386 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ReportMenu/RTrendTime"), sensitive);
2387 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ReportMenu/RBudget"), sensitive);
2388 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ReportMenu/RBalance"), sensitive);
2389 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ReportMenu/RVehiculeCost"), sensitive);
2390
2391 // empty category list: disable Budget
2392 sensitive = da_cat_length() > 1 ? TRUE : FALSE;
2393 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Budget"), sensitive);
2394
2395 //#1501129 no need to disable, P & C can be created from assign dialog
2396 //sensitive = ((da_cat_length() > 1) || (da_pay_length() > 1)) ? TRUE : FALSE;
2397 //gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Assign"), sensitive);
2398
2399 // empty archive list: disable scheduled check
2400 sensitive = g_list_length(GLOBALS->arc_list) > 0 ? TRUE : FALSE;
2401 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/TxnMenu/AddScheduled"), sensitive);
2402
2403 // no active account: disable Edit, Over
2404 sensitive = (active == TRUE ) ? TRUE : FALSE;
2405 if(data->acc && data->acc->window != NULL)
2406 sensitive = FALSE;
2407 gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/TxnMenu/ShowTxn"), sensitive);
2408 }
2409
2410 /* update toolbar, list */
2411 if(flags & UF_VISUAL)
2412 {
2413 DB( g_print(" 8: visual\n") );
2414
2415 if(PREFS->toolbar_style == 0)
2416 gtk_toolbar_unset_style(GTK_TOOLBAR(data->toolbar));
2417 else
2418 gtk_toolbar_set_style(GTK_TOOLBAR(data->toolbar), PREFS->toolbar_style-1);
2419
2420 gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (data->LV_acc), PREFS->grid_lines);
2421 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_acc));
2422
2423 gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (data->LV_upc), PREFS->grid_lines);
2424 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_upc));
2425
2426 DB( g_print(" - show toolbar=%d\n", PREFS->wal_toolbar) );
2427 if(PREFS->wal_toolbar)
2428 gtk_widget_show(GTK_WIDGET(data->toolbar));
2429 else
2430 gtk_widget_hide(GTK_WIDGET(data->toolbar));
2431
2432
2433 DB( g_print(" - show top_spending=%d\n", PREFS->wal_spending) );
2434
2435 gtk_combo_box_set_active(GTK_COMBO_BOX(data->CY_range), PREFS->date_range_wal);
2436
2437 if(PREFS->wal_spending)
2438 gtk_widget_show(GTK_WIDGET(data->GR_top));
2439 else
2440 gtk_widget_hide(GTK_WIDGET(data->GR_top));
2441
2442
2443
2444 DB( g_print(" - show upcoming=%d\n", PREFS->wal_upcoming) );
2445 if(PREFS->wal_upcoming)
2446 gtk_widget_show(GTK_WIDGET(data->GR_upc));
2447 else
2448 gtk_widget_hide(GTK_WIDGET(data->GR_upc));
2449
2450 DB( g_print(" minor %d\n", PREFS->euro_active) );
2451 gtk_action_set_visible(gtk_ui_manager_get_action(data->manager, "/MenuBar/ViewMenu/AsMinor"), PREFS->euro_active);
2452 }
2453
2454 /* update balances */
2455 if(flags & UF_BALANCE)
2456 {
2457
2458 DB( g_print(" 4: balances\n") );
2459
2460 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_acc));
2461
2462 //minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
2463
2464 /*
2465 hb-label_set_colvalue(GTK_LABEL(data->TX_balance[0]), data->bank, minor);
2466 hb-label_set_colvalue(GTK_LABEL(data->TX_balance[1]), data->today, minor);
2467 hb-label_set_colvalue(GTK_LABEL(data->TX_balance[2]), data->future, minor);
2468 */
2469 }
2470
2471 if(flags & UF_REFRESHALL)
2472 {
2473 DB( g_print(" 16: refreshall\n") );
2474
2475 ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
2476 ui_mainwindow_populate_topspending(GLOBALS->mainwindow, NULL);
2477 ui_mainwindow_scheduled_populate(GLOBALS->mainwindow, NULL);
2478 }
2479
2480
2481}
2482
2483
2484
2485static void
2486 ui_mainwindow_onRowActivated (GtkTreeView *treeview,
2487 GtkTreePath *path,
2488 GtkTreeViewColumn *col,
2489 gpointer userdata)
2490 {
2491 GtkTreeModel *model;
2492 GtkTreeIter iter;
2493
2494 DB( g_print ("\n[ui-mainwindow] A row has been double-clicked!\n") );
2495
2496 model = gtk_tree_view_get_model(treeview);
2497
2498 if (gtk_tree_model_get_iter(model, &iter, path))
2499 {
2500 Account *acc;
2501
2502 gtk_tree_model_get(model, &iter, LST_DSPACC_DATAS, &acc, -1);
2503
2504 if( acc != NULL )
2505 {
2506
2507 DB( g_print ("Double-clicked row contains name %s\n", acc->name) );
2508
2509 ui_mainwindow_action_showtransactions();
2510
2511 //g_free(name);
2512 }
2513 }
2514 }
2515
2516
2517static void ui_mainwindow_destroy(GtkTreeView *treeview, gpointer user_data)
2518{
2519 DB( g_print("\n[ui-mainwindow] destroy\n") );
2520
2521}
2522
2523
2524/*
2525**
2526*/
2527static gboolean ui_mainwindow_dispose(GtkWidget *widget, GdkEvent *event, gpointer user_data)
2528{
2529struct hbfile_data *data = user_data;
2530struct WinGeometry *wg;
2531gboolean retval = FALSE;
2532
2533 DB( g_print("\n[ui-mainwindow] delete-event\n") );
2534
2535 //store position and size
2536 wg = &PREFS->wal_wg;
2537 gtk_window_get_position(GTK_WINDOW(widget), &wg->l, &wg->t);
2538 gtk_window_get_size(GTK_WINDOW(widget), &wg->w, &wg->h);
2539 GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(widget));
2540 GdkWindowState state = gdk_window_get_state(gdk_window);
2541 wg->s = (state & GDK_WINDOW_STATE_MAXIMIZED) ? 1 : 0;
2542
2543 DB( g_print(" window: l=%d, t=%d, w=%d, h=%d s=%d, state=%d\n", wg->l, wg->t, wg->w, wg->h, wg->s, state & GDK_WINDOW_STATE_MAXIMIZED) );
2544
2545 PREFS->wal_vpaned = gtk_paned_get_position(GTK_PANED(data->vpaned));
2546 PREFS->wal_hpaned = gtk_paned_get_position(GTK_PANED(data->hpaned));
2547
2548 DB( g_print(" - vpaned=%d hpaned=%d\n", PREFS->wal_vpaned, PREFS->wal_hpaned) );
2549
2550 //todo
2551 if(ui_dialog_msg_savechanges(widget, NULL) == FALSE)
2552 {
2553 retval = TRUE;
2554 }
2555 else
2556 {
2557 //todo: retval is useless and below should move to destroy
2558 retval = TRUE;
2559 gtk_widget_destroy(data->LV_top);
2560
2561 g_free(data->wintitle);
2562 da_filter_free(data->filter);
2563 g_free(user_data);
2564
2565 gtk_main_quit();
2566 }
2567
2568 //TRUE:stop other handlers from being invoked for the event | FALSE: propagate
2569 return retval;
2570}
2571
2572
2573static void ui_mainwindow_recent_chooser_item_activated_cb (GtkRecentChooser *chooser, struct hbfile_data *data)
2574{
2575 gchar *uri, *path;
2576 GError *error = NULL;
2577
2578 uri = gtk_recent_chooser_get_current_uri (chooser);
2579
2580 path = g_filename_from_uri (uri, NULL, NULL);
2581 if (error)
2582 {
2583 g_warning ("Could not convert uri \"%s\" to a local path: %s", uri, error->message);
2584 g_error_free (error);
2585 return;
2586 }
2587
2588 if( ui_dialog_msg_savechanges(data->window, NULL) == TRUE )
2589 {
2590
2591 //todo: FixMe
2592 /*
2593 if (! load)
2594 {
2595 gpw_recent_remove (gpw, path);
2596 }
2597 */
2598
2599 hbfile_change_filepath(path);
2600 ui_mainwindow_open_internal(data->window, NULL);
2601 }
2602 else
2603 {
2604 g_free (path);
2605 }
2606 g_free (uri);
2607}
2608
2609
2610void ui_mainwindow_recent_add (struct hbfile_data *data, const gchar *path)
2611{
2612 GtkRecentData *recent_data;
2613 gchar *uri;
2614 GError *error = NULL;
2615
2616 DB( g_print("\n[ui-mainwindow] recent_add\n") );
2617
2618 DB( g_print(" - file has .xhb suffix = %d\n", g_str_has_suffix (path, ".xhb") ) );
2619
2620 if( g_str_has_suffix (path, ".xhb") == FALSE ) //ignore reverted file
2621 return;
2622
2623 uri = g_filename_to_uri (path, NULL, &error);
2624 if (error)
2625 {
2626 g_warning ("Could not convert uri \"%s\" to a local path: %s", uri, error->message);
2627 g_error_free (error);
2628 return;
2629 }
2630
2631 recent_data = g_slice_new (GtkRecentData);
2632
2633 recent_data->display_name = NULL;
2634 recent_data->description = NULL;
2635 recent_data->mime_type = "application/x-homebank";
2636 recent_data->app_name = (gchar *) g_get_application_name ();
2637 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL);
2638 recent_data->groups = NULL;
2639 recent_data->is_private = FALSE;
2640
2641 if (!gtk_recent_manager_add_full (data->recent_manager,
2642 uri,
2643 recent_data))
2644 {
2645 g_warning ("Unable to add '%s' to the list of recently used documents", uri);
2646 }
2647
2648 g_free (uri);
2649 g_free (recent_data->app_exec);
2650 g_slice_free (GtkRecentData, recent_data);
2651
2652}
2653
2654
2655
2656
2657
2658enum
2659{
2660 TARGET_URI_LIST
2661};
2662
2663static GtkTargetEntry drop_types[] =
2664{
2665 {"text/uri-list", 0, TARGET_URI_LIST}
2666};
2667
2668static void ui_mainwindow_drag_data_received (GtkWidget *widget,
2669 GdkDragContext *context,
2670 gint x, gint y,
2671 GtkSelectionData *selection_data,
2672 guint info, guint time, GtkWindow *window)
2673{
2674gchar **uris, **str;
2675gchar *newseldata;
2676gint n_uris, filetype, slen;
2677GError *error = NULL;
2678
2679 if (info != TARGET_URI_LIST)
2680 return;
2681
2682 DB( g_print("\n[ui-mainwindow] drag_data_received\n") );
2683
2684 /* On MS-Windows, it looks like `selection_data->data' is not NULL terminated. */
2685 slen = gtk_selection_data_get_length(selection_data);
2686 newseldata = g_new (gchar, slen + 1);
2687 memcpy (newseldata, gtk_selection_data_get_data(selection_data), slen);
2688 newseldata[slen] = 0;
2689 //DB( g_print(" - seldata ='%s'\n", gtk_selection_data_get_data(selection_data) ) );
2690 //DB( g_print(" - newseldata ='%s'\n", newseldata ) );
2691
2692 uris = g_uri_list_extract_uris (newseldata);
2693 n_uris = g_strv_length(uris);
2694 DB( g_print(" - dragged %d files (len=%d)\n", n_uris, slen ) );
2695
2696 g_free(newseldata);
2697
2698 //single file: check for xhb
2699 if(n_uris == 1)
2700 {
2701 filetype = hb_filename_type_get_by_extension(*uris);
2702
2703 DB( g_print(" - filetype is homebank (%d)\n", filetype) );
2704
2705 if( filetype == FILETYPE_HOMEBANK )
2706 {
2707 gchar *path = g_filename_from_uri (*uris, NULL, &error);
2708
2709 if( path != NULL )
2710 {
2711 DB( g_print(" - path is '%s'\n", path) );
2712 hbfile_change_filepath(g_strdup(path));
2713 ui_mainwindow_open_internal(GTK_WIDGET(window), NULL);
2714 goto end_drop;
2715 }
2716 else
2717 {
2718 g_warning ("Could not convert uri to local path: %s", error->message);
2719 g_error_free (error);
2720 }
2721 g_free (path);
2722 }
2723 /* we no more manage error here
2724 ui_dialog_msg_infoerror(GTK_WINDOW(window), GTK_MESSAGE_ERROR,
2725 _("File error"),
2726 _("The file %s is not a valid HomeBank file."),
2727 path);
2728 */
2729 }
2730
2731 //collect known filetype to import
2732 DB( g_print(" - collect %d files\n", n_uris) );
2733
2734 gchar **paths = g_new (gchar *, n_uris + 1);
2735 slen = 0;
2736 for (str = uris; *str; str++)
2737 {
2738 filetype = hb_filename_type_get_by_extension(*str);
2739 if( filetype != FILETYPE_HOMEBANK && filetype != FILETYPE_UNKNOWN )
2740 {
2741 gchar *path = g_filename_from_uri (*str, NULL, NULL);
2742
2743 if( path != NULL )
2744 {
2745 DB( g_print(" - append %d '%s'\n", slen, path ) );
2746 paths[slen++] = path;
2747 }
2748 }
2749 }
2750 paths[slen] = NULL;
2751
2752 if( slen > 0 )
2753 {
2754 ui_import_assistant_new( paths );
2755 }
2756
2757
2758end_drop:
2759 g_strfreev (uris);
2760}
2761
2762
2763static GtkWidget *ui_mainwindow_create_recent_chooser_menu (GtkRecentManager *manager)
2764{
2765GtkWidget *recent_menu;
2766GtkRecentFilter *filter;
2767
2768 recent_menu = gtk_recent_chooser_menu_new_for_manager (manager);
2769 gtk_recent_chooser_set_local_only (GTK_RECENT_CHOOSER (recent_menu), FALSE);
2770 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (recent_menu), GTK_RECENT_SORT_MRU);
2771 //todo: add a user pref for this
2772 gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER (recent_menu), 10);
2773 gtk_recent_chooser_set_show_icons (GTK_RECENT_CHOOSER (recent_menu), FALSE);
2774 //gtk_recent_chooser_menu_set_show_numbers (GTK_RECENT_CHOOSER_MENU (recent_menu), TRUE);
2775
2776 filter = gtk_recent_filter_new ();
2777 //gtk_recent_filter_add_application (filter, g_get_application_name());
2778 gtk_recent_filter_add_pattern (filter, "*.[Xx][Hh][Bb]");
2779 gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (recent_menu), filter);
2780
2781 return recent_menu;
2782}
2783
2784
2785static void ui_mainwindow_create_menu_bar_and_toolbar(struct hbfile_data *data, GtkWidget *mainvbox)
2786{
2787GtkUIManager *manager;
2788GtkActionGroup *actions;
2789GtkAction *action;
2790GError *error = NULL;
2791
2792 manager = gtk_ui_manager_new ();
2793 data->manager = manager;
2794
2795 gtk_window_add_accel_group (GTK_WINDOW (data->window),
2796 gtk_ui_manager_get_accel_group(manager));
2797
2798 actions = gtk_action_group_new ("MainWindow");
2799 gtk_action_group_set_translation_domain(actions, GETTEXT_PACKAGE);
2800
2801 gtk_action_group_add_actions (actions,
2802 entries,
2803 n_entries,
2804 NULL);
2805
2806 gtk_action_group_add_toggle_actions (actions,
2807 toggle_entries,
2808 n_toggle_entries,
2809 NULL);
2810
2811 gtk_ui_manager_insert_action_group (data->manager, actions, 0);
2812 g_object_unref (actions);
2813 data->actions = actions;
2814
2815 /* set short labels to use in the toolbar */
2816 action = gtk_action_group_get_action(actions, "Open");
2817 g_object_set(action, "short_label", _("Open"), NULL);
2818
2819 //action = gtk_action_group_get_action(action_group, "Save");
2820 //g_object_set(action, "is_important", TRUE, NULL);
2821
2822 action = gtk_action_group_get_action(actions, "Account");
2823 g_object_set(action, "short_label", _("Account"), NULL);
2824
2825 action = gtk_action_group_get_action(actions, "Payee");
2826 g_object_set(action, "short_label", _("Payee"), NULL);
2827
2828 action = gtk_action_group_get_action(actions, "Category");
2829 g_object_set(action, "short_label", _("Category"), NULL);
2830
2831 action = gtk_action_group_get_action(actions, "Archive");
2832 //TRANSLATORS: an archive is stored transaction buffers (kind of bookmark to prefill manual insertion)
2833 g_object_set(action, "short_label", _("Archive"), NULL);
2834
2835 action = gtk_action_group_get_action(actions, "Budget");
2836 g_object_set(action, "short_label", _("Budget"), NULL);
2837
2838 action = gtk_action_group_get_action(actions, "ShowTxn");
2839 g_object_set(action, "short_label", _("Show"), NULL);
2840
2841 action = gtk_action_group_get_action(actions, "AddTxn");
2842 g_object_set(action, "is_important", TRUE, "short_label", _("Add"), NULL);
2843
2844 action = gtk_action_group_get_action(actions, "RStatistics");
2845 g_object_set(action, "short_label", _("Statistics"), NULL);
2846
2847 action = gtk_action_group_get_action(actions, "RBudget");
2848 g_object_set(action, "short_label", _("Budget"), NULL);
2849
2850 action = gtk_action_group_get_action(actions, "RBalance");
2851 g_object_set(action, "short_label", _("Balance"), NULL);
2852
2853 action = gtk_action_group_get_action(actions, "RVehiculeCost");
2854 g_object_set(action, "short_label", _("Vehicle cost"), NULL);
2855
2856 /* now load the UI definition */
2857 gtk_ui_manager_add_ui_from_string (data->manager, ui_info, -1, &error);
2858 if (error != NULL)
2859 {
2860 g_message ("Building menus failed: %s", error->message);
2861 g_error_free (error);
2862 }
2863
2864
2865 data->recent_manager = gtk_recent_manager_get_default ();
2866
2867 data->menubar = gtk_ui_manager_get_widget (manager, "/MenuBar");
2868 gtk_box_pack_start (GTK_BOX (mainvbox),
2869 data->menubar,
2870 FALSE,
2871 FALSE,
2872 0);
2873
2874 /* recent files menu */
2875 data->recent_menu = ui_mainwindow_create_recent_chooser_menu (data->recent_manager);
2876
2877 g_signal_connect (data->recent_menu,
2878 "item-activated",
2879 G_CALLBACK (ui_mainwindow_recent_chooser_item_activated_cb),
2880 data);
2881
2882 GtkWidget *widget = gtk_ui_manager_get_widget (data->manager, "/MenuBar/FileMenu/RecentMenu");
2883 gtk_menu_item_set_submenu (GTK_MENU_ITEM (widget), data->recent_menu);
2884
2885
2886 data->toolbar = gtk_ui_manager_get_widget (manager, "/ToolBar");
2887 gtk_box_pack_start (GTK_BOX (mainvbox),
2888 data->toolbar,
2889 FALSE,
2890 FALSE,
2891 0);
2892
2893 /* add the custom Open button to the toolbar */
2894 GtkWidget *image = gtk_image_new_from_icon_name (ICONNAME_HB_FILE_OPEN, GTK_ICON_SIZE_BUTTON);
2895 GtkToolItem *open_button = gtk_menu_tool_button_new(image, _("_Open"));
2896 gtk_tool_item_set_tooltip_text (open_button, _("Open a file"));
2897
2898 GtkWidget *recent_menu = ui_mainwindow_create_recent_chooser_menu (data->recent_manager);
2899 gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (open_button), recent_menu);
2900 gtk_menu_tool_button_set_arrow_tooltip_text (GTK_MENU_TOOL_BUTTON (open_button), _("Open a recently used file"));
2901
2902 g_signal_connect (recent_menu,
2903 "item-activated",
2904 G_CALLBACK (ui_mainwindow_recent_chooser_item_activated_cb),
2905 data);
2906
2907 action = gtk_action_group_get_action (data->actions, "Open");
2908 g_object_set (action, "short_label", _("Open"), NULL);
2909 //gtk_action_connect_proxy (action, GTK_WIDGET (open_button));
2910 gtk_activatable_set_related_action (GTK_ACTIVATABLE (open_button), action);
2911
2912 gtk_toolbar_insert (GTK_TOOLBAR (data->toolbar), open_button, 1);
2913
2914}
2915
2916
2917/* Callback function for the undo action */
2918/*static void
2919activate_action (GSimpleAction *action, GVariant *parameter, gpointer user_data)
2920{
2921 g_print ("Action %s activated\n", g_action_get_name (G_ACTION (action)));
2922}*/
2923
2924static void
2925activate_toggle (GSimpleAction *action, GVariant *parameter, gpointer user_data)
2926{
2927struct hbfile_data *data = user_data;
2928 GVariant *old_state, *new_state;
2929
2930 old_state = g_action_get_state (G_ACTION (action));
2931 new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));
2932
2933 DB( g_print ("Toggle action %s activated, state changes from %d to %d\n",
2934 g_action_get_name (G_ACTION (action)),
2935 g_variant_get_boolean (old_state),
2936 g_variant_get_boolean (new_state)) );
2937
2938 data->showall = g_variant_get_boolean (new_state);
2939 ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
2940
2941 g_simple_action_set_state (action, new_state);
2942 g_variant_unref (old_state);
2943}
2944
2945static void
2946activate_radio (GSimpleAction *action, GVariant *parameter, gpointer user_data)
2947{
2948//struct hbfile_data *data = user_data;
2949GVariant *old_state, *new_state;
2950
2951 old_state = g_action_get_state (G_ACTION (action));
2952 new_state = g_variant_new_string (g_variant_get_string (parameter, NULL));
2953
2954 DB( g_print ("Radio action %s activated, state changes from %s to %s\n",
2955 g_action_get_name (G_ACTION (action)),
2956 g_variant_get_string (old_state, NULL),
2957 g_variant_get_string (new_state, NULL)) );
2958
2959 PREFS->pnl_acc_show_by = DSPACC_GROUP_BY_TYPE;
2960 if( !strcmp("bank", g_variant_get_string(new_state, NULL)) )
2961 PREFS->pnl_acc_show_by = DSPACC_GROUP_BY_BANK;
2962
2963 ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
2964
2965 g_simple_action_set_state (action, new_state);
2966 g_variant_unref (old_state);
2967}
2968
2969
2970static const GActionEntry actions[] = {
2971// name, function(), type, state,
2972// { "paste", activate_action, NULL, NULL, NULL, {0,0,0} },
2973 { "showall", activate_toggle, NULL, "false" , NULL, {0,0,0} },
2974 { "groupby", activate_radio , "s", "'type'", NULL, {0,0,0} }
2975};
2976
2977
2978static void ui_panel_accounts_setup(struct hbfile_data *data)
2979{
2980GAction *action;
2981GVariant *new_state;
2982
2983 if( !G_IS_SIMPLE_ACTION_GROUP(data->action_group_acc) )
2984 return;
2985
2986 action = g_action_map_lookup_action (G_ACTION_MAP (data->action_group_acc), "showall");
2987 if( action )
2988 {
2989 new_state = g_variant_new_boolean (data->showall);
2990 g_simple_action_set_state (G_SIMPLE_ACTION(action), new_state);
2991 }
2992
2993 action = g_action_map_lookup_action (G_ACTION_MAP (data->action_group_acc), "groupby");
2994 if( action )
2995 {
2996 const gchar *value = (PREFS->pnl_acc_show_by == DSPACC_GROUP_BY_TYPE) ? "type" : "bank";
2997 new_state = g_variant_new_string (value);
2998 g_simple_action_set_state (G_SIMPLE_ACTION (action), new_state);
2999 }
3000
3001}
3002
3003
3004static GtkWidget *ui_mainwindow_create_youraccounts(struct hbfile_data *data)
3005{
3006GtkWidget *panel, *label, *widget, *sw, *tbar, *hbox, *image;
3007GtkToolItem *toolitem;
3008
3009 panel = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3010 gtk_container_set_border_width(GTK_CONTAINER(panel), SPACING_SMALL);
3011
3012 sw = gtk_scrolled_window_new (NULL, NULL);
3013 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_ETCHED_IN);
3014 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3015 gtk_box_pack_start (GTK_BOX (panel), sw, TRUE, TRUE, 0);
3016 widget = (GtkWidget *)create_list_account();
3017 data->LV_acc = widget;
3018 gtk_container_add (GTK_CONTAINER (sw), widget);
3019
3020 //list toolbar
3021 tbar = gtk_toolbar_new();
3022 gtk_toolbar_set_icon_size (GTK_TOOLBAR(tbar), GTK_ICON_SIZE_MENU);
3023 gtk_toolbar_set_style(GTK_TOOLBAR(tbar), GTK_TOOLBAR_ICONS);
3024 gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
3025 gtk_box_pack_start (GTK_BOX (panel), tbar, FALSE, FALSE, 0);
3026
3027 label = make_label_group(_("Your accounts"));
3028 toolitem = gtk_tool_item_new();
3029 gtk_container_add (GTK_CONTAINER(toolitem), label);
3030 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3031
3032 toolitem = gtk_separator_tool_item_new ();
3033 gtk_tool_item_set_expand (toolitem, TRUE);
3034 gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
3035 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3036
3037 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
3038 toolitem = gtk_tool_item_new();
3039 gtk_container_add (GTK_CONTAINER(toolitem), hbox);
3040 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3041
3042 widget = make_image_button(ICONNAME_HB_BUTTON_EXPAND, _("Expand all"));
3043 data->BT_expandall = widget;
3044 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
3045
3046 widget = make_image_button(ICONNAME_HB_BUTTON_COLLAPSE, _("Collapse all"));
3047 data->BT_collapseall = widget;
3048 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
3049
3050 toolitem = gtk_separator_tool_item_new ();
3051 gtk_tool_item_set_expand (toolitem, FALSE);
3052 gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
3053 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3054
3055
3056 //gmenu test (see test folder into gtk)
3057GMenu *menu, *section;
3058
3059 menu = g_menu_new ();
3060 //g_menu_append (menumodel, "About", "actions.undo");
3061 //g_menu_append (menumodel, "Test", "actions.redo");
3062 section = g_menu_new ();
3063 g_menu_append (section, _("Show all"), "actions.showall");
3064 g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
3065 g_object_unref (section);
3066
3067 section = g_menu_new ();
3068 g_menu_append (section, _("By type"), "actions.groupby::type");
3069 g_menu_append (section, _("By institition"), "actions.groupby::bank");
3070 g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
3071 g_object_unref (section);
3072
3073
3074 GSimpleActionGroup *group = g_simple_action_group_new ();
3075 data->action_group_acc = group;
3076 g_action_map_add_action_entries (G_ACTION_MAP (group), actions, G_N_ELEMENTS (actions), data);
3077
3078
3079 widget = gtk_menu_button_new();
3080 gtk_menu_button_set_direction (GTK_MENU_BUTTON(widget), GTK_ARROW_UP);
3081 gtk_widget_set_halign (widget, GTK_ALIGN_END);
3082 image = gtk_image_new_from_icon_name (ICONNAME_EMBLEM_SYSTEM, GTK_ICON_SIZE_MENU);
3083 g_object_set (widget, "image", image, NULL);
3084
3085 toolitem = gtk_tool_item_new();
3086 gtk_container_add (GTK_CONTAINER(toolitem), widget);
3087 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3088
3089 gtk_widget_insert_action_group (widget, "actions", G_ACTION_GROUP(group));
3090 gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (widget), G_MENU_MODEL (menu));
3091
3092 return panel;
3093}
3094
3095
3096static GtkWidget *ui_mainwindow_create_topspending(struct hbfile_data *data)
3097{
3098GtkWidget *panel, *hbox, *tbar;
3099GtkWidget *label, *widget;
3100GtkToolItem *toolitem;
3101
3102 widget = (GtkWidget *)create_list_topspending();
3103 data->LV_top = widget;
3104
3105 panel = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3106 gtk_container_set_border_width(GTK_CONTAINER(panel), SPACING_SMALL);
3107 data->GR_top = panel;
3108
3109 /* chart + listview */
3110 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
3111 gtk_box_pack_start (GTK_BOX (panel), hbox, TRUE, TRUE, 0);
3112
3113 widget = gtk_chart_new(CHART_TYPE_PIE);
3114 data->RE_pie = widget;
3115 gtk_chart_set_minor_prefs(GTK_CHART(widget), PREFS->euro_value, PREFS->minor_cur.symbol);
3116 gtk_chart_show_legend(GTK_CHART(data->RE_pie), TRUE, TRUE);
3117 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
3118
3119 //list toolbar
3120 tbar = gtk_toolbar_new();
3121 gtk_toolbar_set_icon_size (GTK_TOOLBAR(tbar), GTK_ICON_SIZE_MENU);
3122 gtk_toolbar_set_style(GTK_TOOLBAR(tbar), GTK_TOOLBAR_ICONS);
3123 gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
3124 gtk_box_pack_start (GTK_BOX (panel), tbar, FALSE, FALSE, 0);
3125
3126 label = make_label_group(_("Where your money goes"));
3127 toolitem = gtk_tool_item_new();
3128 gtk_container_add (GTK_CONTAINER(toolitem), label);
3129 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3130
3131 toolitem = gtk_separator_tool_item_new ();
3132 gtk_tool_item_set_expand (toolitem, TRUE);
3133 gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
3134 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3135
3136 /* total + date range */
3137 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
3138 toolitem = gtk_tool_item_new();
3139 gtk_container_add (GTK_CONTAINER(toolitem), hbox);
3140 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3141
3142 data->CY_range = make_daterange(label, FALSE);
3143 gtk_box_pack_end (GTK_BOX (hbox), data->CY_range, FALSE, FALSE, 0);
3144
3145 widget = make_radio(CYA_CATSUBCAT, TRUE, GTK_ORIENTATION_HORIZONTAL);
3146 data->RA_type = widget;
3147 gtk_box_pack_end (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
3148
3149 return panel;
3150}
3151
3152
3153static GtkWidget *ui_mainwindow_scheduled_create(struct hbfile_data *data)
3154{
3155GtkWidget *panel, *hbox, *vbox, *bbox, *sw, *tbar;
3156GtkWidget *label, *widget;
3157GtkToolItem *toolitem;
3158
3159 panel = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3160 gtk_container_set_border_width(GTK_CONTAINER(panel), SPACING_SMALL);
3161 data->GR_upc = panel;
3162
3163 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3164 //gtk_widget_set_margin_top(GTK_WIDGET(vbox), 0);
3165 //gtk_widget_set_margin_bottom(GTK_WIDGET(vbox), SPACING_SMALL);
3166 //gtk_widget_set_margin_start(GTK_WIDGET(vbox), 2*SPACING_SMALL);
3167 //gtk_widget_set_margin_end(GTK_WIDGET(vbox), SPACING_SMALL);
3168 gtk_box_pack_start (GTK_BOX (panel), vbox, TRUE, TRUE, 0);
3169
3170 sw = gtk_scrolled_window_new (NULL, NULL);
3171 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_ETCHED_IN);
3172 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3173 gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
3174
3175 widget = (GtkWidget *)create_list_upcoming();
3176 data->LV_upc = widget;
3177 gtk_container_add (GTK_CONTAINER (sw), widget);
3178
3179 tbar = gtk_toolbar_new();
3180 gtk_toolbar_set_icon_size (GTK_TOOLBAR(tbar), GTK_ICON_SIZE_MENU);
3181 gtk_toolbar_set_style(GTK_TOOLBAR(tbar), GTK_TOOLBAR_ICONS);
3182 gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
3183 gtk_box_pack_start (GTK_BOX (vbox), tbar, FALSE, FALSE, 0);
3184
3185 label = make_label_group(_("Scheduled transactions"));
3186 toolitem = gtk_tool_item_new();
3187 gtk_container_add (GTK_CONTAINER(toolitem), label);
3188 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3189
3190 toolitem = gtk_separator_tool_item_new ();
3191 gtk_tool_item_set_expand (toolitem, FALSE);
3192 gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
3193 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3194
3195
3196 bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
3197 toolitem = gtk_tool_item_new();
3198 gtk_container_add (GTK_CONTAINER(toolitem), bbox);
3199 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3200
3201 widget = gtk_button_new_with_label(_("Skip"));
3202 data->BT_sched_skip = widget;
3203 gtk_box_pack_start (GTK_BOX (bbox), widget, FALSE, FALSE, 0);
3204
3205 widget = gtk_button_new_with_label(_("Edit & Post"));
3206 data->BT_sched_editpost = widget;
3207 gtk_box_pack_start (GTK_BOX (bbox), widget, FALSE, FALSE, 0);
3208
3209 //TRANSLATORS: Posting a scheduled transaction is the action to materialize it into its target account.
3210 //TRANSLATORS: Before that action the automated transaction occurrence is pending and not yet really existing.
3211 widget = gtk_button_new_with_label (_("Post"));
3212 data->BT_sched_post = widget;
3213 gtk_box_pack_start (GTK_BOX (bbox), widget, FALSE, FALSE, 0);
3214
3215 toolitem = gtk_separator_tool_item_new ();
3216 gtk_tool_item_set_expand (toolitem, FALSE);
3217 gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
3218 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3219
3220 hbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3221 gtk_widget_set_valign (hbox, GTK_ALIGN_CENTER);
3222 toolitem = gtk_tool_item_new();
3223 gtk_container_add (GTK_CONTAINER(toolitem), hbox);
3224 gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3225
3226 label = make_label(_("maximum post date"), 0.0, 0.7);
3227 gtk_widget_set_halign (label, GTK_ALIGN_CENTER);
3228 gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
3229 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
3230
3231 label = make_label(NULL, 0.0, 0.7);
3232 data->LB_maxpostdate = label;
3233 gtk_widget_set_halign (label, GTK_ALIGN_CENTER);
3234 gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
3235 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
3236
3237 return panel;
3238}
3239
3240
3241/*
3242** the window creation
3243*/
3244GtkWidget *create_hbfile_window(GtkWidget *do_widget)
3245{
3246struct hbfile_data *data;
3247struct WinGeometry *wg;
3248GtkWidget *mainvbox, *vbox, *vpaned, *hpaned;
3249GtkWidget *widget;
3250GtkWidget *window;
3251GtkAction *action;
3252
3253 DB( g_print("\n[ui-mainwindow] create main window\n") );
3254
3255 data = g_malloc0(sizeof(struct hbfile_data));
3256 if(!data) return NULL;
3257
3258 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3259
3260 //store our window private data
3261 g_object_set_data(G_OBJECT(window), "inst_data", (gpointer)data);
3262 DB( g_print(" - new window=%p, inst_data=%p\n", window, data) );
3263
3264 // this is our mainwindow, so store it to GLOBALS data
3265 data->window = window;
3266 GLOBALS->mainwindow = window;
3267
3268 mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3269 gtk_container_add (GTK_CONTAINER (window), mainvbox);
3270
3271 ui_mainwindow_create_menu_bar_and_toolbar (data, mainvbox);
3272
3273#if HB_UNSTABLE_SHOW == TRUE
3274GtkWidget *bar, *label;
3275
3276 bar = gtk_info_bar_new ();
3277 gtk_box_pack_start (GTK_BOX (mainvbox), bar, FALSE, FALSE, 0);
3278 gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_WARNING);
3279 label = make_label(NULL, 0.5, 0.5);
3280 gtk_label_set_markup (GTK_LABEL(label), "Unstable Development Version");
3281 gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))), label, FALSE, FALSE, 0);
3282#endif
3283
3284 /* Add the main area */
3285 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3286 //gtk_container_set_border_width (GTK_CONTAINER(vbox), SPACING_MEDIUM);
3287 gtk_box_pack_start (GTK_BOX (mainvbox), vbox, TRUE, TRUE, 0);
3288
3289 vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
3290 data->vpaned = vpaned;
3291 gtk_box_pack_start (GTK_BOX (vbox), vpaned, TRUE, TRUE, 0);
3292
3293 hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
3294 data->hpaned = hpaned;
3295 gtk_paned_pack1 (GTK_PANED(vpaned), hpaned, FALSE, FALSE);
3296
3297 widget = ui_mainwindow_scheduled_create(data);
3298 gtk_paned_pack2 (GTK_PANED(vpaned), widget, TRUE, FALSE);
3299
3300 widget = ui_mainwindow_create_youraccounts(data);
3301 //gtk_widget_set_size_request (widget, 100, -1);
3302 gtk_paned_pack1 (GTK_PANED(hpaned), widget, FALSE, FALSE);
3303
3304 widget = ui_mainwindow_create_topspending(data);
3305 //gtk_widget_set_size_request (widget, -1, 100);
3306 gtk_paned_pack2 (GTK_PANED(hpaned), widget, TRUE, FALSE);
3307
3308
3309 //setup, init and show window
3310 wg = &PREFS->wal_wg;
3311 if(wg->s == 0)
3312 {
3313 gtk_window_move(GTK_WINDOW(window), wg->l, wg->t);
3314 gtk_window_resize(GTK_WINDOW(window), wg->w, wg->h);
3315 }
3316 else
3317 gtk_window_maximize(GTK_WINDOW(window));
3318
3319 gtk_widget_show_all (window);
3320
3321 //#1662197/1660910 moved after resize/show
3322 DB( g_print(" - vpaned=%d hpaned=%d\n", PREFS->wal_vpaned, PREFS->wal_hpaned) );
3323
3324 if(PREFS->wal_hpaned > 0)
3325 gtk_paned_set_position(GTK_PANED(data->hpaned), PREFS->wal_hpaned);
3326 if(PREFS->wal_vpaned > 0)
3327 gtk_paned_set_position(GTK_PANED(data->vpaned), PREFS->wal_vpaned);
3328
3329 //todo: move this elsewhere
3330 DB( g_print(" - setup stuff\n") );
3331
3332 data->filter = da_filter_malloc();
3333 filter_default_all_set(data->filter);
3334 gtk_combo_box_set_active(GTK_COMBO_BOX(data->CY_range), PREFS->date_range_wal);
3335
3336 action = gtk_ui_manager_get_action(data->manager, "/MenuBar/ViewMenu/Toolbar");
3337 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), PREFS->wal_toolbar);
3338 action = gtk_ui_manager_get_action(data->manager, "/MenuBar/ViewMenu/Spending");
3339 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), PREFS->wal_spending);
3340 action = gtk_ui_manager_get_action(data->manager, "/MenuBar/ViewMenu/Upcoming");
3341 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), PREFS->wal_upcoming);
3342
3343 /* Drag and drop support, set targets to NULL because we add the
3344 default uri_targets below */
3345
3346 /* support for opening a file by dragging onto the project window */
3347 gtk_drag_dest_set (GTK_WIDGET (window),
3348 GTK_DEST_DEFAULT_ALL,
3349 drop_types,
3350 G_N_ELEMENTS (drop_types),
3351 GDK_ACTION_COPY);
3352
3353 g_signal_connect (G_OBJECT (window), "drag-data-received",
3354 G_CALLBACK (ui_mainwindow_drag_data_received), window);
3355
3356
3357
3358 //connect all our signals
3359 DB( g_print(" - connect signals\n") );
3360
3361
3362 g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc)), "changed", G_CALLBACK (ui_mainwindow_selection), NULL);
3363 g_signal_connect (GTK_TREE_VIEW(data->LV_acc ), "row-activated", G_CALLBACK (ui_mainwindow_onRowActivated), GINT_TO_POINTER(2));
3364 g_signal_connect (G_OBJECT (data->BT_expandall ), "clicked" , G_CALLBACK (ui_panel_accounts_expand_all), NULL);
3365 g_signal_connect (G_OBJECT (data->BT_collapseall), "clicked" , G_CALLBACK (ui_panel_accounts_collapse_all), NULL);
3366
3367 g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_upc)), "changed", G_CALLBACK (ui_mainwindow_scheduled_selection_cb), NULL);
3368 g_signal_connect (GTK_TREE_VIEW(data->LV_upc), "row-activated", G_CALLBACK (ui_mainwindow_scheduled_onRowActivated), NULL);
3369 g_signal_connect (G_OBJECT (data->BT_sched_skip), "clicked", G_CALLBACK (ui_mainwindow_scheduled_skip_cb), data);
3370 g_signal_connect (G_OBJECT (data->BT_sched_editpost), "clicked", G_CALLBACK (ui_mainwindow_scheduled_editpost_cb), data);
3371 g_signal_connect (G_OBJECT (data->BT_sched_post), "clicked", G_CALLBACK (ui_mainwindow_scheduled_post_cb), data);
3372
3373 widget = radio_get_nth_widget(GTK_CONTAINER(data->RA_type), 1);
3374 if(widget)
3375 g_signal_connect (widget, "toggled", G_CALLBACK (ui_mainwindow_populate_topspending), &data);
3376
3377 g_signal_connect (data->CY_range, "changed", G_CALLBACK (ui_mainwindow_populate_topspending), NULL);
3378
3379
3380 /* GtkWindow events */
3381 g_signal_connect (window, "delete-event", G_CALLBACK (ui_mainwindow_dispose), (gpointer)data);
3382 g_signal_connect (window, "destroy", G_CALLBACK (ui_mainwindow_destroy), NULL);
3383
3384 //gtk_action_group_set_sensitive(data->actions, FALSE);
3385
3386 return window;
3387}
3388
03389
=== added file 'src/ui-budget-tabview.c'
--- src/ui-budget-tabview.c 1970-01-01 00:00:00 +0000
+++ src/ui-budget-tabview.c 2019-09-17 21:50:17 +0000
@@ -0,0 +1,2916 @@
1/* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 2018-2019 Adrien Dorsaz <adrien@adorsaz.ch>
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#include "hb-misc.h"
23#include "dsp_mainwindow.h"
24#include "hb-category.h"
25#include "ui-budget-tabview.h"
26
27/****************************************************************************/
28/* Implementation notes */
29/****************************************************************************/
30/*
31 * This dialog allows user to manage its budget within a GtkTreeView.
32 *
33 * The view rows are separated in three main tree roots:
34 * - Income: contains all Homebank categories of income type (see GF_INCOME)
35 * - Expense: contains all Homebank categories of expense type
36 * - Total: contains 3 sub-rows:
37 * - Income: sum all amounts of the Income root
38 * - Expense: sum all amounts of the Expense root
39 * - Summary: difference between the two above sub-rows
40 *
41 * The view columns contain:
42 * - Category: Homebank categories organised in hierarchy
43 * according to the main tree roots above and the categories hierarchy
44 *
45 * - Annual Total: sum all amounts of the year for the category
46 *
47 * - Monthly Average: average of the amounts for the category
48 *
49 * - Monthly: set the monthly amount when the Same flag is active
50 * - That column contains a toggle check box to enable or not monthly values
51 * Check it to disable the GF_CUSTOM flag of Homebank categories
52 * "Does this category has same amount planned every month ?"
53 *
54 * - 12 columns for each month of the year containing their specific amount
55 *
56 * The dialog shows 3 radio buttons on top to choose between 3 viewing modes:
57 * - Summary: show Homebank categories with budget set or set with GF_FORCED
58 * - Expense: show all available Homebank categories of expense type
59 * - Income: show all available Homebank categories of income type
60 *
61 */
62
63/****************************************************************************/
64/* Debug macros */
65/****************************************************************************/
66#define MYDEBUG 0
67
68#if MYDEBUG
69#define DB(x) (x);
70#else
71#define DB(x);
72#endif
73
74/* Global data */
75extern struct HomeBank *GLOBALS;
76extern struct Preferences *PREFS;
77
78static gchar *UI_BUD_TABVIEW_MONTHS[] = {
79 N_("Jan"), N_("Feb"), N_("Mar"),
80 N_("Apr"), N_("May"), N_("Jun"),
81 N_("Jul"), N_("Aug"), N_("Sept"),
82 N_("Oct"), N_("Nov"), N_("Dec"),
83 NULL};
84
85/* The different view mode available */
86static gchar *UI_BUD_TABVIEW_VIEW_MODE[] = {
87 N_("Summary"),
88 N_("Expense"),
89 N_("Income"),
90 NULL
91};
92
93/* These values has to correspond to UI_BUD_TABVIEW_VIEW_MODE[] */
94enum ui_bud_tabview_view_mode
95{
96 UI_BUD_TABVIEW_VIEW_SUMMARY = 0,
97 UI_BUD_TABVIEW_VIEW_EXPENSE,
98 UI_BUD_TABVIEW_VIEW_INCOME
99};
100typedef enum ui_bud_tabview_view_mode ui_bud_tabview_view_mode_t;
101
102/* These values corresponds to the return of category_type_get from hb-category */
103enum ui_bud_tabview_cat_type
104{
105 UI_BUD_TABVIEW_CAT_TYPE_EXPENSE = -1,
106 UI_BUD_TABVIEW_CAT_TYPE_NONE = 0, // Not real category type: used to retrieve tree roots
107 UI_BUD_TABVIEW_CAT_TYPE_INCOME = 1
108};
109typedef enum ui_bud_tabview_cat_type ui_bud_tabview_cat_type_t;
110
111/* enum for the Budget Tree Store model */
112enum ui_bud_tabview_store
113{
114 UI_BUD_TABVIEW_CATEGORY_KEY = 0,
115 UI_BUD_TABVIEW_CATEGORY_NAME,
116 UI_BUD_TABVIEW_CATEGORY_FULLNAME,
117 UI_BUD_TABVIEW_CATEGORY_TYPE,
118 UI_BUD_TABVIEW_IS_ROOT, // To retrieve easier the 3 main tree roots
119 UI_BUD_TABVIEW_IS_TOTAL, // To retrieve rows inside the Total root
120 UI_BUD_TABVIEW_IS_CHILD_HEADER, // The row corresponds to the head child which is shown before the separator
121 UI_BUD_TABVIEW_IS_SEPARATOR, // Row to just display a separator in Tree View
122 UI_BUD_TABVIEW_IS_MONITORING_FORCED,
123 UI_BUD_TABVIEW_IS_SAME_AMOUNT,
124 UI_BUD_TABVIEW_IS_SUB_CATEGORY,
125 UI_BUD_TABVIEW_HAS_BUDGET,
126 UI_BUD_TABVIEW_SAME_AMOUNT,
127 UI_BUD_TABVIEW_JANUARY,
128 UI_BUD_TABVIEW_FEBRUARY,
129 UI_BUD_TABVIEW_MARCH,
130 UI_BUD_TABVIEW_APRIL,
131 UI_BUD_TABVIEW_MAY,
132 UI_BUD_TABVIEW_JUNE,
133 UI_BUD_TABVIEW_JULY,
134 UI_BUD_TABVIEW_AUGUST,
135 UI_BUD_TABVIEW_SEPTEMBER,
136 UI_BUD_TABVIEW_OCTOBER,
137 UI_BUD_TABVIEW_NOVEMBER,
138 UI_BUD_TABVIEW_DECEMBER,
139 UI_BUD_TABVIEW_NUMBER_COLOMNS
140};
141typedef enum ui_bud_tabview_store ui_bud_tabview_store_t;
142
143// Retrieve a row iterator according to specific criterias
144const struct ui_bud_tabview_search_criteria
145{
146 // Search by non-zero category key
147 guint32 row_category_key;
148 // Search by other criterias
149 ui_bud_tabview_cat_type_t row_category_type;
150 gboolean row_is_root;
151 gboolean row_is_total;
152 // Found iterator, NULL if not found
153 GtkTreeIter *iterator;
154} ui_bud_tabview_search_criteria_default = {0, UI_BUD_TABVIEW_CAT_TYPE_NONE, FALSE, FALSE, NULL} ;
155typedef struct ui_bud_tabview_search_criteria ui_bud_tabview_search_criteria_t;
156
157/*
158 * Local headers
159 **/
160
161// GtkTreeStore model
162static gboolean ui_bud_tabview_model_search_iterator (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, ui_bud_tabview_search_criteria_t *search);
163static void ui_bud_tabview_model_add_category_with_lineage(GtkTreeStore *budget, GtkTreeIter *balanceIter, guint32 *key_category);
164static void ui_bud_tabview_model_collapse (GtkTreeView *view);
165static void ui_bud_tabview_model_insert_roots(GtkTreeStore* budget);
166static void ui_bud_tabview_model_update_monthly_total(GtkTreeStore* budget);
167static gboolean ui_bud_tabview_model_row_filter (GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
168#if HB_BUD_TABVIEW_EDIT_ENABLE
169static gboolean ui_bud_tabview_model_row_merge_filter_with_headers (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
170static gboolean ui_bud_tabview_model_row_filter_parents (GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
171#endif
172static gint ui_bud_tabview_model_row_sort (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data);
173static GtkTreeModel * ui_bud_tabview_model_new ();
174
175// GtkTreeView widget
176static void ui_bud_tabview_view_display_category_name (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
177static void ui_bud_tabview_view_display_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
178static void ui_bud_tabview_view_display_is_same_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
179static void ui_bud_tabview_view_display_annual_total (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
180static void ui_bud_tabview_view_display_monthly_average(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
181static void ui_bud_tabview_view_toggle (gpointer user_data, ui_bud_tabview_view_mode_t view_mode);
182static gboolean ui_bud_tabview_view_search (GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer data);
183#if HB_BUD_TABVIEW_EDIT_ENABLE
184static gboolean ui_bud_tabview_view_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
185static void ui_bud_tabview_view_on_select(GtkTreeSelection *treeselection, gpointer user_data);
186#endif
187static GtkWidget *ui_bud_tabview_view_new (gpointer user_data);
188
189// UI actions
190#if HB_BUD_TABVIEW_EDIT_ENABLE
191static void ui_bud_tabview_cell_update_category(GtkCellRendererText *renderer, gchar *filter_path, gchar *new_text, gpointer user_data);
192static void ui_bud_tabview_cell_update_amount(GtkCellRendererText *renderer, gchar *filter_path, gchar *new_text, gpointer user_data);
193static void ui_bud_tabview_cell_update_is_same_amount(GtkCellRendererText *renderer, gchar *filter_path, gpointer user_data);
194#endif
195static void ui_bud_tabview_view_update_mode (GtkToggleButton *button, gpointer user_data);
196static void ui_bud_tabview_view_expand (GtkButton *button, gpointer user_data);
197static void ui_bud_tabview_view_collapse (GtkButton *button, gpointer user_data);
198#if HB_BUD_TABVIEW_EDIT_ENABLE
199static gboolean ui_bud_tabview_get_selected_category (GtkTreeModel **budget, GtkTreeIter *iter, Category **category, ui_bud_tabview_data_t *data);
200static gboolean ui_bud_tabview_get_selected_root_iter (GtkTreeModel **budget, GtkTreeIter *iter, ui_bud_tabview_data_t *data);
201static void ui_bud_tabview_category_add_full_filled (GtkWidget *source, gpointer user_data);
202static void ui_bud_tabview_category_add (GtkButton *button, gpointer user_data);
203static void ui_bud_tabview_category_delete (GtkButton *button, gpointer user_data);
204static void ui_bud_tabview_category_merge_full_filled (GtkWidget *source, gpointer user_data);
205static void ui_bud_tabview_category_merge (GtkButton *button, gpointer user_data);
206static void ui_bud_tabview_category_reset (GtkButton *button, gpointer user_data);
207#endif
208static gboolean ui_bud_tabview_on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data);
209static void ui_bud_tabview_dialog_close(ui_bud_tabview_data_t *data, gint response);
210
211/**
212 * GtkTreeStore model
213 **/
214
215// Look for category by deterministic characteristics
216// Only categories with specific characteristics can be easily found
217// like roots, total rows and categories with real key id
218// You are responsible to g_free iterator
219static gboolean ui_bud_tabview_model_search_iterator (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, ui_bud_tabview_search_criteria_t *search)
220{
221guint32 category_key;
222ui_bud_tabview_cat_type_t category_type;
223gboolean is_found = FALSE, is_root, is_total, is_separator;
224
225 search->iterator = NULL;
226
227 gtk_tree_model_get (model, iter,
228 UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
229 UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
230 UI_BUD_TABVIEW_IS_ROOT, &is_root,
231 UI_BUD_TABVIEW_IS_TOTAL, &is_total,
232 UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
233 -1);
234
235 if (search->row_category_key > 0 // Look for iter of real category row
236 && category_key == search->row_category_key
237 && !(is_total)
238 && !(is_root)
239 )
240 {
241 DB(g_print("\tFound row with key %d\n", category_key));
242 is_found = TRUE;
243 }
244 else if (search->row_category_key == 0 // Look for iter of fake category row
245 && is_root == search->row_is_root
246 && is_total == search->row_is_total
247 && category_type == search->row_category_type
248 && !is_separator
249 )
250 {
251 DB(g_print("\tFound row with is_root = %d, is_total %d, type = %d\n", is_root, is_total, category_type));
252 is_found = TRUE;
253 }
254
255 // If found, save result to struct
256 if (is_found)
257 {
258 search->iterator = g_malloc0(sizeof(GtkTreeIter));
259 *search->iterator = *iter;
260 }
261
262 return is_found;
263}
264
265/* Recursive function which add a new row in the budget model with all its ancestors */
266static void ui_bud_tabview_model_add_category_with_lineage(GtkTreeStore *budget, GtkTreeIter *balanceIter, guint32 *key_category)
267{
268GtkTreeIter child;
269GtkTreeIter *parent;
270Category *bdg_category;
271gboolean cat_is_same_amount;
272ui_bud_tabview_search_criteria_t parent_search = ui_bud_tabview_search_criteria_default;
273
274 bdg_category = da_cat_get(*key_category);
275
276 if (bdg_category == NULL)
277 {
278 return;
279 }
280
281 cat_is_same_amount = (! (bdg_category->flags & GF_CUSTOM));
282
283 /* Check if parent category already exists */
284 parent_search.row_category_key = bdg_category->parent;
285
286 gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
287 (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
288 &parent_search);
289
290 if (bdg_category->parent == 0)
291 {
292 // If we are one of the oldest parent, stop recursion
293 gtk_tree_store_insert (
294 budget,
295 &child,
296 balanceIter,
297 -1);
298 }
299 else
300 {
301 if (parent_search.iterator)
302 {
303 DB(g_print("\tRecursion optimisation: parent key %d already exists\n", parent_search.row_category_key));
304 // If parent already exists, stop recursion
305 parent = parent_search.iterator;
306 }
307 else
308 {
309 // Parent has not been found, ask to create it first
310 ui_bud_tabview_model_add_category_with_lineage(budget, balanceIter, &(bdg_category->parent));
311
312 // Now, we are sure parent exists, look for it again
313 gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
314 (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
315 &parent_search);
316
317 parent = parent_search.iterator;
318 }
319
320 gtk_tree_store_insert (
321 budget,
322 &child,
323 parent,
324 -1);
325 }
326
327 DB(g_print("insert new category %s (key: %d, type: %d)\n",
328 bdg_category->name, bdg_category->key, category_type_get (bdg_category)));
329
330 gtk_tree_store_set(
331 budget,
332 &child,
333 UI_BUD_TABVIEW_CATEGORY_KEY, bdg_category->key,
334 UI_BUD_TABVIEW_CATEGORY_NAME, bdg_category->name,
335 UI_BUD_TABVIEW_CATEGORY_FULLNAME, bdg_category->fullname,
336 UI_BUD_TABVIEW_CATEGORY_TYPE, category_type_get (bdg_category),
337 UI_BUD_TABVIEW_IS_MONITORING_FORCED, (bdg_category->flags & GF_FORCED),
338 UI_BUD_TABVIEW_IS_ROOT, FALSE,
339 UI_BUD_TABVIEW_IS_SAME_AMOUNT, cat_is_same_amount,
340 UI_BUD_TABVIEW_IS_TOTAL, FALSE,
341 UI_BUD_TABVIEW_IS_SUB_CATEGORY, bdg_category->parent != 0,
342 UI_BUD_TABVIEW_HAS_BUDGET, (bdg_category->flags & GF_BUDGET),
343 UI_BUD_TABVIEW_SAME_AMOUNT, bdg_category->budget[0],
344 UI_BUD_TABVIEW_JANUARY, bdg_category->budget[1],
345 UI_BUD_TABVIEW_FEBRUARY, bdg_category->budget[2],
346 UI_BUD_TABVIEW_MARCH, bdg_category->budget[3],
347 UI_BUD_TABVIEW_APRIL, bdg_category->budget[4],
348 UI_BUD_TABVIEW_MAY, bdg_category->budget[5],
349 UI_BUD_TABVIEW_JUNE, bdg_category->budget[6],
350 UI_BUD_TABVIEW_JULY, bdg_category->budget[7],
351 UI_BUD_TABVIEW_AUGUST, bdg_category->budget[8],
352 UI_BUD_TABVIEW_SEPTEMBER, bdg_category->budget[9],
353 UI_BUD_TABVIEW_OCTOBER, bdg_category->budget[10],
354 UI_BUD_TABVIEW_NOVEMBER, bdg_category->budget[11],
355 UI_BUD_TABVIEW_DECEMBER, bdg_category->budget[12],
356 -1);
357
358 // Always add child header and separator
359 parent = gtk_tree_iter_copy(&child);
360 gtk_tree_store_insert_with_values(
361 budget,
362 &child,
363 parent,
364 -1,
365 UI_BUD_TABVIEW_CATEGORY_KEY, bdg_category->key,
366 UI_BUD_TABVIEW_CATEGORY_NAME, bdg_category->name,
367 UI_BUD_TABVIEW_CATEGORY_FULLNAME, bdg_category->fullname,
368 UI_BUD_TABVIEW_CATEGORY_TYPE, category_type_get (bdg_category),
369 UI_BUD_TABVIEW_IS_CHILD_HEADER, TRUE,
370 -1);
371
372 gtk_tree_store_insert_with_values(
373 budget,
374 &child,
375 parent,
376 -1,
377 UI_BUD_TABVIEW_CATEGORY_KEY, bdg_category->key,
378 UI_BUD_TABVIEW_CATEGORY_TYPE, category_type_get (bdg_category),
379 UI_BUD_TABVIEW_IS_SEPARATOR, TRUE,
380 -1);
381
382 gtk_tree_iter_free(parent);
383
384 g_free(parent_search.iterator);
385
386 return;
387}
388
389// Collapse all categories except root
390static void ui_bud_tabview_model_collapse (GtkTreeView *view)
391{
392GtkTreeModel *budget;
393GtkTreePath *path;
394ui_bud_tabview_search_criteria_t root_search = ui_bud_tabview_search_criteria_default;
395
396 budget = gtk_tree_view_get_model (view);
397
398 gtk_tree_view_collapse_all(view);
399
400 // Keep root categories expanded
401
402 // Retrieve income root
403 root_search.row_is_root = TRUE;
404 root_search.row_is_total = FALSE;
405 root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_INCOME;
406 gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
407 (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
408 &root_search);
409
410 if (root_search.iterator != NULL)
411 {
412 path = gtk_tree_model_get_path(budget, root_search.iterator);
413 gtk_tree_view_expand_row(view, path, FALSE);
414 }
415
416 // Retrieve expense root
417 root_search.row_is_root = TRUE;
418 root_search.row_is_total = FALSE;
419 root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_EXPENSE;
420 gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
421 (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
422 &root_search);
423
424 if (root_search.iterator != NULL)
425 {
426 path = gtk_tree_model_get_path(budget, root_search.iterator);
427 gtk_tree_view_expand_row(view, path, FALSE);
428 }
429
430 // Retrieve total root
431 root_search.row_is_root = TRUE;
432 root_search.row_is_total = FALSE;
433 root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_NONE;
434 gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
435 (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
436 &root_search);
437
438 if (root_search.iterator != NULL)
439 {
440 path = gtk_tree_model_get_path(budget, root_search.iterator);
441 gtk_tree_view_expand_row(view, path, FALSE);
442 }
443
444 g_free(root_search.iterator);
445
446 return;
447}
448
449// Create tree roots for the store
450static void ui_bud_tabview_model_insert_roots(GtkTreeStore* budget)
451{
452GtkTreeIter iter, root;
453
454 gtk_tree_store_insert_with_values (
455 budget,
456 &root,
457 NULL,
458 -1,
459 UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
460 UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
461 UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
462 UI_BUD_TABVIEW_IS_ROOT, TRUE,
463 UI_BUD_TABVIEW_IS_TOTAL, FALSE,
464 -1);
465
466 // For add category dialog: copy of the root to be able to select it
467 gtk_tree_store_insert_with_values (
468 budget,
469 &iter,
470 &root,
471 -1,
472 UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
473 UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
474 UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
475 UI_BUD_TABVIEW_CATEGORY_KEY, 0,
476 UI_BUD_TABVIEW_IS_ROOT, FALSE,
477 UI_BUD_TABVIEW_IS_TOTAL, FALSE,
478 UI_BUD_TABVIEW_IS_CHILD_HEADER, TRUE,
479 -1);
480
481 // For add category dialog: add a separator to distinguish root with children
482 gtk_tree_store_insert_with_values (
483 budget,
484 &iter,
485 &root,
486 -1,
487 UI_BUD_TABVIEW_IS_SEPARATOR, TRUE,
488 UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
489 -1);
490
491 gtk_tree_store_insert_with_values (
492 budget,
493 &root,
494 NULL,
495 -1,
496 UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
497 UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
498 UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
499 UI_BUD_TABVIEW_IS_ROOT, TRUE,
500 UI_BUD_TABVIEW_IS_TOTAL, FALSE,
501 -1);
502
503 // For add category dialog: copy of the root to be able to select it
504 gtk_tree_store_insert_with_values (
505 budget,
506 &iter,
507 &root,
508 -1,
509 UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
510 UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
511 UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
512 UI_BUD_TABVIEW_CATEGORY_KEY, 0,
513 UI_BUD_TABVIEW_IS_ROOT, FALSE,
514 UI_BUD_TABVIEW_IS_TOTAL, FALSE,
515 UI_BUD_TABVIEW_IS_CHILD_HEADER, TRUE,
516 -1);
517
518 // For add category dialog: add a separator to distinguish root with children
519 gtk_tree_store_insert_with_values (
520 budget,
521 &iter,
522 &root,
523 -1,
524 UI_BUD_TABVIEW_IS_SEPARATOR, TRUE,
525 UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
526 -1);
527
528 gtk_tree_store_insert_with_values (
529 budget,
530 &root,
531 NULL,
532 -1,
533 UI_BUD_TABVIEW_CATEGORY_NAME, _("Totals"),
534 UI_BUD_TABVIEW_CATEGORY_FULLNAME, _("Totals"),
535 UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_NONE,
536 UI_BUD_TABVIEW_IS_ROOT, TRUE,
537 UI_BUD_TABVIEW_IS_TOTAL, FALSE,
538 -1);
539
540 return;
541}
542
543// Update (or insert) total rows for a budget according to the view mode
544// This function will is used to initiate model and to refresh it after change by user
545static void ui_bud_tabview_model_update_monthly_total(GtkTreeStore* budget)
546{
547ui_bud_tabview_search_criteria_t root_search = ui_bud_tabview_search_criteria_default;
548GtkTreeIter total_root, child;
549double total_income[12] = {0}, total_expense[12] = {0};
550gboolean cat_is_same_amount;
551int n_category;
552
553 // Go through all categories to compute totals
554 n_category = da_cat_get_max_key();
555
556 for(guint32 i=1; i<=n_category; ++i)
557 {
558 Category *bdg_category;
559 gboolean cat_is_income;
560
561 bdg_category = da_cat_get(i);
562
563 if (bdg_category == NULL)
564 {
565 continue;
566 }
567
568 cat_is_income = (category_type_get (bdg_category) == 1);
569 cat_is_same_amount = (! (bdg_category->flags & GF_CUSTOM));
570
571 for (gint j=0; j<=11; ++j)
572 {
573 if (cat_is_income)
574 {
575 if (cat_is_same_amount)
576 {
577 total_income[j] += bdg_category->budget[0];
578 }
579 else
580 {
581 total_income[j] += bdg_category->budget[j+1];
582 }
583 }
584 else
585 {
586 if (cat_is_same_amount)
587 {
588 total_expense[j] += bdg_category->budget[0];
589 }
590 else
591 {
592 total_expense[j] += bdg_category->budget[j+1];
593 }
594 }
595 }
596 }
597
598 // Retrieve total root and insert required total rows
599 root_search.row_is_root = TRUE;
600 root_search.row_is_total = FALSE;
601 root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_NONE;
602 gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
603 (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
604 &root_search);
605
606 if (!root_search.iterator)
607 {
608 return;
609 }
610
611 total_root = *root_search.iterator;
612
613 // Retrieve and set totals
614 root_search.row_is_root = FALSE;
615 root_search.row_is_total = TRUE;
616
617 // First, look for Incomes
618 root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_INCOME;
619
620 gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
621 (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
622 &root_search);
623
624 if (root_search.iterator)
625 {
626 child = *root_search.iterator;
627 }
628 else
629 {
630 gtk_tree_store_insert(budget, &child, &total_root, -1);
631 }
632
633 gtk_tree_store_set (
634 budget,
635 &child,
636 UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
637 UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
638 UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
639 UI_BUD_TABVIEW_IS_TOTAL, TRUE,
640 UI_BUD_TABVIEW_JANUARY, total_income[0],
641 UI_BUD_TABVIEW_FEBRUARY, total_income[1],
642 UI_BUD_TABVIEW_MARCH, total_income[2],
643 UI_BUD_TABVIEW_APRIL, total_income[3],
644 UI_BUD_TABVIEW_MAY, total_income[4],
645 UI_BUD_TABVIEW_JUNE, total_income[5],
646 UI_BUD_TABVIEW_JULY, total_income[6],
647 UI_BUD_TABVIEW_AUGUST, total_income[7],
648 UI_BUD_TABVIEW_SEPTEMBER, total_income[8],
649 UI_BUD_TABVIEW_OCTOBER, total_income[9],
650 UI_BUD_TABVIEW_NOVEMBER, total_income[10],
651 UI_BUD_TABVIEW_DECEMBER, total_income[11],
652 -1);
653
654 // Then look for Expenses
655 root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_EXPENSE;
656
657 gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
658 (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
659 &root_search);
660
661 if (root_search.iterator)
662 {
663 child = *root_search.iterator;
664 }
665 else
666 {
667 gtk_tree_store_insert(budget, &child, &total_root, -1);
668 }
669
670 gtk_tree_store_set (
671 budget,
672 &child,
673 UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
674 UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
675 UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
676 UI_BUD_TABVIEW_IS_TOTAL, TRUE,
677 UI_BUD_TABVIEW_JANUARY, total_expense[0],
678 UI_BUD_TABVIEW_FEBRUARY, total_expense[1],
679 UI_BUD_TABVIEW_MARCH, total_expense[2],
680 UI_BUD_TABVIEW_APRIL, total_expense[3],
681 UI_BUD_TABVIEW_MAY, total_expense[4],
682 UI_BUD_TABVIEW_JUNE, total_expense[5],
683 UI_BUD_TABVIEW_JULY, total_expense[6],
684 UI_BUD_TABVIEW_AUGUST, total_expense[7],
685 UI_BUD_TABVIEW_SEPTEMBER, total_expense[8],
686 UI_BUD_TABVIEW_OCTOBER, total_expense[9],
687 UI_BUD_TABVIEW_NOVEMBER, total_expense[10],
688 UI_BUD_TABVIEW_DECEMBER, total_expense[11],
689 -1);
690
691 // Finally, set Balance total row
692 root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_NONE;
693
694 gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
695 (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
696 &root_search);
697
698 if (root_search.iterator)
699 {
700 child = *root_search.iterator;
701 }
702 else
703 {
704 gtk_tree_store_insert(budget, &child, &total_root, -1);
705 }
706
707 gtk_tree_store_set (
708 budget,
709 &child,
710 UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_SUMMARY]),
711 UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_SUMMARY]),
712 UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_NONE,
713 UI_BUD_TABVIEW_IS_TOTAL, TRUE,
714 UI_BUD_TABVIEW_JANUARY, total_income[0] + total_expense[0],
715 UI_BUD_TABVIEW_FEBRUARY, total_income[1] + total_expense[1],
716 UI_BUD_TABVIEW_MARCH, total_income[2] + total_expense[2],
717 UI_BUD_TABVIEW_APRIL, total_income[3] + total_expense[3],
718 UI_BUD_TABVIEW_MAY, total_income[4] + total_expense[4],
719 UI_BUD_TABVIEW_JUNE, total_income[5] + total_expense[5],
720 UI_BUD_TABVIEW_JULY, total_income[6] + total_expense[6],
721 UI_BUD_TABVIEW_AUGUST, total_income[7] + total_expense[7],
722 UI_BUD_TABVIEW_SEPTEMBER, total_income[8] + total_expense[8],
723 UI_BUD_TABVIEW_OCTOBER, total_income[9] + total_expense[9],
724 UI_BUD_TABVIEW_NOVEMBER, total_income[10] + total_expense[10],
725 UI_BUD_TABVIEW_DECEMBER, total_income[11] + total_expense[11],
726 -1);
727
728 g_free(root_search.iterator);
729
730 return;
731}
732
733// Filter shown rows according to VIEW mode
734static gboolean ui_bud_tabview_model_row_filter (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
735{
736gboolean is_visible, is_root, is_total, is_separator, is_childheader;
737ui_bud_tabview_data_t* data;
738ui_bud_tabview_view_mode_t view_mode;
739guint32 category_key;
740ui_bud_tabview_cat_type_t category_type;
741Category *bdg_category;
742
743 is_visible = TRUE;
744 data = user_data;
745 view_mode = radio_get_active(GTK_CONTAINER(data->RA_mode));
746
747 gtk_tree_model_get(model, iter,
748 UI_BUD_TABVIEW_IS_ROOT, &is_root,
749 UI_BUD_TABVIEW_IS_TOTAL, &is_total,
750 UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
751 UI_BUD_TABVIEW_IS_CHILD_HEADER, &is_childheader,
752 UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
753 UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
754 -1);
755
756 // On specific mode, hide categories of opposite type
757 if (!is_total
758 && category_type == UI_BUD_TABVIEW_CAT_TYPE_INCOME
759 && view_mode == UI_BUD_TABVIEW_VIEW_EXPENSE)
760 {
761 is_visible = FALSE;
762 }
763
764 if (!is_total
765 && category_type == UI_BUD_TABVIEW_CAT_TYPE_EXPENSE
766 && view_mode == UI_BUD_TABVIEW_VIEW_INCOME)
767 {
768 is_visible = FALSE;
769 }
770
771 // Hide fake first child root used for add dialog
772 if (is_childheader
773 || is_separator)
774 {
775 is_visible = FALSE;
776 }
777
778 // On balance mode, hide not forced empty categories
779 if (!is_total
780 && !is_root
781 && !is_childheader
782 && !is_separator
783 && view_mode == UI_BUD_TABVIEW_VIEW_SUMMARY)
784 {
785 bdg_category = da_cat_get(category_key);
786
787 if (bdg_category != NULL)
788 {
789 // Either the category has some budget, or its display is forced
790 is_visible = (bdg_category->flags & (GF_BUDGET|GF_FORCED));
791
792 // Force display if one of its children should be displayed
793 if (!is_visible)
794 {
795 GtkTreeIter child;
796 Category *subcat;
797 guint32 subcat_key;
798 gint child_id=0;
799
800 while (gtk_tree_model_iter_nth_child(model,
801 &child,
802 iter,
803 child_id))
804 {
805 gtk_tree_model_get(model, &child,
806 UI_BUD_TABVIEW_CATEGORY_KEY, &subcat_key,
807 -1);
808
809 if (subcat_key != 0)
810 {
811 subcat = da_cat_get (subcat_key);
812
813 if (subcat != NULL)
814 {
815 is_visible = (subcat->flags & (GF_BUDGET|GF_FORCED));
816 }
817 }
818
819 // Stop loop on first visible children
820 if (is_visible)
821 {
822 break;
823 }
824
825 ++child_id;
826 }
827 }
828 }
829 }
830
831 return is_visible;
832}
833
834#if HB_BUD_TABVIEW_EDIT_ENABLE
835// Filter rows to show only parent categories
836static gboolean ui_bud_tabview_model_row_filter_parents (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
837{
838ui_bud_tabview_data_t *data = user_data;
839gboolean is_visible, is_root, is_total, is_separator, is_childheader;
840Category *bdg_category;
841guint32 category_key;
842ui_bud_tabview_cat_type_t category_type;
843ui_bud_tabview_view_mode_t view_mode = UI_BUD_TABVIEW_VIEW_SUMMARY;
844
845 view_mode = radio_get_active(GTK_CONTAINER(data->RA_mode));
846
847 is_visible = TRUE;
848
849 gtk_tree_model_get(model, iter,
850 UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
851 UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
852 UI_BUD_TABVIEW_IS_ROOT, &is_root,
853 UI_BUD_TABVIEW_IS_TOTAL, &is_total,
854 UI_BUD_TABVIEW_IS_CHILD_HEADER, &is_childheader,
855 UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
856 -1);
857
858 // Show root according to view_mode
859 if (is_root)
860 {
861 // Always hide total root
862 if(category_type == UI_BUD_TABVIEW_CAT_TYPE_NONE)
863 {
864 is_visible = FALSE;
865 }
866 else if(view_mode == UI_BUD_TABVIEW_VIEW_EXPENSE && category_type == UI_BUD_TABVIEW_CAT_TYPE_INCOME)
867 {
868 is_visible = FALSE;
869 }
870 else if(view_mode == UI_BUD_TABVIEW_VIEW_INCOME && category_type == UI_BUD_TABVIEW_CAT_TYPE_EXPENSE)
871 {
872 is_visible = FALSE;
873 }
874 }
875
876 // Hide Total rows
877 if (is_total) {
878 is_visible = FALSE;
879 }
880
881 if (category_key > 0 && (is_separator || is_childheader))
882 {
883 is_visible = FALSE;
884 }
885 else if (category_key > 0)
886 {
887 // Hide rows according to currently view mode
888 if(view_mode == UI_BUD_TABVIEW_VIEW_EXPENSE && category_type == UI_BUD_TABVIEW_CAT_TYPE_INCOME)
889 {
890 is_visible = FALSE;
891 }
892 else if(view_mode == UI_BUD_TABVIEW_VIEW_INCOME && category_type == UI_BUD_TABVIEW_CAT_TYPE_EXPENSE)
893 {
894 is_visible = FALSE;
895 }
896 else
897 {
898 // Show categories without parents
899 bdg_category = da_cat_get(category_key);
900
901 if (bdg_category != NULL)
902 {
903 if (bdg_category->parent > 0)
904 {
905 is_visible = FALSE;
906 }
907 }
908 }
909 }
910
911 return is_visible;
912}
913
914// Filter rows to show only mergeable categories
915static gboolean ui_bud_tabview_model_row_merge_filter_with_headers (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
916{
917ui_bud_tabview_data_t *data = user_data;
918gboolean is_visible, is_root, is_total, is_separator, is_childheader;
919guint32 category_key;
920ui_bud_tabview_cat_type_t category_type;
921
922 is_visible = TRUE;
923
924 gtk_tree_model_get(model, iter,
925 UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
926 UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
927 UI_BUD_TABVIEW_IS_ROOT, &is_root,
928 UI_BUD_TABVIEW_IS_TOTAL, &is_total,
929 UI_BUD_TABVIEW_IS_CHILD_HEADER, &is_childheader,
930 UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
931 -1);
932
933 // Hide source merge row
934 if (data->MERGE_source_category_key == category_key)
935 {
936 is_visible = FALSE;
937 }
938
939 // Hide Total root
940 if (is_root
941 && category_type == UI_BUD_TABVIEW_CAT_TYPE_NONE )
942 {
943 is_visible = FALSE;
944 }
945
946 // Hide Total rows
947 if (is_total)
948 {
949 is_visible = FALSE;
950 }
951
952 if ((is_separator || is_childheader))
953 {
954 GtkTreeIter parent;
955 gtk_tree_model_iter_parent(model, &parent, iter);
956
957 // Show child header and separator if parent has more than 2 children
958 is_visible = (gtk_tree_model_iter_n_children(model, &parent) > 2);
959 }
960
961 return is_visible;
962}
963#endif
964
965static gint ui_bud_tabview_model_row_sort (GtkTreeModel *model, GtkTreeIter *cat_a, GtkTreeIter *cat_b, gpointer user_data)
966{
967const gchar* cat_a_name;
968const gchar* cat_b_name;
969ui_bud_tabview_cat_type_t cat_a_type, cat_b_type;
970guint32 cat_a_key, cat_b_key;
971gboolean cat_a_is_childheader, cat_a_is_separator, cat_b_is_childheader, cat_b_is_separator;
972gint order = 0;
973
974 gtk_tree_model_get(model, cat_a,
975 UI_BUD_TABVIEW_CATEGORY_NAME, &cat_a_name,
976 UI_BUD_TABVIEW_CATEGORY_TYPE, &cat_a_type,
977 UI_BUD_TABVIEW_CATEGORY_KEY, &cat_a_key,
978 UI_BUD_TABVIEW_IS_CHILD_HEADER, &cat_a_is_childheader,
979 UI_BUD_TABVIEW_IS_SEPARATOR, &cat_a_is_separator,
980 -1);
981
982 gtk_tree_model_get(model, cat_b,
983 UI_BUD_TABVIEW_CATEGORY_NAME, &cat_b_name,
984 UI_BUD_TABVIEW_CATEGORY_TYPE, &cat_b_type,
985 UI_BUD_TABVIEW_CATEGORY_KEY, &cat_b_key,
986 UI_BUD_TABVIEW_IS_CHILD_HEADER, &cat_b_is_childheader,
987 UI_BUD_TABVIEW_IS_SEPARATOR, &cat_b_is_separator,
988 -1);
989
990 // Sort first by category type
991 if (cat_a_type != cat_b_type)
992 {
993 switch (cat_a_type)
994 {
995 case UI_BUD_TABVIEW_CAT_TYPE_INCOME:
996 order = -1;
997 break;
998 case UI_BUD_TABVIEW_CAT_TYPE_EXPENSE:
999 order = 0;
1000 break;
1001 case UI_BUD_TABVIEW_CAT_TYPE_NONE:
1002 order = 1;
1003 break;
1004 }
1005 }
1006 else
1007 {
1008 // On standard categories, just order by name
1009 if (!cat_a_is_childheader && !cat_a_is_separator
1010 && !cat_b_is_childheader && !cat_b_is_separator)
1011 {
1012 order = g_utf8_collate(g_utf8_casefold(cat_a_name, -1),
1013 g_utf8_casefold(cat_b_name, -1)
1014 );
1015 }
1016 // Otherwise, fake categories have to be first (header and separator)
1017 else if (cat_a_is_childheader || cat_a_is_separator)
1018 {
1019 if (!cat_b_is_separator && !cat_b_is_childheader)
1020 {
1021 order = -1;
1022 }
1023 // When both are fake, header has to be first
1024 else
1025 {
1026 order = (cat_a_is_childheader ? -1 : 1);
1027 }
1028 }
1029 else
1030 {
1031 // Same idea for fake categories when cat_b is fake, but
1032 // with reversed result, because sort function return
1033 // result according to cat_a
1034
1035 if (!cat_a_is_separator && !cat_a_is_childheader)
1036 {
1037 order = 1;
1038 }
1039 else
1040 {
1041 order = (cat_b_is_childheader ? 1 : -1);
1042 }
1043 }
1044 }
1045
1046 return order;
1047}
1048
1049// the budget model creation
1050static GtkTreeModel * ui_bud_tabview_model_new ()
1051{
1052GtkTreeStore *budget;
1053GtkTreeIter *iter_income, *iter_expense;
1054guint32 n_category;
1055ui_bud_tabview_search_criteria_t root_search = ui_bud_tabview_search_criteria_default;
1056
1057 // Create Tree Store
1058 budget = gtk_tree_store_new ( UI_BUD_TABVIEW_NUMBER_COLOMNS,
1059 G_TYPE_UINT, // UI_BUD_TABVIEW_CATEGORY_KEY
1060 G_TYPE_STRING, // UI_BUD_TABVIEW_CATEGORY_NAME
1061 G_TYPE_STRING, // UI_BUD_TABVIEW_CATEGORY_FULLNAME
1062 G_TYPE_INT, // UI_BUD_TABVIEW_CATEGORY_TYPE
1063 G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_ROOT
1064 G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_TOTAL
1065 G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_CHILD_HEADER
1066 G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_SEPARATOR
1067 G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_MONITORING_FORCED
1068 G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_SAME_AMOUNT
1069 G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_SUB_CATEGORY
1070 G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_HAS_BUDGET
1071 G_TYPE_DOUBLE, // UI_BUD_TABVIEW_SAME_AMOUNT
1072 G_TYPE_DOUBLE, // UI_BUD_TABVIEW_JANUARY
1073 G_TYPE_DOUBLE, // UI_BUD_TABVIEW_FEBRUARY
1074 G_TYPE_DOUBLE, // UI_BUD_TABVIEW_MARCH
1075 G_TYPE_DOUBLE, // UI_BUD_TABVIEW_APRIL
1076 G_TYPE_DOUBLE, // UI_BUD_TABVIEW_MAY
1077 G_TYPE_DOUBLE, // UI_BUD_TABVIEW_JUNE
1078 G_TYPE_DOUBLE, // UI_BUD_TABVIEW_JULY
1079 G_TYPE_DOUBLE, // UI_BUD_TABVIEW_AUGUST
1080 G_TYPE_DOUBLE, // UI_BUD_TABVIEW_SEPTEMBER
1081 G_TYPE_DOUBLE, // UI_BUD_TABVIEW_OCTOBER
1082 G_TYPE_DOUBLE, // UI_BUD_TABVIEW_NOVEMBER
1083 G_TYPE_DOUBLE // UI_BUD_TABVIEW_DECEMBER
1084 );
1085
1086 // Populate the store
1087
1088 /* Create tree roots */
1089 ui_bud_tabview_model_insert_roots (budget);
1090
1091 // Retrieve required root
1092 root_search.row_is_root = TRUE;
1093 root_search.row_is_total = FALSE;
1094
1095 root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_INCOME;
1096 gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
1097 (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
1098 &root_search);
1099 iter_income = root_search.iterator;
1100
1101 root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_EXPENSE;
1102 gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
1103 (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
1104 &root_search);
1105 iter_expense = root_search.iterator;
1106
1107 /* Create rows for real categories */
1108 n_category = da_cat_get_max_key();
1109
1110 for(guint32 i=1; i<=n_category; ++i)
1111 {
1112 Category *bdg_category;
1113 gboolean cat_is_income;
1114
1115 bdg_category = da_cat_get(i);
1116
1117 if (bdg_category == NULL)
1118 {
1119 continue;
1120 }
1121
1122 cat_is_income = (category_type_get (bdg_category) == 1);
1123
1124 DB(g_print(" category %d:'%s' isincome=%d, issub=%d hasbudget=%d parent=%d\n",
1125 bdg_category->key, bdg_category->name,
1126 cat_is_income, (bdg_category->flags & GF_SUB),
1127 (bdg_category->flags & GF_BUDGET), bdg_category->parent));
1128
1129 // Compute totals and initiate category in right tree root
1130 if (cat_is_income)
1131 {
1132 ui_bud_tabview_model_add_category_with_lineage(budget, iter_income, &(bdg_category->key));
1133 }
1134 else if (!cat_is_income)
1135 {
1136 ui_bud_tabview_model_add_category_with_lineage(budget, iter_expense, &(bdg_category->key));
1137 }
1138 }
1139
1140 /* Create rows for total root */
1141 ui_bud_tabview_model_update_monthly_total(GTK_TREE_STORE(budget));
1142
1143 /* Sort categories on same node level */
1144 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(budget),
1145 UI_BUD_TABVIEW_CATEGORY_NAME, ui_bud_tabview_model_row_sort,
1146 NULL, NULL);
1147 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (budget),
1148 UI_BUD_TABVIEW_CATEGORY_NAME, GTK_SORT_ASCENDING);
1149
1150 g_free(root_search.iterator);
1151
1152 return GTK_TREE_MODEL(budget);
1153}
1154
1155/**
1156 * GtkTreeView functions
1157 **/
1158
1159// Display category name in bold if it has budget
1160static void ui_bud_tabview_view_display_category_name (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
1161{
1162ui_bud_tabview_data_t *data = user_data;
1163gboolean has_budget, is_sub_category;
1164PangoWeight weight = PANGO_WEIGHT_NORMAL;
1165ui_bud_tabview_view_mode_t view_mode = UI_BUD_TABVIEW_VIEW_SUMMARY;
1166
1167 gtk_tree_model_get(model, iter,
1168 UI_BUD_TABVIEW_IS_SUB_CATEGORY, &is_sub_category,
1169 UI_BUD_TABVIEW_HAS_BUDGET, &has_budget,
1170 -1);
1171
1172 view_mode = radio_get_active(GTK_CONTAINER(data->RA_mode));
1173
1174 if (view_mode != UI_BUD_TABVIEW_VIEW_SUMMARY && has_budget)
1175 {
1176 weight = PANGO_WEIGHT_BOLD;
1177 }
1178
1179 g_object_set(renderer,
1180 "style", is_sub_category ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL,
1181 "weight", weight,
1182 NULL);
1183}
1184
1185// to enable or not edition on month columns
1186static void ui_bud_tabview_view_display_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
1187{
1188GtkAdjustment *adjustment;
1189gboolean is_same_amount, is_root, is_total, is_visible, is_editable;
1190ui_bud_tabview_cat_type_t row_category_type;
1191gdouble amount = 0.0;
1192gchar *text;
1193gchar *fgcolor;
1194const ui_bud_tabview_store_t column_id = GPOINTER_TO_INT(user_data);
1195
1196 gtk_tree_model_get(model, iter,
1197 UI_BUD_TABVIEW_CATEGORY_TYPE, &row_category_type,
1198 UI_BUD_TABVIEW_IS_ROOT, &is_root,
1199 UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
1200 UI_BUD_TABVIEW_IS_TOTAL, &is_total,
1201 -1);
1202
1203 // Text to display
1204 if (is_same_amount)
1205 {
1206 gtk_tree_model_get(model, iter, UI_BUD_TABVIEW_SAME_AMOUNT, &amount, -1);
1207 }
1208 else if (column_id >= UI_BUD_TABVIEW_JANUARY && column_id <= UI_BUD_TABVIEW_DECEMBER)
1209 {
1210 gtk_tree_model_get(model, iter, column_id, &amount, -1);
1211 }
1212
1213 text = g_strdup_printf("%.2f", amount);
1214 fgcolor = get_normal_color_amount(amount);
1215
1216 // Default styling values
1217 is_visible = TRUE;
1218 is_editable = FALSE;
1219
1220 if (is_root)
1221 {
1222 is_visible = FALSE;
1223 is_editable = FALSE;
1224 }
1225 else if (is_total)
1226 {
1227 is_visible = TRUE;
1228 is_editable = FALSE;
1229
1230 if (column_id == UI_BUD_TABVIEW_SAME_AMOUNT)
1231 {
1232 is_visible = FALSE;
1233 }
1234 }
1235 else if (is_same_amount)
1236 {
1237 is_visible = TRUE;
1238 is_editable = FALSE;
1239
1240 if (column_id == UI_BUD_TABVIEW_SAME_AMOUNT)
1241 {
1242 is_editable = TRUE;
1243 }
1244 }
1245 else if (! is_same_amount)
1246 {
1247 is_visible = TRUE;
1248 is_editable = TRUE;
1249
1250 if (column_id == UI_BUD_TABVIEW_SAME_AMOUNT)
1251 {
1252 is_editable = FALSE;
1253 }
1254 }
1255
1256 // Finally, visibility depends on set amount
1257#if !HB_BUD_TABVIEW_EDIT_ENABLE
1258 is_visible = (is_visible && amount != 0.0);
1259 is_editable = FALSE;
1260#else
1261 is_visible = (is_visible && (is_editable || amount != 0.0));
1262#endif
1263
1264 adjustment = gtk_adjustment_new(
1265 0.0, // initial-value
1266 -G_MAXDOUBLE, // minmal-value
1267 G_MAXDOUBLE, // maximal-value
1268 0.5, // step increment
1269 10, // page increment
1270 0); // page size (0 because irrelevant for GtkSpinButton)
1271
1272 g_object_set(renderer,
1273 "text", text,
1274 "visible", is_visible,
1275 "editable", is_editable,
1276 "foreground", fgcolor,
1277 "xalign", 1.0,
1278 "adjustment", adjustment,
1279 "digits", 2,
1280 NULL);
1281
1282 g_free(text);
1283}
1284
1285// to enable or not edition on month columns
1286static void ui_bud_tabview_view_display_is_same_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
1287{
1288gboolean is_same_amount, is_root, is_total, is_visible, is_sensitive;
1289
1290 gtk_tree_model_get(model, iter,
1291 UI_BUD_TABVIEW_IS_ROOT, &is_root,
1292 UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
1293 UI_BUD_TABVIEW_IS_TOTAL, &is_total,
1294 -1);
1295
1296 // Default values
1297 is_visible = TRUE;
1298 is_sensitive = TRUE;
1299
1300 if (is_root || is_total)
1301 {
1302 is_visible = FALSE;
1303 is_sensitive = FALSE;
1304 }
1305
1306 g_object_set(renderer,
1307 "activatable", TRUE,
1308 "active", is_same_amount,
1309 "visible", is_visible,
1310 "sensitive", is_sensitive,
1311 NULL);
1312}
1313
1314// Compute dynamically the annual total
1315static void ui_bud_tabview_view_display_annual_total (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
1316{
1317gboolean is_same_amount = FALSE, is_total = FALSE, is_root = FALSE;
1318gdouble amount = 0.0;
1319gdouble total = 0.0;
1320gchar *text;
1321gchar *fgcolor;
1322gboolean is_visible = TRUE;
1323
1324 gtk_tree_model_get(model, iter,
1325 UI_BUD_TABVIEW_IS_ROOT, &is_root,
1326 UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
1327 UI_BUD_TABVIEW_SAME_AMOUNT, &amount,
1328 UI_BUD_TABVIEW_IS_TOTAL, &is_total,
1329 -1);
1330
1331 if (is_same_amount)
1332 {
1333 total = 12.0 * amount;
1334 }
1335 else
1336 {
1337 for (int i = UI_BUD_TABVIEW_JANUARY ; i <= UI_BUD_TABVIEW_DECEMBER ; ++i)
1338 {
1339 gtk_tree_model_get(model, iter, i, &amount, -1);
1340 total += amount;
1341 }
1342 }
1343
1344 text = g_strdup_printf("%.2f", total);
1345 fgcolor = get_normal_color_amount(total);
1346
1347 if (is_root)
1348 {
1349 is_visible = FALSE;
1350 }
1351
1352 // Finally, visibility depends on set amount
1353 is_visible = (is_visible && amount != 0.0);
1354
1355 g_object_set(renderer,
1356 "text", text,
1357 "foreground", fgcolor,
1358 "visible", is_visible,
1359 "xalign", 1.0,
1360 NULL);
1361
1362 g_free(text);
1363}
1364
1365// Compute monthly average
1366static void ui_bud_tabview_view_display_monthly_average(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
1367{
1368gboolean is_same_amount = FALSE, is_total = FALSE, is_root = FALSE;
1369gdouble amount = 0.0;
1370gdouble average = 0.0;
1371gchar *text;
1372gchar *fgcolor;
1373gboolean is_visible = TRUE;
1374
1375 gtk_tree_model_get(model, iter,
1376 UI_BUD_TABVIEW_IS_ROOT, &is_root,
1377 UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
1378 UI_BUD_TABVIEW_SAME_AMOUNT, &amount,
1379 UI_BUD_TABVIEW_IS_TOTAL, &is_total,
1380 -1);
1381
1382 if (is_same_amount)
1383 {
1384 average = amount;
1385 }
1386 else
1387 {
1388 for (int i = UI_BUD_TABVIEW_JANUARY ; i <= UI_BUD_TABVIEW_DECEMBER ; ++i)
1389 {
1390 gtk_tree_model_get(model, iter, i, &amount, -1);
1391 average += amount;
1392 }
1393 average = hb_amount_round(average / 12.0, 2);
1394 }
1395
1396 text = g_strdup_printf("%.2f", average);
1397 fgcolor = get_normal_color_amount(average);
1398
1399 if (is_root)
1400 {
1401 is_visible = FALSE;
1402 }
1403
1404 // Finally, visibility depends on set amount
1405 is_visible = (is_visible && average != 0.0);
1406
1407 g_object_set(renderer,
1408 "text", text,
1409 "foreground", fgcolor,
1410 "visible", is_visible,
1411 "xalign", 1.0,
1412 NULL);
1413
1414 g_free(text);
1415}
1416
1417// When view mode is toggled:
1418// - recreate the view to update columns rendering
1419static void ui_bud_tabview_view_toggle (gpointer user_data, ui_bud_tabview_view_mode_t view_mode)
1420{
1421ui_bud_tabview_data_t *data = user_data;
1422GtkTreeModel *budget;
1423GtkWidget *view;
1424GtkTreePath* firstRow;
1425
1426 view = data->TV_budget;
1427
1428 budget = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
1429
1430 gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(budget));
1431
1432 if (data->TV_is_expanded)
1433 {
1434 gtk_tree_view_expand_all(GTK_TREE_VIEW(view));
1435 }
1436 else
1437 {
1438 ui_bud_tabview_model_collapse(GTK_TREE_VIEW(view));
1439 }
1440
1441 gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)));
1442
1443 firstRow = gtk_tree_path_new_from_string("0");
1444 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(view), firstRow, data->TVC_category, TRUE, 0, 0);
1445
1446 DB(g_print("[ui_bud_tabview] : button state changed to: %d\n", view_mode));
1447
1448 return;
1449}
1450
1451static gboolean ui_bud_tabview_view_search (GtkTreeModel *filter, gint column, const gchar *key, GtkTreeIter *filter_iter, gpointer data)
1452{
1453gboolean is_matching = FALSE, is_root, is_total;
1454GtkTreeModel *budget;
1455GtkTreeIter iter;
1456gchar *category_name;
1457
1458 budget = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
1459 gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter),
1460 &iter,
1461 filter_iter);
1462
1463 gtk_tree_model_get(budget, &iter,
1464 UI_BUD_TABVIEW_CATEGORY_NAME, &category_name,
1465 UI_BUD_TABVIEW_IS_ROOT, &is_root,
1466 UI_BUD_TABVIEW_IS_TOTAL, &is_total,
1467 -1);
1468
1469 if (!is_root && !is_total
1470 && g_strstr_len(g_utf8_casefold(category_name, -1), -1,
1471 g_utf8_casefold(key, -1)))
1472 {
1473 is_matching = TRUE;
1474 }
1475
1476 // GtkTreeViewSearchEqualFunc has to return FALSE only if iter matches.
1477 return !is_matching;
1478}
1479
1480#if HB_BUD_TABVIEW_EDIT_ENABLE
1481static gboolean ui_bud_tabview_view_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1482{
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches