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

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

Description of the change

Hello,

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

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

This one add the following features:

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

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

Regards,
Adrien

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

merged this code with my local trunk for review

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

Hi Adrien,

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

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

Here is a list:

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

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

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

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

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

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

Hi Maxime,

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

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

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

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

Thanks again and see you !

105. By Adrien Dorsaz

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

106. By Adrien Dorsaz

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

107. By Adrien Dorsaz

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

108. By Adrien Dorsaz

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

109. By Adrien Dorsaz

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

110. By Adrien Dorsaz

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

111. By Adrien Dorsaz

budget-tabview: center title columns and hide tree lines

That's to be coherent with others tree view style

112. By Adrien Dorsaz

budget-tabview: place total column on second position

113. By Adrien Dorsaz

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

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

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

114. By Adrien Dorsaz

ui-budget-tabview: add monthly average column

115. By Adrien Dorsaz

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

116. By Adrien Dorsaz

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

117. By Adrien Dorsaz

ui-budget-tabview: general redesign interface

Move the search entry to top right of UI.

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

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

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

118. By Adrien Dorsaz

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

119. By Adrien Dorsaz

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

120. By Adrien Dorsaz

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

121. By Adrien Dorsaz

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

122. By Adrien Dorsaz

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

123. By Adrien Dorsaz

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

124. By Adrien Dorsaz

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

125. By Adrien Dorsaz

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

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

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

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

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

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

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

126. By Adrien Dorsaz

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

127. By Adrien Dorsaz

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

128. By Adrien Dorsaz

ui-budget-tabview: fix typo in comments

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

Hi Maxime,

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

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

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

Hi Adrien,

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

Max.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/Makefile.am'
2--- src/Makefile.am 2019-04-11 20:36:11 +0000
3+++ src/Makefile.am 2019-09-17 21:50:17 +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-09-17 21:50:17 +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-09-17 21:50:17 +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-09-17 21:50:17 +0000
3518@@ -0,0 +1,2916 @@
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 gboolean ui_bud_tabview_get_selected_root_iter (GtkTreeModel **budget, GtkTreeIter *iter, ui_bud_tabview_data_t *data);
3719+static void ui_bud_tabview_category_add_full_filled (GtkWidget *source, gpointer user_data);
3720+static void ui_bud_tabview_category_add (GtkButton *button, gpointer user_data);
3721+static void ui_bud_tabview_category_delete (GtkButton *button, gpointer user_data);
3722+static void ui_bud_tabview_category_merge_full_filled (GtkWidget *source, gpointer user_data);
3723+static void ui_bud_tabview_category_merge (GtkButton *button, gpointer user_data);
3724+static void ui_bud_tabview_category_reset (GtkButton *button, gpointer user_data);
3725+#endif
3726+static gboolean ui_bud_tabview_on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data);
3727+static void ui_bud_tabview_dialog_close(ui_bud_tabview_data_t *data, gint response);
3728+
3729+/**
3730+ * GtkTreeStore model
3731+ **/
3732+
3733+// Look for category by deterministic characteristics
3734+// Only categories with specific characteristics can be easily found
3735+// like roots, total rows and categories with real key id
3736+// You are responsible to g_free iterator
3737+static gboolean ui_bud_tabview_model_search_iterator (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, ui_bud_tabview_search_criteria_t *search)
3738+{
3739+guint32 category_key;
3740+ui_bud_tabview_cat_type_t category_type;
3741+gboolean is_found = FALSE, is_root, is_total, is_separator;
3742+
3743+ search->iterator = NULL;
3744+
3745+ gtk_tree_model_get (model, iter,
3746+ UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
3747+ UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
3748+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
3749+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
3750+ UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
3751+ -1);
3752+
3753+ if (search->row_category_key > 0 // Look for iter of real category row
3754+ && category_key == search->row_category_key
3755+ && !(is_total)
3756+ && !(is_root)
3757+ )
3758+ {
3759+ DB(g_print("\tFound row with key %d\n", category_key));
3760+ is_found = TRUE;
3761+ }
3762+ else if (search->row_category_key == 0 // Look for iter of fake category row
3763+ && is_root == search->row_is_root
3764+ && is_total == search->row_is_total
3765+ && category_type == search->row_category_type
3766+ && !is_separator
3767+ )
3768+ {
3769+ DB(g_print("\tFound row with is_root = %d, is_total %d, type = %d\n", is_root, is_total, category_type));
3770+ is_found = TRUE;
3771+ }
3772+
3773+ // If found, save result to struct
3774+ if (is_found)
3775+ {
3776+ search->iterator = g_malloc0(sizeof(GtkTreeIter));
3777+ *search->iterator = *iter;
3778+ }
3779+
3780+ return is_found;
3781+}
3782+
3783+/* Recursive function which add a new row in the budget model with all its ancestors */
3784+static void ui_bud_tabview_model_add_category_with_lineage(GtkTreeStore *budget, GtkTreeIter *balanceIter, guint32 *key_category)
3785+{
3786+GtkTreeIter child;
3787+GtkTreeIter *parent;
3788+Category *bdg_category;
3789+gboolean cat_is_same_amount;
3790+ui_bud_tabview_search_criteria_t parent_search = ui_bud_tabview_search_criteria_default;
3791+
3792+ bdg_category = da_cat_get(*key_category);
3793+
3794+ if (bdg_category == NULL)
3795+ {
3796+ return;
3797+ }
3798+
3799+ cat_is_same_amount = (! (bdg_category->flags & GF_CUSTOM));
3800+
3801+ /* Check if parent category already exists */
3802+ parent_search.row_category_key = bdg_category->parent;
3803+
3804+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
3805+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
3806+ &parent_search);
3807+
3808+ if (bdg_category->parent == 0)
3809+ {
3810+ // If we are one of the oldest parent, stop recursion
3811+ gtk_tree_store_insert (
3812+ budget,
3813+ &child,
3814+ balanceIter,
3815+ -1);
3816+ }
3817+ else
3818+ {
3819+ if (parent_search.iterator)
3820+ {
3821+ DB(g_print("\tRecursion optimisation: parent key %d already exists\n", parent_search.row_category_key));
3822+ // If parent already exists, stop recursion
3823+ parent = parent_search.iterator;
3824+ }
3825+ else
3826+ {
3827+ // Parent has not been found, ask to create it first
3828+ ui_bud_tabview_model_add_category_with_lineage(budget, balanceIter, &(bdg_category->parent));
3829+
3830+ // Now, we are sure parent exists, look for it again
3831+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
3832+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
3833+ &parent_search);
3834+
3835+ parent = parent_search.iterator;
3836+ }
3837+
3838+ gtk_tree_store_insert (
3839+ budget,
3840+ &child,
3841+ parent,
3842+ -1);
3843+ }
3844+
3845+ DB(g_print("insert new category %s (key: %d, type: %d)\n",
3846+ bdg_category->name, bdg_category->key, category_type_get (bdg_category)));
3847+
3848+ gtk_tree_store_set(
3849+ budget,
3850+ &child,
3851+ UI_BUD_TABVIEW_CATEGORY_KEY, bdg_category->key,
3852+ UI_BUD_TABVIEW_CATEGORY_NAME, bdg_category->name,
3853+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, bdg_category->fullname,
3854+ UI_BUD_TABVIEW_CATEGORY_TYPE, category_type_get (bdg_category),
3855+ UI_BUD_TABVIEW_IS_MONITORING_FORCED, (bdg_category->flags & GF_FORCED),
3856+ UI_BUD_TABVIEW_IS_ROOT, FALSE,
3857+ UI_BUD_TABVIEW_IS_SAME_AMOUNT, cat_is_same_amount,
3858+ UI_BUD_TABVIEW_IS_TOTAL, FALSE,
3859+ UI_BUD_TABVIEW_IS_SUB_CATEGORY, bdg_category->parent != 0,
3860+ UI_BUD_TABVIEW_HAS_BUDGET, (bdg_category->flags & GF_BUDGET),
3861+ UI_BUD_TABVIEW_SAME_AMOUNT, bdg_category->budget[0],
3862+ UI_BUD_TABVIEW_JANUARY, bdg_category->budget[1],
3863+ UI_BUD_TABVIEW_FEBRUARY, bdg_category->budget[2],
3864+ UI_BUD_TABVIEW_MARCH, bdg_category->budget[3],
3865+ UI_BUD_TABVIEW_APRIL, bdg_category->budget[4],
3866+ UI_BUD_TABVIEW_MAY, bdg_category->budget[5],
3867+ UI_BUD_TABVIEW_JUNE, bdg_category->budget[6],
3868+ UI_BUD_TABVIEW_JULY, bdg_category->budget[7],
3869+ UI_BUD_TABVIEW_AUGUST, bdg_category->budget[8],
3870+ UI_BUD_TABVIEW_SEPTEMBER, bdg_category->budget[9],
3871+ UI_BUD_TABVIEW_OCTOBER, bdg_category->budget[10],
3872+ UI_BUD_TABVIEW_NOVEMBER, bdg_category->budget[11],
3873+ UI_BUD_TABVIEW_DECEMBER, bdg_category->budget[12],
3874+ -1);
3875+
3876+ // Always add child header and separator
3877+ parent = gtk_tree_iter_copy(&child);
3878+ gtk_tree_store_insert_with_values(
3879+ budget,
3880+ &child,
3881+ parent,
3882+ -1,
3883+ UI_BUD_TABVIEW_CATEGORY_KEY, bdg_category->key,
3884+ UI_BUD_TABVIEW_CATEGORY_NAME, bdg_category->name,
3885+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, bdg_category->fullname,
3886+ UI_BUD_TABVIEW_CATEGORY_TYPE, category_type_get (bdg_category),
3887+ UI_BUD_TABVIEW_IS_CHILD_HEADER, TRUE,
3888+ -1);
3889+
3890+ gtk_tree_store_insert_with_values(
3891+ budget,
3892+ &child,
3893+ parent,
3894+ -1,
3895+ UI_BUD_TABVIEW_CATEGORY_KEY, bdg_category->key,
3896+ UI_BUD_TABVIEW_CATEGORY_TYPE, category_type_get (bdg_category),
3897+ UI_BUD_TABVIEW_IS_SEPARATOR, TRUE,
3898+ -1);
3899+
3900+ gtk_tree_iter_free(parent);
3901+
3902+ g_free(parent_search.iterator);
3903+
3904+ return;
3905+}
3906+
3907+// Collapse all categories except root
3908+static void ui_bud_tabview_model_collapse (GtkTreeView *view)
3909+{
3910+GtkTreeModel *budget;
3911+GtkTreePath *path;
3912+ui_bud_tabview_search_criteria_t root_search = ui_bud_tabview_search_criteria_default;
3913+
3914+ budget = gtk_tree_view_get_model (view);
3915+
3916+ gtk_tree_view_collapse_all(view);
3917+
3918+ // Keep root categories expanded
3919+
3920+ // Retrieve income root
3921+ root_search.row_is_root = TRUE;
3922+ root_search.row_is_total = FALSE;
3923+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_INCOME;
3924+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
3925+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
3926+ &root_search);
3927+
3928+ if (root_search.iterator != NULL)
3929+ {
3930+ path = gtk_tree_model_get_path(budget, root_search.iterator);
3931+ gtk_tree_view_expand_row(view, path, FALSE);
3932+ }
3933+
3934+ // Retrieve expense root
3935+ root_search.row_is_root = TRUE;
3936+ root_search.row_is_total = FALSE;
3937+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_EXPENSE;
3938+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
3939+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
3940+ &root_search);
3941+
3942+ if (root_search.iterator != NULL)
3943+ {
3944+ path = gtk_tree_model_get_path(budget, root_search.iterator);
3945+ gtk_tree_view_expand_row(view, path, FALSE);
3946+ }
3947+
3948+ // Retrieve total root
3949+ root_search.row_is_root = TRUE;
3950+ root_search.row_is_total = FALSE;
3951+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_NONE;
3952+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
3953+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
3954+ &root_search);
3955+
3956+ if (root_search.iterator != NULL)
3957+ {
3958+ path = gtk_tree_model_get_path(budget, root_search.iterator);
3959+ gtk_tree_view_expand_row(view, path, FALSE);
3960+ }
3961+
3962+ g_free(root_search.iterator);
3963+
3964+ return;
3965+}
3966+
3967+// Create tree roots for the store
3968+static void ui_bud_tabview_model_insert_roots(GtkTreeStore* budget)
3969+{
3970+GtkTreeIter iter, root;
3971+
3972+ gtk_tree_store_insert_with_values (
3973+ budget,
3974+ &root,
3975+ NULL,
3976+ -1,
3977+ UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
3978+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
3979+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
3980+ UI_BUD_TABVIEW_IS_ROOT, TRUE,
3981+ UI_BUD_TABVIEW_IS_TOTAL, FALSE,
3982+ -1);
3983+
3984+ // For add category dialog: copy of the root to be able to select it
3985+ gtk_tree_store_insert_with_values (
3986+ budget,
3987+ &iter,
3988+ &root,
3989+ -1,
3990+ UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
3991+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
3992+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
3993+ UI_BUD_TABVIEW_CATEGORY_KEY, 0,
3994+ UI_BUD_TABVIEW_IS_ROOT, FALSE,
3995+ UI_BUD_TABVIEW_IS_TOTAL, FALSE,
3996+ UI_BUD_TABVIEW_IS_CHILD_HEADER, TRUE,
3997+ -1);
3998+
3999+ // For add category dialog: add a separator to distinguish root with children
4000+ gtk_tree_store_insert_with_values (
4001+ budget,
4002+ &iter,
4003+ &root,
4004+ -1,
4005+ UI_BUD_TABVIEW_IS_SEPARATOR, TRUE,
4006+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
4007+ -1);
4008+
4009+ gtk_tree_store_insert_with_values (
4010+ budget,
4011+ &root,
4012+ NULL,
4013+ -1,
4014+ UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
4015+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
4016+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
4017+ UI_BUD_TABVIEW_IS_ROOT, TRUE,
4018+ UI_BUD_TABVIEW_IS_TOTAL, FALSE,
4019+ -1);
4020+
4021+ // For add category dialog: copy of the root to be able to select it
4022+ gtk_tree_store_insert_with_values (
4023+ budget,
4024+ &iter,
4025+ &root,
4026+ -1,
4027+ UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
4028+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
4029+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
4030+ UI_BUD_TABVIEW_CATEGORY_KEY, 0,
4031+ UI_BUD_TABVIEW_IS_ROOT, FALSE,
4032+ UI_BUD_TABVIEW_IS_TOTAL, FALSE,
4033+ UI_BUD_TABVIEW_IS_CHILD_HEADER, TRUE,
4034+ -1);
4035+
4036+ // For add category dialog: add a separator to distinguish root with children
4037+ gtk_tree_store_insert_with_values (
4038+ budget,
4039+ &iter,
4040+ &root,
4041+ -1,
4042+ UI_BUD_TABVIEW_IS_SEPARATOR, TRUE,
4043+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
4044+ -1);
4045+
4046+ gtk_tree_store_insert_with_values (
4047+ budget,
4048+ &root,
4049+ NULL,
4050+ -1,
4051+ UI_BUD_TABVIEW_CATEGORY_NAME, _("Totals"),
4052+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _("Totals"),
4053+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_NONE,
4054+ UI_BUD_TABVIEW_IS_ROOT, TRUE,
4055+ UI_BUD_TABVIEW_IS_TOTAL, FALSE,
4056+ -1);
4057+
4058+ return;
4059+}
4060+
4061+// Update (or insert) total rows for a budget according to the view mode
4062+// This function will is used to initiate model and to refresh it after change by user
4063+static void ui_bud_tabview_model_update_monthly_total(GtkTreeStore* budget)
4064+{
4065+ui_bud_tabview_search_criteria_t root_search = ui_bud_tabview_search_criteria_default;
4066+GtkTreeIter total_root, child;
4067+double total_income[12] = {0}, total_expense[12] = {0};
4068+gboolean cat_is_same_amount;
4069+int n_category;
4070+
4071+ // Go through all categories to compute totals
4072+ n_category = da_cat_get_max_key();
4073+
4074+ for(guint32 i=1; i<=n_category; ++i)
4075+ {
4076+ Category *bdg_category;
4077+ gboolean cat_is_income;
4078+
4079+ bdg_category = da_cat_get(i);
4080+
4081+ if (bdg_category == NULL)
4082+ {
4083+ continue;
4084+ }
4085+
4086+ cat_is_income = (category_type_get (bdg_category) == 1);
4087+ cat_is_same_amount = (! (bdg_category->flags & GF_CUSTOM));
4088+
4089+ for (gint j=0; j<=11; ++j)
4090+ {
4091+ if (cat_is_income)
4092+ {
4093+ if (cat_is_same_amount)
4094+ {
4095+ total_income[j] += bdg_category->budget[0];
4096+ }
4097+ else
4098+ {
4099+ total_income[j] += bdg_category->budget[j+1];
4100+ }
4101+ }
4102+ else
4103+ {
4104+ if (cat_is_same_amount)
4105+ {
4106+ total_expense[j] += bdg_category->budget[0];
4107+ }
4108+ else
4109+ {
4110+ total_expense[j] += bdg_category->budget[j+1];
4111+ }
4112+ }
4113+ }
4114+ }
4115+
4116+ // Retrieve total root and insert required total rows
4117+ root_search.row_is_root = TRUE;
4118+ root_search.row_is_total = FALSE;
4119+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_NONE;
4120+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
4121+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
4122+ &root_search);
4123+
4124+ if (!root_search.iterator)
4125+ {
4126+ return;
4127+ }
4128+
4129+ total_root = *root_search.iterator;
4130+
4131+ // Retrieve and set totals
4132+ root_search.row_is_root = FALSE;
4133+ root_search.row_is_total = TRUE;
4134+
4135+ // First, look for Incomes
4136+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_INCOME;
4137+
4138+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
4139+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
4140+ &root_search);
4141+
4142+ if (root_search.iterator)
4143+ {
4144+ child = *root_search.iterator;
4145+ }
4146+ else
4147+ {
4148+ gtk_tree_store_insert(budget, &child, &total_root, -1);
4149+ }
4150+
4151+ gtk_tree_store_set (
4152+ budget,
4153+ &child,
4154+ UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
4155+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_INCOME]),
4156+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_INCOME,
4157+ UI_BUD_TABVIEW_IS_TOTAL, TRUE,
4158+ UI_BUD_TABVIEW_JANUARY, total_income[0],
4159+ UI_BUD_TABVIEW_FEBRUARY, total_income[1],
4160+ UI_BUD_TABVIEW_MARCH, total_income[2],
4161+ UI_BUD_TABVIEW_APRIL, total_income[3],
4162+ UI_BUD_TABVIEW_MAY, total_income[4],
4163+ UI_BUD_TABVIEW_JUNE, total_income[5],
4164+ UI_BUD_TABVIEW_JULY, total_income[6],
4165+ UI_BUD_TABVIEW_AUGUST, total_income[7],
4166+ UI_BUD_TABVIEW_SEPTEMBER, total_income[8],
4167+ UI_BUD_TABVIEW_OCTOBER, total_income[9],
4168+ UI_BUD_TABVIEW_NOVEMBER, total_income[10],
4169+ UI_BUD_TABVIEW_DECEMBER, total_income[11],
4170+ -1);
4171+
4172+ // Then look for Expenses
4173+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_EXPENSE;
4174+
4175+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
4176+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
4177+ &root_search);
4178+
4179+ if (root_search.iterator)
4180+ {
4181+ child = *root_search.iterator;
4182+ }
4183+ else
4184+ {
4185+ gtk_tree_store_insert(budget, &child, &total_root, -1);
4186+ }
4187+
4188+ gtk_tree_store_set (
4189+ budget,
4190+ &child,
4191+ UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
4192+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_EXPENSE]),
4193+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_EXPENSE,
4194+ UI_BUD_TABVIEW_IS_TOTAL, TRUE,
4195+ UI_BUD_TABVIEW_JANUARY, total_expense[0],
4196+ UI_BUD_TABVIEW_FEBRUARY, total_expense[1],
4197+ UI_BUD_TABVIEW_MARCH, total_expense[2],
4198+ UI_BUD_TABVIEW_APRIL, total_expense[3],
4199+ UI_BUD_TABVIEW_MAY, total_expense[4],
4200+ UI_BUD_TABVIEW_JUNE, total_expense[5],
4201+ UI_BUD_TABVIEW_JULY, total_expense[6],
4202+ UI_BUD_TABVIEW_AUGUST, total_expense[7],
4203+ UI_BUD_TABVIEW_SEPTEMBER, total_expense[8],
4204+ UI_BUD_TABVIEW_OCTOBER, total_expense[9],
4205+ UI_BUD_TABVIEW_NOVEMBER, total_expense[10],
4206+ UI_BUD_TABVIEW_DECEMBER, total_expense[11],
4207+ -1);
4208+
4209+ // Finally, set Balance total row
4210+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_NONE;
4211+
4212+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
4213+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
4214+ &root_search);
4215+
4216+ if (root_search.iterator)
4217+ {
4218+ child = *root_search.iterator;
4219+ }
4220+ else
4221+ {
4222+ gtk_tree_store_insert(budget, &child, &total_root, -1);
4223+ }
4224+
4225+ gtk_tree_store_set (
4226+ budget,
4227+ &child,
4228+ UI_BUD_TABVIEW_CATEGORY_NAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_SUMMARY]),
4229+ UI_BUD_TABVIEW_CATEGORY_FULLNAME, _(UI_BUD_TABVIEW_VIEW_MODE[UI_BUD_TABVIEW_VIEW_SUMMARY]),
4230+ UI_BUD_TABVIEW_CATEGORY_TYPE, UI_BUD_TABVIEW_CAT_TYPE_NONE,
4231+ UI_BUD_TABVIEW_IS_TOTAL, TRUE,
4232+ UI_BUD_TABVIEW_JANUARY, total_income[0] + total_expense[0],
4233+ UI_BUD_TABVIEW_FEBRUARY, total_income[1] + total_expense[1],
4234+ UI_BUD_TABVIEW_MARCH, total_income[2] + total_expense[2],
4235+ UI_BUD_TABVIEW_APRIL, total_income[3] + total_expense[3],
4236+ UI_BUD_TABVIEW_MAY, total_income[4] + total_expense[4],
4237+ UI_BUD_TABVIEW_JUNE, total_income[5] + total_expense[5],
4238+ UI_BUD_TABVIEW_JULY, total_income[6] + total_expense[6],
4239+ UI_BUD_TABVIEW_AUGUST, total_income[7] + total_expense[7],
4240+ UI_BUD_TABVIEW_SEPTEMBER, total_income[8] + total_expense[8],
4241+ UI_BUD_TABVIEW_OCTOBER, total_income[9] + total_expense[9],
4242+ UI_BUD_TABVIEW_NOVEMBER, total_income[10] + total_expense[10],
4243+ UI_BUD_TABVIEW_DECEMBER, total_income[11] + total_expense[11],
4244+ -1);
4245+
4246+ g_free(root_search.iterator);
4247+
4248+ return;
4249+}
4250+
4251+// Filter shown rows according to VIEW mode
4252+static gboolean ui_bud_tabview_model_row_filter (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4253+{
4254+gboolean is_visible, is_root, is_total, is_separator, is_childheader;
4255+ui_bud_tabview_data_t* data;
4256+ui_bud_tabview_view_mode_t view_mode;
4257+guint32 category_key;
4258+ui_bud_tabview_cat_type_t category_type;
4259+Category *bdg_category;
4260+
4261+ is_visible = TRUE;
4262+ data = user_data;
4263+ view_mode = radio_get_active(GTK_CONTAINER(data->RA_mode));
4264+
4265+ gtk_tree_model_get(model, iter,
4266+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4267+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4268+ UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
4269+ UI_BUD_TABVIEW_IS_CHILD_HEADER, &is_childheader,
4270+ UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
4271+ UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
4272+ -1);
4273+
4274+ // On specific mode, hide categories of opposite type
4275+ if (!is_total
4276+ && category_type == UI_BUD_TABVIEW_CAT_TYPE_INCOME
4277+ && view_mode == UI_BUD_TABVIEW_VIEW_EXPENSE)
4278+ {
4279+ is_visible = FALSE;
4280+ }
4281+
4282+ if (!is_total
4283+ && category_type == UI_BUD_TABVIEW_CAT_TYPE_EXPENSE
4284+ && view_mode == UI_BUD_TABVIEW_VIEW_INCOME)
4285+ {
4286+ is_visible = FALSE;
4287+ }
4288+
4289+ // Hide fake first child root used for add dialog
4290+ if (is_childheader
4291+ || is_separator)
4292+ {
4293+ is_visible = FALSE;
4294+ }
4295+
4296+ // On balance mode, hide not forced empty categories
4297+ if (!is_total
4298+ && !is_root
4299+ && !is_childheader
4300+ && !is_separator
4301+ && view_mode == UI_BUD_TABVIEW_VIEW_SUMMARY)
4302+ {
4303+ bdg_category = da_cat_get(category_key);
4304+
4305+ if (bdg_category != NULL)
4306+ {
4307+ // Either the category has some budget, or its display is forced
4308+ is_visible = (bdg_category->flags & (GF_BUDGET|GF_FORCED));
4309+
4310+ // Force display if one of its children should be displayed
4311+ if (!is_visible)
4312+ {
4313+ GtkTreeIter child;
4314+ Category *subcat;
4315+ guint32 subcat_key;
4316+ gint child_id=0;
4317+
4318+ while (gtk_tree_model_iter_nth_child(model,
4319+ &child,
4320+ iter,
4321+ child_id))
4322+ {
4323+ gtk_tree_model_get(model, &child,
4324+ UI_BUD_TABVIEW_CATEGORY_KEY, &subcat_key,
4325+ -1);
4326+
4327+ if (subcat_key != 0)
4328+ {
4329+ subcat = da_cat_get (subcat_key);
4330+
4331+ if (subcat != NULL)
4332+ {
4333+ is_visible = (subcat->flags & (GF_BUDGET|GF_FORCED));
4334+ }
4335+ }
4336+
4337+ // Stop loop on first visible children
4338+ if (is_visible)
4339+ {
4340+ break;
4341+ }
4342+
4343+ ++child_id;
4344+ }
4345+ }
4346+ }
4347+ }
4348+
4349+ return is_visible;
4350+}
4351+
4352+#if HB_BUD_TABVIEW_EDIT_ENABLE
4353+// Filter rows to show only parent categories
4354+static gboolean ui_bud_tabview_model_row_filter_parents (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4355+{
4356+ui_bud_tabview_data_t *data = user_data;
4357+gboolean is_visible, is_root, is_total, is_separator, is_childheader;
4358+Category *bdg_category;
4359+guint32 category_key;
4360+ui_bud_tabview_cat_type_t category_type;
4361+ui_bud_tabview_view_mode_t view_mode = UI_BUD_TABVIEW_VIEW_SUMMARY;
4362+
4363+ view_mode = radio_get_active(GTK_CONTAINER(data->RA_mode));
4364+
4365+ is_visible = TRUE;
4366+
4367+ gtk_tree_model_get(model, iter,
4368+ UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
4369+ UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
4370+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4371+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4372+ UI_BUD_TABVIEW_IS_CHILD_HEADER, &is_childheader,
4373+ UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
4374+ -1);
4375+
4376+ // Show root according to view_mode
4377+ if (is_root)
4378+ {
4379+ // Always hide total root
4380+ if(category_type == UI_BUD_TABVIEW_CAT_TYPE_NONE)
4381+ {
4382+ is_visible = FALSE;
4383+ }
4384+ else if(view_mode == UI_BUD_TABVIEW_VIEW_EXPENSE && category_type == UI_BUD_TABVIEW_CAT_TYPE_INCOME)
4385+ {
4386+ is_visible = FALSE;
4387+ }
4388+ else if(view_mode == UI_BUD_TABVIEW_VIEW_INCOME && category_type == UI_BUD_TABVIEW_CAT_TYPE_EXPENSE)
4389+ {
4390+ is_visible = FALSE;
4391+ }
4392+ }
4393+
4394+ // Hide Total rows
4395+ if (is_total) {
4396+ is_visible = FALSE;
4397+ }
4398+
4399+ if (category_key > 0 && (is_separator || is_childheader))
4400+ {
4401+ is_visible = FALSE;
4402+ }
4403+ else if (category_key > 0)
4404+ {
4405+ // Hide rows according to currently view mode
4406+ if(view_mode == UI_BUD_TABVIEW_VIEW_EXPENSE && category_type == UI_BUD_TABVIEW_CAT_TYPE_INCOME)
4407+ {
4408+ is_visible = FALSE;
4409+ }
4410+ else if(view_mode == UI_BUD_TABVIEW_VIEW_INCOME && category_type == UI_BUD_TABVIEW_CAT_TYPE_EXPENSE)
4411+ {
4412+ is_visible = FALSE;
4413+ }
4414+ else
4415+ {
4416+ // Show categories without parents
4417+ bdg_category = da_cat_get(category_key);
4418+
4419+ if (bdg_category != NULL)
4420+ {
4421+ if (bdg_category->parent > 0)
4422+ {
4423+ is_visible = FALSE;
4424+ }
4425+ }
4426+ }
4427+ }
4428+
4429+ return is_visible;
4430+}
4431+
4432+// Filter rows to show only mergeable categories
4433+static gboolean ui_bud_tabview_model_row_merge_filter_with_headers (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4434+{
4435+ui_bud_tabview_data_t *data = user_data;
4436+gboolean is_visible, is_root, is_total, is_separator, is_childheader;
4437+guint32 category_key;
4438+ui_bud_tabview_cat_type_t category_type;
4439+
4440+ is_visible = TRUE;
4441+
4442+ gtk_tree_model_get(model, iter,
4443+ UI_BUD_TABVIEW_CATEGORY_KEY, &category_key,
4444+ UI_BUD_TABVIEW_CATEGORY_TYPE, &category_type,
4445+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4446+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4447+ UI_BUD_TABVIEW_IS_CHILD_HEADER, &is_childheader,
4448+ UI_BUD_TABVIEW_IS_SEPARATOR, &is_separator,
4449+ -1);
4450+
4451+ // Hide source merge row
4452+ if (data->MERGE_source_category_key == category_key)
4453+ {
4454+ is_visible = FALSE;
4455+ }
4456+
4457+ // Hide Total root
4458+ if (is_root
4459+ && category_type == UI_BUD_TABVIEW_CAT_TYPE_NONE )
4460+ {
4461+ is_visible = FALSE;
4462+ }
4463+
4464+ // Hide Total rows
4465+ if (is_total)
4466+ {
4467+ is_visible = FALSE;
4468+ }
4469+
4470+ if ((is_separator || is_childheader))
4471+ {
4472+ GtkTreeIter parent;
4473+ gtk_tree_model_iter_parent(model, &parent, iter);
4474+
4475+ // Show child header and separator if parent has more than 2 children
4476+ is_visible = (gtk_tree_model_iter_n_children(model, &parent) > 2);
4477+ }
4478+
4479+ return is_visible;
4480+}
4481+#endif
4482+
4483+static gint ui_bud_tabview_model_row_sort (GtkTreeModel *model, GtkTreeIter *cat_a, GtkTreeIter *cat_b, gpointer user_data)
4484+{
4485+const gchar* cat_a_name;
4486+const gchar* cat_b_name;
4487+ui_bud_tabview_cat_type_t cat_a_type, cat_b_type;
4488+guint32 cat_a_key, cat_b_key;
4489+gboolean cat_a_is_childheader, cat_a_is_separator, cat_b_is_childheader, cat_b_is_separator;
4490+gint order = 0;
4491+
4492+ gtk_tree_model_get(model, cat_a,
4493+ UI_BUD_TABVIEW_CATEGORY_NAME, &cat_a_name,
4494+ UI_BUD_TABVIEW_CATEGORY_TYPE, &cat_a_type,
4495+ UI_BUD_TABVIEW_CATEGORY_KEY, &cat_a_key,
4496+ UI_BUD_TABVIEW_IS_CHILD_HEADER, &cat_a_is_childheader,
4497+ UI_BUD_TABVIEW_IS_SEPARATOR, &cat_a_is_separator,
4498+ -1);
4499+
4500+ gtk_tree_model_get(model, cat_b,
4501+ UI_BUD_TABVIEW_CATEGORY_NAME, &cat_b_name,
4502+ UI_BUD_TABVIEW_CATEGORY_TYPE, &cat_b_type,
4503+ UI_BUD_TABVIEW_CATEGORY_KEY, &cat_b_key,
4504+ UI_BUD_TABVIEW_IS_CHILD_HEADER, &cat_b_is_childheader,
4505+ UI_BUD_TABVIEW_IS_SEPARATOR, &cat_b_is_separator,
4506+ -1);
4507+
4508+ // Sort first by category type
4509+ if (cat_a_type != cat_b_type)
4510+ {
4511+ switch (cat_a_type)
4512+ {
4513+ case UI_BUD_TABVIEW_CAT_TYPE_INCOME:
4514+ order = -1;
4515+ break;
4516+ case UI_BUD_TABVIEW_CAT_TYPE_EXPENSE:
4517+ order = 0;
4518+ break;
4519+ case UI_BUD_TABVIEW_CAT_TYPE_NONE:
4520+ order = 1;
4521+ break;
4522+ }
4523+ }
4524+ else
4525+ {
4526+ // On standard categories, just order by name
4527+ if (!cat_a_is_childheader && !cat_a_is_separator
4528+ && !cat_b_is_childheader && !cat_b_is_separator)
4529+ {
4530+ order = g_utf8_collate(g_utf8_casefold(cat_a_name, -1),
4531+ g_utf8_casefold(cat_b_name, -1)
4532+ );
4533+ }
4534+ // Otherwise, fake categories have to be first (header and separator)
4535+ else if (cat_a_is_childheader || cat_a_is_separator)
4536+ {
4537+ if (!cat_b_is_separator && !cat_b_is_childheader)
4538+ {
4539+ order = -1;
4540+ }
4541+ // When both are fake, header has to be first
4542+ else
4543+ {
4544+ order = (cat_a_is_childheader ? -1 : 1);
4545+ }
4546+ }
4547+ else
4548+ {
4549+ // Same idea for fake categories when cat_b is fake, but
4550+ // with reversed result, because sort function return
4551+ // result according to cat_a
4552+
4553+ if (!cat_a_is_separator && !cat_a_is_childheader)
4554+ {
4555+ order = 1;
4556+ }
4557+ else
4558+ {
4559+ order = (cat_b_is_childheader ? 1 : -1);
4560+ }
4561+ }
4562+ }
4563+
4564+ return order;
4565+}
4566+
4567+// the budget model creation
4568+static GtkTreeModel * ui_bud_tabview_model_new ()
4569+{
4570+GtkTreeStore *budget;
4571+GtkTreeIter *iter_income, *iter_expense;
4572+guint32 n_category;
4573+ui_bud_tabview_search_criteria_t root_search = ui_bud_tabview_search_criteria_default;
4574+
4575+ // Create Tree Store
4576+ budget = gtk_tree_store_new ( UI_BUD_TABVIEW_NUMBER_COLOMNS,
4577+ G_TYPE_UINT, // UI_BUD_TABVIEW_CATEGORY_KEY
4578+ G_TYPE_STRING, // UI_BUD_TABVIEW_CATEGORY_NAME
4579+ G_TYPE_STRING, // UI_BUD_TABVIEW_CATEGORY_FULLNAME
4580+ G_TYPE_INT, // UI_BUD_TABVIEW_CATEGORY_TYPE
4581+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_ROOT
4582+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_TOTAL
4583+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_CHILD_HEADER
4584+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_SEPARATOR
4585+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_MONITORING_FORCED
4586+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_SAME_AMOUNT
4587+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_IS_SUB_CATEGORY
4588+ G_TYPE_BOOLEAN, // UI_BUD_TABVIEW_HAS_BUDGET
4589+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_SAME_AMOUNT
4590+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_JANUARY
4591+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_FEBRUARY
4592+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_MARCH
4593+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_APRIL
4594+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_MAY
4595+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_JUNE
4596+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_JULY
4597+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_AUGUST
4598+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_SEPTEMBER
4599+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_OCTOBER
4600+ G_TYPE_DOUBLE, // UI_BUD_TABVIEW_NOVEMBER
4601+ G_TYPE_DOUBLE // UI_BUD_TABVIEW_DECEMBER
4602+ );
4603+
4604+ // Populate the store
4605+
4606+ /* Create tree roots */
4607+ ui_bud_tabview_model_insert_roots (budget);
4608+
4609+ // Retrieve required root
4610+ root_search.row_is_root = TRUE;
4611+ root_search.row_is_total = FALSE;
4612+
4613+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_INCOME;
4614+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
4615+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
4616+ &root_search);
4617+ iter_income = root_search.iterator;
4618+
4619+ root_search.row_category_type = UI_BUD_TABVIEW_CAT_TYPE_EXPENSE;
4620+ gtk_tree_model_foreach(GTK_TREE_MODEL(budget),
4621+ (GtkTreeModelForeachFunc) ui_bud_tabview_model_search_iterator,
4622+ &root_search);
4623+ iter_expense = root_search.iterator;
4624+
4625+ /* Create rows for real categories */
4626+ n_category = da_cat_get_max_key();
4627+
4628+ for(guint32 i=1; i<=n_category; ++i)
4629+ {
4630+ Category *bdg_category;
4631+ gboolean cat_is_income;
4632+
4633+ bdg_category = da_cat_get(i);
4634+
4635+ if (bdg_category == NULL)
4636+ {
4637+ continue;
4638+ }
4639+
4640+ cat_is_income = (category_type_get (bdg_category) == 1);
4641+
4642+ DB(g_print(" category %d:'%s' isincome=%d, issub=%d hasbudget=%d parent=%d\n",
4643+ bdg_category->key, bdg_category->name,
4644+ cat_is_income, (bdg_category->flags & GF_SUB),
4645+ (bdg_category->flags & GF_BUDGET), bdg_category->parent));
4646+
4647+ // Compute totals and initiate category in right tree root
4648+ if (cat_is_income)
4649+ {
4650+ ui_bud_tabview_model_add_category_with_lineage(budget, iter_income, &(bdg_category->key));
4651+ }
4652+ else if (!cat_is_income)
4653+ {
4654+ ui_bud_tabview_model_add_category_with_lineage(budget, iter_expense, &(bdg_category->key));
4655+ }
4656+ }
4657+
4658+ /* Create rows for total root */
4659+ ui_bud_tabview_model_update_monthly_total(GTK_TREE_STORE(budget));
4660+
4661+ /* Sort categories on same node level */
4662+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(budget),
4663+ UI_BUD_TABVIEW_CATEGORY_NAME, ui_bud_tabview_model_row_sort,
4664+ NULL, NULL);
4665+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (budget),
4666+ UI_BUD_TABVIEW_CATEGORY_NAME, GTK_SORT_ASCENDING);
4667+
4668+ g_free(root_search.iterator);
4669+
4670+ return GTK_TREE_MODEL(budget);
4671+}
4672+
4673+/**
4674+ * GtkTreeView functions
4675+ **/
4676+
4677+// Display category name in bold if it has budget
4678+static void ui_bud_tabview_view_display_category_name (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4679+{
4680+ui_bud_tabview_data_t *data = user_data;
4681+gboolean has_budget, is_sub_category;
4682+PangoWeight weight = PANGO_WEIGHT_NORMAL;
4683+ui_bud_tabview_view_mode_t view_mode = UI_BUD_TABVIEW_VIEW_SUMMARY;
4684+
4685+ gtk_tree_model_get(model, iter,
4686+ UI_BUD_TABVIEW_IS_SUB_CATEGORY, &is_sub_category,
4687+ UI_BUD_TABVIEW_HAS_BUDGET, &has_budget,
4688+ -1);
4689+
4690+ view_mode = radio_get_active(GTK_CONTAINER(data->RA_mode));
4691+
4692+ if (view_mode != UI_BUD_TABVIEW_VIEW_SUMMARY && has_budget)
4693+ {
4694+ weight = PANGO_WEIGHT_BOLD;
4695+ }
4696+
4697+ g_object_set(renderer,
4698+ "style", is_sub_category ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL,
4699+ "weight", weight,
4700+ NULL);
4701+}
4702+
4703+// to enable or not edition on month columns
4704+static void ui_bud_tabview_view_display_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4705+{
4706+GtkAdjustment *adjustment;
4707+gboolean is_same_amount, is_root, is_total, is_visible, is_editable;
4708+ui_bud_tabview_cat_type_t row_category_type;
4709+gdouble amount = 0.0;
4710+gchar *text;
4711+gchar *fgcolor;
4712+const ui_bud_tabview_store_t column_id = GPOINTER_TO_INT(user_data);
4713+
4714+ gtk_tree_model_get(model, iter,
4715+ UI_BUD_TABVIEW_CATEGORY_TYPE, &row_category_type,
4716+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4717+ UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
4718+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4719+ -1);
4720+
4721+ // Text to display
4722+ if (is_same_amount)
4723+ {
4724+ gtk_tree_model_get(model, iter, UI_BUD_TABVIEW_SAME_AMOUNT, &amount, -1);
4725+ }
4726+ else if (column_id >= UI_BUD_TABVIEW_JANUARY && column_id <= UI_BUD_TABVIEW_DECEMBER)
4727+ {
4728+ gtk_tree_model_get(model, iter, column_id, &amount, -1);
4729+ }
4730+
4731+ text = g_strdup_printf("%.2f", amount);
4732+ fgcolor = get_normal_color_amount(amount);
4733+
4734+ // Default styling values
4735+ is_visible = TRUE;
4736+ is_editable = FALSE;
4737+
4738+ if (is_root)
4739+ {
4740+ is_visible = FALSE;
4741+ is_editable = FALSE;
4742+ }
4743+ else if (is_total)
4744+ {
4745+ is_visible = TRUE;
4746+ is_editable = FALSE;
4747+
4748+ if (column_id == UI_BUD_TABVIEW_SAME_AMOUNT)
4749+ {
4750+ is_visible = FALSE;
4751+ }
4752+ }
4753+ else if (is_same_amount)
4754+ {
4755+ is_visible = TRUE;
4756+ is_editable = FALSE;
4757+
4758+ if (column_id == UI_BUD_TABVIEW_SAME_AMOUNT)
4759+ {
4760+ is_editable = TRUE;
4761+ }
4762+ }
4763+ else if (! is_same_amount)
4764+ {
4765+ is_visible = TRUE;
4766+ is_editable = TRUE;
4767+
4768+ if (column_id == UI_BUD_TABVIEW_SAME_AMOUNT)
4769+ {
4770+ is_editable = FALSE;
4771+ }
4772+ }
4773+
4774+ // Finally, visibility depends on set amount
4775+#if !HB_BUD_TABVIEW_EDIT_ENABLE
4776+ is_visible = (is_visible && amount != 0.0);
4777+ is_editable = FALSE;
4778+#else
4779+ is_visible = (is_visible && (is_editable || amount != 0.0));
4780+#endif
4781+
4782+ adjustment = gtk_adjustment_new(
4783+ 0.0, // initial-value
4784+ -G_MAXDOUBLE, // minmal-value
4785+ G_MAXDOUBLE, // maximal-value
4786+ 0.5, // step increment
4787+ 10, // page increment
4788+ 0); // page size (0 because irrelevant for GtkSpinButton)
4789+
4790+ g_object_set(renderer,
4791+ "text", text,
4792+ "visible", is_visible,
4793+ "editable", is_editable,
4794+ "foreground", fgcolor,
4795+ "xalign", 1.0,
4796+ "adjustment", adjustment,
4797+ "digits", 2,
4798+ NULL);
4799+
4800+ g_free(text);
4801+}
4802+
4803+// to enable or not edition on month columns
4804+static void ui_bud_tabview_view_display_is_same_amount (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4805+{
4806+gboolean is_same_amount, is_root, is_total, is_visible, is_sensitive;
4807+
4808+ gtk_tree_model_get(model, iter,
4809+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4810+ UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
4811+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4812+ -1);
4813+
4814+ // Default values
4815+ is_visible = TRUE;
4816+ is_sensitive = TRUE;
4817+
4818+ if (is_root || is_total)
4819+ {
4820+ is_visible = FALSE;
4821+ is_sensitive = FALSE;
4822+ }
4823+
4824+ g_object_set(renderer,
4825+ "activatable", TRUE,
4826+ "active", is_same_amount,
4827+ "visible", is_visible,
4828+ "sensitive", is_sensitive,
4829+ NULL);
4830+}
4831+
4832+// Compute dynamically the annual total
4833+static void ui_bud_tabview_view_display_annual_total (GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4834+{
4835+gboolean is_same_amount = FALSE, is_total = FALSE, is_root = FALSE;
4836+gdouble amount = 0.0;
4837+gdouble total = 0.0;
4838+gchar *text;
4839+gchar *fgcolor;
4840+gboolean is_visible = TRUE;
4841+
4842+ gtk_tree_model_get(model, iter,
4843+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4844+ UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
4845+ UI_BUD_TABVIEW_SAME_AMOUNT, &amount,
4846+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4847+ -1);
4848+
4849+ if (is_same_amount)
4850+ {
4851+ total = 12.0 * amount;
4852+ }
4853+ else
4854+ {
4855+ for (int i = UI_BUD_TABVIEW_JANUARY ; i <= UI_BUD_TABVIEW_DECEMBER ; ++i)
4856+ {
4857+ gtk_tree_model_get(model, iter, i, &amount, -1);
4858+ total += amount;
4859+ }
4860+ }
4861+
4862+ text = g_strdup_printf("%.2f", total);
4863+ fgcolor = get_normal_color_amount(total);
4864+
4865+ if (is_root)
4866+ {
4867+ is_visible = FALSE;
4868+ }
4869+
4870+ // Finally, visibility depends on set amount
4871+ is_visible = (is_visible && amount != 0.0);
4872+
4873+ g_object_set(renderer,
4874+ "text", text,
4875+ "foreground", fgcolor,
4876+ "visible", is_visible,
4877+ "xalign", 1.0,
4878+ NULL);
4879+
4880+ g_free(text);
4881+}
4882+
4883+// Compute monthly average
4884+static void ui_bud_tabview_view_display_monthly_average(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
4885+{
4886+gboolean is_same_amount = FALSE, is_total = FALSE, is_root = FALSE;
4887+gdouble amount = 0.0;
4888+gdouble average = 0.0;
4889+gchar *text;
4890+gchar *fgcolor;
4891+gboolean is_visible = TRUE;
4892+
4893+ gtk_tree_model_get(model, iter,
4894+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4895+ UI_BUD_TABVIEW_IS_SAME_AMOUNT, &is_same_amount,
4896+ UI_BUD_TABVIEW_SAME_AMOUNT, &amount,
4897+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4898+ -1);
4899+
4900+ if (is_same_amount)
4901+ {
4902+ average = amount;
4903+ }
4904+ else
4905+ {
4906+ for (int i = UI_BUD_TABVIEW_JANUARY ; i <= UI_BUD_TABVIEW_DECEMBER ; ++i)
4907+ {
4908+ gtk_tree_model_get(model, iter, i, &amount, -1);
4909+ average += amount;
4910+ }
4911+ average = hb_amount_round(average / 12.0, 2);
4912+ }
4913+
4914+ text = g_strdup_printf("%.2f", average);
4915+ fgcolor = get_normal_color_amount(average);
4916+
4917+ if (is_root)
4918+ {
4919+ is_visible = FALSE;
4920+ }
4921+
4922+ // Finally, visibility depends on set amount
4923+ is_visible = (is_visible && average != 0.0);
4924+
4925+ g_object_set(renderer,
4926+ "text", text,
4927+ "foreground", fgcolor,
4928+ "visible", is_visible,
4929+ "xalign", 1.0,
4930+ NULL);
4931+
4932+ g_free(text);
4933+}
4934+
4935+// When view mode is toggled:
4936+// - recreate the view to update columns rendering
4937+static void ui_bud_tabview_view_toggle (gpointer user_data, ui_bud_tabview_view_mode_t view_mode)
4938+{
4939+ui_bud_tabview_data_t *data = user_data;
4940+GtkTreeModel *budget;
4941+GtkWidget *view;
4942+GtkTreePath* firstRow;
4943+
4944+ view = data->TV_budget;
4945+
4946+ budget = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
4947+
4948+ gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(budget));
4949+
4950+ if (data->TV_is_expanded)
4951+ {
4952+ gtk_tree_view_expand_all(GTK_TREE_VIEW(view));
4953+ }
4954+ else
4955+ {
4956+ ui_bud_tabview_model_collapse(GTK_TREE_VIEW(view));
4957+ }
4958+
4959+ gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)));
4960+
4961+ firstRow = gtk_tree_path_new_from_string("0");
4962+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(view), firstRow, data->TVC_category, TRUE, 0, 0);
4963+
4964+ DB(g_print("[ui_bud_tabview] : button state changed to: %d\n", view_mode));
4965+
4966+ return;
4967+}
4968+
4969+static gboolean ui_bud_tabview_view_search (GtkTreeModel *filter, gint column, const gchar *key, GtkTreeIter *filter_iter, gpointer data)
4970+{
4971+gboolean is_matching = FALSE, is_root, is_total;
4972+GtkTreeModel *budget;
4973+GtkTreeIter iter;
4974+gchar *category_name;
4975+
4976+ budget = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filter));
4977+ gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter),
4978+ &iter,
4979+ filter_iter);
4980+
4981+ gtk_tree_model_get(budget, &iter,
4982+ UI_BUD_TABVIEW_CATEGORY_NAME, &category_name,
4983+ UI_BUD_TABVIEW_IS_ROOT, &is_root,
4984+ UI_BUD_TABVIEW_IS_TOTAL, &is_total,
4985+ -1);
4986+
4987+ if (!is_root && !is_total
4988+ && g_strstr_len(g_utf8_casefold(category_name, -1), -1,
4989+ g_utf8_casefold(key, -1)))
4990+ {
4991+ is_matching = TRUE;
4992+ }
4993+
4994+ // GtkTreeViewSearchEqualFunc has to return FALSE only if iter matches.
4995+ return !is_matching;
4996+}
4997+
4998+#if HB_BUD_TABVIEW_EDIT_ENABLE
4999+static gboolean ui_bud_tabview_view_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
5000+{
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches