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

Proposed by Adrien Dorsaz on 2019-02-07
Status: Needs review
Proposed branch: lp:~adorsaz/homebank/adv-budget-with-categories
Merge into: lp:homebank
Diff against target: 6435 lines (+6372/-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 (+2843/-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 2019-02-07 Needs Fixing on 2019-07-07
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.
Maxime DOYEN (mdoyen) wrote :

merged this code with my local trunk for review

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
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 on 2019-07-22

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

106. By Adrien Dorsaz on 2019-07-22

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

107. By Adrien Dorsaz on 2019-07-22

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

108. By Adrien Dorsaz on 2019-07-22

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

109. By Adrien Dorsaz on 2019-07-22

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

110. By Adrien Dorsaz on 2019-07-22

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

111. By Adrien Dorsaz on 2019-07-22

budget-tabview: center title columns and hide tree lines

That's to be coherent with others tree view style

112. By Adrien Dorsaz on 2019-07-22

budget-tabview: place total column on second position

113. By Adrien Dorsaz on 2019-07-22

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 on 2019-07-22

ui-budget-tabview: add monthly average column

115. By Adrien Dorsaz on 2019-07-22

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

116. By Adrien Dorsaz on 2019-07-22

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

117. By Adrien Dorsaz on 2019-07-22

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 on 2019-07-22

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

119. By Adrien Dorsaz on 2019-07-22

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

120. By Adrien Dorsaz on 2019-07-22

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

121. By Adrien Dorsaz on 2019-07-22

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

122. By Adrien Dorsaz on 2019-07-22

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

123. By Adrien Dorsaz on 2019-07-22

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

124. By Adrien Dorsaz on 2019-07-22

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

125. By Adrien Dorsaz on 2019-07-22

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

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 :-)

Unmerged revisions

125. By Adrien Dorsaz on 2019-07-22

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

124. By Adrien Dorsaz on 2019-07-22

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

123. By Adrien Dorsaz on 2019-07-22

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

122. By Adrien Dorsaz on 2019-07-22

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

121. By Adrien Dorsaz on 2019-07-22

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

120. By Adrien Dorsaz on 2019-07-22

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

119. By Adrien Dorsaz on 2019-07-22

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

118. By Adrien Dorsaz on 2019-07-22

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

117. By Adrien Dorsaz on 2019-07-22

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.

116. By Adrien Dorsaz on 2019-07-22

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/Makefile.am'
2--- src/Makefile.am 2019-04-11 20:36:11 +0000
3+++ src/Makefile.am 2019-07-22 20:14:59 +0000
4@@ -106,6 +106,8 @@
5 ui-assist-start.h \
6 ui-budget.c \
7 ui-budget.h \
8+ ui-budget-tabview.c \
9+ ui-budget-tabview.h \
10 ui-category.c \
11 ui-category.h \
12 ui-currency.c \
13
14=== modified file 'src/Makefile.in'
15--- src/Makefile.in 2019-04-11 20:36:11 +0000
16+++ src/Makefile.in 2019-07-22 20:14:59 +0000
17@@ -119,12 +119,21 @@
18 rep-vehicle.$(OBJEXT) ui-account.$(OBJEXT) \
19 ui-archive.$(OBJEXT) ui-assign.$(OBJEXT) \
20 ui-assist-import.$(OBJEXT) ui-assist-start.$(OBJEXT) \
21+<<<<<<< TREE
22 ui-budget.$(OBJEXT) ui-category.$(OBJEXT) \
23 ui-currency.$(OBJEXT) ui-dialogs.$(OBJEXT) ui-filter.$(OBJEXT) \
24 ui-hbfile.$(OBJEXT) ui-payee.$(OBJEXT) ui-pref.$(OBJEXT) \
25 ui-split.$(OBJEXT) ui-tag.$(OBJEXT) ui-transaction.$(OBJEXT) \
26 ui-txn-multi.$(OBJEXT) ui-widgets-data.$(OBJEXT) \
27 ui-widgets.$(OBJEXT)
28+=======
29+ ui-budget.$(OBJEXT) ui-budget-tabview.$(OBJEXT) \
30+ ui-category.$(OBJEXT) ui-currency.$(OBJEXT) \
31+ ui-dialogs.$(OBJEXT) ui-filter.$(OBJEXT) ui-hbfile.$(OBJEXT) \
32+ ui-payee.$(OBJEXT) ui-pref.$(OBJEXT) ui-split.$(OBJEXT) \
33+ ui-transaction.$(OBJEXT) ui-txn-multi.$(OBJEXT) \
34+ ui-widgets.$(OBJEXT)
35+>>>>>>> MERGE-SOURCE
36 homebank_OBJECTS = $(am_homebank_OBJECTS)
37 am__DEPENDENCIES_1 =
38 homebank_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
39@@ -441,6 +450,8 @@
40 ui-assist-start.h \
41 ui-budget.c \
42 ui-budget.h \
43+ ui-budget-tabview.c \
44+ ui-budget-tabview.h \
45 ui-category.c \
46 ui-category.h \
47 ui-currency.c \
48@@ -561,6 +572,7 @@
49 distclean-compile:
50 -rm -f *.tab.c
51
52+<<<<<<< TREE
53 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp-account.Po@am__quote@ # am--include-marker
54 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp-mainwindow.Po@am__quote@ # am--include-marker
55 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-chart-colors.Po@am__quote@ # am--include-marker
56@@ -627,6 +639,64 @@
57 @echo '# dummy' >$@-t && $(am__mv) $@-t $@
58
59 am--depfiles: $(am__depfiles_remade)
60+=======
61+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp_account.Po@am__quote@
62+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dsp_mainwindow.Po@am__quote@
63+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-chart-colors.Po@am__quote@
64+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-chart-stack.Po@am__quote@
65+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-chart.Po@am__quote@
66+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gtk-dateentry.Po@am__quote@
67+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-account.Po@am__quote@
68+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-archive.Po@am__quote@
69+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-assign.Po@am__quote@
70+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-category.Po@am__quote@
71+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-currency.Po@am__quote@
72+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-encoding.Po@am__quote@
73+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-export.Po@am__quote@
74+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-filter.Po@am__quote@
75+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-hbfile.Po@am__quote@
76+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-import-csv.Po@am__quote@
77+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-import-ofx.Po@am__quote@
78+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-import-qif.Po@am__quote@
79+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-import.Po@am__quote@
80+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-misc.Po@am__quote@
81+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-payee.Po@am__quote@
82+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-preferences.Po@am__quote@
83+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-report.Po@am__quote@
84+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-split.Po@am__quote@
85+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-tag.Po@am__quote@
86+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-transaction.Po@am__quote@
87+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hb-xml.Po@am__quote@
88+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/homebank.Po@am__quote@
89+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/language.Po@am__quote@
90+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list_account.Po@am__quote@
91+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list_operation.Po@am__quote@
92+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list_topspending.Po@am__quote@
93+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list_upcoming.Po@am__quote@
94+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep_balance.Po@am__quote@
95+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep_budget.Po@am__quote@
96+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep_stats.Po@am__quote@
97+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep_time.Po@am__quote@
98+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rep_vehicle.Po@am__quote@
99+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-account.Po@am__quote@
100+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-archive.Po@am__quote@
101+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-assign.Po@am__quote@
102+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-assist-import.Po@am__quote@
103+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-assist-start.Po@am__quote@
104+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-budget-tabview.Po@am__quote@
105+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-budget.Po@am__quote@
106+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-category.Po@am__quote@
107+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-currency.Po@am__quote@
108+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-dialogs.Po@am__quote@
109+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-filter.Po@am__quote@
110+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-hbfile.Po@am__quote@
111+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-payee.Po@am__quote@
112+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-pref.Po@am__quote@
113+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-split.Po@am__quote@
114+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-transaction.Po@am__quote@
115+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-txn-multi.Po@am__quote@
116+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui-widgets.Po@am__quote@
117+>>>>>>> MERGE-SOURCE
118
119 .c.o:
120 @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
121
122=== added file 'src/dsp_mainwindow.c.OTHER'
123--- src/dsp_mainwindow.c.OTHER 1970-01-01 00:00:00 +0000
124+++ src/dsp_mainwindow.c.OTHER 2019-07-22 20:14:59 +0000
125@@ -0,0 +1,3388 @@
126+/* HomeBank -- Free, easy, personal accounting for everyone.
127+ * Copyright (C) 1995-2018 Maxime DOYEN
128+ *
129+ * This file is part of HomeBank.
130+ *
131+ * HomeBank is free software; you can redistribute it and/or modify
132+ * it under the terms of the GNU General Public License as published by
133+ * the Free Software Foundation; either version 2 of the License, or
134+ * (at your option) any later version.
135+ *
136+ * HomeBank is distributed in the hope that it will be useful,
137+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
138+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
139+ * GNU General Public License for more details.
140+ *
141+ * You should have received a copy of the GNU General Public License
142+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
143+ */
144+
145+
146+#include "homebank.h"
147+
148+#include "dsp_mainwindow.h"
149+
150+#include "list_account.h"
151+#include "list_upcoming.h"
152+#include "list_topspending.h"
153+
154+#include "dsp_account.h"
155+#include "ui-assist-import.h"
156+#include "ui-assist-start.h"
157+#include "ui-account.h"
158+#include "ui-currency.h"
159+#include "ui-payee.h"
160+#include "ui-category.h"
161+#include "ui-archive.h"
162+#include "ui-assign.h"
163+#include "ui-budget.h"
164+#include "ui-budget-tabview.h"
165+#include "ui-pref.h"
166+#include "ui-hbfile.h"
167+#include "ui-transaction.h"
168+
169+#include "rep_balance.h"
170+#include "rep_budget.h"
171+#include "rep_stats.h"
172+#include "rep_time.h"
173+#include "rep_vehicle.h"
174+
175+#include "gtk-chart.h"
176+
177+//old url prior 2018
178+//#define HOMEBANK_URL_HELP "http://homebank.free.fr/help/"
179+//#define HOMEBANK_URL_HELP_ONLINE "https://launchpad.net/homebank/+addquestion"
180+//#define HOMEBANK_URL_HELP_PROBLEM "https://launchpad.net/homebank/+filebug"
181+//#define HOMEBANK_URL_HELP_TRANSLATE "https://launchpad.net/homebank/+translations"
182+
183+#define HOMEBANK_URL_HELP "index.html"
184+#define HOMEBANK_URL_HELP_ONLINE "http://homebank.free.fr/support.php"
185+#define HOMEBANK_URL_HELP_UPDATES "http://homebank.free.fr/downloads.php"
186+#define HOMEBANK_URL_HELP_PROBLEM "http://homebank.free.fr/development.php#bug"
187+#define HOMEBANK_URL_HELP_TRANSLATE "http://homebank.free.fr/development.php#translate"
188+
189+
190+/****************************************************************************/
191+/* Debug macros */
192+/****************************************************************************/
193+#define MYDEBUG 0
194+
195+#if MYDEBUG
196+#define DB(x) (x);
197+#else
198+#define DB(x);
199+#endif
200+
201+/* our global datas */
202+extern struct HomeBank *GLOBALS;
203+extern struct Preferences *PREFS;
204+extern gchar *homebank_pixmaps_dir;
205+
206+
207+/* our functions prototype */
208+static void ui_mainwindow_action_new(void);
209+static void ui_mainwindow_action_open(void);
210+static void ui_mainwindow_action_save(void);
211+static void ui_mainwindow_action_saveas(void);
212+static void ui_mainwindow_action_revert(void);
213+static void ui_mainwindow_action_openbak(void);
214+static void ui_mainwindow_action_properties(void);
215+static void ui_mainwindow_action_close(void);
216+static void ui_mainwindow_action_quit(void);
217+
218+static void ui_mainwindow_action_defcurrency(void);
219+static void ui_mainwindow_action_defaccount(void);
220+static void ui_mainwindow_action_defpayee(void);
221+static void ui_mainwindow_action_defcategory(void);
222+static void ui_mainwindow_action_defarchive(void);
223+static void ui_mainwindow_action_defbudget(void);
224+static void ui_mainwindow_action_defbudget_tabview(void);
225+static void ui_mainwindow_action_defassign(void);
226+static void ui_mainwindow_action_preferences(void);
227+
228+static void ui_mainwindow_action_toggle_toolbar(GtkToggleAction *action);
229+static void ui_mainwindow_action_toggle_upcoming(GtkToggleAction *action);
230+static void ui_mainwindow_action_toggle_topspending(GtkToggleAction *action);
231+static void ui_mainwindow_action_toggle_minor(GtkToggleAction *action);
232+
233+static void ui_mainwindow_action_showtransactions(void);
234+static void ui_mainwindow_action_showalltransactions(void);
235+
236+static void ui_mainwindow_action_addtransactions(void);
237+static void ui_mainwindow_action_checkscheduled(void);
238+
239+static void ui_mainwindow_action_statistic(void);
240+static void ui_mainwindow_action_trendtime(void);
241+static void ui_mainwindow_action_budget(void);
242+static void ui_mainwindow_action_balance(void);
243+static void ui_mainwindow_action_vehiclecost(void);
244+
245+static void ui_mainwindow_action_import(GtkAction *action);
246+static void ui_mainwindow_action_export(void);
247+static void ui_mainwindow_action_anonymize(void);
248+static void ui_mainwindow_action_file_statistics(void);
249+
250+static void ui_mainwindow_action_help(void);
251+void ui_mainwindow_action_help_welcome(void);
252+static void ui_mainwindow_action_help_online(void);
253+static void ui_mainwindow_action_help_updates(void);
254+static void ui_mainwindow_action_help_releasenotes(void);
255+static void ui_mainwindow_action_help_translate(void);
256+static void ui_mainwindow_action_help_problem(void);
257+static void ui_mainwindow_action_about(void);
258+
259+
260+static GtkWidget *ui_mainwindow_create_recent_chooser_menu (GtkRecentManager *manager);
261+
262+static void ui_mainwindow_populate_topspending(GtkWidget *widget, gpointer user_data);
263+
264+void ui_mainwindow_open(GtkWidget *widget, gpointer user_data);
265+
266+void ui_mainwindow_save(GtkWidget *widget, gpointer user_data);
267+void ui_mainwindow_revert(GtkWidget *widget, gpointer user_data);
268+void ui_mainwindow_action(GtkWidget *widget, gpointer user_data);
269+void ui_mainwindow_toggle_minor(GtkWidget *widget, gpointer user_data);
270+void ui_mainwindow_clear(GtkWidget *widget, gpointer user_data);
271+
272+gboolean ui_dialog_msg_savechanges(GtkWidget *widget, gpointer user_data);
273+
274+void ui_mainwindow_update(GtkWidget *widget, gpointer user_data);
275+void ui_mainwindow_addtransactions(GtkWidget *widget, gpointer user_data);
276+void ui_mainwindow_recent_add (struct hbfile_data *data, const gchar *path);
277+
278+static void ui_panel_topspending_update(GtkWidget *widget, gpointer user_data);
279+
280+static void ui_mainwindow_scheduled_populate(GtkWidget *widget, gpointer user_data);
281+void ui_mainwindow_scheduled_postall(GtkWidget *widget, gpointer user_data);
282+
283+void ui_mainwindow_recent_add (struct hbfile_data *data, const gchar *path);
284+
285+static void ui_panel_accounts_setup(struct hbfile_data *data);
286+
287+extern gchar *CYA_ACC_TYPE[];
288+
289+gchar *CYA_CATSUBCAT[] = {
290+ N_("Category"),
291+ N_("Subcategory"),
292+ NULL
293+};
294+
295+
296+static GtkActionEntry entries[] = {
297+
298+ /* name, icon-name, label */
299+
300+ { "FileMenu" , NULL, N_("_File"), NULL, NULL, NULL },
301+ //{ "ImportMenu" , NULL, N_("_Import"), NULL, NULL, NULL },
302+ { "RecentMenu" , NULL, N_("Open _Recent"), NULL, NULL, NULL },
303+ { "EditMenu" , NULL, N_("_Edit"), NULL, NULL, NULL },
304+ { "ViewMenu" , NULL, N_("_View"), NULL, NULL, NULL },
305+ { "ManageMenu" , NULL, N_("_Manage"), NULL, NULL, NULL },
306+ { "TxnMenu" , NULL, N_("_Transactions"), NULL, NULL, NULL },
307+ { "ReportMenu" , NULL, N_("_Reports"), NULL, NULL, NULL },
308+ { "ToolsMenu" , NULL, N_("_Tools"), NULL, NULL, NULL },
309+ { "HelpMenu" , NULL, N_("_Help"), NULL, NULL, NULL },
310+
311+// { "Import" , NULL, N_("Import") },
312+// { "Export" , NULL, N_("Export to") },
313+ /* name, icon-name, label, accelerator, tooltip */
314+
315+ /* FileMenu */
316+ { "New" , ICONNAME_HB_FILE_NEW , N_("_New") , "<control>N", N_("Create a new file"), G_CALLBACK (ui_mainwindow_action_new) },
317+ { "Open" , ICONNAME_HB_FILE_OPEN , N_("_Open...") , "<control>O", N_("Open a file"), G_CALLBACK (ui_mainwindow_action_open) },
318+ { "Save" , ICONNAME_HB_FILE_SAVE , N_("_Save") , "<control>S", N_("Save the current file"), G_CALLBACK (ui_mainwindow_action_save) },
319+ { "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) },
320+
321+ { "Revert" , ICONNAME_REVERT , N_("Revert") , NULL, N_("Revert to a saved version of this file"), G_CALLBACK (ui_mainwindow_action_revert) },
322+ { "OpenBak" , NULL , N_("Restore backup") , NULL, N_("Restore from a backup file"), G_CALLBACK (ui_mainwindow_action_openbak) },
323+
324+ { "Properties" , ICONNAME_PROPERTIES , N_("Properties..."), NULL, N_("Configure the file"), G_CALLBACK (ui_mainwindow_action_properties) },
325+ { "Close" , ICONNAME_CLOSE , N_("_Close") , "<control>W", N_("Close the current file"), G_CALLBACK (ui_mainwindow_action_close) },
326+ { "Quit" , ICONNAME_QUIT , N_("_Quit") , "<control>Q", N_("Quit HomeBank"), G_CALLBACK (ui_mainwindow_action_quit) },
327+
328+ /* Exchange */
329+ { "Import" , ICONNAME_HB_FILE_IMPORT , N_("Import...") , NULL, N_("Open the import assistant"), G_CALLBACK (ui_mainwindow_action_import) },
330+ //{ "ImportQIF" , ICONNAME_HB_FILE_IMPORT , N_("QIF file...") , NULL, N_("Open the import assistant"), G_CALLBACK (ui_mainwindow_action_import) },
331+ //{ "ImportOFX" , ICONNAME_HB_FILE_IMPORT , N_("OFX/QFX file...") , NULL, N_("Open the import assistant"), G_CALLBACK (ui_mainwindow_action_import) },
332+ //{ "ImportCSV" , ICONNAME_HB_FILE_IMPORT , N_("CSV file...") , NULL, N_("Open the import assistant"), G_CALLBACK (ui_mainwindow_action_import) },
333+
334+ { "ExportQIF" , ICONNAME_HB_FILE_EXPORT , N_("Export as QIF...") , NULL, N_("Export all account in a QIF file"), G_CALLBACK (ui_mainwindow_action_export) },
335+
336+ /* EditMenu */
337+ { "Preferences", ICONNAME_PREFERENCES , N_("Preferences..."), NULL, N_("Configure HomeBank"), G_CALLBACK (ui_mainwindow_action_preferences) },
338+
339+ /* ManageMenu */
340+ { "Currency" , ICONNAME_HB_CURRENCY , N_("Currencies...") , NULL, N_("Configure the currencies"), G_CALLBACK (ui_mainwindow_action_defcurrency) },
341+ { "Account" , ICONNAME_HB_ACCOUNT , N_("Acc_ounts...") , NULL, N_("Configure the accounts"), G_CALLBACK (ui_mainwindow_action_defaccount) },
342+ { "Payee" , ICONNAME_HB_PAYEE , N_("_Payees...") , NULL, N_("Configure the payees"), G_CALLBACK (ui_mainwindow_action_defpayee) },
343+ { "Category" , ICONNAME_HB_CATEGORY , N_("Categories...") , NULL, N_("Configure the categories"), G_CALLBACK (ui_mainwindow_action_defcategory) },
344+ { "Archive" , ICONNAME_HB_ARCHIVE , N_("Scheduled/Template...") , NULL, N_("Configure the scheduled/template transactions"), G_CALLBACK (ui_mainwindow_action_defarchive) },
345+ { "Budget" , ICONNAME_HB_BUDGET , N_("Budget...") , NULL, N_("Configure the budget"), G_CALLBACK (ui_mainwindow_action_defbudget) },
346+ { "BudgetTabview", ICONNAME_HB_BUDGET , N_("Budget (tab view)...") , NULL, N_("View the budget"), G_CALLBACK (ui_mainwindow_action_defbudget_tabview) },
347+ { "Assign" , ICONNAME_HB_ASSIGN , N_("Assignments..."), NULL, N_("Configure the automatic assignments"), G_CALLBACK (ui_mainwindow_action_defassign) },
348+
349+ /* TxnMenu */
350+ { "AddTxn" , ICONNAME_HB_OPE_ADD , N_("Add...") , NULL, N_("Add transactions"), G_CALLBACK (ui_mainwindow_action_addtransactions) },
351+ { "ShowTxn" , ICONNAME_HB_OPE_SHOW , N_("Show...") , NULL, N_("Shows selected account transactions"), G_CALLBACK (ui_mainwindow_action_showtransactions) },
352+ { "ShowAllTxn" , ICONNAME_HB_OPE_SHOW , N_("Show all...") , NULL, N_("Shows all account transactions"), G_CALLBACK (ui_mainwindow_action_showalltransactions) },
353+ { "Scheduler" , NULL , N_("Set scheduler...") , NULL, N_("Configure the transaction scheduler"), G_CALLBACK (ui_mainwindow_action_properties) },
354+ { "AddScheduled", NULL , N_("Post scheduled"), NULL, N_("Post pending scheduled transactions"), G_CALLBACK (ui_mainwindow_action_checkscheduled) },
355+
356+ /* ReportMenu */
357+ { "RStatistics" , ICONNAME_HB_REP_STATS , N_("_Statistics...") , NULL, N_("Open the Statistics report"), G_CALLBACK (ui_mainwindow_action_statistic) },
358+ { "RTrendTime" , ICONNAME_HB_REP_TIME , N_("_Trend Time...") , NULL, N_("Open the Trend Time report"), G_CALLBACK (ui_mainwindow_action_trendtime) },
359+ { "RBudget" , ICONNAME_HB_REP_BUDGET , N_("B_udget...") , NULL, N_("Open the Budget report"), G_CALLBACK (ui_mainwindow_action_budget) },
360+ { "RBalance" , ICONNAME_HB_REP_BALANCE, N_("Balance...") , NULL, N_("Open the Balance report"), G_CALLBACK (ui_mainwindow_action_balance) },
361+ { "RVehiculeCost", ICONNAME_HB_REP_CAR , N_("_Vehicle cost...") , NULL, N_("Open the Vehicle cost report"), G_CALLBACK (ui_mainwindow_action_vehiclecost) },
362+
363+ /* Tools */
364+ { "Welcome" , NULL , N_("Show welcome dialog...") , NULL, NULL, G_CALLBACK (ui_mainwindow_action_help_welcome) },
365+ { "FileStats" , NULL , N_("File statistics...") , NULL, NULL, G_CALLBACK (ui_mainwindow_action_file_statistics) },
366+ { "Anonymize" , NULL , N_("Anonymize...") , NULL, NULL, G_CALLBACK (ui_mainwindow_action_anonymize) },
367+
368+ /* HelpMenu */
369+ { "Contents" , ICONNAME_HELP , N_("_Contents") , "F1", N_("Documentation about HomeBank"), G_CALLBACK (ui_mainwindow_action_help) },
370+ { "Online" , "lpi-help" , N_("Get Help Online...") , NULL, N_("Connect to the LaunchPad website for online help"), G_CALLBACK (ui_mainwindow_action_help_online) },
371+
372+ { "Updates" , NULL , N_("Check for updates...") , NULL, N_("Visit HomeBank website to check for update"), G_CALLBACK (ui_mainwindow_action_help_updates) },
373+ { "ReleaseNotes", NULL , N_("Release Notes") , NULL, N_("Display the release notes"), G_CALLBACK (ui_mainwindow_action_help_releasenotes) },
374+ { "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) },
375+ { "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) },
376+
377+ { "About" , ICONNAME_ABOUT , N_("_About") , NULL, N_("About HomeBank") ,G_CALLBACK (ui_mainwindow_action_about) },
378+
379+};
380+static guint n_entries = G_N_ELEMENTS (entries);
381+
382+
383+static GtkToggleActionEntry toggle_entries[] = {
384+/* name , icon-name, label, accelerator, tooltip, callback, is_active */
385+ { "Toolbar" , NULL , N_("_Toolbar") , NULL, NULL, G_CALLBACK (ui_mainwindow_action_toggle_toolbar), TRUE },
386+ { "Spending" , NULL , N_("_Top spending") , NULL, NULL, G_CALLBACK (ui_mainwindow_action_toggle_topspending), TRUE },
387+ { "Upcoming" , NULL , N_("_Scheduled list") , NULL, NULL, G_CALLBACK (ui_mainwindow_action_toggle_upcoming), TRUE },
388+ { "AsMinor" , NULL , N_("Euro minor"), "<control>M", NULL, G_CALLBACK (ui_mainwindow_action_toggle_minor), FALSE },
389+};
390+
391+static guint n_toggle_entries = G_N_ELEMENTS (toggle_entries);
392+
393+
394+static const gchar *ui_info =
395+"<ui>"
396+
397+" <menubar name='MenuBar'>"
398+" <menu action='FileMenu'>"
399+" <menuitem action='New'/>"
400+" <menuitem action='Open'/>"
401+" <menuitem action='RecentMenu'/>"
402+" <separator/>"
403+" <menuitem action='Save'/>"
404+" <menuitem action='SaveAs'/>"
405+" <separator/>"
406+" <menuitem action='Import'/>"
407+/*" <menu action='ImportMenu'>"
408+" <menuitem action='ImportQIF'/>"
409+" <menuitem action='ImportOFX'/>"
410+" <menuitem action='ImportCSV'/>"
411+" </menu>"*/
412+" <menuitem action='ExportQIF'/>"
413+// future: print to come here
414+" <separator/>"
415+" <menuitem action='Revert'/>"
416+" <menuitem action='OpenBak'/>"
417+" <separator/>"
418+" <menuitem action='Properties'/>"
419+" <separator/>"
420+" <menuitem action='Close'/>"
421+" <menuitem action='Quit'/>"
422+" </menu>"
423+" <menu action='EditMenu'>"
424+" <menuitem action='Preferences'/>"
425+" </menu>"
426+" <menu action='ViewMenu'>"
427+" <menuitem action='Toolbar'/>"
428+" <separator/>"
429+" <menuitem action='Spending'/>"
430+" <menuitem action='Upcoming'/>"
431+" <separator/>"
432+" <menuitem action='AsMinor'/>"
433+" </menu>"
434+" <menu action='ManageMenu'>"
435+" <menuitem action='Account'/>"
436+" <menuitem action='Payee'/>"
437+" <menuitem action='Category'/>"
438+" <menuitem action='Archive'/>"
439+" <menuitem action='Budget'/>"
440+" <menuitem action='BudgetTabview'/>"
441+" <menuitem action='Assign'/>"
442+" <menuitem action='Currency'/>"
443+" </menu>"
444+" <menu action='TxnMenu'>"
445+" <menuitem action='AddTxn'/>"
446+" <menuitem action='ShowTxn'/>"
447+" <menuitem action='ShowAllTxn'/>"
448+" <separator/>"
449+" <menuitem action='Scheduler'/>"
450+" <menuitem action='AddScheduled'/>"
451+" </menu>"
452+" <menu action='ReportMenu'>"
453+" <menuitem action='RStatistics'/>"
454+" <menuitem action='RTrendTime'/>"
455+" <menuitem action='RBalance'/>"
456+" <menuitem action='RBudget'/>"
457+" <menuitem action='RVehiculeCost'/>"
458+" </menu>"
459+" <menu action='ToolsMenu'>"
460+" <menuitem action='Welcome'/>"
461+" <menuitem action='FileStats'/>"
462+" <separator/>"
463+" <menuitem action='Anonymize'/>"
464+" </menu>"
465+" <menu action='HelpMenu'>"
466+" <menuitem action='Contents'/>"
467+" <menuitem action='Online'/>"
468+" <separator/>"
469+" <menuitem action='Updates'/>"
470+" <menuitem action='ReleaseNotes'/>"
471+" <menuitem action='Problem'/>"
472+" <menuitem action='Translate'/>"
473+" <separator/>"
474+" <menuitem action='About'/>"
475+" </menu>"
476+" </menubar>"
477+
478+" <toolbar name='ToolBar'>"
479+" <toolitem action='New'/>"
480+// here Open + recent is coded
481+" <toolitem action='Save'/>"
482+" <separator/>"
483+" <toolitem action='Account'/>"
484+" <toolitem action='Payee'/>"
485+" <toolitem action='Category'/>"
486+" <toolitem action='Archive'/>"
487+" <toolitem action='Budget'/>"
488+" <toolitem action='Assign'/>"
489+" <toolitem action='Currency'/>"
490+" <separator/>"
491+" <toolitem action='ShowTxn'/>"
492+" <toolitem action='AddTxn'/>"
493+" <separator/>"
494+" <toolitem action='RStatistics'/>"
495+" <toolitem action='RTrendTime'/>"
496+" <toolitem action='RBalance'/>"
497+" <toolitem action='RBudget'/>"
498+" <toolitem action='RVehiculeCost'/>"
499+" </toolbar>"
500+
501+"</ui>";
502+
503+
504+
505+/* TODO: a bouger */
506+
507+
508+/*
509+**
510+*/
511+void ui_mainwindow_revert(GtkWidget *widget, gpointer user_data)
512+{
513+//struct hbfile_data *data;
514+gchar *basename;
515+gchar *title;
516+gchar *secondtext;
517+gint result;
518+
519+ DB( g_print("\n[ui-mainwindow] revert\n") );
520+
521+ //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
522+
523+ basename = g_path_get_basename(GLOBALS->xhb_filepath);
524+ title = g_strdup_printf (
525+ _("Revert unsaved changes to file '%s'?"), basename);
526+
527+ secondtext =
528+ _("- Changes made to the file will be permanently lost\n"
529+ "- File will be reloaded from the last save (.xhb~)");
530+
531+ result = ui_dialog_msg_confirm_alert(
532+ GTK_WINDOW(GLOBALS->mainwindow),
533+ title,
534+ secondtext,
535+ _("_Revert")
536+ );
537+
538+ g_free(title);
539+ g_free(basename);
540+
541+ if( result == GTK_RESPONSE_OK )
542+ {
543+ DB( g_print(" - should revert\n") );
544+
545+ hbfile_change_filepath(hb_filename_new_with_extension(GLOBALS->xhb_filepath, "xhb~"));
546+ ui_mainwindow_open_internal(widget, NULL);
547+ hbfile_change_filepath(hb_filename_new_with_extension(GLOBALS->xhb_filepath, "xhb"));
548+ }
549+
550+}
551+
552+
553+static void
554+activate_url (GtkAboutDialog *about,
555+ const gchar *link,
556+ gpointer data)
557+{
558+ DB( g_print("activate url %s\n", link) );
559+
560+ homebank_util_url_show (link);
561+}
562+
563+static void hbfile_about(void)
564+{
565+GtkWidget *dialog;
566+GdkPixbuf *pixbuf;
567+gchar *pathfilename;
568+gchar *version;
569+
570+ static const gchar *artists[] = {
571+ "Maxime DOYEN",
572+ NULL
573+ };
574+
575+ static const gchar *authors[] = {
576+ "Lead developer:\n" \
577+ "Maxime DOYEN",
578+ "\nContributor:\n" \
579+ "Ga\xc3\xabtan LORIDANT (Maths formulas for charts)\n",
580+ NULL
581+ };
582+
583+/*
584+ const gchar *documenters[] = {
585+ "Maxime DOYEN",
586+ NULL
587+ };
588+*/
589+
590+ static const gchar *copyright = "Copyright \xc2\xa9 1995-2018 - Maxime DOYEN";
591+
592+
593+
594+ version = g_strdup_printf (PACKAGE_VERSION "\n<small>Running against GTK+ %d.%d.%d</small>",
595+ gtk_get_major_version (),
596+ gtk_get_minor_version (),
597+ gtk_get_micro_version ());
598+
599+ dialog = gtk_about_dialog_new();
600+
601+ gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(GLOBALS->mainwindow));
602+ gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
603+
604+ gtk_about_dialog_set_program_name (GTK_ABOUT_DIALOG(dialog), g_get_application_name ());
605+ gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), version);
606+ gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog), copyright);
607+ gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog), _("Free, easy, personal accounting for everyone"));
608+ gtk_about_dialog_set_license_type (GTK_ABOUT_DIALOG(dialog), GTK_LICENSE_GPL_2_0);
609+
610+ //gtk_about_dialog_set_wrap_license(GTK_ABOUT_DIALOG(dialog), );
611+ gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog), "http://homebank.free.fr");
612+ gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(dialog), "Visit the HomeBank website");
613+
614+ gtk_about_dialog_set_logo_icon_name(GTK_ABOUT_DIALOG(dialog), "homebank");
615+
616+ pathfilename = g_build_filename(homebank_app_get_images_dir(), "splash.png", NULL);
617+ pixbuf = gdk_pixbuf_new_from_file(pathfilename, NULL);
618+ g_free(pathfilename);
619+
620+ if( pixbuf )
621+ {
622+ gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), pixbuf);
623+ g_object_unref (pixbuf);
624+ }
625+
626+ gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(dialog), authors);
627+ gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(dialog), artists);
628+ //gtk_about_dialog_set_documenters(GTK_ABOUT_DIALOG(dialog), );
629+ //gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(dialog), );
630+
631+ g_signal_connect (dialog, "activate-link", G_CALLBACK (activate_url), NULL);
632+
633+ gtk_dialog_run (GTK_DIALOG (dialog));
634+
635+ gtk_widget_destroy (dialog);
636+
637+ g_free(version);
638+
639+}
640+
641+
642+/* hbfile action functions -------------------- */
643+static void ui_mainwindow_action_new(void)
644+{
645+GtkWidget *widget = GLOBALS->mainwindow;
646+
647+ if( ui_dialog_msg_savechanges(widget,NULL) == TRUE )
648+ {
649+ //clear all, and init GLOBALS->xhb_filepath to default
650+ ui_mainwindow_clear(widget, GINT_TO_POINTER(TRUE)); // GPOINTER_TO_INT(
651+ ui_mainwindow_update(widget, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
652+
653+ ui_start_assistant();
654+ //ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
655+ //ui_mainwindow_scheduled_populate(GLOBALS->mainwindow, NULL);
656+ //ui_mainwindow_populate_topspending(GLOBALS->mainwindow, NULL);
657+ }
658+}
659+
660+static void ui_mainwindow_action_open(void)
661+{
662+ ui_mainwindow_open(GLOBALS->mainwindow, GINT_TO_POINTER(FALSE));
663+}
664+
665+static void ui_mainwindow_action_openbak(void)
666+{
667+ ui_mainwindow_open(GLOBALS->mainwindow, GINT_TO_POINTER(TRUE));
668+}
669+
670+static void ui_mainwindow_action_save(void)
671+{
672+ ui_mainwindow_save(GLOBALS->mainwindow, GINT_TO_POINTER(FALSE));
673+}
674+
675+static void ui_mainwindow_action_saveas(void)
676+{
677+ ui_mainwindow_save(GLOBALS->mainwindow, GINT_TO_POINTER(TRUE));
678+}
679+
680+static void ui_mainwindow_action_revert(void)
681+{
682+ ui_mainwindow_revert(GLOBALS->mainwindow, NULL);
683+}
684+
685+static void ui_mainwindow_action_close(void)
686+{
687+GtkWidget *widget = GLOBALS->mainwindow;
688+
689+ if( ui_dialog_msg_savechanges(widget,NULL) == TRUE )
690+ {
691+ //clear all, and init GLOBALS->xhb_filepath to default
692+ ui_mainwindow_clear(widget, GINT_TO_POINTER(TRUE));
693+ ui_mainwindow_update(widget, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_BALANCE+UF_REFRESHALL));
694+ }
695+
696+}
697+
698+
699+static void ui_mainwindow_action_quit(void)
700+{
701+gboolean result;
702+
703+ //emulate the wm close button
704+ g_signal_emit_by_name(GLOBALS->mainwindow, "delete-event", NULL, &result);
705+}
706+
707+
708+static void ui_mainwindow_action_file_statistics(void)
709+{
710+ ui_dialog_file_statistics();
711+}
712+
713+
714+static void ui_mainwindow_action_properties(void)
715+{
716+ create_defhbfile_dialog();
717+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
718+}
719+
720+static void ui_mainwindow_action_anonymize(void)
721+{
722+gint result;
723+gchar *title;
724+gchar *secondtext;
725+
726+ title = _("Are you sure you want to anonymize the file?");
727+
728+ secondtext =
729+ _("Proceeding will anonymize any text, \n"
730+ "like 'account x', 'payee y', 'memo z', ...");
731+
732+ result = ui_dialog_msg_confirm_alert(
733+ GTK_WINDOW(GLOBALS->mainwindow),
734+ title,
735+ secondtext,
736+ _("_Anonymize")
737+ );
738+
739+ //#1707201
740+ //if( result == GTK_RESPONSE_CANCEL )
741+ // return;
742+ if( result == GTK_RESPONSE_OK )
743+ {
744+ hbfile_anonymize();
745+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
746+ }
747+}
748+
749+
750+static void ui_mainwindow_action_defcurrency(void)
751+{
752+ ui_cur_manage_dialog();
753+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
754+}
755+
756+
757+static void ui_mainwindow_action_defaccount(void)
758+{
759+ ui_acc_manage_dialog();
760+
761+ //our global list has changed, so update the treeview
762+ //todo: optimize this, should not call compute balance here
763+ account_compute_balances ();
764+ ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
765+
766+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_BALANCE));
767+}
768+
769+static void ui_mainwindow_action_defpayee(void)
770+{
771+ ui_pay_manage_dialog();
772+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
773+}
774+
775+static void ui_mainwindow_action_defcategory(void)
776+{
777+ ui_cat_manage_dialog();
778+ //todo:why refresh upcoming here??
779+ //ui_mainwindow_populate_upcoming(GLOBALS->mainwindow, NULL);
780+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_REFRESHALL));
781+}
782+
783+
784+static void ui_mainwindow_defarchive(Archive *arc)
785+{
786+struct hbfile_data *data;
787+GtkTreeModel *model;
788+
789+ data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
790+
791+ // upcoming list have direct pointer to the arc (which may have changed)
792+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_upc));
793+ gtk_list_store_clear (GTK_LIST_STORE(model));
794+
795+ ui_arc_manage_dialog(arc);
796+
797+ ui_mainwindow_scheduled_populate(GLOBALS->mainwindow, NULL);
798+
799+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
800+}
801+
802+
803+static void ui_mainwindow_action_defarchive(void)
804+{
805+ ui_mainwindow_defarchive(NULL);
806+}
807+
808+
809+static void ui_mainwindow_action_defbudget(void)
810+{
811+ ui_bud_manage_dialog();
812+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
813+}
814+
815+static void ui_mainwindow_action_defbudget_tabview(void)
816+{
817+ ui_bud_tabview_manage_dialog();
818+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
819+}
820+
821+
822+static void ui_mainwindow_action_defassign(void)
823+{
824+
825+ ui_asg_manage_dialog();
826+
827+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE));
828+}
829+
830+
831+static void ui_mainwindow_action_preferences(void)
832+{
833+struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
834+
835+ defpref_dialog_new();
836+ if(!PREFS->euro_active)
837+ {
838+ GtkToggleAction *action = (GtkToggleAction *)gtk_ui_manager_get_action(data->manager, "/MenuBar/ViewMenu/AsMinor");
839+
840+ gtk_toggle_action_set_active(action, FALSE);
841+ ui_mainwindow_action_toggle_minor(action);
842+ }
843+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_VISUAL+UF_REFRESHALL));
844+}
845+
846+/* display action */
847+
848+static void ui_mainwindow_action_toggle_toolbar(GtkToggleAction *action)
849+{
850+//struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
851+
852+ PREFS->wal_toolbar = gtk_toggle_action_get_active(action);
853+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_VISUAL));
854+}
855+
856+static void ui_mainwindow_action_toggle_upcoming(GtkToggleAction *action)
857+{
858+//struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
859+
860+ PREFS->wal_upcoming = gtk_toggle_action_get_active(action);
861+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_VISUAL));
862+}
863+
864+static void ui_mainwindow_action_toggle_topspending(GtkToggleAction *action)
865+{
866+//struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
867+
868+ PREFS->wal_spending = gtk_toggle_action_get_active(action);
869+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_VISUAL));
870+}
871+
872+static void ui_mainwindow_action_toggle_minor(GtkToggleAction *action)
873+{
874+struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
875+
876+ GLOBALS->minor = gtk_toggle_action_get_active(action);
877+
878+ gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_acc));
879+ gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_upc));
880+
881+ // top spending
882+ gtk_chart_show_minor(GTK_CHART(data->RE_pie), GLOBALS->minor);
883+
884+ ui_panel_topspending_update(data->window, data);
885+
886+}
887+
888+static void ui_mainwindow_action_showtransactions(void)
889+{
890+struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
891+GtkWidget *window;
892+
893+ //todo:change this
894+ if( data->acc )
895+ {
896+ if( data->acc->window == NULL )
897+ {
898+ window = register_panel_window_new(data->acc);
899+ register_panel_window_init(window, NULL);
900+ }
901+ else
902+ {
903+ if(GTK_IS_WINDOW(data->acc->window))
904+ gtk_window_present(GTK_WINDOW(data->acc->window));
905+
906+ }
907+ }
908+}
909+
910+
911+static void ui_mainwindow_action_showalltransactions(void)
912+{
913+GtkWidget *window;
914+
915+ if( GLOBALS->alltxnwindow == NULL )
916+ {
917+ window = register_panel_window_new(NULL);
918+ register_panel_window_init(window, NULL);
919+ }
920+ else
921+ {
922+ if(GTK_IS_WINDOW(GLOBALS->alltxnwindow))
923+ gtk_window_present(GTK_WINDOW(GLOBALS->alltxnwindow));
924+ }
925+
926+}
927+
928+
929+static void ui_mainwindow_action_addtransactions(void)
930+{
931+ ui_mainwindow_addtransactions(GLOBALS->mainwindow, NULL);
932+}
933+
934+static void ui_mainwindow_action_checkscheduled(void)
935+{
936+ ui_mainwindow_scheduled_postall(GLOBALS->mainwindow, GINT_TO_POINTER(TRUE));
937+}
938+
939+static void ui_mainwindow_action_statistic(void)
940+{
941+ ui_repdist_window_new();
942+}
943+
944+static void ui_mainwindow_action_trendtime(void)
945+{
946+struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
947+
948+ ui_reptime_window_new(data->acc != NULL ? data->acc->key : 0);
949+}
950+
951+static void ui_mainwindow_action_budget(void)
952+{
953+ repbudget_window_new();
954+}
955+
956+static void ui_mainwindow_action_balance(void)
957+{
958+struct hbfile_data *data = g_object_get_data(G_OBJECT(GLOBALS->mainwindow), "inst_data");
959+
960+ repbalance_window_new(data->acc != NULL ? data->acc->key : 0);
961+}
962+
963+static void ui_mainwindow_action_vehiclecost(void)
964+{
965+ repcost_window_new();
966+}
967+
968+static void ui_mainwindow_action_import(GtkAction *action)
969+{
970+/*const gchar *name;
971+gint filetype = FILETYPE_UNKNOWN;
972+
973+ name = gtk_action_get_name(action);
974+
975+ if( g_str_has_suffix (name, "QIF"))
976+ filetype= FILETYPE_QIF;
977+ else
978+ if( g_str_has_suffix (name, "OFX"))
979+ filetype= FILETYPE_OFX;
980+ else
981+ if( g_str_has_suffix (name, "CSV"))
982+ filetype= FILETYPE_CSV_HB;*/
983+
984+ //DB( g_print("action %s type=%d\n", name, filetype) );
985+
986+ ui_import_assistant_new(NULL);
987+
988+}
989+
990+
991+static void ui_mainwindow_action_about(void)
992+{
993+ hbfile_about();
994+
995+
996+}
997+
998+
999+static void ui_mainwindow_action_export(void)
1000+{
1001+gchar *filename;
1002+
1003+ if( ui_file_chooser_qif(NULL, &filename) == TRUE )
1004+ {
1005+ hb_export_qif_account_all(filename);
1006+ g_free( filename );
1007+ }
1008+}
1009+
1010+
1011+static void ui_mainwindow_action_help(void)
1012+{
1013+gchar *link;
1014+
1015+ link = g_build_filename("file:///", homebank_app_get_help_dir(), HOMEBANK_URL_HELP, NULL );
1016+ homebank_util_url_show (link);
1017+ g_free(link);
1018+}
1019+
1020+
1021+static void ui_mainwindow_action_help_releasenotes(void)
1022+{
1023+gchar *link;
1024+
1025+ #ifdef G_OS_WIN32
1026+ link = g_build_filename("file:///", homebank_app_get_datas_dir(), "ChangeLog.txt", NULL );
1027+ #else
1028+ link = g_build_filename("file:///", homebank_app_get_datas_dir(), "ChangeLog", NULL );
1029+ #endif
1030+ homebank_util_url_show (link);
1031+ g_free(link);
1032+}
1033+
1034+
1035+//todo: move this to a ui-assist-welcome.c
1036+
1037+static void ui_mainwindow_action_help_welcome1 (GtkButton *button, gpointer user_data)
1038+{
1039+ gtk_dialog_response (GTK_DIALOG(user_data), 1);
1040+}
1041+
1042+static void ui_mainwindow_action_help_welcome2 (GtkButton *button, gpointer user_data)
1043+{
1044+ gtk_dialog_response (GTK_DIALOG(user_data), 2);
1045+}
1046+
1047+static void ui_mainwindow_action_help_welcome3 (GtkButton *button, gpointer user_data)
1048+{
1049+ gtk_dialog_response (GTK_DIALOG(user_data), 3);
1050+}
1051+
1052+static void ui_mainwindow_action_help_welcome4 (GtkButton *button, gpointer user_data)
1053+{
1054+ gtk_dialog_response (GTK_DIALOG(user_data), 4);
1055+}
1056+
1057+static void ui_mainwindow_action_help_welcome5 (GtkButton *button, gpointer user_data)
1058+{
1059+ gtk_dialog_response (GTK_DIALOG(user_data), 5);
1060+}
1061+
1062+void ui_mainwindow_action_help_welcome(void)
1063+{
1064+GtkWidget *dialog, *content_area;
1065+GtkWidget *mainvbox, *widget, *label;
1066+
1067+ dialog = gtk_dialog_new_with_buttons (_("Welcome to HomeBank"),
1068+ GTK_WINDOW(GLOBALS->mainwindow),
1069+ 0,
1070+ _("_Close"),
1071+ GTK_RESPONSE_ACCEPT,
1072+ NULL);
1073+
1074+ content_area = gtk_dialog_get_content_area(GTK_DIALOG (dialog));
1075+
1076+ mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1077+ gtk_box_pack_start (GTK_BOX (content_area), mainvbox, FALSE, FALSE, 0);
1078+ gtk_container_set_border_width (GTK_CONTAINER(mainvbox), SPACING_MEDIUM);
1079+
1080+ label = make_label (_("HomeBank"), 0, 0);
1081+ gimp_label_set_attributes(GTK_LABEL(label), PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, -1);
1082+ gtk_box_pack_start (GTK_BOX (mainvbox), label, FALSE, FALSE, 0);
1083+
1084+ label = make_label (_("Free, easy, personal accounting for everyone"), 0, 0);
1085+ gtk_box_pack_start (GTK_BOX (mainvbox), label, FALSE, FALSE, 0);
1086+
1087+ widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
1088+ gtk_box_pack_start (GTK_BOX (content_area), widget, FALSE, FALSE, 0);
1089+
1090+ mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, SPACING_MEDIUM);
1091+ gtk_box_pack_start (GTK_BOX (content_area), mainvbox, TRUE, TRUE, 0);
1092+ gtk_container_set_border_width (GTK_CONTAINER(mainvbox), SPACING_MEDIUM);
1093+
1094+ label = make_label (_("What do you want to do:"), 0, 0);
1095+ gimp_label_set_attributes(GTK_LABEL(label), PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD, -1);
1096+ gtk_box_pack_start (GTK_BOX (mainvbox), label, FALSE, FALSE, 0);
1097+
1098+ widget = gtk_button_new_with_mnemonic(_("Read HomeBank _Manual"));
1099+ gtk_box_pack_start (GTK_BOX (mainvbox), widget, FALSE, FALSE, 0);
1100+ g_signal_connect (widget, "clicked", G_CALLBACK (ui_mainwindow_action_help_welcome1), dialog);
1101+
1102+ widget = gtk_button_new_with_mnemonic(_("Configure _preferences"));
1103+ gtk_box_pack_start (GTK_BOX (mainvbox), widget, FALSE, FALSE, 0);
1104+ g_signal_connect (widget, "clicked", G_CALLBACK (ui_mainwindow_action_help_welcome2), dialog);
1105+
1106+ widget = gtk_button_new_with_mnemonic(_("Create a _new file"));
1107+ gtk_box_pack_start (GTK_BOX (mainvbox), widget, FALSE, FALSE, 0);
1108+ g_signal_connect (widget, "clicked", G_CALLBACK (ui_mainwindow_action_help_welcome3), dialog);
1109+
1110+ widget = gtk_button_new_with_mnemonic(_("_Open an existing file"));
1111+ gtk_box_pack_start (GTK_BOX (mainvbox), widget, FALSE, FALSE, 0);
1112+ g_signal_connect (widget, "clicked", G_CALLBACK (ui_mainwindow_action_help_welcome4), dialog);
1113+
1114+ widget = gtk_button_new_with_mnemonic(_("Open the _example file"));
1115+ gtk_box_pack_start (GTK_BOX (mainvbox), widget, FALSE, FALSE, 0);
1116+ g_signal_connect (widget, "clicked", G_CALLBACK (ui_mainwindow_action_help_welcome5), dialog);
1117+
1118+ //connect all our signals
1119+ g_signal_connect (dialog, "destroy", G_CALLBACK (gtk_widget_destroyed), &dialog);
1120+
1121+ gtk_widget_show_all (dialog);
1122+
1123+ //wait for the user
1124+ gint result = gtk_dialog_run (GTK_DIALOG (dialog));
1125+
1126+ // cleanup and destroy
1127+ gtk_widget_destroy (dialog);
1128+
1129+ // do appropriate action
1130+ switch(result)
1131+ {
1132+ case 1:
1133+ ui_mainwindow_action_help();
1134+ break;
1135+ case 2:
1136+ ui_mainwindow_action_preferences();
1137+ break;
1138+ case 3:
1139+ ui_mainwindow_action_new();
1140+ break;
1141+ case 4:
1142+ ui_mainwindow_action_open();
1143+ break;
1144+ case 5:
1145+ hbfile_change_filepath(g_build_filename(homebank_app_get_datas_dir(), "example.xhb", NULL));
1146+ ui_mainwindow_open_internal(GLOBALS->mainwindow, NULL);
1147+ break;
1148+ }
1149+
1150+}
1151+
1152+
1153+static void ui_mainwindow_action_help_updates(void)
1154+{
1155+const gchar *link = HOMEBANK_URL_HELP_UPDATES;
1156+
1157+ homebank_util_url_show (link);
1158+}
1159+
1160+
1161+static void ui_mainwindow_action_help_online(void)
1162+{
1163+const gchar *link = HOMEBANK_URL_HELP_ONLINE;
1164+
1165+ homebank_util_url_show (link);
1166+}
1167+
1168+
1169+static void ui_mainwindow_action_help_translate(void)
1170+{
1171+const gchar *link = HOMEBANK_URL_HELP_TRANSLATE;
1172+
1173+ homebank_util_url_show (link);
1174+}
1175+
1176+
1177+static void ui_mainwindow_action_help_problem(void)
1178+{
1179+const gchar *link = HOMEBANK_URL_HELP_PROBLEM;
1180+
1181+ homebank_util_url_show (link);
1182+}
1183+
1184+
1185+/* hbfile functions -------------------- */
1186+
1187+
1188+/*
1189+**
1190+*/
1191+static void ui_mainwindow_selection(GtkTreeSelection *treeselection, gpointer user_data)
1192+{
1193+ ui_mainwindow_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(UF_SENSITIVE));
1194+}
1195+
1196+
1197+static void ui_mainwindow_close_openbooks(void)
1198+{
1199+GList *lacc, *elt;
1200+
1201+ DB( g_print("\n[ui-mainwindow] close openbooks\n") );
1202+
1203+ lacc = elt = g_hash_table_get_values(GLOBALS->h_acc);
1204+ while (elt != NULL)
1205+ {
1206+ Account *item = elt->data;
1207+
1208+ if(item->window)
1209+ {
1210+ gtk_widget_destroy(GTK_WIDGET(item->window));
1211+ item->window = NULL;
1212+ }
1213+
1214+ elt = g_list_next(elt);
1215+ }
1216+ g_list_free(lacc);
1217+
1218+}
1219+
1220+
1221+
1222+/*
1223+**
1224+*/
1225+void ui_mainwindow_clear(GtkWidget *widget, gpointer user_data)
1226+{
1227+struct hbfile_data *data;
1228+gboolean file_clear = GPOINTER_TO_INT(user_data);
1229+
1230+ DB( g_print("\n[ui-mainwindow] clear\n") );
1231+
1232+ data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1233+
1234+ // Close opened account window
1235+ // Clear TreeView
1236+ ui_mainwindow_close_openbooks();
1237+ gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_acc))));
1238+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_upc))));
1239+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_top))));
1240+
1241+ data->showall = FALSE;
1242+ ui_panel_accounts_setup(data);
1243+
1244+ hbfile_cleanup(file_clear);
1245+ hbfile_setup(file_clear);
1246+
1247+}
1248+
1249+
1250+/*
1251+** add some transactions directly
1252+*/
1253+void ui_mainwindow_addtransactions(GtkWidget *widget, gpointer user_data)
1254+{
1255+struct hbfile_data *data;
1256+GtkWidget *window;
1257+gint result = 1;
1258+guint32 date;
1259+gint account, count;
1260+
1261+ DB( g_print("\n[ui-mainwindow] add transactions\n") );
1262+
1263+ data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1264+
1265+ /* init the transaction */
1266+ date = homebank_app_date_get_julian();
1267+
1268+ //#1656531
1269+ account = 0;
1270+ if(data->acc != NULL)
1271+ account = data->acc->key;
1272+
1273+ window = create_deftransaction_window(GTK_WINDOW(data->window), TRANSACTION_EDIT_ADD, FALSE, account);
1274+ count = 0;
1275+ while(result == HB_RESPONSE_ADD || result == HB_RESPONSE_ADDKEEP)
1276+ {
1277+ Transaction *ope;
1278+
1279+ /* fill in the transaction */
1280+ if( result == HB_RESPONSE_ADD )
1281+ {
1282+ ope = da_transaction_malloc();
1283+ ope->date = date;
1284+ ope->kacc = account;
1285+
1286+ if( PREFS->heritdate == FALSE ) //fix: 318733
1287+ ope->date = GLOBALS->today;
1288+
1289+ da_transaction_set_default_template(ope);
1290+ }
1291+
1292+ // normally we can't be in addkeep without initialized ope with add
1293+
1294+ deftransaction_set_transaction(window, ope);
1295+
1296+ result = gtk_dialog_run (GTK_DIALOG (window));
1297+
1298+ DB( g_print(" - dialog result is %d\n", result) );
1299+
1300+ if(result == HB_RESPONSE_ADD || result == HB_RESPONSE_ADDKEEP || result == GTK_RESPONSE_ACCEPT)
1301+ {
1302+ deftransaction_get(window, NULL);
1303+ transaction_add(GTK_WINDOW(GLOBALS->mainwindow), ope);
1304+
1305+ DB( g_print(" - added 1 transaction to %d\n", ope->kacc) );
1306+
1307+ ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
1308+
1309+ count++;
1310+ //todo: still usefull ? store last date
1311+ date = ope->date;
1312+ }
1313+
1314+ if( result == HB_RESPONSE_ADD )
1315+ {
1316+ da_transaction_free(ope);
1317+ ope = NULL;
1318+ }
1319+
1320+ }
1321+
1322+
1323+ deftransaction_dispose(window, NULL);
1324+ gtk_widget_destroy (window);
1325+
1326+ /* todo optimize this */
1327+ if(count > 0)
1328+ {
1329+ GLOBALS->changes_count += count;
1330+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_BALANCE+UF_REFRESHALL));
1331+ }
1332+}
1333+
1334+struct tmptop
1335+{
1336+ guint32 key;
1337+ gdouble value;
1338+};
1339+
1340+
1341+#define MAX_TOPSPENDING 10
1342+
1343+
1344+static gint tmptop_compare_func(struct tmptop *tt1, struct tmptop *tt2)
1345+{
1346+ return tt1->value > tt2->value ? 1 : -1;
1347+}
1348+
1349+
1350+static void ui_panel_topspending_update(GtkWidget *widget, gpointer user_data)
1351+{
1352+struct hbfile_data *data;
1353+GtkTreeModel *model;
1354+gchar *title;
1355+gchar strbuffer[G_ASCII_DTOSTR_BUF_SIZE];
1356+
1357+ DB( g_print("\n[ui-mainwindow] topspending_update\n") );
1358+
1359+ data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1360+
1361+ hb_strfmon(strbuffer, G_ASCII_DTOSTR_BUF_SIZE-1, data->toptotal, GLOBALS->kcur, GLOBALS->minor);
1362+ //hb_label_set_amount(GTK_LABEL(data->TX_topamount), total, GLOBALS->kcur, GLOBALS->minor);
1363+ title = g_strdup_printf("%s %s", _("Top spending"), strbuffer);
1364+
1365+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_top));
1366+
1367+ gtk_chart_set_color_scheme(GTK_CHART(data->RE_pie), PREFS->report_color_scheme);
1368+ gtk_chart_set_currency(GTK_CHART(data->RE_pie), GLOBALS->kcur);
1369+ gtk_chart_set_datas(GTK_CHART(data->RE_pie), model, LST_TOPSPEND_AMOUNT, title, NULL);
1370+
1371+ g_free(title);
1372+
1373+ //future usage
1374+ gchar *fu = _("Top %d spending"); title = fu;
1375+}
1376+
1377+
1378+static void ui_mainwindow_populate_topspending(GtkWidget *widget, gpointer user_data)
1379+{
1380+struct hbfile_data *data;
1381+GtkTreeModel *model;
1382+GtkTreeIter iter;
1383+GList *list;
1384+gint type, range;
1385+guint n_result, i, n_items;
1386+GArray *garray;
1387+gdouble total, other;
1388+Account *acc;
1389+
1390+
1391+ DB( g_print("\n[ui-mainwindow] populate_topspending\n") );
1392+
1393+ data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1394+
1395+ type = radio_get_active(GTK_CONTAINER(data->RA_type));
1396+ range = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_range));
1397+
1398+ DB( g_print(" - type=%d, range=%d\n", type, range) );
1399+ DB( g_print(" - pref range=%d\n", PREFS->date_range_wal) );
1400+
1401+ if(range == FLT_RANGE_OTHER)
1402+ return;
1403+
1404+ filter_preset_daterange_set(data->filter, range, 0);
1405+
1406+
1407+ n_result = da_cat_get_max_key() + 1;
1408+ total = 0.0;
1409+
1410+ DB( g_print(" - max key is %d\n", n_result) );
1411+
1412+ /* allocate some memory */
1413+ garray = g_array_sized_new(FALSE, FALSE, sizeof(struct tmptop), n_result);
1414+
1415+ if(garray)
1416+ {
1417+ struct tmptop zero = { .key=0, .value=0.0 };
1418+ GQueue *txn_queue;
1419+
1420+ //DB( g_print(" - array length=%d\n", garray->len) );
1421+
1422+ for(i=0 ; i<n_result ; i++)
1423+ {
1424+ g_array_append_vals(garray, &zero, 1);
1425+ //g_array_insert_vals(garray, i, &zero, 1);
1426+
1427+ //struct tmptop *tt = &g_array_index (garray, struct tmptop, i);
1428+ //DB( g_print("%4d, %4d %f\n", i, tt->key, tt->value) );
1429+ }
1430+
1431+ //DB( g_print("\n - end array length=%d\n", garray->len) );
1432+
1433+ //todo: not ideal, has ot force to get_acc for each txn below
1434+ txn_queue = hbfile_transaction_get_partial(data->filter->mindate, data->filter->maxdate);
1435+
1436+ /* compute the results */
1437+ list = g_queue_peek_head_link(txn_queue);
1438+ while (list != NULL)
1439+ {
1440+ Transaction *ope = list->data;
1441+
1442+ //DB( g_print(" - eval txn: '%s', cat=%d ==> flt-test=%d\n", ope->memo, ope->kcat, filter_test(data->filter, ope)) );
1443+
1444+ if( !(ope->paymode == PAYMODE_INTXFER) )
1445+ {
1446+ guint32 pos = 0;
1447+ gdouble trn_amount;
1448+
1449+ //todo: optimize here
1450+ trn_amount = ope->amount;
1451+ acc = da_acc_get(ope->kacc);
1452+ if(acc)
1453+ trn_amount = hb_amount_base(ope->amount, acc->kcur);
1454+
1455+ if( ope->flags & OF_SPLIT )
1456+ {
1457+ guint nbsplit = da_splits_length(ope->splits);
1458+ Split *split;
1459+ struct tmptop *item;
1460+
1461+ for(i=0;i<nbsplit;i++)
1462+ {
1463+ split = da_splits_get(ope->splits, i);
1464+ pos = category_report_id(split->kcat, type);
1465+ if( pos <= garray->len )
1466+ {
1467+ trn_amount = hb_amount_base(split->amount, acc->kcur);
1468+ //trn_amount = split->amount;
1469+ //#1297054 if( trn_amount < 0 ) {
1470+ item = &g_array_index (garray, struct tmptop, pos);
1471+ item->key = pos;
1472+ item->value += trn_amount;
1473+ //DB( g_print(" - stored %.2f to item %d\n", trn_amount, pos) );
1474+ //}
1475+ }
1476+ }
1477+ }
1478+ else
1479+ {
1480+ struct tmptop *item;
1481+
1482+ pos = category_report_id(ope->kcat, type);
1483+ if( pos <= garray->len )
1484+ {
1485+ //#1297054 if( trn_amount < 0 ) {
1486+ item = &g_array_index (garray, struct tmptop, pos);
1487+ item->key = pos;
1488+ item->value += trn_amount;
1489+ //DB( g_print(" - stored %.2f to item %d\n", trn_amount, pos) );
1490+ //}
1491+ }
1492+ }
1493+
1494+ }
1495+
1496+ list = g_list_next(list);
1497+ }
1498+
1499+ g_queue_free (txn_queue);
1500+
1501+ // we need to sort this and limit before
1502+ g_array_sort(garray, (GCompareFunc)tmptop_compare_func);
1503+
1504+ n_items = MIN(garray->len,MAX_TOPSPENDING);
1505+ other = 0;
1506+ for(i=0 ; i<garray->len ; i++)
1507+ {
1508+ struct tmptop *item;
1509+
1510+ item = &g_array_index (garray, struct tmptop, i);
1511+ if(item->value < 0)
1512+ {
1513+ total += item->value;
1514+
1515+ if(i >= n_items)
1516+ other += item->value;
1517+
1518+ DB( g_print(" - %d : k='%d' v='%f' t='%f'\n", i, item->key, item->value, total) );
1519+
1520+ }
1521+ }
1522+
1523+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_top));
1524+ gtk_list_store_clear (GTK_LIST_STORE(model));
1525+ g_object_ref(model); /* Make sure the model stays with us after the tree view unrefs it */
1526+ gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_top), NULL); /* Detach model from view */
1527+
1528+ /* insert into the treeview */
1529+ for(i=0 ; i<MIN(garray->len,MAX_TOPSPENDING) ; i++)
1530+ {
1531+ gchar *name;
1532+ Category *entry;
1533+ struct tmptop *item;
1534+ gdouble value;
1535+
1536+ item = &g_array_index (garray, struct tmptop, i);
1537+
1538+ if(!item->value) continue;
1539+ //#1767659 top spending should restrict to... spending
1540+ if(item->value < 0)
1541+ {
1542+ value = hb_amount_round(item->value, 2);
1543+ entry = da_cat_get(item->key);
1544+ if(entry == NULL) continue;
1545+
1546+ name = entry->key == 0 ? _("(no category)") : entry->fullname;
1547+
1548+ // append test
1549+ gtk_list_store_append (GTK_LIST_STORE(model), &iter);
1550+ gtk_list_store_set (GTK_LIST_STORE(model), &iter,
1551+ LST_TOPSPEND_ID, i,
1552+ LST_TOPSPEND_KEY, 0,
1553+ LST_TOPSPEND_NAME, name,
1554+ LST_TOPSPEND_AMOUNT, value,
1555+ //LST_TOPSPEND_RATE, (gint)(((ABS(value)*100)/ABS(total)) + 0.5),
1556+ -1);
1557+ }
1558+ }
1559+
1560+ // append test
1561+ if(ABS(other) > 0)
1562+ {
1563+ gtk_list_store_append (GTK_LIST_STORE(model), &iter);
1564+ gtk_list_store_set (GTK_LIST_STORE(model), &iter,
1565+ LST_TOPSPEND_ID, n_items,
1566+ LST_TOPSPEND_KEY, 0,
1567+ LST_TOPSPEND_NAME, _("Other"),
1568+ LST_TOPSPEND_AMOUNT, other,
1569+ //LST_TOPSPEND_RATE, (gint)(((ABS(other)*100)/ABS(total)) + 0.5),
1570+ -1);
1571+ }
1572+
1573+ /* Re-attach model to view */
1574+ gtk_tree_view_set_model(GTK_TREE_VIEW(data->LV_top), model);
1575+ g_object_unref(model);
1576+
1577+
1578+ // update chart and widgets
1579+ {
1580+ gchar *daterange;
1581+
1582+ data->toptotal = total;
1583+ ui_panel_topspending_update(widget, data);
1584+
1585+ daterange = filter_daterange_text_get(data->filter);
1586+ gtk_widget_set_tooltip_markup(GTK_WIDGET(data->CY_range), daterange);
1587+ g_free(daterange);
1588+ }
1589+ }
1590+
1591+ /* free our memory */
1592+ g_array_free (garray, TRUE);
1593+
1594+}
1595+
1596+
1597+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1598+/* scheduled */
1599+/* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
1600+static Archive *
1601+ui_mainwindow_scheduled_get_selected_item(GtkTreeView *treeview)
1602+{
1603+GtkTreeSelection *treeselection;
1604+GtkTreeModel *model;
1605+GtkTreeIter iter;
1606+
1607+ treeselection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
1608+ if( gtk_tree_selection_get_selected(treeselection, &model, &iter) )
1609+ {
1610+ Archive *arc;
1611+
1612+ gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, LST_DSPUPC_DATAS, &arc, -1);
1613+ return arc;
1614+ }
1615+
1616+ return NULL;
1617+}
1618+
1619+
1620+static void ui_mainwindow_scheduled_onRowActivated (GtkTreeView *treeview,
1621+ GtkTreePath *path,
1622+ GtkTreeViewColumn *col,
1623+ gpointer userdata)
1624+{
1625+//struct hbfile_data *data;
1626+Archive *arc;
1627+
1628+ DB( g_print ("\n[ui-mainwindow] A scheduled row has been double-clicked!\n") );
1629+
1630+ //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(treeview, GTK_TYPE_WINDOW)), "inst_data");
1631+
1632+ arc = ui_mainwindow_scheduled_get_selected_item(treeview);
1633+ ui_mainwindow_defarchive(arc);
1634+}
1635+
1636+
1637+static void ui_mainwindow_scheduled_do_post(Archive *arc, gboolean doedit, gpointer user_data)
1638+{
1639+struct hbfile_data *data = user_data;
1640+GtkWidget *window;
1641+gint result;
1642+Transaction *txn;
1643+
1644+ window = create_deftransaction_window(GTK_WINDOW(data->window), TRANSACTION_EDIT_ADD, TRUE, 0);
1645+
1646+ /* fill in the transaction */
1647+ txn = da_transaction_malloc();
1648+ da_transaction_init_from_template(txn, arc);
1649+ txn->date = scheduled_get_postdate(arc, arc->nextdate);
1650+
1651+ deftransaction_set_transaction(window, txn);
1652+
1653+ result = gtk_dialog_run (GTK_DIALOG (window));
1654+
1655+ DB( g_print(" - dialog result is %d\n", result) );
1656+
1657+ if(result == HB_RESPONSE_ADD || result == GTK_RESPONSE_ACCEPT)
1658+ {
1659+ deftransaction_get(window, NULL);
1660+ transaction_add(GTK_WINDOW(GLOBALS->mainwindow), txn);
1661+ GLOBALS->changes_count++;
1662+
1663+ scheduled_date_advance(arc);
1664+
1665+ DB( g_print(" - added 1 transaction to %d\n", txn->kacc) );
1666+ }
1667+
1668+ da_transaction_free(txn);
1669+
1670+ deftransaction_dispose(window, NULL);
1671+ gtk_widget_destroy (window);
1672+
1673+}
1674+
1675+
1676+static void ui_mainwindow_scheduled_editpost_cb(GtkWidget *widget, gpointer user_data)
1677+{
1678+struct hbfile_data *data = user_data;
1679+
1680+ Archive *arc = ui_mainwindow_scheduled_get_selected_item(GTK_TREE_VIEW(data->LV_upc));
1681+
1682+ if( (arc != NULL) )
1683+ {
1684+ ui_mainwindow_scheduled_do_post(arc, TRUE, data);
1685+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE|UF_REFRESHALL));
1686+ }
1687+}
1688+
1689+
1690+static void ui_mainwindow_scheduled_post_cb(GtkWidget *widget, gpointer user_data)
1691+{
1692+struct hbfile_data *data = user_data;
1693+
1694+ DB( g_print("\n[ui-mainwindow] scheduled post\n") );
1695+
1696+ Archive *arc = ui_mainwindow_scheduled_get_selected_item(GTK_TREE_VIEW(data->LV_upc));
1697+
1698+ if( (arc != NULL) )
1699+ {
1700+ if( scheduled_is_postable(arc) )
1701+ {
1702+ Transaction *txn = da_transaction_malloc ();
1703+
1704+ da_transaction_init_from_template(txn, arc);
1705+ txn->date = scheduled_get_postdate(arc, arc->nextdate);
1706+ transaction_add(GTK_WINDOW(GLOBALS->mainwindow), txn);
1707+
1708+ GLOBALS->changes_count++;
1709+ scheduled_date_advance(arc);
1710+
1711+ da_transaction_free (txn);
1712+ }
1713+ else
1714+ {
1715+ ui_mainwindow_scheduled_do_post(arc, FALSE, data);
1716+ }
1717+
1718+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE|UF_REFRESHALL));
1719+ }
1720+}
1721+
1722+
1723+static void ui_mainwindow_scheduled_skip_cb(GtkWidget *widget, gpointer user_data)
1724+{
1725+struct hbfile_data *data = user_data;
1726+
1727+ Archive *arc = ui_mainwindow_scheduled_get_selected_item(GTK_TREE_VIEW(data->LV_upc));
1728+ if( (arc != NULL) && (arc->flags & OF_AUTO) )
1729+ {
1730+ GLOBALS->changes_count++;
1731+ scheduled_date_advance(arc);
1732+
1733+ ui_mainwindow_scheduled_populate(GLOBALS->mainwindow, NULL);
1734+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_SENSITIVE));
1735+ }
1736+}
1737+
1738+
1739+
1740+static void ui_mainwindow_scheduled_update(GtkWidget *widget, gpointer user_data)
1741+{
1742+struct hbfile_data *data;
1743+//gint filter;
1744+
1745+ DB( g_print("\n[ui-mainwindow] scheduled update\n") );
1746+
1747+ data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1748+
1749+ //filter = gtk_combo_box_get_active(GTK_COMBO_BOX(data->CY_sched_filter));
1750+
1751+ Archive *arc = ui_mainwindow_scheduled_get_selected_item(GTK_TREE_VIEW(data->LV_upc));
1752+
1753+ if(arc)
1754+ {
1755+ DB( g_print("archive is %s\n", arc->memo) );
1756+
1757+ gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_skip), TRUE);
1758+ gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_post), TRUE);
1759+ gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_editpost), TRUE);
1760+ }
1761+ else
1762+ {
1763+ gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_skip), FALSE);
1764+ gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_post), FALSE);
1765+ gtk_widget_set_sensitive(GTK_WIDGET(data->BT_sched_editpost), FALSE);
1766+ }
1767+
1768+}
1769+
1770+
1771+
1772+static void ui_mainwindow_scheduled_selection_cb(GtkTreeSelection *treeselection, gpointer user_data)
1773+{
1774+
1775+
1776+ ui_mainwindow_scheduled_update(GTK_WIDGET(gtk_tree_selection_get_tree_view (treeselection)), GINT_TO_POINTER(UF_SENSITIVE));
1777+}
1778+
1779+
1780+
1781+/*
1782+** called after load, importamiga, on demand
1783+*/
1784+void ui_mainwindow_scheduled_postall(GtkWidget *widget, gpointer user_data)
1785+{
1786+//struct hbfile_data *data;
1787+gint count;
1788+gint usermode = GPOINTER_TO_INT(user_data);
1789+
1790+ DB( g_print("\n[ui-mainwindow] check scheduled\n") );
1791+
1792+ //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1793+
1794+ count = scheduled_post_all_pending();
1795+
1796+ //inform the user
1797+ if(usermode == TRUE)
1798+ {
1799+ gchar *txt;
1800+
1801+ //#125534
1802+ if( count > 0 )
1803+ {
1804+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_REFRESHALL));
1805+ }
1806+
1807+ if(count == 0)
1808+ txt = _("No transaction to add");
1809+ else
1810+ txt = _("transaction added: %d");
1811+
1812+ ui_dialog_msg_infoerror(GTK_WINDOW(GLOBALS->mainwindow), GTK_MESSAGE_INFO,
1813+ _("Check scheduled transactions result"),
1814+ txt,
1815+ count);
1816+ }
1817+
1818+}
1819+
1820+
1821+static void ui_mainwindow_scheduled_populate(GtkWidget *widget, gpointer user_data)
1822+{
1823+struct hbfile_data *data;
1824+GtkTreeModel *model;
1825+GtkTreeIter iter;
1826+GList *list;
1827+gdouble totexp = 0;
1828+gdouble totinc = 0;
1829+gint count = 0;
1830+gchar buffer[256];
1831+guint32 maxpostdate;
1832+GDate *date;
1833+//Account *acc;
1834+
1835+ DB( g_print("\n[ui-mainwindow] scheduled populate list\n") );
1836+
1837+ data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1838+
1839+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_upc));
1840+ gtk_list_store_clear (GTK_LIST_STORE(model));
1841+
1842+ homebank_app_date_get_julian();
1843+
1844+ maxpostdate = scheduled_date_get_post_max();
1845+
1846+ date = g_date_new_julian (maxpostdate);
1847+ g_date_strftime (buffer, 256-1, PREFS->date_format, date);
1848+ g_date_free(date);
1849+
1850+ gtk_label_set_text(GTK_LABEL(data->LB_maxpostdate), buffer);
1851+
1852+
1853+ list = g_list_first(GLOBALS->arc_list);
1854+ while (list != NULL)
1855+ {
1856+ Archive *arc = list->data;
1857+ Account *acc;
1858+ gdouble inc, exp;
1859+ guint nbdays, nblate;
1860+
1861+ if((arc->flags & OF_AUTO) ) //&& arc->kacc > 0)
1862+ {
1863+ count++;
1864+ nbdays = arc->nextdate - maxpostdate;
1865+ nblate = scheduled_get_latepost_count(arc, GLOBALS->today);
1866+
1867+ DB( g_print(" - append '%s' : %d\n", arc->memo, nbdays) );
1868+
1869+ if(arc->flags & OF_INCOME)
1870+ {
1871+ inc = arc->amount;
1872+ exp = 0.0;
1873+ }
1874+ else
1875+ {
1876+ exp = arc->amount;
1877+ inc = 0.0;
1878+ }
1879+
1880+ /* insert normal txn */
1881+ acc = da_acc_get(arc->kacc);
1882+ if( acc)
1883+ {
1884+ totinc += hb_amount_base(inc, acc->kcur);
1885+ totexp += hb_amount_base(exp, acc->kcur);
1886+ }
1887+ gtk_list_store_append (GTK_LIST_STORE(model), &iter);
1888+ gtk_list_store_set (GTK_LIST_STORE(model), &iter,
1889+ LST_DSPUPC_DATAS, arc,
1890+ LST_DSPUPC_ACCOUNT, acc,
1891+ LST_DSPUPC_MEMO, arc->memo,
1892+ LST_DSPUPC_EXPENSE, exp,
1893+ LST_DSPUPC_INCOME, inc,
1894+ LST_DSPUPC_REMAINING, nbdays,
1895+ LST_DSPUPC_NB_LATE, nblate,
1896+ -1);
1897+
1898+ /* insert internal xfer txn : 1378836 */
1899+ if(arc->paymode == PAYMODE_INTXFER)
1900+ {
1901+ acc = da_acc_get(arc->kxferacc);
1902+ if( acc)
1903+ {
1904+ totinc += hb_amount_base(-inc, acc->kcur);
1905+ totexp += hb_amount_base(-exp, acc->kcur);
1906+ }
1907+ gtk_list_store_append (GTK_LIST_STORE(model), &iter);
1908+ gtk_list_store_set (GTK_LIST_STORE(model), &iter,
1909+ LST_DSPUPC_DATAS, arc,
1910+ LST_DSPUPC_ACCOUNT, acc,
1911+ LST_DSPUPC_MEMO, arc->memo,
1912+ LST_DSPUPC_EXPENSE, -inc,
1913+ LST_DSPUPC_INCOME, -exp,
1914+ LST_DSPUPC_REMAINING, nbdays,
1915+ LST_DSPUPC_NB_LATE, nblate,
1916+ -1);
1917+ }
1918+
1919+ }
1920+ list = g_list_next(list);
1921+ }
1922+
1923+ // insert total
1924+ if(count > 0 )
1925+ {
1926+ gtk_list_store_append (GTK_LIST_STORE(model), &iter);
1927+ gtk_list_store_set (GTK_LIST_STORE(model), &iter,
1928+ LST_DSPUPC_DATAS, NULL,
1929+ LST_DSPUPC_ACCOUNT, NULL,
1930+ LST_DSPUPC_MEMO, _("Total"),
1931+ LST_DSPUPC_EXPENSE, totexp,
1932+ LST_DSPUPC_INCOME, totinc,
1933+ -1);
1934+ }
1935+
1936+ ui_mainwindow_scheduled_update(widget, NULL);
1937+
1938+}
1939+
1940+
1941+gboolean ui_mainwindow_open_backup_check_confirm(gchar *filepath)
1942+{
1943+gboolean retval = FALSE;
1944+gchar *basename, *secondtext;
1945+gboolean result;
1946+
1947+ basename = g_path_get_basename(filepath);
1948+ secondtext = g_strdup_printf (
1949+ _("Your are about to open the backup file '%s'.\n\nAre you sure you want to do this ?"), basename);
1950+
1951+ result = ui_dialog_msg_confirm_alert(
1952+ GTK_WINDOW(GLOBALS->mainwindow),
1953+ _("Open the backup file ?"),
1954+ secondtext,
1955+ _("_Open backup")
1956+ );
1957+
1958+ g_free(secondtext);
1959+ g_free(basename);
1960+
1961+ if( result == GTK_RESPONSE_OK )
1962+ retval = TRUE;
1963+
1964+ return retval;
1965+}
1966+
1967+
1968+/*
1969+**
1970+*/
1971+void ui_mainwindow_open(GtkWidget *widget, gpointer user_data)
1972+{
1973+//struct hbfile_data *data;
1974+gboolean bakmode = GPOINTER_TO_INT(user_data);;
1975+gboolean doopen = TRUE;
1976+gchar *filename = NULL;
1977+
1978+ DB( g_print("\n[ui-mainwindow] open\n") );
1979+
1980+ //data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
1981+
1982+ //#1791554 do ask for save confirm
1983+ if( bakmode != TRUE )
1984+ doopen = ui_dialog_msg_savechanges(widget,NULL);
1985+
1986+ if( doopen == TRUE )
1987+ {
1988+ if( ui_file_chooser_xhb(GTK_FILE_CHOOSER_ACTION_OPEN, &filename, bakmode) == TRUE )
1989+ {
1990+ //#1710955 test for backup open
1991+ if( hbfile_file_isbackup(filename) )
1992+ {
1993+ if( ui_mainwindow_open_backup_check_confirm(filename) == TRUE )
1994+ {
1995+ GLOBALS->hbfile_is_bak = TRUE;
1996+ }
1997+ else
1998+ {
1999+ g_free(filename);
2000+ return;
2001+ }
2002+ }
2003+
2004+ hbfile_change_filepath(filename);
2005+ ui_mainwindow_open_internal(widget, NULL);
2006+ }
2007+ }
2008+}
2009+
2010+
2011+/*
2012+ * open the file stored in GLOBALS->xhb_filepath
2013+ */
2014+void ui_mainwindow_open_internal(GtkWidget *widget, gpointer user_data)
2015+{
2016+struct hbfile_data *data;
2017+gint r;
2018+
2019+ DB( g_print("\n[ui-mainwindow] open internal\n") );
2020+
2021+ data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
2022+
2023+ if( GLOBALS->xhb_filepath != NULL )
2024+ {
2025+ DB( g_print(" - filename: '%s'\n", GLOBALS->xhb_filepath) );
2026+
2027+ ui_mainwindow_clear(GLOBALS->mainwindow, GINT_TO_POINTER(FALSE));
2028+ GLOBALS->hbfile_is_new = FALSE;
2029+
2030+ r = homebank_load_xml(GLOBALS->xhb_filepath);
2031+ if( r == XML_OK )
2032+ {
2033+ DB( g_print(" - file loaded ok : rcode=%d\n", r) );
2034+
2035+ GLOBALS->xhb_timemodified = hbfile_file_get_time_modified(GLOBALS->xhb_filepath);
2036+ hbfile_file_hasrevert(GLOBALS->xhb_filepath);
2037+
2038+ if(PREFS->appendscheduled)
2039+ scheduled_post_all_pending();
2040+
2041+ if(PREFS->do_update_currency)
2042+ ui_cur_manage_dialog_update_currencies(GTK_WINDOW(GLOBALS->mainwindow));
2043+
2044+ homebank_lastopenedfiles_save();
2045+
2046+ //todo: delete this after computing done at xml read
2047+ account_compute_balances();
2048+
2049+ ui_mainwindow_recent_add(data, GLOBALS->xhb_filepath);
2050+ }
2051+ else
2052+ {
2053+ gchar *msg = _("Unknown error");
2054+
2055+ switch(r)
2056+ {
2057+ case XML_IO_ERROR:
2058+ msg = _("I/O error for file '%s'.");
2059+ break;
2060+ case XML_FILE_ERROR:
2061+ msg = _("The file '%s' is not a valid HomeBank file.");
2062+ break;
2063+ case XML_VERSION_ERROR:
2064+ msg = _("The file '%s' was saved with a higher version of HomeBank\nand cannot be loaded by the current version.");
2065+ break;
2066+ }
2067+
2068+ ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_ERROR,
2069+ _("File error"),
2070+ msg,
2071+ GLOBALS->xhb_filepath
2072+ );
2073+
2074+ ui_mainwindow_clear(GLOBALS->mainwindow, GINT_TO_POINTER(TRUE));
2075+
2076+ }
2077+
2078+ ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
2079+ ui_mainwindow_scheduled_populate(GLOBALS->mainwindow, NULL);
2080+ ui_mainwindow_populate_topspending(GLOBALS->mainwindow, NULL);
2081+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_VISUAL));
2082+ }
2083+
2084+
2085+}
2086+
2087+
2088+/*
2089+**
2090+*/
2091+void ui_mainwindow_save(GtkWidget *widget, gpointer user_data)
2092+{
2093+struct hbfile_data *data;
2094+gboolean saveas = GPOINTER_TO_INT(user_data);
2095+gchar *filename = NULL;
2096+gint r = XML_UNSET;
2097+
2098+ DB( g_print("\n[ui-mainwindow] save\n") );
2099+
2100+ data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
2101+
2102+ if( GLOBALS->hbfile_is_new == TRUE )
2103+ saveas = 1;
2104+
2105+ //#1710955 test for backup open
2106+ if( GLOBALS->hbfile_is_bak == TRUE )
2107+ {
2108+ //todo: later for backup, should also remove datetime and .bak
2109+ hbfile_change_filepath(hb_filename_new_with_extension(GLOBALS->xhb_filepath, "xhb"));
2110+ saveas = 1;
2111+ }
2112+
2113+ if(saveas == 1)
2114+ {
2115+ if(ui_file_chooser_xhb(GTK_FILE_CHOOSER_ACTION_SAVE, &filename, FALSE) == TRUE)
2116+ {
2117+ DB( g_print(" + should save as '%s'\n", filename) );
2118+ homebank_file_ensure_xhb(filename);
2119+ homebank_backup_current_file();
2120+ r = homebank_save_xml(GLOBALS->xhb_filepath);
2121+ GLOBALS->hbfile_is_new = FALSE;
2122+ GLOBALS->hbfile_is_bak = FALSE;
2123+ }
2124+ else
2125+ return;
2126+ }
2127+ else
2128+ {
2129+ guint64 time_modified = hbfile_file_get_time_modified (GLOBALS->xhb_filepath);
2130+ gint result = GTK_RESPONSE_OK;
2131+
2132+ DB( g_print(" + should quick save '%s'\n + time: open=%lu :: now=%lu\n", GLOBALS->xhb_filepath, GLOBALS->xhb_timemodified, time_modified) );
2133+
2134+ if( GLOBALS->xhb_timemodified != time_modified )
2135+ {
2136+ result = ui_dialog_msg_confirm_alert(
2137+ GTK_WINDOW(GLOBALS->mainwindow),
2138+ _("The file has been modified since reading it."),
2139+ _("If you save it, all the external changes could be lost. Save it anyway?"),
2140+ _("S_ave Anyway")
2141+ );
2142+
2143+ if( result != GTK_RESPONSE_OK )
2144+ return;
2145+ }
2146+
2147+ DB( g_print(" + saving...\n") );
2148+ homebank_file_ensure_xhb(NULL);
2149+ homebank_backup_current_file();
2150+ r = homebank_save_xml(GLOBALS->xhb_filepath);
2151+ }
2152+
2153+ if(r == XML_OK)
2154+ {
2155+ DB( g_print(" + OK...\n") );
2156+ GLOBALS->changes_count = 0;
2157+ GLOBALS->xhb_timemodified = hbfile_file_get_time_modified (GLOBALS->xhb_filepath);
2158+ ui_mainwindow_update(GLOBALS->mainwindow, GINT_TO_POINTER(UF_TITLE+UF_SENSITIVE+UF_VISUAL));
2159+ }
2160+ else
2161+ {
2162+ gchar *msg = _("I/O error for file '%s'.");
2163+
2164+ ui_dialog_msg_infoerror(GTK_WINDOW(data->window), GTK_MESSAGE_ERROR,
2165+ _("File error"),
2166+ msg,
2167+ GLOBALS->xhb_filepath
2168+ );
2169+ }
2170+}
2171+
2172+
2173+static void ui_panel_accounts_expand_all(GtkWidget *widget, gpointer user_data)
2174+{
2175+struct hbfile_data *data;
2176+
2177+ data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
2178+ gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_acc));
2179+}
2180+
2181+
2182+static void ui_panel_accounts_collapse_all(GtkWidget *widget, gpointer user_data)
2183+{
2184+struct hbfile_data *data;
2185+
2186+ data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
2187+ gtk_tree_view_collapse_all(GTK_TREE_VIEW(data->LV_acc));
2188+}
2189+
2190+
2191+
2192+static GHashTable *ui_panel_accounts_groups_get(GList *lacc, gint groupby, gboolean showall)
2193+{
2194+GHashTable *hash;
2195+GList *elt;
2196+gchar *groupname;
2197+gint nballoc;
2198+
2199+ DB( g_print("\n[ui-mainwindow] accounts_groups_get\n") );
2200+
2201+ nballoc = da_acc_length ();
2202+
2203+ DB( g_print(" %d accounts\n", nballoc) );
2204+
2205+ hash = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
2206+ elt = g_list_first(lacc);
2207+ while (elt != NULL)
2208+ {
2209+ Account *acc = elt->data;
2210+ GPtrArray *group;
2211+
2212+ //#1674045 ony rely on nosummary
2213+ //if( showall || !(acc->flags & (AF_CLOSED|AF_NOSUMMARY)) )
2214+ if( showall || !(acc->flags & AF_NOSUMMARY) )
2215+ {
2216+ if( groupby == DSPACC_GROUP_BY_BANK )
2217+ {
2218+ groupname = _("(no institution)");
2219+ if( (acc->bankname != NULL) && strlen(acc->bankname) > 0 )
2220+ groupname = acc->bankname;
2221+ }
2222+ else
2223+ {
2224+ //pre 5.1.3 historical by type display
2225+ groupname = _(CYA_ACC_TYPE[acc->type]);
2226+ }
2227+
2228+ if( g_hash_table_contains(hash, groupname) == FALSE )
2229+ {
2230+ g_hash_table_insert(hash, g_strdup(groupname), g_ptr_array_sized_new(nballoc) );
2231+ //DB( g_print(" - type hash insert '%s' = %d\n", groupname, inserted) );
2232+ }
2233+
2234+ group = g_hash_table_lookup(hash, groupname);
2235+ if( group != NULL )
2236+ {
2237+ g_ptr_array_add(group, (gpointer)acc);
2238+ DB( g_print(" -- add '%s' to group '%s'\n", acc->name, groupname) );
2239+ }
2240+ }
2241+ elt = g_list_next(elt);
2242+ }
2243+
2244+ DB( g_print(" end\n") );
2245+
2246+ return hash;
2247+}
2248+
2249+
2250+
2251+
2252+
2253+
2254+void ui_mainwindow_populate_accounts(GtkWidget *widget, gpointer user_data)
2255+{
2256+struct hbfile_data *data;
2257+GtkTreeModel *model;
2258+GtkTreeIter iter1, child_iter;
2259+GList *lacc, *elt;
2260+Account *acc;
2261+guint j, nbtype;
2262+gdouble gtbank, gttoday, gtfuture;
2263+
2264+GHashTable *h_group;
2265+GHashTableIter grp_iter;
2266+gpointer key, value;
2267+
2268+ DB( g_print("\n[ui-mainwindow] populate accounts\n") );
2269+
2270+ data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
2271+
2272+ /* here we create a count and a list of every account pointer by type */
2273+ lacc = elt = g_hash_table_get_values(GLOBALS->h_acc);
2274+
2275+ h_group = ui_panel_accounts_groups_get(lacc, PREFS->pnl_acc_show_by, data->showall);
2276+ g_list_free(lacc);
2277+
2278+
2279+ gtbank = gttoday = gtfuture = 0;
2280+
2281+ DB( g_print(" - populate listview, %d group(s)\n", g_hash_table_size(h_group)) );
2282+
2283+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(data->LV_acc));
2284+ gtk_tree_store_clear (GTK_TREE_STORE(model));
2285+
2286+ nbtype = 0;
2287+ g_hash_table_iter_init (&grp_iter, h_group);
2288+ while (g_hash_table_iter_next (&grp_iter, &key, &value))
2289+ {
2290+ GPtrArray *gpa = value;
2291+ gdouble tbank, ttoday, tfuture;
2292+ gint position;
2293+
2294+ if(gpa != NULL)
2295+ {
2296+ nbtype++;
2297+ //1: Header: Bank, Cash, ...
2298+ DB( g_print(" - add group '%s'\n", (gchar *)key) );
2299+
2300+ //#1663399 keep type position like in dropdown
2301+ position = 0;
2302+ if( PREFS->pnl_acc_show_by == DSPACC_GROUP_BY_TYPE )
2303+ {
2304+ gint t = 0;
2305+
2306+ while(CYA_ACC_TYPE[t] != NULL && t < 15)
2307+ {
2308+ if( !strcmp(CYA_ACC_TYPE[t], key) )
2309+ break;
2310+ t++;
2311+ }
2312+
2313+ position = t;
2314+ }
2315+
2316+ gtk_tree_store_append (GTK_TREE_STORE(model), &iter1, NULL);
2317+ gtk_tree_store_set (GTK_TREE_STORE(model), &iter1,
2318+ LST_DSPACC_POS, position,
2319+ LST_DSPACC_DATATYPE, DSPACC_TYPE_HEADER,
2320+ LST_DSPACC_NAME, key,
2321+ -1);
2322+
2323+ tbank = ttoday = tfuture = 0;
2324+
2325+ //2: Accounts for real
2326+ for(j=0;j<gpa->len;j++)
2327+ {
2328+ acc = g_ptr_array_index(gpa, j);
2329+
2330+ //tbank += acc->bal_bank;
2331+ //ttoday += acc->bal_today;
2332+ //tfuture += acc->bal_future;
2333+ tbank += hb_amount_base(acc->bal_bank, acc->kcur);
2334+ ttoday += hb_amount_base(acc->bal_today, acc->kcur);
2335+ tfuture += hb_amount_base(acc->bal_future, acc->kcur);
2336+
2337+ DB( g_print(" - add account '%s' :: %.2f %.2f %.2f\n", acc->name, acc->bal_bank, acc->bal_today, acc->bal_future) );
2338+
2339+ gtk_tree_store_append (GTK_TREE_STORE(model), &child_iter, &iter1);
2340+ gtk_tree_store_set (GTK_TREE_STORE(model), &child_iter,
2341+ LST_DSPACC_DATAS, acc,
2342+ LST_DSPACC_DATATYPE, DSPACC_TYPE_NORMAL,
2343+ LST_DSPACC_BANK, acc->bal_bank,
2344+ LST_DSPACC_TODAY, acc->bal_today,
2345+ LST_DSPACC_FUTURE, acc->bal_future,
2346+ -1);
2347+ }
2348+
2349+ if(gpa->len > 1)
2350+ {
2351+ DB( g_print(" - group total :: %.2f %.2f %.2f\n", tbank, ttoday, tfuture) );
2352+
2353+ // insert the total line
2354+ gtk_tree_store_append (GTK_TREE_STORE(model), &child_iter, &iter1);
2355+ gtk_tree_store_set (GTK_TREE_STORE(model), &child_iter,
2356+ LST_DSPACC_DATATYPE, DSPACC_TYPE_SUBTOTAL,
2357+ LST_DSPACC_NAME, _("Total"),
2358+ LST_DSPACC_BANK, tbank,
2359+ LST_DSPACC_TODAY, ttoday,
2360+ LST_DSPACC_FUTURE, tfuture,
2361+ -1);
2362+ }
2363+
2364+ /* set balance to header to display when collasped */
2365+ DB( g_print(" - enrich group total header :: %.2f %.2f %.2f\n", tbank, ttoday, tfuture) );
2366+ gtk_tree_store_set (GTK_TREE_STORE(model), &iter1,
2367+ LST_DSPACC_BANK, tbank,
2368+ LST_DSPACC_TODAY, ttoday,
2369+ LST_DSPACC_FUTURE, tfuture,
2370+ -1);
2371+
2372+ /* add to grand total */
2373+ gtbank += tbank;
2374+ gttoday += ttoday;
2375+ gtfuture += tfuture;
2376+
2377+ }
2378+
2379+ }
2380+
2381+ DB( g_print(" - grand total :: %.2f %.2f %.2f\n", gtbank, gttoday, gtfuture) );
2382+
2383+ // Grand total
2384+ if( nbtype > 1 )
2385+ {
2386+ gtk_tree_store_append (GTK_TREE_STORE(model), &iter1, NULL);
2387+ gtk_tree_store_set (GTK_TREE_STORE(model), &iter1,
2388+ LST_DSPACC_DATATYPE, DSPACC_TYPE_SUBTOTAL,
2389+ LST_DSPACC_NAME, _("Grand total"),
2390+ LST_DSPACC_BANK, gtbank,
2391+ LST_DSPACC_TODAY, gttoday,
2392+ LST_DSPACC_FUTURE, gtfuture,
2393+ -1);
2394+ }
2395+
2396+
2397+ gtk_tree_view_expand_all(GTK_TREE_VIEW(data->LV_acc));
2398+
2399+ DB( g_print(" - free ressources\n") );
2400+
2401+ g_hash_table_iter_init (&grp_iter, h_group);
2402+ while (g_hash_table_iter_next (&grp_iter, &key, &value))
2403+ {
2404+ g_ptr_array_free (value, TRUE);
2405+ }
2406+ g_hash_table_destroy (h_group);
2407+
2408+}
2409+
2410+
2411+void ui_mainwindow_update(GtkWidget *widget, gpointer user_data)
2412+{
2413+struct hbfile_data *data;
2414+gint flags;
2415+
2416+ DB( g_print("\n[ui-mainwindow] update %p\n", user_data) );
2417+
2418+ data = g_object_get_data(G_OBJECT(gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), "inst_data");
2419+ //data = INST_DATA(widget);
2420+
2421+ flags = GPOINTER_TO_INT(user_data);
2422+
2423+ /* set window title */
2424+ if(flags & UF_TITLE)
2425+ {
2426+ gchar *basename;
2427+ gchar *changed;
2428+
2429+ DB( g_print(" 1: wintitle %p\n", data->wintitle) );
2430+
2431+ basename = g_path_get_basename(GLOBALS->xhb_filepath);
2432+
2433+ DB( g_print(" global changes: %d\n", GLOBALS->changes_count) );
2434+
2435+ g_free(data->wintitle);
2436+
2437+ changed = (GLOBALS->changes_count > 0) ? "*" : "";
2438+
2439+#if MYDEBUG == 1
2440+ data->wintitle = g_strdup_printf("%s%s (%d)- %s - " PROGNAME, changed, basename, GLOBALS->changes_count, GLOBALS->owner);
2441+#else
2442+ data->wintitle = g_strdup_printf("%s%s - %s - " PROGNAME, changed, basename, GLOBALS->owner);
2443+#endif
2444+
2445+ gtk_window_set_title (GTK_WINDOW (gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW)), data->wintitle);
2446+
2447+ g_free(basename);
2448+ }
2449+
2450+ /* update disabled things */
2451+ if(flags & UF_SENSITIVE)
2452+ {
2453+ GtkTreeSelection *selection;
2454+ GtkTreeModel *model;
2455+ GtkTreeIter iter;
2456+ GtkTreePath *path;
2457+ gboolean active,sensitive;
2458+
2459+ DB( g_print(" 2: disabled, opelist count\n") );
2460+
2461+ //#1656531
2462+ data->acc = NULL;
2463+
2464+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc));
2465+ active = gtk_tree_selection_get_selected(selection, &model, &iter);
2466+ if(active)
2467+ {
2468+ Account *acc;
2469+ gint depth;
2470+
2471+ path = gtk_tree_model_get_path(model, &iter);
2472+ depth = gtk_tree_path_get_depth(path);
2473+
2474+ if( depth > 1 )
2475+ {
2476+ DB( g_print(" depth is %d\n", depth) );
2477+
2478+ gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, LST_DSPACC_DATAS, &acc, -1);
2479+ data->acc = acc;
2480+ }
2481+ else
2482+ active = FALSE;
2483+ }
2484+
2485+ DB( g_print(" changes %d - new %d\n", GLOBALS->changes_count, GLOBALS->hbfile_is_new) );
2486+
2487+ // save
2488+ sensitive = (GLOBALS->changes_count != 0 ) ? TRUE : FALSE;
2489+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/FileMenu/Save"), sensitive);
2490+
2491+ // backup
2492+ sensitive = ( (GLOBALS->changes_count != 0) && GLOBALS->xhb_hasrevert ) ? TRUE : FALSE;
2493+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/FileMenu/Revert"), sensitive);
2494+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/FileMenu/OpenBak"), sensitive);
2495+
2496+ // define off ?
2497+ sensitive = GLOBALS->define_off == 0 ? TRUE : FALSE;
2498+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Account"), sensitive);
2499+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Payee"), sensitive);
2500+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Category"), sensitive);
2501+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Budget"), sensitive);
2502+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/EditMenu/Preferences"), sensitive);
2503+
2504+ // empty account list: disable Archives, Edit, Filter, Add, Statistics, Overdrawn, Car Cost
2505+ sensitive = da_acc_length() > 0 ? TRUE : FALSE;
2506+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/FileMenu/Close"), sensitive);
2507+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Archive"), sensitive);
2508+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/TxnMenu/AddTxn"), sensitive);
2509+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/TxnMenu/ShowTxn"), sensitive);
2510+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ReportMenu/RStatistics"), sensitive);
2511+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ReportMenu/RTrendTime"), sensitive);
2512+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ReportMenu/RBudget"), sensitive);
2513+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ReportMenu/RBalance"), sensitive);
2514+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ReportMenu/RVehiculeCost"), sensitive);
2515+
2516+ // empty category list: disable Budget
2517+ sensitive = da_cat_length() > 1 ? TRUE : FALSE;
2518+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Budget"), sensitive);
2519+
2520+ //#1501129 no need to disable, P & C can be created from assign dialog
2521+ //sensitive = ((da_cat_length() > 1) || (da_pay_length() > 1)) ? TRUE : FALSE;
2522+ //gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/ManageMenu/Assign"), sensitive);
2523+
2524+ // empty archive list: disable scheduled check
2525+ sensitive = g_list_length(GLOBALS->arc_list) > 0 ? TRUE : FALSE;
2526+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/TxnMenu/AddScheduled"), sensitive);
2527+
2528+ // no active account: disable Edit, Over
2529+ sensitive = (active == TRUE ) ? TRUE : FALSE;
2530+ if(data->acc && data->acc->window != NULL)
2531+ sensitive = FALSE;
2532+ gtk_action_set_sensitive(gtk_ui_manager_get_action(data->manager, "/MenuBar/TxnMenu/ShowTxn"), sensitive);
2533+ }
2534+
2535+ /* update toolbar, list */
2536+ if(flags & UF_VISUAL)
2537+ {
2538+ DB( g_print(" 8: visual\n") );
2539+
2540+ if(PREFS->toolbar_style == 0)
2541+ gtk_toolbar_unset_style(GTK_TOOLBAR(data->toolbar));
2542+ else
2543+ gtk_toolbar_set_style(GTK_TOOLBAR(data->toolbar), PREFS->toolbar_style-1);
2544+
2545+ gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (data->LV_acc), PREFS->grid_lines);
2546+ gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_acc));
2547+
2548+ gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (data->LV_upc), PREFS->grid_lines);
2549+ gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_upc));
2550+
2551+ DB( g_print(" - show toolbar=%d\n", PREFS->wal_toolbar) );
2552+ if(PREFS->wal_toolbar)
2553+ gtk_widget_show(GTK_WIDGET(data->toolbar));
2554+ else
2555+ gtk_widget_hide(GTK_WIDGET(data->toolbar));
2556+
2557+
2558+ DB( g_print(" - show top_spending=%d\n", PREFS->wal_spending) );
2559+
2560+ gtk_combo_box_set_active(GTK_COMBO_BOX(data->CY_range), PREFS->date_range_wal);
2561+
2562+ if(PREFS->wal_spending)
2563+ gtk_widget_show(GTK_WIDGET(data->GR_top));
2564+ else
2565+ gtk_widget_hide(GTK_WIDGET(data->GR_top));
2566+
2567+
2568+
2569+ DB( g_print(" - show upcoming=%d\n", PREFS->wal_upcoming) );
2570+ if(PREFS->wal_upcoming)
2571+ gtk_widget_show(GTK_WIDGET(data->GR_upc));
2572+ else
2573+ gtk_widget_hide(GTK_WIDGET(data->GR_upc));
2574+
2575+ DB( g_print(" minor %d\n", PREFS->euro_active) );
2576+ gtk_action_set_visible(gtk_ui_manager_get_action(data->manager, "/MenuBar/ViewMenu/AsMinor"), PREFS->euro_active);
2577+ }
2578+
2579+ /* update balances */
2580+ if(flags & UF_BALANCE)
2581+ {
2582+
2583+ DB( g_print(" 4: balances\n") );
2584+
2585+ gtk_tree_view_columns_autosize (GTK_TREE_VIEW(data->LV_acc));
2586+
2587+ //minor = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->CM_minor));
2588+
2589+ /*
2590+ hb-label_set_colvalue(GTK_LABEL(data->TX_balance[0]), data->bank, minor);
2591+ hb-label_set_colvalue(GTK_LABEL(data->TX_balance[1]), data->today, minor);
2592+ hb-label_set_colvalue(GTK_LABEL(data->TX_balance[2]), data->future, minor);
2593+ */
2594+ }
2595+
2596+ if(flags & UF_REFRESHALL)
2597+ {
2598+ DB( g_print(" 16: refreshall\n") );
2599+
2600+ ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
2601+ ui_mainwindow_populate_topspending(GLOBALS->mainwindow, NULL);
2602+ ui_mainwindow_scheduled_populate(GLOBALS->mainwindow, NULL);
2603+ }
2604+
2605+
2606+}
2607+
2608+
2609+
2610+static void
2611+ ui_mainwindow_onRowActivated (GtkTreeView *treeview,
2612+ GtkTreePath *path,
2613+ GtkTreeViewColumn *col,
2614+ gpointer userdata)
2615+ {
2616+ GtkTreeModel *model;
2617+ GtkTreeIter iter;
2618+
2619+ DB( g_print ("\n[ui-mainwindow] A row has been double-clicked!\n") );
2620+
2621+ model = gtk_tree_view_get_model(treeview);
2622+
2623+ if (gtk_tree_model_get_iter(model, &iter, path))
2624+ {
2625+ Account *acc;
2626+
2627+ gtk_tree_model_get(model, &iter, LST_DSPACC_DATAS, &acc, -1);
2628+
2629+ if( acc != NULL )
2630+ {
2631+
2632+ DB( g_print ("Double-clicked row contains name %s\n", acc->name) );
2633+
2634+ ui_mainwindow_action_showtransactions();
2635+
2636+ //g_free(name);
2637+ }
2638+ }
2639+ }
2640+
2641+
2642+static void ui_mainwindow_destroy(GtkTreeView *treeview, gpointer user_data)
2643+{
2644+ DB( g_print("\n[ui-mainwindow] destroy\n") );
2645+
2646+}
2647+
2648+
2649+/*
2650+**
2651+*/
2652+static gboolean ui_mainwindow_dispose(GtkWidget *widget, GdkEvent *event, gpointer user_data)
2653+{
2654+struct hbfile_data *data = user_data;
2655+struct WinGeometry *wg;
2656+gboolean retval = FALSE;
2657+
2658+ DB( g_print("\n[ui-mainwindow] delete-event\n") );
2659+
2660+ //store position and size
2661+ wg = &PREFS->wal_wg;
2662+ gtk_window_get_position(GTK_WINDOW(widget), &wg->l, &wg->t);
2663+ gtk_window_get_size(GTK_WINDOW(widget), &wg->w, &wg->h);
2664+ GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(widget));
2665+ GdkWindowState state = gdk_window_get_state(gdk_window);
2666+ wg->s = (state & GDK_WINDOW_STATE_MAXIMIZED) ? 1 : 0;
2667+
2668+ 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) );
2669+
2670+ PREFS->wal_vpaned = gtk_paned_get_position(GTK_PANED(data->vpaned));
2671+ PREFS->wal_hpaned = gtk_paned_get_position(GTK_PANED(data->hpaned));
2672+
2673+ DB( g_print(" - vpaned=%d hpaned=%d\n", PREFS->wal_vpaned, PREFS->wal_hpaned) );
2674+
2675+ //todo
2676+ if(ui_dialog_msg_savechanges(widget, NULL) == FALSE)
2677+ {
2678+ retval = TRUE;
2679+ }
2680+ else
2681+ {
2682+ //todo: retval is useless and below should move to destroy
2683+ retval = TRUE;
2684+ gtk_widget_destroy(data->LV_top);
2685+
2686+ g_free(data->wintitle);
2687+ da_filter_free(data->filter);
2688+ g_free(user_data);
2689+
2690+ gtk_main_quit();
2691+ }
2692+
2693+ //TRUE:stop other handlers from being invoked for the event | FALSE: propagate
2694+ return retval;
2695+}
2696+
2697+
2698+static void ui_mainwindow_recent_chooser_item_activated_cb (GtkRecentChooser *chooser, struct hbfile_data *data)
2699+{
2700+ gchar *uri, *path;
2701+ GError *error = NULL;
2702+
2703+ uri = gtk_recent_chooser_get_current_uri (chooser);
2704+
2705+ path = g_filename_from_uri (uri, NULL, NULL);
2706+ if (error)
2707+ {
2708+ g_warning ("Could not convert uri \"%s\" to a local path: %s", uri, error->message);
2709+ g_error_free (error);
2710+ return;
2711+ }
2712+
2713+ if( ui_dialog_msg_savechanges(data->window, NULL) == TRUE )
2714+ {
2715+
2716+ //todo: FixMe
2717+ /*
2718+ if (! load)
2719+ {
2720+ gpw_recent_remove (gpw, path);
2721+ }
2722+ */
2723+
2724+ hbfile_change_filepath(path);
2725+ ui_mainwindow_open_internal(data->window, NULL);
2726+ }
2727+ else
2728+ {
2729+ g_free (path);
2730+ }
2731+ g_free (uri);
2732+}
2733+
2734+
2735+void ui_mainwindow_recent_add (struct hbfile_data *data, const gchar *path)
2736+{
2737+ GtkRecentData *recent_data;
2738+ gchar *uri;
2739+ GError *error = NULL;
2740+
2741+ DB( g_print("\n[ui-mainwindow] recent_add\n") );
2742+
2743+ DB( g_print(" - file has .xhb suffix = %d\n", g_str_has_suffix (path, ".xhb") ) );
2744+
2745+ if( g_str_has_suffix (path, ".xhb") == FALSE ) //ignore reverted file
2746+ return;
2747+
2748+ uri = g_filename_to_uri (path, NULL, &error);
2749+ if (error)
2750+ {
2751+ g_warning ("Could not convert uri \"%s\" to a local path: %s", uri, error->message);
2752+ g_error_free (error);
2753+ return;
2754+ }
2755+
2756+ recent_data = g_slice_new (GtkRecentData);
2757+
2758+ recent_data->display_name = NULL;
2759+ recent_data->description = NULL;
2760+ recent_data->mime_type = "application/x-homebank";
2761+ recent_data->app_name = (gchar *) g_get_application_name ();
2762+ recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL);
2763+ recent_data->groups = NULL;
2764+ recent_data->is_private = FALSE;
2765+
2766+ if (!gtk_recent_manager_add_full (data->recent_manager,
2767+ uri,
2768+ recent_data))
2769+ {
2770+ g_warning ("Unable to add '%s' to the list of recently used documents", uri);
2771+ }
2772+
2773+ g_free (uri);
2774+ g_free (recent_data->app_exec);
2775+ g_slice_free (GtkRecentData, recent_data);
2776+
2777+}
2778+
2779+
2780+
2781+
2782+
2783+enum
2784+{
2785+ TARGET_URI_LIST
2786+};
2787+
2788+static GtkTargetEntry drop_types[] =
2789+{
2790+ {"text/uri-list", 0, TARGET_URI_LIST}
2791+};
2792+
2793+static void ui_mainwindow_drag_data_received (GtkWidget *widget,
2794+ GdkDragContext *context,
2795+ gint x, gint y,
2796+ GtkSelectionData *selection_data,
2797+ guint info, guint time, GtkWindow *window)
2798+{
2799+gchar **uris, **str;
2800+gchar *newseldata;
2801+gint n_uris, filetype, slen;
2802+GError *error = NULL;
2803+
2804+ if (info != TARGET_URI_LIST)
2805+ return;
2806+
2807+ DB( g_print("\n[ui-mainwindow] drag_data_received\n") );
2808+
2809+ /* On MS-Windows, it looks like `selection_data->data' is not NULL terminated. */
2810+ slen = gtk_selection_data_get_length(selection_data);
2811+ newseldata = g_new (gchar, slen + 1);
2812+ memcpy (newseldata, gtk_selection_data_get_data(selection_data), slen);
2813+ newseldata[slen] = 0;
2814+ //DB( g_print(" - seldata ='%s'\n", gtk_selection_data_get_data(selection_data) ) );
2815+ //DB( g_print(" - newseldata ='%s'\n", newseldata ) );
2816+
2817+ uris = g_uri_list_extract_uris (newseldata);
2818+ n_uris = g_strv_length(uris);
2819+ DB( g_print(" - dragged %d files (len=%d)\n", n_uris, slen ) );
2820+
2821+ g_free(newseldata);
2822+
2823+ //single file: check for xhb
2824+ if(n_uris == 1)
2825+ {
2826+ filetype = hb_filename_type_get_by_extension(*uris);
2827+
2828+ DB( g_print(" - filetype is homebank (%d)\n", filetype) );
2829+
2830+ if( filetype == FILETYPE_HOMEBANK )
2831+ {
2832+ gchar *path = g_filename_from_uri (*uris, NULL, &error);
2833+
2834+ if( path != NULL )
2835+ {
2836+ DB( g_print(" - path is '%s'\n", path) );
2837+ hbfile_change_filepath(g_strdup(path));
2838+ ui_mainwindow_open_internal(GTK_WIDGET(window), NULL);
2839+ goto end_drop;
2840+ }
2841+ else
2842+ {
2843+ g_warning ("Could not convert uri to local path: %s", error->message);
2844+ g_error_free (error);
2845+ }
2846+ g_free (path);
2847+ }
2848+ /* we no more manage error here
2849+ ui_dialog_msg_infoerror(GTK_WINDOW(window), GTK_MESSAGE_ERROR,
2850+ _("File error"),
2851+ _("The file %s is not a valid HomeBank file."),
2852+ path);
2853+ */
2854+ }
2855+
2856+ //collect known filetype to import
2857+ DB( g_print(" - collect %d files\n", n_uris) );
2858+
2859+ gchar **paths = g_new (gchar *, n_uris + 1);
2860+ slen = 0;
2861+ for (str = uris; *str; str++)
2862+ {
2863+ filetype = hb_filename_type_get_by_extension(*str);
2864+ if( filetype != FILETYPE_HOMEBANK && filetype != FILETYPE_UNKNOWN )
2865+ {
2866+ gchar *path = g_filename_from_uri (*str, NULL, NULL);
2867+
2868+ if( path != NULL )
2869+ {
2870+ DB( g_print(" - append %d '%s'\n", slen, path ) );
2871+ paths[slen++] = path;
2872+ }
2873+ }
2874+ }
2875+ paths[slen] = NULL;
2876+
2877+ if( slen > 0 )
2878+ {
2879+ ui_import_assistant_new( paths );
2880+ }
2881+
2882+
2883+end_drop:
2884+ g_strfreev (uris);
2885+}
2886+
2887+
2888+static GtkWidget *ui_mainwindow_create_recent_chooser_menu (GtkRecentManager *manager)
2889+{
2890+GtkWidget *recent_menu;
2891+GtkRecentFilter *filter;
2892+
2893+ recent_menu = gtk_recent_chooser_menu_new_for_manager (manager);
2894+ gtk_recent_chooser_set_local_only (GTK_RECENT_CHOOSER (recent_menu), FALSE);
2895+ gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (recent_menu), GTK_RECENT_SORT_MRU);
2896+ //todo: add a user pref for this
2897+ gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER (recent_menu), 10);
2898+ gtk_recent_chooser_set_show_icons (GTK_RECENT_CHOOSER (recent_menu), FALSE);
2899+ //gtk_recent_chooser_menu_set_show_numbers (GTK_RECENT_CHOOSER_MENU (recent_menu), TRUE);
2900+
2901+ filter = gtk_recent_filter_new ();
2902+ //gtk_recent_filter_add_application (filter, g_get_application_name());
2903+ gtk_recent_filter_add_pattern (filter, "*.[Xx][Hh][Bb]");
2904+ gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (recent_menu), filter);
2905+
2906+ return recent_menu;
2907+}
2908+
2909+
2910+static void ui_mainwindow_create_menu_bar_and_toolbar(struct hbfile_data *data, GtkWidget *mainvbox)
2911+{
2912+GtkUIManager *manager;
2913+GtkActionGroup *actions;
2914+GtkAction *action;
2915+GError *error = NULL;
2916+
2917+ manager = gtk_ui_manager_new ();
2918+ data->manager = manager;
2919+
2920+ gtk_window_add_accel_group (GTK_WINDOW (data->window),
2921+ gtk_ui_manager_get_accel_group(manager));
2922+
2923+ actions = gtk_action_group_new ("MainWindow");
2924+ gtk_action_group_set_translation_domain(actions, GETTEXT_PACKAGE);
2925+
2926+ gtk_action_group_add_actions (actions,
2927+ entries,
2928+ n_entries,
2929+ NULL);
2930+
2931+ gtk_action_group_add_toggle_actions (actions,
2932+ toggle_entries,
2933+ n_toggle_entries,
2934+ NULL);
2935+
2936+ gtk_ui_manager_insert_action_group (data->manager, actions, 0);
2937+ g_object_unref (actions);
2938+ data->actions = actions;
2939+
2940+ /* set short labels to use in the toolbar */
2941+ action = gtk_action_group_get_action(actions, "Open");
2942+ g_object_set(action, "short_label", _("Open"), NULL);
2943+
2944+ //action = gtk_action_group_get_action(action_group, "Save");
2945+ //g_object_set(action, "is_important", TRUE, NULL);
2946+
2947+ action = gtk_action_group_get_action(actions, "Account");
2948+ g_object_set(action, "short_label", _("Account"), NULL);
2949+
2950+ action = gtk_action_group_get_action(actions, "Payee");
2951+ g_object_set(action, "short_label", _("Payee"), NULL);
2952+
2953+ action = gtk_action_group_get_action(actions, "Category");
2954+ g_object_set(action, "short_label", _("Category"), NULL);
2955+
2956+ action = gtk_action_group_get_action(actions, "Archive");
2957+ //TRANSLATORS: an archive is stored transaction buffers (kind of bookmark to prefill manual insertion)
2958+ g_object_set(action, "short_label", _("Archive"), NULL);
2959+
2960+ action = gtk_action_group_get_action(actions, "Budget");
2961+ g_object_set(action, "short_label", _("Budget"), NULL);
2962+
2963+ action = gtk_action_group_get_action(actions, "ShowTxn");
2964+ g_object_set(action, "short_label", _("Show"), NULL);
2965+
2966+ action = gtk_action_group_get_action(actions, "AddTxn");
2967+ g_object_set(action, "is_important", TRUE, "short_label", _("Add"), NULL);
2968+
2969+ action = gtk_action_group_get_action(actions, "RStatistics");
2970+ g_object_set(action, "short_label", _("Statistics"), NULL);
2971+
2972+ action = gtk_action_group_get_action(actions, "RBudget");
2973+ g_object_set(action, "short_label", _("Budget"), NULL);
2974+
2975+ action = gtk_action_group_get_action(actions, "RBalance");
2976+ g_object_set(action, "short_label", _("Balance"), NULL);
2977+
2978+ action = gtk_action_group_get_action(actions, "RVehiculeCost");
2979+ g_object_set(action, "short_label", _("Vehicle cost"), NULL);
2980+
2981+ /* now load the UI definition */
2982+ gtk_ui_manager_add_ui_from_string (data->manager, ui_info, -1, &error);
2983+ if (error != NULL)
2984+ {
2985+ g_message ("Building menus failed: %s", error->message);
2986+ g_error_free (error);
2987+ }
2988+
2989+
2990+ data->recent_manager = gtk_recent_manager_get_default ();
2991+
2992+ data->menubar = gtk_ui_manager_get_widget (manager, "/MenuBar");
2993+ gtk_box_pack_start (GTK_BOX (mainvbox),
2994+ data->menubar,
2995+ FALSE,
2996+ FALSE,
2997+ 0);
2998+
2999+ /* recent files menu */
3000+ data->recent_menu = ui_mainwindow_create_recent_chooser_menu (data->recent_manager);
3001+
3002+ g_signal_connect (data->recent_menu,
3003+ "item-activated",
3004+ G_CALLBACK (ui_mainwindow_recent_chooser_item_activated_cb),
3005+ data);
3006+
3007+ GtkWidget *widget = gtk_ui_manager_get_widget (data->manager, "/MenuBar/FileMenu/RecentMenu");
3008+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (widget), data->recent_menu);
3009+
3010+
3011+ data->toolbar = gtk_ui_manager_get_widget (manager, "/ToolBar");
3012+ gtk_box_pack_start (GTK_BOX (mainvbox),
3013+ data->toolbar,
3014+ FALSE,
3015+ FALSE,
3016+ 0);
3017+
3018+ /* add the custom Open button to the toolbar */
3019+ GtkWidget *image = gtk_image_new_from_icon_name (ICONNAME_HB_FILE_OPEN, GTK_ICON_SIZE_BUTTON);
3020+ GtkToolItem *open_button = gtk_menu_tool_button_new(image, _("_Open"));
3021+ gtk_tool_item_set_tooltip_text (open_button, _("Open a file"));
3022+
3023+ GtkWidget *recent_menu = ui_mainwindow_create_recent_chooser_menu (data->recent_manager);
3024+ gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (open_button), recent_menu);
3025+ gtk_menu_tool_button_set_arrow_tooltip_text (GTK_MENU_TOOL_BUTTON (open_button), _("Open a recently used file"));
3026+
3027+ g_signal_connect (recent_menu,
3028+ "item-activated",
3029+ G_CALLBACK (ui_mainwindow_recent_chooser_item_activated_cb),
3030+ data);
3031+
3032+ action = gtk_action_group_get_action (data->actions, "Open");
3033+ g_object_set (action, "short_label", _("Open"), NULL);
3034+ //gtk_action_connect_proxy (action, GTK_WIDGET (open_button));
3035+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (open_button), action);
3036+
3037+ gtk_toolbar_insert (GTK_TOOLBAR (data->toolbar), open_button, 1);
3038+
3039+}
3040+
3041+
3042+/* Callback function for the undo action */
3043+/*static void
3044+activate_action (GSimpleAction *action, GVariant *parameter, gpointer user_data)
3045+{
3046+ g_print ("Action %s activated\n", g_action_get_name (G_ACTION (action)));
3047+}*/
3048+
3049+static void
3050+activate_toggle (GSimpleAction *action, GVariant *parameter, gpointer user_data)
3051+{
3052+struct hbfile_data *data = user_data;
3053+ GVariant *old_state, *new_state;
3054+
3055+ old_state = g_action_get_state (G_ACTION (action));
3056+ new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));
3057+
3058+ DB( g_print ("Toggle action %s activated, state changes from %d to %d\n",
3059+ g_action_get_name (G_ACTION (action)),
3060+ g_variant_get_boolean (old_state),
3061+ g_variant_get_boolean (new_state)) );
3062+
3063+ data->showall = g_variant_get_boolean (new_state);
3064+ ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
3065+
3066+ g_simple_action_set_state (action, new_state);
3067+ g_variant_unref (old_state);
3068+}
3069+
3070+static void
3071+activate_radio (GSimpleAction *action, GVariant *parameter, gpointer user_data)
3072+{
3073+//struct hbfile_data *data = user_data;
3074+GVariant *old_state, *new_state;
3075+
3076+ old_state = g_action_get_state (G_ACTION (action));
3077+ new_state = g_variant_new_string (g_variant_get_string (parameter, NULL));
3078+
3079+ DB( g_print ("Radio action %s activated, state changes from %s to %s\n",
3080+ g_action_get_name (G_ACTION (action)),
3081+ g_variant_get_string (old_state, NULL),
3082+ g_variant_get_string (new_state, NULL)) );
3083+
3084+ PREFS->pnl_acc_show_by = DSPACC_GROUP_BY_TYPE;
3085+ if( !strcmp("bank", g_variant_get_string(new_state, NULL)) )
3086+ PREFS->pnl_acc_show_by = DSPACC_GROUP_BY_BANK;
3087+
3088+ ui_mainwindow_populate_accounts(GLOBALS->mainwindow, NULL);
3089+
3090+ g_simple_action_set_state (action, new_state);
3091+ g_variant_unref (old_state);
3092+}
3093+
3094+
3095+static const GActionEntry actions[] = {
3096+// name, function(), type, state,
3097+// { "paste", activate_action, NULL, NULL, NULL, {0,0,0} },
3098+ { "showall", activate_toggle, NULL, "false" , NULL, {0,0,0} },
3099+ { "groupby", activate_radio , "s", "'type'", NULL, {0,0,0} }
3100+};
3101+
3102+
3103+static void ui_panel_accounts_setup(struct hbfile_data *data)
3104+{
3105+GAction *action;
3106+GVariant *new_state;
3107+
3108+ if( !G_IS_SIMPLE_ACTION_GROUP(data->action_group_acc) )
3109+ return;
3110+
3111+ action = g_action_map_lookup_action (G_ACTION_MAP (data->action_group_acc), "showall");
3112+ if( action )
3113+ {
3114+ new_state = g_variant_new_boolean (data->showall);
3115+ g_simple_action_set_state (G_SIMPLE_ACTION(action), new_state);
3116+ }
3117+
3118+ action = g_action_map_lookup_action (G_ACTION_MAP (data->action_group_acc), "groupby");
3119+ if( action )
3120+ {
3121+ const gchar *value = (PREFS->pnl_acc_show_by == DSPACC_GROUP_BY_TYPE) ? "type" : "bank";
3122+ new_state = g_variant_new_string (value);
3123+ g_simple_action_set_state (G_SIMPLE_ACTION (action), new_state);
3124+ }
3125+
3126+}
3127+
3128+
3129+static GtkWidget *ui_mainwindow_create_youraccounts(struct hbfile_data *data)
3130+{
3131+GtkWidget *panel, *label, *widget, *sw, *tbar, *hbox, *image;
3132+GtkToolItem *toolitem;
3133+
3134+ panel = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3135+ gtk_container_set_border_width(GTK_CONTAINER(panel), SPACING_SMALL);
3136+
3137+ sw = gtk_scrolled_window_new (NULL, NULL);
3138+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_ETCHED_IN);
3139+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3140+ gtk_box_pack_start (GTK_BOX (panel), sw, TRUE, TRUE, 0);
3141+ widget = (GtkWidget *)create_list_account();
3142+ data->LV_acc = widget;
3143+ gtk_container_add (GTK_CONTAINER (sw), widget);
3144+
3145+ //list toolbar
3146+ tbar = gtk_toolbar_new();
3147+ gtk_toolbar_set_icon_size (GTK_TOOLBAR(tbar), GTK_ICON_SIZE_MENU);
3148+ gtk_toolbar_set_style(GTK_TOOLBAR(tbar), GTK_TOOLBAR_ICONS);
3149+ gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
3150+ gtk_box_pack_start (GTK_BOX (panel), tbar, FALSE, FALSE, 0);
3151+
3152+ label = make_label_group(_("Your accounts"));
3153+ toolitem = gtk_tool_item_new();
3154+ gtk_container_add (GTK_CONTAINER(toolitem), label);
3155+ gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3156+
3157+ toolitem = gtk_separator_tool_item_new ();
3158+ gtk_tool_item_set_expand (toolitem, TRUE);
3159+ gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
3160+ gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3161+
3162+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
3163+ toolitem = gtk_tool_item_new();
3164+ gtk_container_add (GTK_CONTAINER(toolitem), hbox);
3165+ gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3166+
3167+ widget = make_image_button(ICONNAME_HB_BUTTON_EXPAND, _("Expand all"));
3168+ data->BT_expandall = widget;
3169+ gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
3170+
3171+ widget = make_image_button(ICONNAME_HB_BUTTON_COLLAPSE, _("Collapse all"));
3172+ data->BT_collapseall = widget;
3173+ gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
3174+
3175+ toolitem = gtk_separator_tool_item_new ();
3176+ gtk_tool_item_set_expand (toolitem, FALSE);
3177+ gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
3178+ gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3179+
3180+
3181+ //gmenu test (see test folder into gtk)
3182+GMenu *menu, *section;
3183+
3184+ menu = g_menu_new ();
3185+ //g_menu_append (menumodel, "About", "actions.undo");
3186+ //g_menu_append (menumodel, "Test", "actions.redo");
3187+ section = g_menu_new ();
3188+ g_menu_append (section, _("Show all"), "actions.showall");
3189+ g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
3190+ g_object_unref (section);
3191+
3192+ section = g_menu_new ();
3193+ g_menu_append (section, _("By type"), "actions.groupby::type");
3194+ g_menu_append (section, _("By institition"), "actions.groupby::bank");
3195+ g_menu_append_section(menu, NULL, G_MENU_MODEL(section));
3196+ g_object_unref (section);
3197+
3198+
3199+ GSimpleActionGroup *group = g_simple_action_group_new ();
3200+ data->action_group_acc = group;
3201+ g_action_map_add_action_entries (G_ACTION_MAP (group), actions, G_N_ELEMENTS (actions), data);
3202+
3203+
3204+ widget = gtk_menu_button_new();
3205+ gtk_menu_button_set_direction (GTK_MENU_BUTTON(widget), GTK_ARROW_UP);
3206+ gtk_widget_set_halign (widget, GTK_ALIGN_END);
3207+ image = gtk_image_new_from_icon_name (ICONNAME_EMBLEM_SYSTEM, GTK_ICON_SIZE_MENU);
3208+ g_object_set (widget, "image", image, NULL);
3209+
3210+ toolitem = gtk_tool_item_new();
3211+ gtk_container_add (GTK_CONTAINER(toolitem), widget);
3212+ gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3213+
3214+ gtk_widget_insert_action_group (widget, "actions", G_ACTION_GROUP(group));
3215+ gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (widget), G_MENU_MODEL (menu));
3216+
3217+ return panel;
3218+}
3219+
3220+
3221+static GtkWidget *ui_mainwindow_create_topspending(struct hbfile_data *data)
3222+{
3223+GtkWidget *panel, *hbox, *tbar;
3224+GtkWidget *label, *widget;
3225+GtkToolItem *toolitem;
3226+
3227+ widget = (GtkWidget *)create_list_topspending();
3228+ data->LV_top = widget;
3229+
3230+ panel = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3231+ gtk_container_set_border_width(GTK_CONTAINER(panel), SPACING_SMALL);
3232+ data->GR_top = panel;
3233+
3234+ /* chart + listview */
3235+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
3236+ gtk_box_pack_start (GTK_BOX (panel), hbox, TRUE, TRUE, 0);
3237+
3238+ widget = gtk_chart_new(CHART_TYPE_PIE);
3239+ data->RE_pie = widget;
3240+ gtk_chart_set_minor_prefs(GTK_CHART(widget), PREFS->euro_value, PREFS->minor_cur.symbol);
3241+ gtk_chart_show_legend(GTK_CHART(data->RE_pie), TRUE, TRUE);
3242+ gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
3243+
3244+ //list toolbar
3245+ tbar = gtk_toolbar_new();
3246+ gtk_toolbar_set_icon_size (GTK_TOOLBAR(tbar), GTK_ICON_SIZE_MENU);
3247+ gtk_toolbar_set_style(GTK_TOOLBAR(tbar), GTK_TOOLBAR_ICONS);
3248+ gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
3249+ gtk_box_pack_start (GTK_BOX (panel), tbar, FALSE, FALSE, 0);
3250+
3251+ label = make_label_group(_("Where your money goes"));
3252+ toolitem = gtk_tool_item_new();
3253+ gtk_container_add (GTK_CONTAINER(toolitem), label);
3254+ gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3255+
3256+ toolitem = gtk_separator_tool_item_new ();
3257+ gtk_tool_item_set_expand (toolitem, TRUE);
3258+ gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
3259+ gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3260+
3261+ /* total + date range */
3262+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, SPACING_SMALL);
3263+ toolitem = gtk_tool_item_new();
3264+ gtk_container_add (GTK_CONTAINER(toolitem), hbox);
3265+ gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3266+
3267+ data->CY_range = make_daterange(label, FALSE);
3268+ gtk_box_pack_end (GTK_BOX (hbox), data->CY_range, FALSE, FALSE, 0);
3269+
3270+ widget = make_radio(CYA_CATSUBCAT, TRUE, GTK_ORIENTATION_HORIZONTAL);
3271+ data->RA_type = widget;
3272+ gtk_box_pack_end (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
3273+
3274+ return panel;
3275+}
3276+
3277+
3278+static GtkWidget *ui_mainwindow_scheduled_create(struct hbfile_data *data)
3279+{
3280+GtkWidget *panel, *hbox, *vbox, *bbox, *sw, *tbar;
3281+GtkWidget *label, *widget;
3282+GtkToolItem *toolitem;
3283+
3284+ panel = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3285+ gtk_container_set_border_width(GTK_CONTAINER(panel), SPACING_SMALL);
3286+ data->GR_upc = panel;
3287+
3288+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3289+ //gtk_widget_set_margin_top(GTK_WIDGET(vbox), 0);
3290+ //gtk_widget_set_margin_bottom(GTK_WIDGET(vbox), SPACING_SMALL);
3291+ //gtk_widget_set_margin_start(GTK_WIDGET(vbox), 2*SPACING_SMALL);
3292+ //gtk_widget_set_margin_end(GTK_WIDGET(vbox), SPACING_SMALL);
3293+ gtk_box_pack_start (GTK_BOX (panel), vbox, TRUE, TRUE, 0);
3294+
3295+ sw = gtk_scrolled_window_new (NULL, NULL);
3296+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_ETCHED_IN);
3297+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3298+ gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
3299+
3300+ widget = (GtkWidget *)create_list_upcoming();
3301+ data->LV_upc = widget;
3302+ gtk_container_add (GTK_CONTAINER (sw), widget);
3303+
3304+ tbar = gtk_toolbar_new();
3305+ gtk_toolbar_set_icon_size (GTK_TOOLBAR(tbar), GTK_ICON_SIZE_MENU);
3306+ gtk_toolbar_set_style(GTK_TOOLBAR(tbar), GTK_TOOLBAR_ICONS);
3307+ gtk_style_context_add_class (gtk_widget_get_style_context (tbar), GTK_STYLE_CLASS_INLINE_TOOLBAR);
3308+ gtk_box_pack_start (GTK_BOX (vbox), tbar, FALSE, FALSE, 0);
3309+
3310+ label = make_label_group(_("Scheduled transactions"));
3311+ toolitem = gtk_tool_item_new();
3312+ gtk_container_add (GTK_CONTAINER(toolitem), label);
3313+ gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3314+
3315+ toolitem = gtk_separator_tool_item_new ();
3316+ gtk_tool_item_set_expand (toolitem, FALSE);
3317+ gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
3318+ gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3319+
3320+
3321+ bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
3322+ toolitem = gtk_tool_item_new();
3323+ gtk_container_add (GTK_CONTAINER(toolitem), bbox);
3324+ gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3325+
3326+ widget = gtk_button_new_with_label(_("Skip"));
3327+ data->BT_sched_skip = widget;
3328+ gtk_box_pack_start (GTK_BOX (bbox), widget, FALSE, FALSE, 0);
3329+
3330+ widget = gtk_button_new_with_label(_("Edit & Post"));
3331+ data->BT_sched_editpost = widget;
3332+ gtk_box_pack_start (GTK_BOX (bbox), widget, FALSE, FALSE, 0);
3333+
3334+ //TRANSLATORS: Posting a scheduled transaction is the action to materialize it into its target account.
3335+ //TRANSLATORS: Before that action the automated transaction occurrence is pending and not yet really existing.
3336+ widget = gtk_button_new_with_label (_("Post"));
3337+ data->BT_sched_post = widget;
3338+ gtk_box_pack_start (GTK_BOX (bbox), widget, FALSE, FALSE, 0);
3339+
3340+ toolitem = gtk_separator_tool_item_new ();
3341+ gtk_tool_item_set_expand (toolitem, FALSE);
3342+ gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE);
3343+ gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3344+
3345+ hbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3346+ gtk_widget_set_valign (hbox, GTK_ALIGN_CENTER);
3347+ toolitem = gtk_tool_item_new();
3348+ gtk_container_add (GTK_CONTAINER(toolitem), hbox);
3349+ gtk_toolbar_insert(GTK_TOOLBAR(tbar), GTK_TOOL_ITEM(toolitem), -1);
3350+
3351+ label = make_label(_("maximum post date"), 0.0, 0.7);
3352+ gtk_widget_set_halign (label, GTK_ALIGN_CENTER);
3353+ gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
3354+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
3355+
3356+ label = make_label(NULL, 0.0, 0.7);
3357+ data->LB_maxpostdate = label;
3358+ gtk_widget_set_halign (label, GTK_ALIGN_CENTER);
3359+ gimp_label_set_attributes (GTK_LABEL (label), PANGO_ATTR_SCALE, PANGO_SCALE_SMALL, -1);
3360+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
3361+
3362+ return panel;
3363+}
3364+
3365+
3366+/*
3367+** the window creation
3368+*/
3369+GtkWidget *create_hbfile_window(GtkWidget *do_widget)
3370+{
3371+struct hbfile_data *data;
3372+struct WinGeometry *wg;
3373+GtkWidget *mainvbox, *vbox, *vpaned, *hpaned;
3374+GtkWidget *widget;
3375+GtkWidget *window;
3376+GtkAction *action;
3377+
3378+ DB( g_print("\n[ui-mainwindow] create main window\n") );
3379+
3380+ data = g_malloc0(sizeof(struct hbfile_data));
3381+ if(!data) return NULL;
3382+
3383+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3384+
3385+ //store our window private data
3386+ g_object_set_data(G_OBJECT(window), "inst_data", (gpointer)data);
3387+ DB( g_print(" - new window=%p, inst_data=%p\n", window, data) );
3388+
3389+ // this is our mainwindow, so store it to GLOBALS data
3390+ data->window = window;
3391+ GLOBALS->mainwindow = window;
3392+
3393+ mainvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3394+ gtk_container_add (GTK_CONTAINER (window), mainvbox);
3395+
3396+ ui_mainwindow_create_menu_bar_and_toolbar (data, mainvbox);
3397+
3398+#if HB_UNSTABLE_SHOW == TRUE
3399+GtkWidget *bar, *label;
3400+
3401+ bar = gtk_info_bar_new ();
3402+ gtk_box_pack_start (GTK_BOX (mainvbox), bar, FALSE, FALSE, 0);
3403+ gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_WARNING);
3404+ label = make_label(NULL, 0.5, 0.5);
3405+ gtk_label_set_markup (GTK_LABEL(label), "Unstable Development Version");
3406+ gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (bar))), label, FALSE, FALSE, 0);
3407+#endif
3408+
3409+ /* Add the main area */
3410+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
3411+ //gtk_container_set_border_width (GTK_CONTAINER(vbox), SPACING_MEDIUM);
3412+ gtk_box_pack_start (GTK_BOX (mainvbox), vbox, TRUE, TRUE, 0);
3413+
3414+ vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
3415+ data->vpaned = vpaned;
3416+ gtk_box_pack_start (GTK_BOX (vbox), vpaned, TRUE, TRUE, 0);
3417+
3418+ hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
3419+ data->hpaned = hpaned;
3420+ gtk_paned_pack1 (GTK_PANED(vpaned), hpaned, FALSE, FALSE);
3421+
3422+ widget = ui_mainwindow_scheduled_create(data);
3423+ gtk_paned_pack2 (GTK_PANED(vpaned), widget, TRUE, FALSE);
3424+
3425+ widget = ui_mainwindow_create_youraccounts(data);
3426+ //gtk_widget_set_size_request (widget, 100, -1);
3427+ gtk_paned_pack1 (GTK_PANED(hpaned), widget, FALSE, FALSE);
3428+
3429+ widget = ui_mainwindow_create_topspending(data);
3430+ //gtk_widget_set_size_request (widget, -1, 100);
3431+ gtk_paned_pack2 (GTK_PANED(hpaned), widget, TRUE, FALSE);
3432+
3433+
3434+ //setup, init and show window
3435+ wg = &PREFS->wal_wg;
3436+ if(wg->s == 0)
3437+ {
3438+ gtk_window_move(GTK_WINDOW(window), wg->l, wg->t);
3439+ gtk_window_resize(GTK_WINDOW(window), wg->w, wg->h);
3440+ }
3441+ else
3442+ gtk_window_maximize(GTK_WINDOW(window));
3443+
3444+ gtk_widget_show_all (window);
3445+
3446+ //#1662197/1660910 moved after resize/show
3447+ DB( g_print(" - vpaned=%d hpaned=%d\n", PREFS->wal_vpaned, PREFS->wal_hpaned) );
3448+
3449+ if(PREFS->wal_hpaned > 0)
3450+ gtk_paned_set_position(GTK_PANED(data->hpaned), PREFS->wal_hpaned);
3451+ if(PREFS->wal_vpaned > 0)
3452+ gtk_paned_set_position(GTK_PANED(data->vpaned), PREFS->wal_vpaned);
3453+
3454+ //todo: move this elsewhere
3455+ DB( g_print(" - setup stuff\n") );
3456+
3457+ data->filter = da_filter_malloc();
3458+ filter_default_all_set(data->filter);
3459+ gtk_combo_box_set_active(GTK_COMBO_BOX(data->CY_range), PREFS->date_range_wal);
3460+
3461+ action = gtk_ui_manager_get_action(data->manager, "/MenuBar/ViewMenu/Toolbar");
3462+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), PREFS->wal_toolbar);
3463+ action = gtk_ui_manager_get_action(data->manager, "/MenuBar/ViewMenu/Spending");
3464+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), PREFS->wal_spending);
3465+ action = gtk_ui_manager_get_action(data->manager, "/MenuBar/ViewMenu/Upcoming");
3466+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), PREFS->wal_upcoming);
3467+
3468+ /* Drag and drop support, set targets to NULL because we add the
3469+ default uri_targets below */
3470+
3471+ /* support for opening a file by dragging onto the project window */
3472+ gtk_drag_dest_set (GTK_WIDGET (window),
3473+ GTK_DEST_DEFAULT_ALL,
3474+ drop_types,
3475+ G_N_ELEMENTS (drop_types),
3476+ GDK_ACTION_COPY);
3477+
3478+ g_signal_connect (G_OBJECT (window), "drag-data-received",
3479+ G_CALLBACK (ui_mainwindow_drag_data_received), window);
3480+
3481+
3482+
3483+ //connect all our signals
3484+ DB( g_print(" - connect signals\n") );
3485+
3486+
3487+ g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_acc)), "changed", G_CALLBACK (ui_mainwindow_selection), NULL);
3488+ g_signal_connect (GTK_TREE_VIEW(data->LV_acc ), "row-activated", G_CALLBACK (ui_mainwindow_onRowActivated), GINT_TO_POINTER(2));
3489+ g_signal_connect (G_OBJECT (data->BT_expandall ), "clicked" , G_CALLBACK (ui_panel_accounts_expand_all), NULL);
3490+ g_signal_connect (G_OBJECT (data->BT_collapseall), "clicked" , G_CALLBACK (ui_panel_accounts_collapse_all), NULL);
3491+
3492+ g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(data->LV_upc)), "changed", G_CALLBACK (ui_mainwindow_scheduled_selection_cb), NULL);
3493+ g_signal_connect (GTK_TREE_VIEW(data->LV_upc), "row-activated", G_CALLBACK (ui_mainwindow_scheduled_onRowActivated), NULL);
3494+ g_signal_connect (G_OBJECT (data->BT_sched_skip), "clicked", G_CALLBACK (ui_mainwindow_scheduled_skip_cb), data);
3495+ g_signal_connect (G_OBJECT (data->BT_sched_editpost), "clicked", G_CALLBACK (ui_mainwindow_scheduled_editpost_cb), data);
3496+ g_signal_connect (G_OBJECT (data->BT_sched_post), "clicked", G_CALLBACK (ui_mainwindow_scheduled_post_cb), data);
3497+
3498+ widget = radio_get_nth_widget(GTK_CONTAINER(data->RA_type), 1);
3499+ if(widget)
3500+ g_signal_connect (widget, "toggled", G_CALLBACK (ui_mainwindow_populate_topspending), &data);
3501+
3502+ g_signal_connect (data->CY_range, "changed", G_CALLBACK (ui_mainwindow_populate_topspending), NULL);
3503+
3504+
3505+ /* GtkWindow events */
3506+ g_signal_connect (window, "delete-event", G_CALLBACK (ui_mainwindow_dispose), (gpointer)data);
3507+ g_signal_connect (window, "destroy", G_CALLBACK (ui_mainwindow_destroy), NULL);
3508+
3509+ //gtk_action_group_set_sensitive(data->actions, FALSE);
3510+
3511+ return window;
3512+}
3513+
3514
3515=== added file 'src/ui-budget-tabview.c'
3516--- src/ui-budget-tabview.c 1970-01-01 00:00:00 +0000
3517+++ src/ui-budget-tabview.c 2019-07-22 20:14:59 +0000
3518@@ -0,0 +1,2843 @@
3519+/* HomeBank -- Free, easy, personal accounting for everyone.
3520+ * Copyright (C) 2018-2019 Adrien Dorsaz <adrien@adorsaz.ch>
3521+ *
3522+ * This file is part of HomeBank.
3523+ *
3524+ * HomeBank is free software; you can redistribute it and/or modify
3525+ * it under the terms of the GNU General Public License as published by
3526+ * the Free Software Foundation; either version 2 of the License, or
3527+ * (at your option) any later version.
3528+ *
3529+ * HomeBank is distributed in the hope that it will be useful,
3530+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3531+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3532+ * GNU General Public License for more details.
3533+ *
3534+ * You should have received a copy of the GNU General Public License
3535+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3536+ */
3537+
3538+
3539+#include "homebank.h"
3540+#include "hb-misc.h"
3541+#include "dsp_mainwindow.h"
3542+#include "hb-category.h"
3543+#include "ui-budget-tabview.h"
3544+
3545+/****************************************************************************/
3546+/* Implementation notes */
3547+/****************************************************************************/
3548+/*
3549+ * This dialog allows user to manage its budget within a GtkTreeView.
3550+ *
3551+ * The view rows are separated in three main tree roots:
3552+ * - Income: contains all Homebank categories of income type (see GF_INCOME)
3553+ * - Expense: contains all Homebank categories of expense type
3554+ * - Total: contains 3 sub-rows:
3555+ * - Income: sum all amounts of the Income root
3556+ * - Expense: sum all amounts of the Expense root
3557+ * - Summary: difference between the two above sub-rows
3558+ *
3559+ * The view columns contain:
3560+ * - Category: Homebank categories organised in hierarchy
3561+ * according to the main tree roots above and the categories hierarchy
3562+ *
3563+ * - Annual Total: sum all amounts of the year for the category
3564+ *
3565+ * - Monthly Average: average of the amounts for the category
3566+ *
3567+ * - Monthly: set the monthly amount when the Same flag is active
3568+ * - That column contains a toggle check box to enable or not monthly values
3569+ * Check it to disable the GF_CUSTOM flag of Homebank categories
3570+ * "Does this category has same amount planned every month ?"
3571+ *
3572+ * - 12 columns for each month of the year containing their specific amount
3573+ *
3574+ * The dialog shows 3 radio buttons on top to choose between 3 viewing modes:
3575+ * - Summary: show Homebank categories with budget set or set with GF_FORCED
3576+ * - Expense: show all available Homebank categories of expense type
3577+ * - Income: show all available Homebank categories of income type
3578+ *
3579+ */
3580+
3581+/****************************************************************************/
3582+/* Debug macros */
3583+/****************************************************************************/
3584+#define MYDEBUG 0
3585+
3586+#if MYDEBUG
3587+#define DB(x) (x);
3588+#else
3589+#define DB(x);
3590+#endif
3591+
3592+/* Global data */
3593+extern struct HomeBank *GLOBALS;
3594+extern struct Preferences *PREFS;
3595+
3596+static gchar *UI_BUD_TABVIEW_MONTHS[] = {
3597+ N_("Jan"), N_("Feb"), N_("Mar"),
3598+ N_("Apr"), N_("May"), N_("Jun"),
3599+ N_("Jul"), N_("Aug"), N_("Sept"),
3600+ N_("Oct"), N_("Nov"), N_("Dec"),
3601+ NULL};
3602+
3603+/* The different view mode available */
3604+static gchar *UI_BUD_TABVIEW_VIEW_MODE[] = {
3605+ N_("Summary"),
3606+ N_("Expense"),
3607+ N_("Income"),
3608+ NULL
3609+};
3610+
3611+/* These values has to correspond to UI_BUD_TABVIEW_VIEW_MODE[] */
3612+enum ui_bud_tabview_view_mode
3613+{
3614+ UI_BUD_TABVIEW_VIEW_SUMMARY = 0,
3615+ UI_BUD_TABVIEW_VIEW_EXPENSE,
3616+ UI_BUD_TABVIEW_VIEW_INCOME
3617+};
3618+typedef enum ui_bud_tabview_view_mode ui_bud_tabview_view_mode_t;
3619+
3620+/* These values corresponds to the return of category_type_get from hb-category */
3621+enum ui_bud_tabview_cat_type
3622+{
3623+ UI_BUD_TABVIEW_CAT_TYPE_EXPENSE = -1,
3624+ UI_BUD_TABVIEW_CAT_TYPE_NONE = 0, // Not real category type: used to retrieve tree roots
3625+ UI_BUD_TABVIEW_CAT_TYPE_INCOME = 1
3626+};
3627+typedef enum ui_bud_tabview_cat_type ui_bud_tabview_cat_type_t;
3628+
3629+/* enum for the Budget Tree Store model */
3630+enum ui_bud_tabview_store
3631+{
3632+ UI_BUD_TABVIEW_CATEGORY_KEY = 0,
3633+ UI_BUD_TABVIEW_CATEGORY_NAME,
3634+ UI_BUD_TABVIEW_CATEGORY_FULLNAME,
3635+ UI_BUD_TABVIEW_CATEGORY_TYPE,
3636+ UI_BUD_TABVIEW_IS_ROOT, // To retrieve easier the 3 main tree roots
3637+ UI_BUD_TABVIEW_IS_TOTAL, // To retrieve rows inside the Total root
3638+ UI_BUD_TABVIEW_IS_CHILD_HEADER, // The row corresponds to the head child which is shown before the separator
3639+ UI_BUD_TABVIEW_IS_SEPARATOR, // Row to just display a separator in Tree View
3640+ UI_BUD_TABVIEW_IS_MONITORING_FORCED,
3641+ UI_BUD_TABVIEW_IS_SAME_AMOUNT,
3642+ UI_BUD_TABVIEW_IS_SUB_CATEGORY,
3643+ UI_BUD_TABVIEW_HAS_BUDGET,
3644+ UI_BUD_TABVIEW_SAME_AMOUNT,
3645+ UI_BUD_TABVIEW_JANUARY,
3646+ UI_BUD_TABVIEW_FEBRUARY,
3647+ UI_BUD_TABVIEW_MARCH,
3648+ UI_BUD_TABVIEW_APRIL,
3649+ UI_BUD_TABVIEW_MAY,
3650+ UI_BUD_TABVIEW_JUNE,
3651+ UI_BUD_TABVIEW_JULY,
3652+ UI_BUD_TABVIEW_AUGUST,
3653+ UI_BUD_TABVIEW_SEPTEMBER,
3654+ UI_BUD_TABVIEW_OCTOBER,
3655+ UI_BUD_TABVIEW_NOVEMBER,
3656+ UI_BUD_TABVIEW_DECEMBER,
3657+ UI_BUD_TABVIEW_NUMBER_COLOMNS
3658+};
3659+typedef enum ui_bud_tabview_store ui_bud_tabview_store_t;
3660+
3661+// Retrieve a row iterator according to specific criterias
3662+const struct ui_bud_tabview_search_criteria
3663+{
3664+ // Search by non-zero category key
3665+ guint32 row_category_key;
3666+ // Search by other criterias
3667+ ui_bud_tabview_cat_type_t row_category_type;
3668+ gboolean row_is_root;
3669+ gboolean row_is_total;
3670+ // Found iterator, NULL if not found
3671+ GtkTreeIter *iterator;
3672+} ui_bud_tabview_search_criteria_default = {0, UI_BUD_TABVIEW_CAT_TYPE_NONE, FALSE, FALSE, NULL} ;
3673+typedef struct ui_bud_tabview_search_criteria ui_bud_tabview_search_criteria_t;
3674+
3675+/*
3676+ * Local headers
3677+ **/
3678+
3679+// GtkTreeStore model
3680+static gboolean ui_bud_tabview_model_search_iterator (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, ui_bud_tabview_search_criteria_t *search);
3681+static void ui_bud_tabview_model_add_category_with_lineage(GtkTreeStore *budget, GtkTreeIter *balanceIter, guint32 *key_category);
3682+static void ui_bud_tabview_model_collapse (GtkTreeView *view);
3683+static void ui_bud_tabview_model_insert_roots(GtkTreeStore* budget);
3684+static void ui_bud_tabview_model_update_monthly_total(GtkTreeStore* budget);
3685+static gboolean ui_bud_tabview_model_row_filter (GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
3686+#if HB_BUD_TABVIEW_EDIT_ENABLE
3687+static gboolean ui_bud_tabview_model_row_merge_filter_with_headers (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
3688+static gboolean ui_bud_tabview_model_row_filter_parents (GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
3689+#endif
3690+static gint ui_bud_tabview_model_row_sort (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data);
3691+static GtkTreeModel * ui_bud_tabview_model_new ();
3692+
3693+// GtkTreeView widget
3694+static void ui_bud_tabview_view_display_category_name (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
3695+static void ui_bud_tabview_view_display_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
3696+static void ui_bud_tabview_view_display_is_same_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
3697+static void ui_bud_tabview_view_display_annual_total (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
3698+static void ui_bud_tabview_view_display_monthly_average(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
3699+static void ui_bud_tabview_view_toggle (gpointer user_data, ui_bud_tabview_view_mode_t view_mode);
3700+static gboolean ui_bud_tabview_view_search (GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter, gpointer data);
3701+#if HB_BUD_TABVIEW_EDIT_ENABLE
3702+static gboolean ui_bud_tabview_view_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
3703+static void ui_bud_tabview_view_on_select(GtkTreeSelection *treeselection, gpointer user_data);
3704+#endif
3705+static GtkWidget *ui_bud_tabview_view_new (gpointer user_data);
3706+
3707+// UI actions
3708+#if HB_BUD_TABVIEW_EDIT_ENABLE
3709+static void ui_bud_tabview_cell_update_category(GtkCellRendererText *renderer, gchar *filter_path, gchar *new_text, gpointer user_data);
3710+static void ui_bud_tabview_cell_update_amount(GtkCellRendererText *renderer, gchar *filter_path, gchar *new_text, gpointer user_data);
3711+static void ui_bud_tabview_cell_update_is_same_amount(GtkCellRendererText *renderer, gchar *filter_path, gpointer user_data);
3712+#endif
3713+static void ui_bud_tabview_view_update_mode (GtkToggleButton *button, gpointer user_data);
3714+static void ui_bud_tabview_view_expand (GtkButton *button, gpointer user_data);
3715+static void ui_bud_tabview_view_collapse (GtkButton *button, gpointer user_data);
3716+#if HB_BUD_TABVIEW_EDIT_ENABLE
3717+static gboolean ui_bud_tabview_get_selected_category (GtkTreeModel **budget, GtkTreeIter *iter, Category **category, ui_bud_tabview_data_t *data);
3718+static void ui_bud_tabview_category_add_full_filled (GtkWidget *source, gpointer user_data);
3719+static void ui_bud_tabview_category_add (GtkButton *button, gpointer user_data);
3720+static void ui_bud_tabview_category_delete (GtkButton *button, gpointer user_data);
3721+static void ui_bud_tabview_category_merge_full_filled (GtkWidget *source, gpointer user_data);
3722+static void ui_bud_tabview_category_merge (GtkButton *button, gpointer user_data);
3723+static void ui_bud_tabview_category_reset (GtkButton *button, gpointer user_data);
3724+#endif
3725+static gboolean ui_bud_tabview_on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data);
3726+static void ui_bud_tabview_dialog_close(ui_bud_tabview_data_t *data, gint response);
3727+
3728+/**
3729+ * GtkTreeStore model
3730+ **/
3731+
3732+// Look for category by deterministic characteristics
3733+// Only categories with specific characteristics can be easily found
3734+// like roots, total rows and categories with real key id
3735+// You are responsible to g_free iterator
3736+static gboolean ui_bud_tabview_model_search_iterator (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, ui_bud_tabview_search_criteria_t *search)
3737+{
3738+guint32 category_key;
3739+ui_bud_tabview_cat_type_t category_type;
3740+gboolean is_found = FALSE, is_root, is_total, is_separator;
3741+
3742+ search->iterator = NULL;
3743+
3744+ gtk_tree_model_get (model, iter,
3745+ UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
3746+ UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
3747+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
3748+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
3749+ UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
3750+ -1);
3751+
3752+ if (search->row_category_key > 0 // Look for iter of real category row
3753+ && category_key == search->row_category_key
3754+ && !(is_total)
3755+ && !(is_root)
3756+ )
3757+ {
3758+ DB(g_print("\tFound row with key %d\n", category_key));
3759+ is_found = TRUE;
3760+ }
3761+ else if (search->row_category_key == 0 // Look for iter of fake category row
3762+ && is_root == search->row_is_root
3763+ && is_total == search->row_is_total
3764+ && category_type == search->row_category_type
3765+ && !is_separator
3766+ )
3767+ {
3768+ DB(g_print("\tFound row with is_root = %d, is_total %d, type = %d\n", is_root, is_total, category_type));
3769+ is_found = TRUE;
3770+ }
3771+
3772+ // If found, save result to struct
3773+ if (is_found)
3774+ {
3775+ search->iterator = g_malloc0(sizeof(GtkTreeIter));
3776+ *search->iterator = *iter;
3777+ }
3778+
3779+ return is_found;
3780+}
3781+
3782+/* Recursive function which add a new row in the budget model with all its ancestors */
3783+static void ui_bud_tabview_model_add_category_with_lineage(GtkTreeStore *budget, GtkTreeIter *balanceIter, guint32 *key_category)
3784+{
3785+GtkTreeIter child;
3786+GtkTreeIter *parent;
3787+Category *bdg_category;
3788+gboolean cat_is_same_amount;
3789+ui_bud_tabview_search_criteria_t parent_search = ui_bud_tabview_search_criteria_default;
3790+
3791+ bdg_category = da_cat_get(*key_category);
3792+
3793+ if (bdg_category == NULL)
3794+ {
3795+ return;
3796+ }
3797+
3798+ cat_is_same_amount = (! (bdg_category->flags & GF_CUSTOM));
3799+
3800+ /* Check if parent category already exists */
3801+ parent_search.row_category_key = bdg_category->parent;
3802+
3803+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
3804+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
3805+ &parent_search);
3806+
3807+ if (bdg_category->parent == 0)
3808+ {
3809+ // If we are one of the oldest parent, stop recursion
3810+ gtk_tree_store_insert (
3811+ budget,
3812+ &child,
3813+ balanceIter,
3814+ -1);
3815+ }
3816+ else
3817+ {
3818+ if (parent_search.iterator)
3819+ {
3820+ DB(g_print("\tRecursion optimisation: parent key %d already exists\n", parent_search.row_category_key));
3821+ // If parent already exists, stop recursion
3822+ parent = parent_search.iterator;
3823+ }
3824+ else
3825+ {
3826+ // Parent has not been found, ask to create it first
3827+ ui_bud_tabview_model_add_category_with_lineage(budget, balanceIter, &(bdg_category->parent));
3828+
3829+ // Now, we are sure parent exists, look for it again
3830+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
3831+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
3832+ &parent_search);
3833+
3834+ parent = parent_search.iterator;
3835+ }
3836+
3837+ gtk_tree_store_insert (
3838+ budget,
3839+ &child,
3840+ parent,
3841+ -1);
3842+ }
3843+
3844+ DB(g_print("insert new category %s (key: %d, type: %d)\n",
3845+ bdg_category->name, bdg_category->key, category_type_get (bdg_category)));
3846+
3847+ gtk_tree_store_set(
3848+ budget,
3849+ &child,
3850+ UI_BUD_TABVIEW_CATEGORY_KEY, bdg_category->key,
3851+ UI_BUD_TABVIEW_CATEGORY_NAME, bdg_category->name,
3852+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, bdg_category->fullname,
3853+ UI_BUD_TABVIEW_CATEGORY_TYPE, category_type_get (bdg_category),
3854+ UI_BUD_TABVIEW_IS_MONITORING_FORCED, (bdg_category->flags & GF_FORCED),
3855+ UI_BUD_TABVIEW_IS_ROOT, FALSE,
3856+ UI_BUD_TABVIEW_IS_SAME_AMOUNT, cat_is_same_amount,
3857+ UI_BUD_TABVIEW_IS_TOTAL, FALSE,
3858+ UI_BUD_TABVIEW_IS_SUB_CATEGORY, bdg_category->parent != 0,
3859+ UI_BUD_TABVIEW_HAS_BUDGET, (bdg_category->flags & GF_BUDGET),
3860+ UI_BUD_TABVIEW_SAME_AMOUNT, bdg_category->budget[0],
3861+ UI_BUD_TABVIEW_JANUARY, bdg_category->budget[1],
3862+ UI_BUD_TABVIEW_FEBRUARY, bdg_category->budget[2],
3863+ UI_BUD_TABVIEW_MARCH, bdg_category->budget[3],
3864+ UI_BUD_TABVIEW_APRIL, bdg_category->budget[4],
3865+ UI_BUD_TABVIEW_MAY, bdg_category->budget[5],
3866+ UI_BUD_TABVIEW_JUNE, bdg_category->budget[6],
3867+ UI_BUD_TABVIEW_JULY, bdg_category->budget[7],
3868+ UI_BUD_TABVIEW_AUGUST, bdg_category->budget[8],
3869+ UI_BUD_TABVIEW_SEPTEMBER, bdg_category->budget[9],
3870+ UI_BUD_TABVIEW_OCTOBER, bdg_category->budget[10],
3871+ UI_BUD_TABVIEW_NOVEMBER, bdg_category->budget[11],
3872+ UI_BUD_TABVIEW_DECEMBER, bdg_category->budget[12],
3873+ -1);
3874+
3875+ // Always add child header and separator
3876+ parent = gtk_tree_iter_copy(&child);
3877+ gtk_tree_store_insert_with_values(
3878+ budget,
3879+ &child,
3880+ parent,
3881+ -1,
3882+ UI_BUD_TABVIEW_CATEGORY_KEY, bdg_category->key,
3883+ UI_BUD_TABVIEW_CATEGORY_NAME, bdg_category->name,
3884+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, bdg_category->fullname,
3885+ UI_BUD_TABVIEW_CATEGORY_TYPE, category_type_get (bdg_category),
3886+ UI_BUD_TABVIEW_IS_CHILD_HEADER, TRUE,
3887+ -1);
3888+
3889+ gtk_tree_store_insert_with_values(
3890+ budget,
3891+ &child,
3892+ parent,
3893+ -1,
3894+ UI_BUD_TABVIEW_CATEGORY_KEY, bdg_category->key,
3895+ UI_BUD_TABVIEW_CATEGORY_TYPE, category_type_get (bdg_category),
3896+ UI_BUD_TABVIEW_IS_SEPARATOR, TRUE,
3897+ -1);
3898+
3899+ gtk_tree_iter_free(parent);
3900+
3901+ g_free(parent_search.iterator);
3902+
3903+ return;
3904+}
3905+
3906+// Collapse all categories except root
3907+static void ui_bud_tabview_model_collapse (GtkTreeView *view)
3908+{
3909+GtkTreeModel *budget;
3910+GtkTreePath *path;
3911+ui_bud_tabview_search_criteria_t root_search = ui_bud_tabview_search_criteria_default;
3912+
3913+ budget = gtk_tree_view_get_model (view);
3914+
3915+ gtk_tree_view_collapse_all(view);
3916+
3917+ // Keep root categories expanded
3918+
3919+ // Retrieve income root
3920+ root_search.row_is_root = TRUE;
3921+ root_search.row_is_total = FALSE;
3922+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_INCOME;
3923+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
3924+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
3925+ &root_search);
3926+
3927+ if (root_search.iterator != NULL)
3928+ {
3929+ path = gtk_tree_model_get_path(budget, root_search.iterator);
3930+ gtk_tree_view_expand_row(view, path, FALSE);
3931+ }
3932+
3933+ // Retrieve expense root
3934+ root_search.row_is_root = TRUE;
3935+ root_search.row_is_total = FALSE;
3936+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_EXPENSE;
3937+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
3938+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
3939+ &root_search);
3940+
3941+ if (root_search.iterator != NULL)
3942+ {
3943+ path = gtk_tree_model_get_path(budget, root_search.iterator);
3944+ gtk_tree_view_expand_row(view, path, FALSE);
3945+ }
3946+
3947+ // Retrieve total root
3948+ root_search.row_is_root = TRUE;
3949+ root_search.row_is_total = FALSE;
3950+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_NONE;
3951+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
3952+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
3953+ &root_search);
3954+
3955+ if (root_search.iterator != NULL)
3956+ {
3957+ path = gtk_tree_model_get_path(budget, root_search.iterator);
3958+ gtk_tree_view_expand_row(view, path, FALSE);
3959+ }
3960+
3961+ g_free(root_search.iterator);
3962+
3963+ return;
3964+}
3965+
3966+// Create tree roots for the store
3967+static void ui_bud_tabview_model_insert_roots(GtkTreeStore* budget)
3968+{
3969+GtkTreeIter iter, root;
3970+
3971+ gtk_tree_store_insert_with_values (
3972+ budget,
3973+ &root,
3974+ NULL,
3975+ -1,
3976+ UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
3977+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
3978+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
3979+ UI_BUD_TABVIEW_IS_ROOT, TRUE,
3980+ UI_BUD_TABVIEW_IS_TOTAL, FALSE,
3981+ -1);
3982+
3983+ // For add category dialog: copy of the root to be able to select it
3984+ gtk_tree_store_insert_with_values (
3985+ budget,
3986+ &iter,
3987+ &root,
3988+ -1,
3989+ UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
3990+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
3991+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
3992+ UI_BUD_TABVIEW_CATEGORY_KEY, 0,
3993+ UI_BUD_TABVIEW_IS_ROOT, FALSE,
3994+ UI_BUD_TABVIEW_IS_TOTAL, FALSE,
3995+ UI_BUD_TABVIEW_IS_CHILD_HEADER, TRUE,
3996+ -1);
3997+
3998+ // For add category dialog: add a separator to distinguish root with children
3999+ gtk_tree_store_insert_with_values (
4000+ budget,
4001+ &iter,
4002+ &root,
4003+ -1,
4004+ UI_BUD_TABVIEW_IS_SEPARATOR, TRUE,
4005+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
4006+ -1);
4007+
4008+ gtk_tree_store_insert_with_values (
4009+ budget,
4010+ &root,
4011+ NULL,
4012+ -1,
4013+ UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
4014+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
4015+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
4016+ UI_BUD_TABVIEW_IS_ROOT, TRUE,
4017+ UI_BUD_TABVIEW_IS_TOTAL, FALSE,
4018+ -1);
4019+
4020+ // For add category dialog: copy of the root to be able to select it
4021+ gtk_tree_store_insert_with_values (
4022+ budget,
4023+ &iter,
4024+ &root,
4025+ -1,
4026+ UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
4027+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
4028+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
4029+ UI_BUD_TABVIEW_CATEGORY_KEY, 0,
4030+ UI_BUD_TABVIEW_IS_ROOT, FALSE,
4031+ UI_BUD_TABVIEW_IS_TOTAL, FALSE,
4032+ UI_BUD_TABVIEW_IS_CHILD_HEADER, TRUE,
4033+ -1);
4034+
4035+ // For add category dialog: add a separator to distinguish root with children
4036+ gtk_tree_store_insert_with_values (
4037+ budget,
4038+ &iter,
4039+ &root,
4040+ -1,
4041+ UI_BUD_TABVIEW_IS_SEPARATOR, TRUE,
4042+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
4043+ -1);
4044+
4045+ gtk_tree_store_insert_with_values (
4046+ budget,
4047+ &root,
4048+ NULL,
4049+ -1,
4050+ UI_BUD_TABVIEW_CATEGORY_NAME, _("Totals"),
4051+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _("Totals"),
4052+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_NONE,
4053+ UI_BUD_TABVIEW_IS_ROOT, TRUE,
4054+ UI_BUD_TABVIEW_IS_TOTAL, FALSE,
4055+ -1);
4056+
4057+ return;
4058+}
4059+
4060+// Update (or insert) total rows for a budget according to the view mode
4061+// This function will is used to initiate model and to refresh it after change by user
4062+static void ui_bud_tabview_model_update_monthly_total(GtkTreeStore* budget)
4063+{
4064+ui_bud_tabview_search_criteria_t root_search = ui_bud_tabview_search_criteria_default;
4065+GtkTreeIter total_root, child;
4066+double total_income[12] = {0}, total_expense[12] = {0};
4067+gboolean cat_is_same_amount;
4068+int n_category;
4069+
4070+ // Go through all categories to compute totals
4071+ n_category = da_cat_get_max_key();
4072+
4073+ for(guint32 i=1; i<=n_category; ++i)
4074+ {
4075+ Category *bdg_category;
4076+ gboolean cat_is_income;
4077+
4078+ bdg_category = da_cat_get(i);
4079+
4080+ if (bdg_category == NULL)
4081+ {
4082+ continue;
4083+ }
4084+
4085+ cat_is_income = (category_type_get (bdg_category) == 1);
4086+ cat_is_same_amount = (! (bdg_category->flags & GF_CUSTOM));
4087+
4088+ for (gint j=0; j<=11; ++j)
4089+ {
4090+ if (cat_is_income)
4091+ {
4092+ if (cat_is_same_amount)
4093+ {
4094+ total_income[j] += bdg_category->budget[0];
4095+ }
4096+ else
4097+ {
4098+ total_income[j] += bdg_category->budget[j+1];
4099+ }
4100+ }
4101+ else
4102+ {
4103+ if (cat_is_same_amount)
4104+ {
4105+ total_expense[j] += bdg_category->budget[0];
4106+ }
4107+ else
4108+ {
4109+ total_expense[j] += bdg_category->budget[j+1];
4110+ }
4111+ }
4112+ }
4113+ }
4114+
4115+ // Retrieve total root and insert required total rows
4116+ root_search.row_is_root = TRUE;
4117+ root_search.row_is_total = FALSE;
4118+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_NONE;
4119+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
4120+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
4121+ &root_search);
4122+
4123+ if (!root_search.iterator)
4124+ {
4125+ return;
4126+ }
4127+
4128+ total_root = *root_search.iterator;
4129+
4130+ // Retrieve and set totals
4131+ root_search.row_is_root = FALSE;
4132+ root_search.row_is_total = TRUE;
4133+
4134+ // First, look for Incomes
4135+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_INCOME;
4136+
4137+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
4138+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
4139+ &root_search);
4140+
4141+ if (root_search.iterator)
4142+ {
4143+ child = *root_search.iterator;
4144+ }
4145+ else
4146+ {
4147+ gtk_tree_store_insert(budget, &child, &total_root, -1);
4148+ }
4149+
4150+ gtk_tree_store_set (
4151+ budget,
4152+ &child,
4153+ UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
4154+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
4155+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
4156+ UI_BUD_TABVIEW_IS_TOTAL, TRUE,
4157+ UI_BUD_TABVIEW_JANUARY, total_income[0],
4158+ UI_BUD_TABVIEW_FEBRUARY, total_income[1],
4159+ UI_BUD_TABVIEW_MARCH, total_income[2],
4160+ UI_BUD_TABVIEW_APRIL, total_income[3],
4161+ UI_BUD_TABVIEW_MAY, total_income[4],
4162+ UI_BUD_TABVIEW_JUNE, total_income[5],
4163+ UI_BUD_TABVIEW_JULY, total_income[6],
4164+ UI_BUD_TABVIEW_AUGUST, total_income[7],
4165+ UI_BUD_TABVIEW_SEPTEMBER, total_income[8],
4166+ UI_BUD_TABVIEW_OCTOBER, total_income[9],
4167+ UI_BUD_TABVIEW_NOVEMBER, total_income[10],
4168+ UI_BUD_TABVIEW_DECEMBER, total_income[11],
4169+ -1);
4170+
4171+ // Then look for Expenses
4172+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_EXPENSE;
4173+
4174+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
4175+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
4176+ &root_search);
4177+
4178+ if (root_search.iterator)
4179+ {
4180+ child = *root_search.iterator;
4181+ }
4182+ else
4183+ {
4184+ gtk_tree_store_insert(budget, &child, &total_root, -1);
4185+ }
4186+
4187+ gtk_tree_store_set (
4188+ budget,
4189+ &child,
4190+ UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
4191+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
4192+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
4193+ UI_BUD_TABVIEW_IS_TOTAL, TRUE,
4194+ UI_BUD_TABVIEW_JANUARY, total_expense[0],
4195+ UI_BUD_TABVIEW_FEBRUARY, total_expense[1],
4196+ UI_BUD_TABVIEW_MARCH, total_expense[2],
4197+ UI_BUD_TABVIEW_APRIL, total_expense[3],
4198+ UI_BUD_TABVIEW_MAY, total_expense[4],
4199+ UI_BUD_TABVIEW_JUNE, total_expense[5],
4200+ UI_BUD_TABVIEW_JULY, total_expense[6],
4201+ UI_BUD_TABVIEW_AUGUST, total_expense[7],
4202+ UI_BUD_TABVIEW_SEPTEMBER, total_expense[8],
4203+ UI_BUD_TABVIEW_OCTOBER, total_expense[9],
4204+ UI_BUD_TABVIEW_NOVEMBER, total_expense[10],
4205+ UI_BUD_TABVIEW_DECEMBER, total_expense[11],
4206+ -1);
4207+
4208+ // Finally, set Balance total row
4209+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_NONE;
4210+
4211+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
4212+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
4213+ &root_search);
4214+
4215+ if (root_search.iterator)
4216+ {
4217+ child = *root_search.iterator;
4218+ }
4219+ else
4220+ {
4221+ gtk_tree_store_insert(budget, &child, &total_root, -1);
4222+ }
4223+
4224+ gtk_tree_store_set (
4225+ budget,
4226+ &child,
4227+ UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_SUMMARY]),
4228+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_SUMMARY]),
4229+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_NONE,
4230+ UI_BUD_TABVIEW_IS_TOTAL, TRUE,
4231+ UI_BUD_TABVIEW_JANUARY, total_income[0] + total_expense[0],
4232+ UI_BUD_TABVIEW_FEBRUARY, total_income[1] + total_expense[1],
4233+ UI_BUD_TABVIEW_MARCH, total_income[2] + total_expense[2],
4234+ UI_BUD_TABVIEW_APRIL, total_income[3] + total_expense[3],
4235+ UI_BUD_TABVIEW_MAY, total_income[4] + total_expense[4],
4236+ UI_BUD_TABVIEW_JUNE, total_income[5] + total_expense[5],
4237+ UI_BUD_TABVIEW_JULY, total_income[6] + total_expense[6],
4238+ UI_BUD_TABVIEW_AUGUST, total_income[7] + total_expense[7],
4239+ UI_BUD_TABVIEW_SEPTEMBER, total_income[8] + total_expense[8],
4240+ UI_BUD_TABVIEW_OCTOBER, total_income[9] + total_expense[9],
4241+ UI_BUD_TABVIEW_NOVEMBER, total_income[10] + total_expense[10],
4242+ UI_BUD_TABVIEW_DECEMBER, total_income[11] + total_expense[11],
4243+ -1);
4244+
4245+ g_free(root_search.iterator);
4246+
4247+ return;
4248+}
4249+
4250+// Filter shown rows according to VIEW mode
4251+static gboolean ui_bud_tabview_model_row_filter (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4252+{
4253+gboolean is_visible, is_root, is_total, is_separator, is_childheader;
4254+ui_bud_tabview_data_t* data;
4255+ui_bud_tabview_view_mode_t view_mode;
4256+guint32 category_key;
4257+ui_bud_tabview_cat_type_t category_type;
4258+Category *bdg_category;
4259+
4260+ is_visible = TRUE;
4261+ data = user_data;
4262+ view_mode = radio_get_active(GTK_CONTAINER(data->RA_mode));
4263+
4264+ gtk_tree_model_get(model, iter,
4265+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4266+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4267+ UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
4268+ UI_BUD_TABVIEW_IS_CHILD_HEADER, &is_childheader,
4269+ UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
4270+ UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
4271+ -1);
4272+
4273+ // On specific mode, hidde categories of opposite type
4274+ if (!is_total
4275+ && category_type == UI_BUD_TABVIEW_CAT_TYPE_INCOME
4276+ && view_mode == UI_BUD_TABVIEW_VIEW_EXPENSE)
4277+ {
4278+ is_visible = FALSE;
4279+ }
4280+
4281+ if (!is_total
4282+ && category_type == UI_BUD_TABVIEW_CAT_TYPE_EXPENSE
4283+ && view_mode == UI_BUD_TABVIEW_VIEW_INCOME)
4284+ {
4285+ is_visible = FALSE;
4286+ }
4287+
4288+ // Hidde fake first child root used for add dialog
4289+ if (is_childheader
4290+ || is_separator)
4291+ {
4292+ is_visible = FALSE;
4293+ }
4294+
4295+ // On balance mode, hidde not forced empty categories
4296+ if (!is_total
4297+ && !is_root
4298+ && !is_childheader
4299+ && !is_separator
4300+ && view_mode == UI_BUD_TABVIEW_VIEW_SUMMARY)
4301+ {
4302+ bdg_category = da_cat_get(category_key);
4303+
4304+ if (bdg_category != NULL)
4305+ {
4306+ // Either the category has some budget, or its display is forced
4307+ is_visible = (bdg_category->flags & (GF_BUDGET|GF_FORCED));
4308+
4309+ // Force display if one of its children should be displayed
4310+ if (!is_visible)
4311+ {
4312+ GtkTreeIter child;
4313+ Category *subcat;
4314+ guint32 subcat_key;
4315+ gint child_id=0;
4316+
4317+ while (gtk_tree_model_iter_nth_child(model,
4318+ &child,
4319+ iter,
4320+ child_id))
4321+ {
4322+ gtk_tree_model_get(model, &child,
4323+ UI_BUD_TABVIEW_CATEGORY_KEY, &subcat_key,
4324+ -1);
4325+
4326+ if (subcat_key != 0)
4327+ {
4328+ subcat = da_cat_get (subcat_key);
4329+
4330+ if (subcat != NULL)
4331+ {
4332+ is_visible = (subcat->flags & (GF_BUDGET|GF_FORCED));
4333+ }
4334+ }
4335+
4336+ // Stop loop on first visible children
4337+ if (is_visible)
4338+ {
4339+ break;
4340+ }
4341+
4342+ ++child_id;
4343+ }
4344+ }
4345+ }
4346+ }
4347+
4348+ return is_visible;
4349+}
4350+
4351+#if HB_BUD_TABVIEW_EDIT_ENABLE
4352+// Filter rows to show only parent categories
4353+static gboolean ui_bud_tabview_model_row_filter_parents (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4354+{
4355+gboolean is_visible, is_root, is_total, is_separator, is_childheader;
4356+Category *bdg_category;
4357+guint32 category_key;
4358+ui_bud_tabview_cat_type_t category_type;
4359+
4360+ is_visible = TRUE;
4361+
4362+ gtk_tree_model_get(model, iter,
4363+ UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
4364+ UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
4365+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4366+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4367+ UI_BUD_TABVIEW_IS_CHILD_HEADER, &is_childheader,
4368+ UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
4369+ -1);
4370+
4371+ // Hidde Total root
4372+ if (is_root
4373+ && category_type == UI_BUD_TABVIEW_CAT_TYPE_NONE )
4374+ {
4375+ is_visible = FALSE;
4376+ }
4377+
4378+ // Hidde Total rows
4379+ if (is_total) {
4380+ is_visible = FALSE;
4381+ }
4382+
4383+ if (category_key > 0 && (is_separator || is_childheader))
4384+ {
4385+ is_visible = FALSE;
4386+ }
4387+ else if (category_key > 0)
4388+ {
4389+ // Show categories without parents
4390+ bdg_category = da_cat_get(category_key);
4391+
4392+ if (bdg_category != NULL)
4393+ {
4394+ if (bdg_category->parent > 0)
4395+ {
4396+ is_visible = FALSE;
4397+ }
4398+ }
4399+ }
4400+
4401+ return is_visible;
4402+}
4403+
4404+// Filter rows to show only mergeable categories
4405+static gboolean ui_bud_tabview_model_row_merge_filter_with_headers (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4406+{
4407+ui_bud_tabview_data_t *data = user_data;
4408+gboolean is_visible, is_root, is_total, is_separator, is_childheader;
4409+guint32 category_key;
4410+ui_bud_tabview_cat_type_t category_type;
4411+
4412+ is_visible = TRUE;
4413+
4414+ gtk_tree_model_get(model, iter,
4415+ UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
4416+ UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
4417+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4418+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4419+ UI_BUD_TABVIEW_IS_CHILD_HEADER, &is_childheader,
4420+ UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
4421+ -1);
4422+
4423+ // Hide source merge row
4424+ if (data->MERGE_source_category_key == category_key)
4425+ {
4426+ is_visible = FALSE;
4427+ }
4428+
4429+ // Hide Total root
4430+ if (is_root
4431+ && category_type == UI_BUD_TABVIEW_CAT_TYPE_NONE )
4432+ {
4433+ is_visible = FALSE;
4434+ }
4435+
4436+ // Hide Total rows
4437+ if (is_total)
4438+ {
4439+ is_visible = FALSE;
4440+ }
4441+
4442+ if ((is_separator || is_childheader))
4443+ {
4444+ GtkTreeIter parent;
4445+ gtk_tree_model_iter_parent(model, &parent, iter);
4446+
4447+ // Show child header and separator if parent has more than 2 children
4448+ is_visible = (gtk_tree_model_iter_n_children(model, &parent) > 2);
4449+ }
4450+
4451+ return is_visible;
4452+}
4453+#endif
4454+
4455+static gint ui_bud_tabview_model_row_sort (GtkTreeModel *model, GtkTreeIter *cat_a, GtkTreeIter *cat_b, gpointer user_data)
4456+{
4457+const gchar* cat_a_name;
4458+const gchar* cat_b_name;
4459+ui_bud_tabview_cat_type_t cat_a_type, cat_b_type;
4460+guint32 cat_a_key, cat_b_key;
4461+gboolean cat_a_is_childheader, cat_a_is_separator, cat_b_is_childheader, cat_b_is_separator;
4462+gint order = 0;
4463+
4464+ gtk_tree_model_get(model, cat_a,
4465+ UI_BUD_TABVIEW_CATEGORY_NAME, &cat_a_name,
4466+ UI_BUD_TABVIEW_CATEGORY_TYPE, &cat_a_type,
4467+ UI_BUD_TABVIEW_CATEGORY_KEY, &cat_a_key,
4468+ UI_BUD_TABVIEW_IS_CHILD_HEADER, &cat_a_is_childheader,
4469+ UI_BUD_TABVIEW_IS_SEPARATOR, &cat_a_is_separator,
4470+ -1);
4471+
4472+ gtk_tree_model_get(model, cat_b,
4473+ UI_BUD_TABVIEW_CATEGORY_NAME, &cat_b_name,
4474+ UI_BUD_TABVIEW_CATEGORY_TYPE, &cat_b_type,
4475+ UI_BUD_TABVIEW_CATEGORY_KEY, &cat_b_key,
4476+ UI_BUD_TABVIEW_IS_CHILD_HEADER, &cat_b_is_childheader,
4477+ UI_BUD_TABVIEW_IS_SEPARATOR, &cat_b_is_separator,
4478+ -1);
4479+
4480+ // Sort first by category type
4481+ if (cat_a_type != cat_b_type)
4482+ {
4483+ switch (cat_a_type)
4484+ {
4485+ case UI_BUD_TABVIEW_CAT_TYPE_INCOME:
4486+ order = -1;
4487+ break;
4488+ case UI_BUD_TABVIEW_CAT_TYPE_EXPENSE:
4489+ order = 0;
4490+ break;
4491+ case UI_BUD_TABVIEW_CAT_TYPE_NONE:
4492+ order = 1;
4493+ break;
4494+ }
4495+ }
4496+ else
4497+ {
4498+ // On standard categories, just order by name
4499+ if (!cat_a_is_childheader && !cat_a_is_separator
4500+ && !cat_b_is_childheader && !cat_b_is_separator)
4501+ {
4502+ order = g_utf8_collate(g_utf8_casefold(cat_a_name, -1),
4503+ g_utf8_casefold(cat_b_name, -1)
4504+ );
4505+ }
4506+ // Otherwise, fake categories have to be first (header and separator)
4507+ else if (cat_a_is_childheader || cat_a_is_separator)
4508+ {
4509+ if (!cat_b_is_separator && !cat_b_is_childheader)
4510+ {
4511+ order = -1;
4512+ }
4513+ // When both are fake, header has to be first
4514+ else
4515+ {
4516+ order = (cat_a_is_childheader ? -1 : 1);
4517+ }
4518+ }
4519+ else
4520+ {
4521+ // Same idea for fake categories when cat_b is fake, but
4522+ // with reversed result, because sort function return
4523+ // result according to cat_a
4524+
4525+ if (!cat_a_is_separator && !cat_a_is_childheader)
4526+ {
4527+ order = 1;
4528+ }
4529+ else
4530+ {
4531+ order = (cat_b_is_childheader ? 1 : -1);
4532+ }
4533+ }
4534+ }
4535+
4536+ return order;
4537+}
4538+
4539+// the budget model creation
4540+static GtkTreeModel * ui_bud_tabview_model_new ()
4541+{
4542+GtkTreeStore *budget;
4543+GtkTreeIter *iter_income, *iter_expense;
4544+guint32 n_category;
4545+ui_bud_tabview_search_criteria_t root_search = ui_bud_tabview_search_criteria_default;
4546+
4547+ // Create Tree Store
4548+ budget = gtk_tree_store_new ( UI_BUD_TABVIEW_NUMBER_COLOMNS,
4549+ G_TYPE_UINT, // UI_BUD_TABVIEW_CATEGORY_KEY
4550+ G_TYPE_STRING, // UI_BUD_TABVIEW_CATEGORY_NAME
4551+ G_TYPE_STRING, // UI_BUD_TABVIEW_CATEGORY_FULLNAME
4552+ G_TYPE_INT, // UI_BUD_TABVIEW_CATEGORY_TYPE
4553+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_ROOT
4554+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_TOTAL
4555+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_CHILD_HEADER
4556+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_SEPARATOR
4557+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_MONITORING_FORCED
4558+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_SAME_AMOUNT
4559+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_SUB_CATEGORY
4560+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_HAS_BUDGET
4561+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_SAME_AMOUNT
4562+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_JANUARY
4563+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_FEBRUARY
4564+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_MARCH
4565+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_APRIL
4566+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_MAY
4567+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_JUNE
4568+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_JULY
4569+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_AUGUST
4570+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_SEPTEMBER
4571+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_OCTOBER
4572+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_NOVEMBER
4573+ G_TYPE_DOUBLE // UI_BUD_TABVIEW_DECEMBER
4574+ );
4575+
4576+ // Populate the store
4577+
4578+ /* Create tree roots */
4579+ ui_bud_tabview_model_insert_roots (budget);
4580+
4581+ // Retrieve required root
4582+ root_search.row_is_root = TRUE;
4583+ root_search.row_is_total = FALSE;
4584+
4585+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_INCOME;
4586+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
4587+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
4588+ &root_search);
4589+ iter_income = root_search.iterator;
4590+
4591+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_EXPENSE;
4592+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
4593+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
4594+ &root_search);
4595+ iter_expense = root_search.iterator;
4596+
4597+ /* Create rows for real categories */
4598+ n_category = da_cat_get_max_key();
4599+
4600+ for(guint32 i=1; i<=n_category; ++i)
4601+ {
4602+ Category *bdg_category;
4603+ gboolean cat_is_income;
4604+
4605+ bdg_category = da_cat_get(i);
4606+
4607+ if (bdg_category == NULL)
4608+ {
4609+ continue;
4610+ }
4611+
4612+ cat_is_income = (category_type_get (bdg_category) == 1);
4613+
4614+ DB(g_print(" category %d:'%s' isincome=%d, issub=%d hasbudget=%d parent=%d\n",
4615+ bdg_category->key, bdg_category->name,
4616+ cat_is_income, (bdg_category->flags & GF_SUB),
4617+ (bdg_category->flags & GF_BUDGET), bdg_category->parent));
4618+
4619+ // Compute totals and initiate category in right tree root
4620+ if (cat_is_income)
4621+ {
4622+ ui_bud_tabview_model_add_category_with_lineage(budget, iter_income, &(bdg_category->key));
4623+ }
4624+ else if (!cat_is_income)
4625+ {
4626+ ui_bud_tabview_model_add_category_with_lineage(budget, iter_expense, &(bdg_category->key));
4627+ }
4628+ }
4629+
4630+ /* Create rows for total root */
4631+ ui_bud_tabview_model_update_monthly_total(GTK_TREE_STORE(budget));
4632+
4633+ /* Sort categories on same node level */
4634+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(budget),
4635+ UI_BUD_TABVIEW_CATEGORY_NAME, ui_bud_tabview_model_row_sort,
4636+ NULL, NULL);
4637+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (budget),
4638+ UI_BUD_TABVIEW_CATEGORY_NAME, GTK_SORT_ASCENDING);
4639+
4640+ g_free(root_search.iterator);
4641+
4642+ return GTK_TREE_MODEL(budget);
4643+}
4644+
4645+/**
4646+ * GtkTreeView functions
4647+ **/
4648+
4649+// Display category name in bold if it has budget
4650+static void ui_bud_tabview_view_display_category_name (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4651+{
4652+ui_bud_tabview_data_t *data = user_data;
4653+gboolean has_budget, is_sub_category;
4654+PangoWeight weight = PANGO_WEIGHT_NORMAL;
4655+ui_bud_tabview_view_mode_t view_mode = UI_BUD_TABVIEW_VIEW_SUMMARY;
4656+
4657+ gtk_tree_model_get(model, iter,
4658+ UI_BUD_TABVIEW_IS_SUB_CATEGORY, &is_sub_category,
4659+ UI_BUD_TABVIEW_HAS_BUDGET, &has_budget,
4660+ -1);
4661+
4662+ view_mode = radio_get_active(GTK_CONTAINER(data->RA_mode));
4663+
4664+ if (view_mode != UI_BUD_TABVIEW_VIEW_SUMMARY && has_budget)
4665+ {
4666+ weight = PANGO_WEIGHT_BOLD;
4667+ }
4668+
4669+ g_object_set(renderer,
4670+ "style", is_sub_category ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL,
4671+ "weight", weight,
4672+ NULL);
4673+}
4674+
4675+// to enable or not edition on month columns
4676+static void ui_bud_tabview_view_display_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4677+{
4678+GtkAdjustment *adjustment;
4679+gboolean is_same_amount, is_root, is_total, is_visible, is_editable;
4680+ui_bud_tabview_cat_type_t row_category_type;
4681+gdouble amount = 0.0;
4682+gchar *text;
4683+gchar *fgcolor;
4684+const ui_bud_tabview_store_t column_id = GPOINTER_TO_INT(user_data);
4685+
4686+ gtk_tree_model_get(model, iter,
4687+ UI_BUD_TABVIEW_CATEGORY_TYPE, &row_category_type,
4688+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4689+ UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
4690+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4691+ -1);
4692+
4693+ // Text to display
4694+ if (is_same_amount)
4695+ {
4696+ gtk_tree_model_get(model, iter, UI_BUD_TABVIEW_SAME_AMOUNT, &amount, -1);
4697+ }
4698+ else if (column_id >= UI_BUD_TABVIEW_JANUARY && column_id <= UI_BUD_TABVIEW_DECEMBER)
4699+ {
4700+ gtk_tree_model_get(model, iter, column_id, &amount, -1);
4701+ }
4702+
4703+ text = g_strdup_printf("%.2f", amount);
4704+ fgcolor = get_normal_color_amount(amount);
4705+
4706+ // Default styling values
4707+ is_visible = TRUE;
4708+ is_editable = FALSE;
4709+
4710+ if (is_root)
4711+ {
4712+ is_visible = FALSE;
4713+ is_editable = FALSE;
4714+ }
4715+ else if (is_total)
4716+ {
4717+ is_visible = TRUE;
4718+ is_editable = FALSE;
4719+
4720+ if (column_id == UI_BUD_TABVIEW_SAME_AMOUNT)
4721+ {
4722+ is_visible = FALSE;
4723+ }
4724+ }
4725+ else if (is_same_amount)
4726+ {
4727+ is_visible = TRUE;
4728+ is_editable = FALSE;
4729+
4730+ if (column_id == UI_BUD_TABVIEW_SAME_AMOUNT)
4731+ {
4732+ is_editable = TRUE;
4733+ }
4734+ }
4735+ else if (! is_same_amount)
4736+ {
4737+ is_visible = TRUE;
4738+ is_editable = TRUE;
4739+
4740+ if (column_id == UI_BUD_TABVIEW_SAME_AMOUNT)
4741+ {
4742+ is_editable = FALSE;
4743+ }
4744+ }
4745+
4746+ // Finally, visibility depends on set amount
4747+#if !HB_BUD_TABVIEW_EDIT_ENABLE
4748+ is_visible = (is_visible && amount != 0.0);
4749+ is_editable = FALSE;
4750+#else
4751+ is_visible = (is_visible && (is_editable || amount != 0.0));
4752+#endif
4753+
4754+ adjustment = gtk_adjustment_new(
4755+ 0.0, // initial-value
4756+ -G_MAXDOUBLE, // minmal-value
4757+ G_MAXDOUBLE, // maximal-value
4758+ 0.5, // step increment
4759+ 10, // page increment
4760+ 0); // page size (0 because irrelevant for GtkSpinButton)
4761+
4762+ g_object_set(renderer,
4763+ "text", text,
4764+ "visible", is_visible,
4765+ "editable", is_editable,
4766+ "foreground", fgcolor,
4767+ "xalign", 1.0,
4768+ "adjustment", adjustment,
4769+ "digits", 2,
4770+ NULL);
4771+
4772+ g_free(text);
4773+}
4774+
4775+// to enable or not edition on month columns
4776+static void ui_bud_tabview_view_display_is_same_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4777+{
4778+gboolean is_same_amount, is_root, is_total, is_visible, is_sensitive;
4779+
4780+ gtk_tree_model_get(model, iter,
4781+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4782+ UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
4783+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4784+ -1);
4785+
4786+ // Default values
4787+ is_visible = TRUE;
4788+ is_sensitive = TRUE;
4789+
4790+ if (is_root || is_total)
4791+ {
4792+ is_visible = FALSE;
4793+ is_sensitive = FALSE;
4794+ }
4795+
4796+ g_object_set(renderer,
4797+ "activatable", TRUE,
4798+ "active", is_same_amount,
4799+ "visible", is_visible,
4800+ "sensitive", is_sensitive,
4801+ NULL);
4802+}
4803+
4804+// Compute dynamically the annual total
4805+static void ui_bud_tabview_view_display_annual_total (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4806+{
4807+gboolean is_same_amount = FALSE, is_total = FALSE, is_root = FALSE;
4808+gdouble amount = 0.0;
4809+gdouble total = 0.0;
4810+gchar *text;
4811+gchar *fgcolor;
4812+gboolean is_visible = TRUE;
4813+
4814+ gtk_tree_model_get(model, iter,
4815+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4816+ UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
4817+ UI_BUD_TABVIEW_SAME_AMOUNT, &amount,
4818+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4819+ -1);
4820+
4821+ if (is_same_amount)
4822+ {
4823+ total = 12.0 * amount;
4824+ }
4825+ else
4826+ {
4827+ for (int i = UI_BUD_TABVIEW_JANUARY ; i <= UI_BUD_TABVIEW_DECEMBER ; ++i)
4828+ {
4829+ gtk_tree_model_get(model, iter, i, &amount, -1);
4830+ total += amount;
4831+ }
4832+ }
4833+
4834+ text = g_strdup_printf("%.2f", total);
4835+ fgcolor = get_normal_color_amount(total);
4836+
4837+ if (is_root)
4838+ {
4839+ is_visible = FALSE;
4840+ }
4841+
4842+ // Finally, visibility depends on set amount
4843+ is_visible = (is_visible && amount != 0.0);
4844+
4845+ g_object_set(renderer,
4846+ "text", text,
4847+ "foreground", fgcolor,
4848+ "visible", is_visible,
4849+ "xalign", 1.0,
4850+ NULL);
4851+
4852+ g_free(text);
4853+}
4854+
4855+// Compute monthly average
4856+static void ui_bud_tabview_view_display_monthly_average(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4857+{
4858+gboolean is_same_amount = FALSE, is_total = FALSE, is_root = FALSE;
4859+gdouble amount = 0.0;
4860+gdouble average = 0.0;
4861+gchar *text;
4862+gchar *fgcolor;
4863+gboolean is_visible = TRUE;
4864+
4865+ gtk_tree_model_get(model, iter,
4866+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4867+ UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
4868+ UI_BUD_TABVIEW_SAME_AMOUNT, &amount,
4869+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4870+ -1);
4871+
4872+ if (is_same_amount)
4873+ {
4874+ average = amount;
4875+ }
4876+ else
4877+ {
4878+ for (int i = UI_BUD_TABVIEW_JANUARY ; i <= UI_BUD_TABVIEW_DECEMBER ; ++i)
4879+ {
4880+ gtk_tree_model_get(model, iter, i, &amount, -1);
4881+ average += amount;
4882+ }
4883+ average = hb_amount_round(average / 12.0, 2);
4884+ }
4885+
4886+ text = g_strdup_printf("%.2f", average);
4887+ fgcolor = get_normal_color_amount(average);
4888+
4889+ if (is_root)
4890+ {
4891+ is_visible = FALSE;
4892+ }
4893+
4894+ // Finally, visibility depends on set amount
4895+ is_visible = (is_visible && average != 0.0);
4896+
4897+ g_object_set(renderer,
4898+ "text", text,
4899+ "foreground", fgcolor,
4900+ "visible", is_visible,
4901+ "xalign", 1.0,
4902+ NULL);
4903+
4904+ g_free(text);
4905+}
4906+
4907+// When view mode is toggled:
4908+// - recreate the view to update columns rendering
4909+static void ui_bud_tabview_view_toggle (gpointer user_data, ui_bud_tabview_view_mode_t view_mode)
4910+{
4911+ui_bud_tabview_data_t *data = user_data;
4912+GtkTreeModel *budget;
4913+GtkWidget *view;
4914+GtkTreePath* firstRow;
4915+
4916+ view = data->TV_budget;
4917+
4918+ budget = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
4919+
4920+ gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(budget));
4921+
4922+ if (data->TV_is_expanded)
4923+ {
4924+ gtk_tree_view_expand_all(GTK_TREE_VIEW(view));
4925+ }
4926+ else
4927+ {
4928+ ui_bud_tabview_model_collapse(GTK_TREE_VIEW(view));
4929+ }
4930+
4931+ gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)));
4932+
4933+ firstRow = gtk_tree_path_new_from_string("0");
4934+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(view), firstRow, data->TVC_category, TRUE, 0, 0);
4935+
4936+ DB(g_print("[ui_bud_tabview] : button state changed to: %d\n", view_mode));
4937+
4938+ return;
4939+}
4940+
4941+static gboolean ui_bud_tabview_view_search (GtkTreeModel *filter, gint column, const gchar *key, GtkTreeIter *filter_iter, gpointer data)
4942+{
4943+gboolean is_matching = FALSE, is_root, is_total;
4944+GtkTreeModel *budget;
4945+GtkTreeIter iter;
4946+gchar *category_name;
4947+
4948+ budget = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
4949+ gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter),
4950+ &iter,
4951+ filter_iter);
4952+
4953+ gtk_tree_model_get(budget, &iter,
4954+ UI_BUD_TABVIEW_CATEGORY_NAME, &category_name,
4955+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4956+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4957+ -1);
4958+
4959+ if (!is_root && !is_total
4960+ && g_strstr_len(g_utf8_casefold(category_name, -1), -1,
4961+ g_utf8_casefold(key, -1)))
4962+ {
4963+ is_matching = TRUE;
4964+ }
4965+
4966+ // GtkTreeViewSearchEqualFunc has to return FALSE only if iter matches.
4967+ return !is_matching;
4968+}
4969+
4970+#if HB_BUD_TABVIEW_EDIT_ENABLE
4971+static gboolean ui_bud_tabview_view_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
4972+{
4973+gboolean is_separator;
4974+
4975+ gtk_tree_model_get(model, iter,
4976+ UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
4977+ -1);
4978+
4979+ return is_separator;
4980+}
4981+#endif
4982+
4983+// the budget view creation which run the model creation tool
4984+
4985+static GtkWidget *ui_bud_tabview_view_new (gpointer user_data)
4986+{
4987+GtkTreeViewColumn *col;
4988+GtkCellRenderer *renderer, *cat_name_renderer;
4989+GtkWidget *view;
4990+ui_bud_tabview_data_t *data = user_data;
4991+
4992+ view = gtk_tree_view_new();
4993+
4994+ /* --- Category column --- */
4995+ col = gtk_tree_view_column_new();
4996+ data->TVC_category = col;
4997+
4998+ gtk_tree_view_column_set_title(col, _("Category"));
4999+ gtk_tree_view_column_set_alignment(col, 0.5);
5000+ gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: