diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..ff45129 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,47 @@ +v0.2 (04/07/2011) + +** User ** + Better use of sizers (so better interface!) + No further problems with scrollbar in AccountPanel/SearchPanel/PreferencesPanel when there is a lot of operations + Better fit of interface when displaying grouped operations + Show stats for current month even if next month has not been generated + Possibility to choose a replacement when deleting accounts and categories + Possibility to find operations with unknown category/account + Possibility to work on multiple operations (rename, change category, change account) + Add virtual accounts + Searchs aren't case sensitive enough + Default categories in native language (even if switching language at runtime) + Add Real mode + Database is now at ~/.kisscount/kc.bdd + Add Debian's packages !! + Add Import Panel, XML, OFX and Grisbi imports + Add Export Panel (only XML) + Add update next months + Change charts and real/virtual mode position + Add "non fix" category to statistics (sum of all non fix categories) + Auto completion does not require date to be set + +** Dev ** + Use a factory to create panels (prepare for plug-in) + Better cross compilation support (Makefile and package.sh) + Update Copyright + New database version (2) : + fix_cost for categories + virtual field for account and operation + import_panel + Database checking & upgrading + +** Bugs ** + Bug on GenerateMonth with different years + In StatsPanel : + Bad selection of year with multiple years + Bad year range check + Unordered accounts (makes account selection points to bad indexes on account graph) + Bug in account creation (readonly fields) + Bug in search bad computation of month with start_date and end_date + Grouped operations have bad month/year in SearchPanel + Categories fonts not updated for new category --> crash + New category had read only fields in PreferencesPanel + Update all panels after generating/deleting month + Don't take in account unknown categories (in stats) + Don't try to draw more than MAX_CATEGORIES (12) in charts diff --git a/INSTALL b/INSTALL index 794d553..aa12c3d 100644 --- a/INSTALL +++ b/INSTALL @@ -26,9 +26,9 @@ cd - ** Compilation of KissCount ** +make clean make - +sudo make install ** Execution ** -export LD_LIBRARY_PATH=$PWD/lib/freechart/lib:$PWD/lib/wxsqlite3-1.9.9/lib/ -./kc +kc diff --git a/Makefile b/Makefile old mode 100755 new mode 100644 index 092e224..25f7569 --- a/Makefile +++ b/Makefile @@ -1,8 +1,21 @@ +LIB_DIR=$(DESTDIR)"/usr/lib/kisscount/" +SHARE_DIR=$(DESTDIR)"/usr/share/kisscount/" +DOC_DIR=$(DESTDIR)"/usr/share/doc/kisscount/" +BIN_DIR=$(DESTDIR)"/usr/bin/" + CXXFLAGS+=`wx-config --cxxflags` -Wall -Isrc -ggdb CXXFLAGS+=-I./lib/wxsqlite3-1.9.9/include CXXFLAGS+=-I./lib/freechart/include +CXXFLAGS+=-I/usr/include/libxml2 +CXXFLAGS+=-Wl,--rpath,"$(LIB_DIR)" +#CXXFLAGS+=-DRESSOURCES_ROOT="\"$(SHARE_DIR)\"" +# For developpers +CXXFLAGS+=-DRESSOURCES_ROOT="\"./ressources/\"" LDFLAGS+=`wx-config --libs` +LDFLAGS+=-lofx +LDFLAGS+=-lxml2 + ifdef WIN32 LDFLAGS+=-L./lib/wxsqlite3-1.9.9/lib/ -lwxcode_msw_wxsqlite3-2.8 LDFLAGS+=-L./lib/freechart/lib -lwxcode_msw_freechart-2.8 @@ -11,27 +24,20 @@ LDFLAGS+=-L./lib/wxsqlite3-1.9.9/lib/ -lwxcode_gtk2u_wxsqlite3-2.8 LDFLAGS+=-L./lib/freechart/lib -lwxcode_gtk2u_freechart-2.8 endif -CXX=$(PREFIX)g++ +CXX=$(HOST)g++ -SOURCES=$(wildcard src/model/*.cpp) -SOURCES+=$(wildcard src/view/*.cpp) -SOURCES+=$(wildcard src/view/grid/*.cpp) -SOURCES+=$(wildcard src/controller/*.cpp) -SOURCES+=src/main.cpp src/sha1.cpp src/ParseExp.cpp -HEADERS=$(wildcard src/model/*.h) -HEADERS+=$(wildcard src/view/*.h) -HEADERS+=$(wildcard src/view/grid/*.h) -HEADERS+=$(wildcard src/controller/*.h) -HEADERS+=src/main.h src/sha1.h +SOURCES=$(shell find src -name '*.cpp' -type f | tr '\n' ' ') +HEADERS=$(shell find src -name '*.h' -type f) OBJS=$(SOURCES:.cpp=.o) all: check kc clean: - rm -f *~ src/*~ src/*.o src/model/*.o src/model/*~ src/view/*.o src/view/grid/*.o src/view/grid/*~ src/view/*~ src/controller/*.o src/controller/*~ kc + find src -type f -name '*.[o~]' -exec rm -f \{\} \; + rm -f kc -%.o : src/model/%.cpp src/view/%.cpp src/view/grid/%.cpp src/controller/%.cpp src/%.cpp - $(CXX) $(CXXFLAGS) $< -c +%.o : src/model/%.cpp src/model/import/%.cpp src/model/export/%.cpp src/view/%.cpp src/view/grid/%.cpp src/controller/%.cpp src/%.cpp + $(CXX) $(CXXFLAGS) -c $< check: if ! test -d lib ; then echo lib directory not found, please see INSTALL ; return 1 ; fi @@ -48,4 +54,14 @@ package: else package: ./tools/package.sh -endif \ No newline at end of file +endif + +install: + mkdir -p $(LIB_DIR) $(BIN_DIR) $(SHARE_DIR) $(DOC_DIR) + cp kc $(BIN_DIR) + cp -rf lib/*.so* $(LIB_DIR) + cp -rf ressources/* $(SHARE_DIR) + cp -rf README* ChangeLog CONTRIBUTORS COPYING TODO $(DOC_DIR) + +remove: + rm -rf $(LIB_DIR) $(SHARE_DIR) $(DOC_DIR) $(BIN_DIR)/kc diff --git a/README b/README index de07df8..9d79853 100644 --- a/README +++ b/README @@ -1,8 +1,8 @@ KissCount is personnal account software delivered under GPL v3 licence terms. -Current version is 0.1 +Current version is 0.2 -wxWidgets 2.8 and sqlite3 are needed +wxWidgets 2.8, sqlite3, libofx and libxml2 are needed A modified version of wxFreeChart is used : warning during recompilation, don't overwrite autotools files with ./configure diff --git a/README.fr b/README.fr index d352efa..594c3d1 100644 --- a/README.fr +++ b/README.fr @@ -1,8 +1,8 @@ KissCount est un logiciel de gestion de comptes personnels délivré sous licence GPL v3 -La version actuelle est 0.1 +La version actuelle est 0.2 -wxWidgets 2.8 et sqlite3 sont nécessaires +wxWidgets 2.8, sqlite3, libofx et libxml2 sont nécessaires Une version modifiée de wxFreeChart est utilisée : attention à lors de la recompilation à ne pas écraser les fichiers des autotools (pas de ./configure) diff --git a/TODO b/TODO index eb78945..e9a15ec 100644 --- a/TODO +++ b/TODO @@ -1,27 +1,26 @@ -Version 0.2 +Version 0.3 Statistics (need to add months/years label on graph) Auto completion (already up into wxwidgets 2.9) Using tabulation to navigate throw interface (Search Panel) -Improve Scrolled Windows and widgets placement Can type a letter with a comboboxes Windows version -Need packaging (.deb) +Choosing accounts & categories position -Cool for 0.2: +Cool for 0.3: Database auto saving at startup Use caches for created panels (avoid destroying/creating panels for nothing) Add search function to web view Need optimizations by caching operations and categories (using hastables) +Packaging for more distributions =============================================================== Next version More translations -Import/Export module Printing (maybe in xml/html) -Refactor web view code Plugins ? +WxWidgets 2.9 =============================================================== Will not be implemented diff --git a/debian/Makefile b/debian/Makefile new file mode 100644 index 0000000..76a2bc2 --- /dev/null +++ b/debian/Makefile @@ -0,0 +1,72 @@ +LIB_DIR=$(DESTDIR)"/usr/lib/kisscount/" +SHARE_DIR=$(DESTDIR)"/usr/share/kisscount/" +DOC_DIR=$(DESTDIR)"/usr/share/doc/kisscount/" +BIN_DIR=$(DESTDIR)"/usr/bin/" + +CXXFLAGS+=`wx-config --cxxflags` -Wall -Isrc -ggdb +CXXFLAGS+=-I./lib/wxsqlite3-1.9.9/include +CXXFLAGS+=-I./lib/freechart/include +CXXFLAGS+=-Wl,--rpath,"$(LIB_DIR)" +CXXFLAGS+=-DRESSOURCES_ROOT="\"$(SHARE_DIR)\"" +#CXXFLAGS+=-DRESSOURCES_ROOT="\"./ressources/\"" + +LDFLAGS+=`wx-config --libs` +ifdef WIN32 +LDFLAGS+=-L./lib/wxsqlite3-1.9.9/lib/ -lwxcode_msw_wxsqlite3-2.8 +LDFLAGS+=-L./lib/freechart/lib -lwxcode_msw_freechart-2.8 +else +LDFLAGS+=-L./lib/wxsqlite3-1.9.9/lib/ -lwxcode_gtk2u_wxsqlite3-2.8 +LDFLAGS+=-L./lib/freechart/lib -lwxcode_gtk2u_freechart-2.8 +endif + +CXX=$(PREFIX)g++ + +SOURCES=$(wildcard src/model/*.cpp) +SOURCES+=$(wildcard src/view/*.cpp) +SOURCES+=$(wildcard src/view/grid/*.cpp) +SOURCES+=$(wildcard src/controller/*.cpp) +SOURCES+=src/main.cpp src/sha1.cpp src/ParseExp.cpp +HEADERS=$(wildcard src/model/*.h) +HEADERS+=$(wildcard src/view/*.h) +HEADERS+=$(wildcard src/view/grid/*.h) +HEADERS+=$(wildcard src/controller/*.h) +HEADERS+=src/main.h src/sha1.h +OBJS=$(SOURCES:.cpp=.o) + +all: check kc + +clean: + rm -f *~ src/*~ src/*.o src/model/*.o src/model/*~ src/view/*.o src/view/grid/*.o src/view/grid/*~ src/view/*~ src/controller/*.o src/controller/*~ kc + +# %.o : src/model/%.cpp src/view/%.cpp src/view/grid/%.cpp src/controller/%.cpp src/%.cpp +# $(CXX) $(CXXFLAGS) $< -c + +check: +# if ! test -d lib ; then echo lib directory not found, please see INSTALL ; return 1 ; fi + +#kc: $(OBJS) +# $(CXX) $(CXXFLAGS) $^ -o $@ $(LDFLAGS) + +kc: + cp ../kc . + +generate_locales: + ./tools/generate_locales.sh + +ifdef WIN32 +package: + ./tools/package_win32.sh +else +package: + ./tools/package.sh +endif + +install: + mkdir -p $(LIB_DIR) $(BIN_DIR) $(SHARE_DIR) $(DOC_DIR) + cp kc $(BIN_DIR) + cp -rf lib/*.so* $(LIB_DIR) + cp -rf ressources/* $(SHARE_DIR) + cp -rf README* ChangeLog CONTRIBUTORS TODO $(DOC_DIR) + +remove: + rm -rf $(LIB_DIR) $(SHARE_DIR) $(DOC_DIR) $(BIN_DIR)/kc diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..bc81166 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +kisscount (0.2-1) unstable; urgency=low + + * Initial release + + -- Grégory Soutadé Sat, 26 Feb 2011 13:13:52 +0100 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +7 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..9f5440a --- /dev/null +++ b/debian/control @@ -0,0 +1,17 @@ +Source: kisscount +Section: misc +Priority: extra +Maintainer: Grégory Soutadé +Build-Depends: debhelper (>= 7.0.50~), libsqlite3-dev, libwxgtk2.8-dev (>= 2.8.10), libofx-dev, libxml2-dev +Standards-Version: 3.9.1 +Homepage: http://indefero.soutade.fr/p/kisscount/ +Vcs-Git: git://soutade.fr/kisscount.git +Vcs-Browser: http://indefero.soutade.fr/p/kisscount/source/tree/master/ + +Package: kisscount +Architecture: any +Depends: ${shlibs:Depends} ${misc:Depends} +Description: Personal accounting software + KissCount is a personal accounting software. + It focuses on simplicity and everyday + users requirements. \ No newline at end of file diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..22a3eb9 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,29 @@ +Format: http://dep.debian.net/deps/dep5 +Upstream-Name: kisscount +Source: http://indefero.soutade.fr/p/kisscount/ + +Files: * +Copyright: 2010-2011 Grégory Soutadé + +License: GPL-3.0+ + +Files: debian/* +Copyright: 2011 Grégory Soutadé +License: GPL-3.0+ + +License: GPL-3.0+ + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..c2788bf --- /dev/null +++ b/debian/docs @@ -0,0 +1,5 @@ +README +README.fr +ChangeLog +TODO +CONTRIBUTORS diff --git a/debian/kisscount.manpages b/debian/kisscount.manpages new file mode 100644 index 0000000..3a00e83 --- /dev/null +++ b/debian/kisscount.manpages @@ -0,0 +1 @@ +kc.1 \ No newline at end of file diff --git a/debian/menu b/debian/menu new file mode 100644 index 0000000..ed7c806 --- /dev/null +++ b/debian/menu @@ -0,0 +1,2 @@ +?package(kisscount):needs="X11" section="Applications/Office"\ + title="kisscount" command="/usr/bin/kc" diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..5c7d3aa --- /dev/null +++ b/debian/rules @@ -0,0 +1,30 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +export DH_VERBOSE=1 + +%: + dh $@ + +# clean: + +# build: +# $(MAKE) + +# binary: + +# binary-arch: + +# binary-indep: + +# install: +# $(MAKE) install DESTDIR=$(CURDIR)/debian/kisscount/ + + +override_dh_strip: diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/kc.1 b/kc.1 new file mode 100644 index 0000000..b37925d --- /dev/null +++ b/kc.1 @@ -0,0 +1,35 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH KC 1 "February 26, 2011" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +KissCount \- Personal Accounting Software +.SH SYNOPSIS +.B kc +.RI [ bdd_file ] +.SH DESCRIPTION +KissCount is a personal accounting software. Its goal is to be as simple as possible (matching KISS philosophy). +.br +It focuses on PERSONAL accounting (not companies/associations). You only have to enter operations and see what you have (or not). +.TP +.B bdd_file +Choose another database file than ~/.kisscount/kc.bdd +.SH FILES +Default database is ~/.kisscount/kc.bdd +.SH AUTHOR +KissCount was written by . +.PP +This manual page was written by Grégory Soutadé , diff --git a/ressources/icons/SOURCE b/ressources/icons/SOURCE index db176a4..2e0b92e 100644 --- a/ressources/icons/SOURCE +++ b/ressources/icons/SOURCE @@ -1 +1,2 @@ http://www.iconarchive.com/category/application-icons.html +http://www.webidev.com/fr/WebiInscrit diff --git a/ressources/icons/Search-icon.png b/ressources/icons/Search-icon.png index 69bf37e..8fa0d16 100644 Binary files a/ressources/icons/Search-icon.png and b/ressources/icons/Search-icon.png differ diff --git a/ressources/icons/export-icon.png b/ressources/icons/export-icon.png new file mode 100644 index 0000000..2e0edd7 Binary files /dev/null and b/ressources/icons/export-icon.png differ diff --git a/ressources/icons/import-icon.png b/ressources/icons/import-icon.png new file mode 100644 index 0000000..11ce262 Binary files /dev/null and b/ressources/icons/import-icon.png differ diff --git a/init.sql b/ressources/init.sql similarity index 66% rename from init.sql rename to ressources/init.sql index 08b9bcc..ab44767 100755 --- a/init.sql +++ b/ressources/init.sql @@ -1,9 +1,10 @@ -CREATE TABLE kisscount(db_version VARCHAR(20)); +CREATE TABLE kisscount(db_version VARCHAR(20)); CREATE TABLE user (id INTEGER PRIMARY KEY, name VARCHAR(255), password VARCHAR(255)); -CREATE TABLE account(id INTEGER PRIMARY KEY, user REFERENCES user(id), name VARCHAR(255), number VARCHAR(255), shared CHAR(1), blocked CHAR(1), default_account CHAR(1)); +CREATE TABLE account(id INTEGER PRIMARY KEY, user REFERENCES user(id), name VARCHAR(255), number VARCHAR(255), shared CHAR(1), blocked CHAR(1), default_account CHAR(1), virtual CHAR(1)); CREATE TABLE shared_account(account REFERENCES account(id), user REFERENCES user(id)); CREATE TABLE account_amount(id INTEGER PRIMARY KEY, account REFERENCES account(id), year INTEGER, month INTEGER, amount FLOAT); -CREATE TABLE operation(id INTEGER PRIMARY KEY, parent REFERENCES operation(id), user REFERENCES user(id), account REFERENCES account(id), year INTEGER, month INTEGER, day INTEGER, amount FLOAT, description VARCHAR(255), category REFERENCES category(id), fix_cost CHAR(1), checked CHAR(1), formula VARCHAR(255), transfert REFERENCES operation(id), meta CHAR(1)); -CREATE TABLE category(id INTEGER PRIMARY KEY, user REFERENCES user(id), parent REFERENCES category(id), name VARCHAR(255), backcolor VARCHAR(10), forecolor VARCHAR(10), font VARCHAR(255)); +CREATE TABLE operation(id INTEGER PRIMARY KEY, parent REFERENCES operation(id), user REFERENCES user(id), account REFERENCES account(id), year INTEGER, month INTEGER, day INTEGER, amount FLOAT, description VARCHAR(255), category REFERENCES category(id), fix_cost CHAR(1), checked CHAR(1), formula VARCHAR(255), transfert REFERENCES operation(id), meta CHAR(1), virtual CHAR(1)); +CREATE TABLE category(id INTEGER PRIMARY KEY, user REFERENCES user(id), parent REFERENCES category(id), name VARCHAR(255), backcolor VARCHAR(10), forecolor VARCHAR(10), font VARCHAR(255), fix_cost CHAR(1)); CREATE TABLE preference(id INTEGER PRIMARY KEY, user REFERENCES user(id), name VARCHAR(255), value VARCHAR(255)); -INSERT INTO kisscount ("db_version") VALUES ("1"); \ No newline at end of file +CREATE TABLE import_pattern(id INTEGER PRIMARY KEY, user REFERENCES user(id), description VARCHAR(255), pattern VARCHAR(255), account REFERENCES account(id), category REFERENCES category(id)); +INSERT INTO kisscount ("db_version") VALUES ("2"); diff --git a/ressources/po/french.mo b/ressources/po/french.mo index c503e56..c16724f 100644 Binary files a/ressources/po/french.mo and b/ressources/po/french.mo differ diff --git a/ressources/po/french.po b/ressources/po/french.po index 182001b..20f08da 100644 --- a/ressources/po/french.po +++ b/ressources/po/french.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-12-02 19:11+0100\n" +"POT-Creation-Date: 2011-07-04 20:23+0200\n" "PO-Revision-Date: \n" "Last-Translator: Soutadé \n" "Language-Team: \n" @@ -19,293 +19,383 @@ msgstr "" # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # -#: src/view/StatsPanel.cpp:327 -#: src/view/PreferencesPanel.cpp:775 +#: src/view/StatsPanel.cpp:384 +#: src/view/PreferencesPanel.cpp:832 msgid " - " msgstr " - " -#: src/view/UsersDialog.cpp:118 -#: src/view/PreferencesPanel.cpp:456 -#: src/view/PreferencesPanel.cpp:476 -#: src/view/PreferencesPanel.cpp:677 -#: src/view/PreferencesPanel.cpp:696 -#: src/view/PreferencesPanel.cpp:733 +#: src/view/UsersDialog.cpp:125 +#: src/view/PreferencesPanel.cpp:496 +#: src/view/PreferencesPanel.cpp:516 +#: src/view/PreferencesPanel.cpp:727 +#: src/view/PreferencesPanel.cpp:746 +#: src/view/PreferencesPanel.cpp:790 msgid " already exists" msgstr " existe déjà" -#: src/view/SearchPanel.cpp:226 +#: src/view/ImportPanel.cpp:283 +msgid " and " +msgstr " et " + +#: src/view/SearchPanel.cpp:114 +#: src/view/ExportPanel.cpp:106 msgid " entries found" msgstr " entrées trouvées" -#: src/model/Database.cpp:147 +#: src/model/Database.cpp:106 msgid " not found, aborting" msgstr " non trouvé, arrêt" -#: src/view/AccountPanel.cpp:742 +#: src/view/AccountPanel.cpp:879 msgid " operations ?" msgstr " opérations ?" -#: src/view/PreferencesPanel.cpp:782 +#: src/view/PreferencesPanel.cpp:839 msgid " profil ?" msgstr " profil ?" -#: src/model/Database.cpp:136 +#: src/view/ImportPanel.cpp:289 +msgid " will be created, is it ok ?" +msgstr " vont être créés, êtes vous d'accord ?" + +#: src/model/Database.cpp:92 msgid "!! Warning !! If there was a bug, the old database will be suppressed !" msgstr "!! Attention !! S'il y a eu un bug, l'ancienne base de donnée va être supprimée !" -#: src/view/SearchPanel.cpp:228 +#: src/view/ImportPanel.cpp:282 +#, c-format +msgid "%d accounts" +msgstr "%d comptes" + +#: src/view/ImportPanel.cpp:287 +#, c-format +msgid "%d categories" +msgstr "%d catégories" + +#: src/view/SearchPanel.cpp:116 +#: src/view/ExportPanel.cpp:108 msgid "1 entry found" msgstr "1 entrée trouvée" -#: src/view/ButtonPanel.cpp:74 -msgid "About" -msgstr "A propos" - -#: src/view/SearchPanel.cpp:90 -#: src/view/grid/GridAccount.cpp:64 +#: src/view/SearchBanner.cpp:83 +#: src/view/grid/GridAccount.cpp:63 msgid "Account" msgstr "Compte" -#: src/view/PreferencesPanel.cpp:456 -#: src/view/PreferencesPanel.cpp:476 +#: src/view/PreferencesPanel.cpp:496 +#: src/view/PreferencesPanel.cpp:516 msgid "Account " msgstr "Le compte " -#: src/controller/KissCount.cpp:343 +#: src/controller/KissCount.cpp:372 msgid "Account 1" msgstr "Compte 1" -#: src/view/AccountPanel.cpp:106 +#: src/view/ImportPanel.cpp:74 +#: src/view/AccountPanel.cpp:112 msgid "Account name" msgstr "Nom du compte" -#: src/view/AccountPanel.cpp:105 +#: src/view/AccountPanel.cpp:111 msgid "Account number" msgstr "Numéro de compte" -#: src/view/StatsPanel.cpp:292 -#: src/view/PreferencesPanel.cpp:59 +#: src/view/StatsPanel.cpp:336 +#: src/view/PreferencesPanel.cpp:60 msgid "Accounts" msgstr "Comptes" -#: src/view/SearchPanel.cpp:87 +#: src/view/AccountPanel.cpp:1107 +msgid "Accounts updated until " +msgstr "Comptes mis à jours jusqu'à " + +#: src/view/SearchBanner.cpp:79 msgid "Amount from" msgstr "Montant min" -#: src/view/SearchPanel.cpp:88 +#: src/view/SearchBanner.cpp:80 msgid "Amount to" msgstr "Montant max" -#: src/view/PreferencesPanel.cpp:408 -#: src/view/PreferencesPanel.cpp:782 -#: src/view/AccountPanel.cpp:737 +#: src/view/AccountPanel.cpp:1114 +msgid "Any account updated !" +msgstr "Aucun compte mis à jour" + +#: src/view/ImportPanel.cpp:175 +#: src/view/ExportPanel.cpp:138 +msgid "Any engine can process this file !" +msgstr "Format de fichier non reconnu" + +#: src/view/PreferencesPanel.cpp:839 +#: src/view/AccountPanel.cpp:874 msgid "Are you sure want to delete " msgstr "Etes vous sûr de vouloir supprimer " -#: src/view/PreferencesPanel.cpp:657 -#: src/view/grid/GridAccount.cpp:814 +#: src/view/grid/GridAccount.cpp:859 msgid "Are you sure want to delete : \n" msgstr "Etes vous sûr de vouloir supprimer : \n" -#: src/view/PreferencesPanel.cpp:343 +#: src/view/ImportPanel.cpp:367 +msgid "Are you sure want to integrate these operations ?" +msgstr "Etes vous sûr de vouloir intégreer ces opérations" + +#: src/view/PreferencesPanel.cpp:369 msgid "Ascending" msgstr "Croissant" -#: src/view/PreferencesPanel.cpp:267 +#: src/view/PreferencesPanel.cpp:293 msgid "Background color" msgstr "Couleur d'arrière plan" -#: src/view/PreferencesPanel.cpp:187 +#: src/view/PreferencesPanel.cpp:200 msgid "Blocked" msgstr "Bloqué" -#: src/view/UsersDialog.cpp:51 -#: src/view/GenerateDialog.cpp:60 +#: src/view/UsersDialog.cpp:52 +#: src/view/GenerateDialog.cpp:61 #: src/view/PasswordDialog.cpp:56 msgid "Cancel" msgstr "Annuler" -#: src/view/grid/GridAccount.cpp:1245 -#: src/view/grid/GridAccount.cpp:1252 +#: src/view/grid/GridAccount.cpp:1294 +#: src/view/grid/GridAccount.cpp:1301 msgid "Cannot group these operations" msgstr "Impossible de grouper ces opérations" -#: src/view/grid/GridAccount.cpp:1371 -#: src/view/grid/GridAccount.cpp:1377 +#: src/view/grid/GridAccount.cpp:1420 +#: src/view/grid/GridAccount.cpp:1426 msgid "Cannot ungroup these operations" msgstr "Impossible de dégrouper ces opérations" -#: src/view/PreferencesPanel.cpp:60 +#: src/controller/KissCount.cpp:363 +msgid "Car" +msgstr "Voiture" + +#: src/view/PreferencesPanel.cpp:61 msgid "Categories" msgstr "Catégories" -#: src/view/SearchPanel.cpp:89 -#: src/view/grid/GridAccount.cpp:64 +#: src/view/SearchBanner.cpp:81 +#: src/view/grid/GridAccount.cpp:63 msgid "Category" msgstr "Catégorie" -#: src/view/PreferencesPanel.cpp:677 -#: src/view/PreferencesPanel.cpp:696 +#: src/view/PreferencesPanel.cpp:727 +#: src/view/PreferencesPanel.cpp:746 msgid "Category " msgstr "La catégorie " -#: src/view/PreferencesPanel.cpp:77 +#: src/view/ImportPanel.cpp:82 +msgid "Category name" +msgstr "Catégorie" + +#: src/view/PreferencesPanel.cpp:78 msgid "Change Name" msgstr "Changer de nom" -#: src/view/PreferencesPanel.cpp:78 +#: src/view/PreferencesPanel.cpp:79 msgid "Change Password" msgstr "Changer le mot de passe" +#: src/view/SearchPanel.cpp:59 +msgid "Change account" +msgstr "Changer de compte" + +#: src/view/SearchPanel.cpp:60 +msgid "Change category" +msgstr "Changer la catégorie" + #: src/view/PasswordDialog.cpp:29 msgid "Change password" msgstr "Changer le mot de passe" -#: src/view/ButtonPanel.cpp:73 -msgid "Change user" -msgstr "Changer d'utilisateur" +#: src/view/AccountPanel.cpp:128 +msgid "Check" +msgstr "Rapprochement" -#: src/view/AccountPanel.cpp:122 -msgid "Check mode" -msgstr "Mode rapprochement" - -#: src/view/SearchPanel.cpp:76 +#: src/view/SearchBanner.cpp:70 msgid "Checked" msgstr "Rapprochée" +#: src/view/ImportPanel.cpp:133 +msgid "Choose a database to open" +msgstr "Choisissez une base de données à ouvrir" + +#: src/view/SearchPanel.cpp:154 +msgid "Choose a new account" +msgstr "Nouveau compte" + +#: src/view/SearchPanel.cpp:196 +msgid "Choose a new category" +msgstr "Nouvelle catégorie" + #: src/view/PasswordDialog.cpp:46 msgid "Confirm password " msgstr "Confirmer le mot de passe " -#: src/view/StatsPanel.cpp:130 -#: src/view/AccountPanel.cpp:116 +#: src/view/StatsPanel.cpp:142 +#: src/view/AccountPanel.cpp:122 msgid "Cost repartition" msgstr "Répartition des coûts" -#: src/view/grid/GridAccount.cpp:64 +#: src/view/ImportPanel.cpp:187 +#: src/view/ImportPanel.cpp:217 +#: src/view/ImportPanel.cpp:260 +#: src/view/ImportPanel.cpp:269 +#: src/view/ImportPanel.cpp:297 +#: src/view/ImportPanel.cpp:315 +msgid "Create one" +msgstr "En créer un" + +#: src/view/grid/GridAccount.cpp:63 msgid "Credit" msgstr "Crédit" -#: src/view/AccountPanel.cpp:202 +#: src/view/AccountPanel.cpp:234 msgid "Cur Credit" msgstr "Cur Crédit" -#: src/view/AccountPanel.cpp:203 +#: src/view/AccountPanel.cpp:235 msgid "Cur Debit" msgstr "Cur Débit" -#: src/view/AccountPanel.cpp:108 +#: src/view/AccountPanel.cpp:114 msgid "Current value" msgstr "Valeur courante" -#: src/view/grid/GridAccount.cpp:64 +#: src/view/grid/GridAccount.cpp:63 msgid "Date" msgstr "Date" -#: src/view/SearchPanel.cpp:49 +#: src/view/SearchBanner.cpp:41 msgid "Date from" msgstr "Date min" -#: src/view/SearchPanel.cpp:50 +#: src/view/SearchBanner.cpp:42 msgid "Date to" msgstr "Date max" -#: src/view/grid/GridAccount.cpp:64 +#: src/view/grid/GridAccount.cpp:63 msgid "Debit" msgstr "Débit" -#: src/view/PreferencesPanel.cpp:186 +#: src/view/PreferencesPanel.cpp:198 msgid "Default" msgstr "Défaut" -#: src/view/PreferencesPanel.cpp:188 -#: src/view/PreferencesPanel.cpp:270 -#: src/view/AccountPanel.cpp:596 +#: src/view/PreferencesPanel.cpp:201 +#: src/view/PreferencesPanel.cpp:296 +#: src/view/AccountPanel.cpp:733 msgid "Delete" msgstr "Supprimer" -#: src/view/PreferencesPanel.cpp:344 +#: src/view/PreferencesPanel.cpp:370 msgid "Descending" msgstr "Décroissant" -#: src/view/SearchPanel.cpp:86 -#: src/view/grid/GridAccount.cpp:64 +#: src/view/SearchBanner.cpp:78 +#: src/view/grid/GridAccount.cpp:63 msgid "Description" msgstr "Description" -#: src/view/UsersDialog.cpp:88 -#: src/view/UsersDialog.cpp:118 -#: src/view/SearchPanel.cpp:141 -#: src/view/SearchPanel.cpp:151 -#: src/view/SearchPanel.cpp:165 -#: src/view/SearchPanel.cpp:176 -#: src/view/PreferencesPanel.cpp:404 -#: src/view/PreferencesPanel.cpp:456 -#: src/view/PreferencesPanel.cpp:476 -#: src/view/PreferencesPanel.cpp:677 -#: src/view/PreferencesPanel.cpp:696 +#: src/view/SearchPanel.cpp:238 +msgid "Enter a new description" +msgstr "Nouvelle description" + +#: src/view/UsersDialog.cpp:95 +#: src/view/UsersDialog.cpp:125 +#: src/view/SearchBanner.cpp:140 +#: src/view/SearchBanner.cpp:150 +#: src/view/SearchBanner.cpp:163 +#: src/view/SearchBanner.cpp:172 +#: src/view/PreferencesPanel.cpp:436 +#: src/view/PreferencesPanel.cpp:496 +#: src/view/PreferencesPanel.cpp:516 #: src/view/PreferencesPanel.cpp:727 -#: src/view/PreferencesPanel.cpp:733 +#: src/view/PreferencesPanel.cpp:746 +#: src/view/PreferencesPanel.cpp:784 +#: src/view/PreferencesPanel.cpp:790 #: src/view/PasswordDialog.cpp:72 #: src/view/PasswordDialog.cpp:78 -#: src/view/AccountPanel.cpp:733 -#: src/view/grid/GridAccount.cpp:1245 -#: src/view/grid/GridAccount.cpp:1252 -#: src/view/grid/GridAccount.cpp:1371 -#: src/view/grid/GridAccount.cpp:1377 +#: src/view/AccountPanel.cpp:870 +#: src/view/grid/GridAccount.cpp:1294 +#: src/view/grid/GridAccount.cpp:1301 +#: src/view/grid/GridAccount.cpp:1420 +#: src/view/grid/GridAccount.cpp:1426 #: src/view/grid/wxGridCellFormulaEditor.cpp:69 -#: src/model/Database.cpp:38 -#: src/model/Database.cpp:55 -#: src/model/Database.cpp:95 -#: src/model/Database.cpp:102 -#: src/model/Database.cpp:120 +#: src/model/Database.cpp:45 +#: src/model/Database.cpp:52 +#: src/model/Database.cpp:70 +#: src/model/Database.cpp:106 +#: src/model/Database.cpp:112 +#: src/model/Database.cpp:118 +#: src/model/Database.cpp:126 #: src/model/Database.cpp:147 -#: src/model/Database.cpp:155 -#: src/model/Database.cpp:176 +#: src/model/Database_Update.cpp:23 msgid "Error" msgstr "Erreur" -#: src/model/Database.cpp:176 +#: src/model/Database.cpp:147 msgid "Error creating original database" msgstr "Erreur durant la création de la base de données initiale" -#: src/view/AccountPanel.cpp:109 +#: src/view/ExportPanel.cpp:55 +#: src/view/ExportPanel.cpp:89 +msgid "Export" +msgstr "Export" + +#: src/view/ExportPanel.cpp:146 +msgid "Failed to save operations" +msgstr "Erreur lors de la sauvegarde des opérations" + +#: src/view/ImportPanel.cpp:73 +msgid "File account" +msgstr "Compte du fichier" + +#: src/view/ImportPanel.cpp:81 +msgid "File category" +msgstr "Catégorie du fichier" + +#: src/view/AccountPanel.cpp:115 msgid "Final value" msgstr "Valeur finale" -#: src/view/SearchPanel.cpp:76 -#: src/view/grid/GridAccount.cpp:323 -#: src/model/Database.cpp:331 -#: src/controller/KissCount.cpp:359 +#: src/view/SearchBanner.cpp:70 +#: src/view/grid/GridAccount.cpp:340 +#: src/controller/KissCount.cpp:363 msgid "Fix" msgstr "Fixe" -#: src/view/PreferencesPanel.cpp:269 +#: src/view/PreferencesPanel.cpp:295 msgid "Font" msgstr "Police" -#: src/view/PreferencesPanel.cpp:268 +#: src/view/PreferencesPanel.cpp:294 msgid "Foreground color" msgstr "Couleur d'avant plan" -#: src/view/StatsPanel.cpp:62 +#: src/view/StatsPanel.cpp:65 msgid "From" msgstr "De" -#: src/view/GenerateDialog.cpp:45 +#: src/view/GenerateDialog.cpp:46 msgid "From " msgstr "A partir de " #: src/view/GenerateDialog.cpp:31 -#: src/view/AccountPanel.cpp:593 +#: src/view/AccountPanel.cpp:730 msgid "Generate month" msgstr "Générer mois" -#: src/controller/KissCount.cpp:361 +#: src/model/import/GrisbiImportEngine.cpp:201 +msgid "Grisbi files (*.gsb)|*.gsb" +msgstr "Fichiers Grisbi (*.gsb)|*.gsb" + +#: src/controller/KissCount.cpp:363 msgid "Groceries" msgstr "Courses" -#: src/view/AccountPanel.cpp:126 +#: src/view/AccountPanel.cpp:134 msgid "Group" msgstr "Grouper" @@ -313,23 +403,39 @@ msgstr "Grouper" msgid "Hobbies" msgstr "Loisirs" -#: src/view/AccountPanel.cpp:107 +#: src/view/ImportPanel.cpp:123 +msgid "Import" +msgstr "Import" + +#: src/view/AccountPanel.cpp:113 msgid "Initial value" msgstr "Valeur initiale" -#: src/view/SearchPanel.cpp:151 +#: src/view/ImportPanel.cpp:57 +msgid "Integrate operations" +msgstr "Intégrer les opérations" + +#: src/view/ImportPanel.cpp:75 +msgid "Internal account" +msgstr "Compte interne" + +#: src/view/ImportPanel.cpp:83 +msgid "Internal category" +msgstr "Catégorie interne" + +#: src/view/SearchBanner.cpp:150 msgid "Invalid amount from" msgstr "Montant min invalide" -#: src/view/SearchPanel.cpp:176 +#: src/view/SearchBanner.cpp:172 msgid "Invalid amount range" msgstr "Intervalle des montants invalide" -#: src/view/SearchPanel.cpp:165 +#: src/view/SearchBanner.cpp:163 msgid "Invalid amount to" msgstr "Montant max invalide" -#: src/view/SearchPanel.cpp:141 +#: src/view/SearchBanner.cpp:140 msgid "Invalid date range" msgstr "Intervalle de temps invalide" @@ -337,7 +443,7 @@ msgstr "Intervalle de temps invalide" msgid "Invalid formula !" msgstr "Formule invalide !" -#: src/view/PreferencesPanel.cpp:727 +#: src/view/PreferencesPanel.cpp:784 msgid "Invalid name" msgstr "Nom invalide" @@ -345,56 +451,77 @@ msgstr "Nom invalide" msgid "Invalid old password" msgstr "Ancien mot de passe invalide" -#: src/view/UsersDialog.cpp:88 +#: src/view/UsersDialog.cpp:95 msgid "Invalid password" msgstr "Mot de passe invalide" -#: src/view/StatsPanel.cpp:346 +#: src/view/StatsPanel.cpp:403 msgid "Invalide date range" msgstr "Intervalle de temps invalide" -#: src/view/PreferencesPanel.cpp:404 +#: src/view/PreferencesPanel.cpp:436 msgid "It must be at least one account !" msgstr "Il doit y avoir au moins un compte !" -#: src/view/AccountPanel.cpp:733 +#: src/view/AccountPanel.cpp:870 msgid "It must be at least one month !" msgstr "Il doit rester au moins un mois" -#: src/view/PreferencesPanel.cpp:79 +#: src/view/PreferencesPanel.cpp:80 msgid "Kill me" msgstr "Kill me" -#: src/view/StatsPanel.cpp:346 -#: src/view/PreferencesPanel.cpp:767 -#: src/view/PreferencesPanel.cpp:770 +#: src/view/StatsPanel.cpp:403 +#: src/view/PreferencesPanel.cpp:824 +#: src/view/PreferencesPanel.cpp:827 +#: src/model/Database_Update.cpp:140 msgid "KissCount" msgstr "KissCount" -#: src/view/PreferencesPanel.cpp:61 +#: src/view/ExportPanel.cpp:152 +msgid "KissCount - Export" +msgstr "KissCount - Export" + +#: src/view/ImportPanel.cpp:128 +msgid "KissCount - Import" +msgstr "KissCount - Import" + +#: src/model/import/XMLImportEngine.cpp:268 +msgid "KissCount xml files (*.xml)|*.xml" +msgstr "Fichiers KissCount xml (*.xml)|*.xml" + +#: src/view/PreferencesPanel.cpp:62 msgid "Language" msgstr "Langue" -#: src/view/PreferencesPanel.cpp:770 +#: src/view/PreferencesPanel.cpp:827 msgid "Language not changed" msgstr "Langue non changée" -#: src/view/PreferencesPanel.cpp:767 +#: src/view/PreferencesPanel.cpp:824 msgid "Language successfully changed, please go to another panel" msgstr "Langue changée, allez sur un autre panneau pour rendre le changement effectif" -#: src/view/PreferencesPanel.cpp:71 -#: src/view/PreferencesPanel.cpp:184 -#: src/view/PreferencesPanel.cpp:266 +#: src/view/ImportPanel.cpp:54 +msgid "Load operations" +msgstr "Charger les opérations" + +#: src/view/AccountPanel.cpp:129 +msgid "Mode" +msgstr "Mode" + +#: src/view/PreferencesPanel.cpp:72 +#: src/view/PreferencesPanel.cpp:196 +#: src/view/PreferencesPanel.cpp:292 msgid "Name" msgstr "Nom" -#: src/view/PreferencesPanel.cpp:739 +#: src/view/PreferencesPanel.cpp:796 msgid "Name changed" msgstr "Nom changé" -#: src/view/UsersDialog.cpp:52 -#: src/view/UsersDialog.cpp:107 +#: src/view/UsersDialog.cpp:53 +#: src/view/UsersDialog.cpp:114 msgid "New User" msgstr "Nouvel utilisateur" @@ -402,7 +529,7 @@ msgstr "Nouvel utilisateur" msgid "New password " msgstr "Nouveau mot de passe " -#: src/model/Database.cpp:135 +#: src/model/Database.cpp:91 msgid "" "No database found, would you like to create a new one ?\n" "\n" @@ -410,24 +537,46 @@ msgstr "" "Aucune base de données trouvée, voulez vous en créer une nouvelle ?\n" "\n" -#: src/view/SearchPanel.cpp:231 +#: src/view/SearchPanel.cpp:119 +#: src/view/ExportPanel.cpp:111 msgid "No entry found" msgstr "Pas d'entrée trouvée" -#: src/view/SearchPanel.cpp:76 +#: src/view/ImportPanel.cpp:351 +msgid "No operation found into this file" +msgstr "Aucun opération trouvée dans ce fichier" + +#: src/view/ExportPanel.cpp:124 +msgid "No operation to save" +msgstr "Aucun opération à sauvegarder" + +#: src/view/StatsPanel.cpp:117 +#: src/view/SearchBanner.cpp:70 +#: src/view/AccountPanel.cpp:237 msgid "Non fix" msgstr "Courantes" -#: src/view/SearchPanel.cpp:76 +#: src/view/SearchPanel.cpp:149 +#: src/view/SearchPanel.cpp:191 +#: src/view/PreferencesPanel.cpp:441 +#: src/view/PreferencesPanel.cpp:702 +msgid "None" +msgstr "Aucun" + +#: src/view/SearchBanner.cpp:70 msgid "Not checked" msgstr "Non rapprochée" -#: src/view/PreferencesPanel.cpp:185 +#: src/view/PreferencesPanel.cpp:197 msgid "Number" msgstr "Numéro de compte" -#: src/view/UsersDialog.cpp:50 -#: src/view/GenerateDialog.cpp:59 +#: src/model/import/OFXImportEngine.cpp:135 +msgid "OFX files (*.ofx)|*.ofx" +msgstr "Fichiers OFX (*.ofx)|*.ofx" + +#: src/view/UsersDialog.cpp:51 +#: src/view/GenerateDialog.cpp:60 #: src/view/PasswordDialog.cpp:55 msgid "OK" msgstr "OK" @@ -436,23 +585,28 @@ msgstr "OK" msgid "Old password " msgstr "Ancien mot de passe " -#: src/controller/KissCount.cpp:365 -msgid "Operating exepense" -msgstr "Fonctionnement" - -#: src/view/PreferencesPanel.cpp:62 +#: src/view/PreferencesPanel.cpp:63 msgid "Operation order" msgstr "Ordre des opérations" -#: src/view/ButtonPanel.cpp:69 +#: src/view/SearchBanner.cpp:82 +#: src/view/AccountPanel.cpp:186 msgid "Operations" msgstr "Opérations" -#: src/controller/KissCount.cpp:369 +#: src/view/ImportPanel.cpp:421 +msgid "Operations successfully imported" +msgstr "les opérations ont été importées avec succès" + +#: src/view/ExportPanel.cpp:144 +msgid "Operations successfuly saved" +msgstr "Opérations sauvegardées avec succès" + +#: src/controller/KissCount.cpp:364 msgid "Other" msgstr "Autres" -#: src/view/UsersDialog.cpp:43 +#: src/view/UsersDialog.cpp:45 msgid "Password " msgstr "Mot de passe " @@ -460,7 +614,7 @@ msgstr "Mot de passe " msgid "Password changed" msgstr "Mot de passe changé" -#: src/view/ButtonPanel.cpp:105 +#: src/view/wxUI.cpp:249 msgid "" "Personal accounting software\n" "\n" @@ -468,7 +622,7 @@ msgid "" "\n" "Licenced under GNU GPL v3\n" "\n" -"Copyright (C) 2010 Grégory Soutadé" +"Copyright (C) 2010-2011 Grégory Soutadé" msgstr "" "Logiciel de comptabilité personnelle\n" "\n" @@ -476,169 +630,235 @@ msgstr "" "\n" "Licence GNU GPL v3\n" "\n" -"Copyright (C) 2010 Grégory Soutadé" +"Copyright (C) 2010-2011 Grégory Soutadé" #: src/view/PasswordDialog.cpp:78 msgid "Please retype new password" msgstr "Re entrez le mot de passe" -#: src/view/PreferencesPanel.cpp:741 -#: src/view/PreferencesPanel.cpp:775 -#: src/view/ButtonPanel.cpp:72 +#: src/view/PreferencesPanel.cpp:184 +#: src/view/PreferencesPanel.cpp:798 +#: src/view/PreferencesPanel.cpp:832 msgid "Preferences" msgstr "Préférences" -#: src/model/Database.cpp:55 -msgid "Query failed !\n" -msgstr "La requête a échouée !\n" - -#: src/view/ButtonPanel.cpp:75 -msgid "Quit" -msgstr "Quitter" - -#: src/view/ButtonPanel.cpp:112 +#: src/view/wxUI.cpp:256 msgid "Quit KissCount ?" msgstr "Quitter KissCount ?" -#: src/view/AccountPanel.cpp:204 +#: src/view/AccountPanel.cpp:128 +msgid "Real" +msgstr "Réel" + +#: src/view/AccountPanel.cpp:236 msgid "Remains" msgstr "Restant" -#: src/view/SearchPanel.cpp:84 -#: src/view/SearchPanel.cpp:242 -#: src/view/ButtonPanel.cpp:71 +#: src/view/SearchPanel.cpp:61 +msgid "Rename" +msgstr "Renommer" + +#: src/view/ExportPanel.cpp:128 +msgid "Save as" +msgstr "Sauvegarder sous" + +#: src/view/ImportPanel.cpp:60 +msgid "Save import patterns" +msgstr "Sauvegarder les motifs d'import" + +#: src/view/SearchPanel.cpp:48 +#: src/view/SearchPanel.cpp:97 +#: src/view/SearchPanel.cpp:254 +#: src/view/ExportPanel.cpp:44 msgid "Search" msgstr "Chercher" -#: src/view/StatsPanel.cpp:122 -#: src/view/AccountPanel.cpp:90 +#: src/view/StatsPanel.cpp:134 +#: src/view/AccountPanel.cpp:95 msgid "Serie 1" msgstr "Série 1" -#: src/view/PreferencesPanel.cpp:63 +#: src/view/PreferencesPanel.cpp:64 msgid "Shared with" msgstr "Partagé avec" -#: src/view/StatsPanel.cpp:327 -#: src/view/ButtonPanel.cpp:70 +#: src/view/StatsPanel.cpp:172 +#: src/view/StatsPanel.cpp:384 msgid "Statistics" msgstr "Statistiques" -#: src/view/StatsPanel.cpp:69 +#: src/view/StatsPanel.cpp:70 msgid "To" msgstr "A" -#: src/view/GenerateDialog.cpp:52 +#: src/view/GenerateDialog.cpp:53 msgid "To " msgstr "Vers " -#: src/view/AccountPanel.cpp:186 +#: src/view/AccountPanel.cpp:210 msgid "Total Credit" msgstr "Total Crédit" -#: src/view/AccountPanel.cpp:187 +#: src/view/AccountPanel.cpp:211 msgid "Total Debit" msgstr "Total Débit" -#: src/view/AccountPanel.cpp:127 +#: src/view/AccountPanel.cpp:135 msgid "UnGroup" msgstr "Dégrouper" -#: src/model/Database.cpp:95 -#: src/model/Database.cpp:102 -#: src/model/Database.cpp:120 -#: src/model/Database.cpp:155 +#: src/model/Database.cpp:112 +#: src/model/Database.cpp:118 +msgid "Unable to Create " +msgstr "Impossible de créer " + +#: src/model/Database.cpp:45 +#: src/model/Database.cpp:52 +#: src/model/Database.cpp:70 +#: src/model/Database.cpp:126 msgid "Unable to open Database" msgstr "Impossible d'ouvrir la base de données" -#: src/controller/KissCount.cpp:367 +#: src/controller/KissCount.cpp:364 msgid "Unexpected" msgstr "Exceptionnel" -#: src/model/User.cpp:52 -#: src/model/User.cpp:72 -#: src/model/User.cpp:120 +#: src/view/SearchBanner.cpp:66 +#: src/view/SearchBanner.cpp:74 +#: src/view/AccountPanel.cpp:529 +#: src/view/AccountPanel.cpp:565 +#: src/view/AccountPanel.cpp:603 +#: src/model/User.cpp:59 +#: src/model/User.cpp:79 +#: src/model/User.cpp:127 msgid "Unknown" msgstr "Inconnu" -#: src/model/Database.cpp:38 -msgid "Update failed !\n" -msgstr "La mise à jour à échouée !\n" +#: src/view/ImportPanel.cpp:42 +msgid "Unresolved accounts" +msgstr "Comptes non résolus" -#: src/view/PreferencesPanel.cpp:58 +#: src/view/ImportPanel.cpp:43 +msgid "Unresolved categories" +msgstr "Catégories non résolues" + +#: src/view/AccountPanel.cpp:136 +msgid "Update next months" +msgstr "Mettre à jour mois suivants" + +#: src/view/PreferencesPanel.cpp:59 msgid "User" msgstr "Utilisateur" -#: src/view/UsersDialog.cpp:38 -#: src/view/UsersDialog.cpp:118 -#: src/view/PreferencesPanel.cpp:733 +#: src/view/UsersDialog.cpp:40 +#: src/view/UsersDialog.cpp:125 +#: src/view/PreferencesPanel.cpp:790 msgid "User " msgstr "Utilisateur " -#: src/view/UsersDialog.cpp:30 +#: src/view/UsersDialog.cpp:31 msgid "Users" msgstr "Utilisateurs" -#: src/view/grid/GridAccount.cpp:1022 +#: src/view/PreferencesPanel.cpp:199 +#: src/view/AccountPanel.cpp:128 +msgid "Virtual" +msgstr "Virtuel" + +#: src/view/grid/GridAccount.cpp:1071 msgid "Warning" msgstr "Attention" -#: src/view/grid/GridAccount.cpp:1022 +#: src/view/PreferencesPanel.cpp:446 +msgid "Wich account will replace this one ?" +msgstr "Quel compte va remplacer celui-ci ?" + +#: src/view/PreferencesPanel.cpp:707 +msgid "Wich category will replace this one ?" +msgstr "Quelle catégorie va remplacer celle-ci" + +#: src/model/export/XMLExportEngine.cpp:29 +msgid "XML files (*.xml)|*.xml" +msgstr "Fichiers XML (*.xml)|*.xml" + +#: src/view/grid/GridAccount.cpp:1071 msgid "You made a debit on a blocked account" msgstr "Vous avez effectué une opération de débit sur un compte bloqué" -#: src/view/wxUI.cpp:110 +#: src/view/wxUI.cpp:125 msgid "april" msgstr "avril" -#: src/view/wxUI.cpp:114 +#: src/view/wxUI.cpp:129 msgid "august" msgstr "août" -#: src/view/wxUI.cpp:118 +#: src/view/wxUI.cpp:133 msgid "december" msgstr "décembre" -#: src/view/wxUI.cpp:108 +#: src/view/wxUI.cpp:123 msgid "february" msgstr "février" -#: src/view/wxUI.cpp:107 +#: src/view/wxUI.cpp:122 msgid "january" msgstr "janvier" -#: src/view/wxUI.cpp:113 +#: src/view/wxUI.cpp:128 msgid "july" msgstr "juillet" -#: src/view/wxUI.cpp:112 +#: src/view/wxUI.cpp:127 msgid "june" msgstr "juin" -#: src/view/wxUI.cpp:109 +#: src/view/wxUI.cpp:124 msgid "march" msgstr "mars" -#: src/view/wxUI.cpp:111 +#: src/view/wxUI.cpp:126 msgid "may" msgstr "mai" -#: src/view/wxUI.cpp:117 +#: src/view/wxUI.cpp:132 msgid "november" msgstr "novembre" -#: src/view/wxUI.cpp:116 +#: src/view/wxUI.cpp:131 msgid "october" msgstr "octobre" -#: src/view/wxUI.cpp:115 +#: src/view/wxUI.cpp:130 msgid "september" msgstr "septembre" +#~ msgid "Operating expense" +#~ msgstr "Fonctionnement" + +#~ msgid "Check mode" +#~ msgstr "Mode rapprochement" + +#~ msgid "Query failed !\n" +#~ msgstr "La requête a échouée !\n" + +#~ msgid "Update failed !\n" +#~ msgstr "La mise à jour à échouée !\n" + +#~ msgid "About" +#~ msgstr "A propos" + +#~ msgid "Change user" +#~ msgstr "Changer d'utilisateur" + +#~ msgid "Quit" +#~ msgstr "Quitter" + #~ msgid "Both" #~ msgstr "Les deux" + #~ msgid "Color" #~ msgstr "Couleur" + #~ msgid "Fixe" #~ msgstr "Fixe" - diff --git a/ressources/po/kisscount.pot b/ressources/po/kisscount.pot index a490c31..6101e75 100644 --- a/ressources/po/kisscount.pot +++ b/ressources/po/kisscount.pot @@ -8,267 +8,351 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-02-05 14:48+0100\n" +"POT-Creation-Date: 2011-07-04 20:23+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" +"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: src/view/PreferencesPanel.cpp:775 src/view/StatsPanel.cpp:328 +#: src/view/StatsPanel.cpp:384 src/view/PreferencesPanel.cpp:832 msgid " - " msgstr "" -#: src/view/PreferencesPanel.cpp:456 src/view/PreferencesPanel.cpp:476 -#: src/view/PreferencesPanel.cpp:677 src/view/PreferencesPanel.cpp:696 -#: src/view/PreferencesPanel.cpp:733 src/view/UsersDialog.cpp:118 +#: src/view/UsersDialog.cpp:125 src/view/PreferencesPanel.cpp:496 +#: src/view/PreferencesPanel.cpp:516 src/view/PreferencesPanel.cpp:727 +#: src/view/PreferencesPanel.cpp:746 src/view/PreferencesPanel.cpp:790 msgid " already exists" msgstr "" -#: src/view/SearchPanel.cpp:227 +#: src/view/ImportPanel.cpp:283 +msgid " and " +msgstr "" + +#: src/view/SearchPanel.cpp:114 src/view/ExportPanel.cpp:106 msgid " entries found" msgstr "" -#: src/model/Database.cpp:147 +#: src/model/Database.cpp:106 msgid " not found, aborting" msgstr "" -#: src/view/AccountPanel.cpp:742 +#: src/view/AccountPanel.cpp:879 msgid " operations ?" msgstr "" -#: src/view/PreferencesPanel.cpp:782 +#: src/view/PreferencesPanel.cpp:839 msgid " profil ?" msgstr "" -#: src/model/Database.cpp:136 +#: src/view/ImportPanel.cpp:289 +msgid " will be created, is it ok ?" +msgstr "" + +#: src/model/Database.cpp:92 msgid "!! Warning !! If there was a bug, the old database will be suppressed !" msgstr "" -#: src/view/SearchPanel.cpp:229 +#: src/view/ImportPanel.cpp:282 +#, c-format +msgid "%d accounts" +msgstr "" + +#: src/view/ImportPanel.cpp:287 +#, c-format +msgid "%d categories" +msgstr "" + +#: src/view/SearchPanel.cpp:116 src/view/ExportPanel.cpp:108 msgid "1 entry found" msgstr "" -#: src/view/ButtonPanel.cpp:74 -msgid "About" -msgstr "" - -#: src/view/SearchPanel.cpp:90 src/view/grid/GridAccount.cpp:67 +#: src/view/SearchBanner.cpp:83 src/view/grid/GridAccount.cpp:63 msgid "Account" msgstr "" -#: src/view/PreferencesPanel.cpp:456 src/view/PreferencesPanel.cpp:476 +#: src/view/PreferencesPanel.cpp:496 src/view/PreferencesPanel.cpp:516 msgid "Account " msgstr "" -#: src/controller/KissCount.cpp:343 +#: src/controller/KissCount.cpp:372 msgid "Account 1" msgstr "" -#: src/view/AccountPanel.cpp:106 +#: src/view/ImportPanel.cpp:74 src/view/AccountPanel.cpp:112 msgid "Account name" msgstr "" -#: src/view/AccountPanel.cpp:105 +#: src/view/AccountPanel.cpp:111 msgid "Account number" msgstr "" -#: src/view/PreferencesPanel.cpp:59 src/view/StatsPanel.cpp:293 +#: src/view/StatsPanel.cpp:336 src/view/PreferencesPanel.cpp:60 msgid "Accounts" msgstr "" -#: src/view/SearchPanel.cpp:86 +#: src/view/AccountPanel.cpp:1107 +msgid "Accounts updated until " +msgstr "" + +#: src/view/SearchBanner.cpp:79 msgid "Amount from" msgstr "" -#: src/view/SearchPanel.cpp:87 +#: src/view/SearchBanner.cpp:80 msgid "Amount to" msgstr "" -#: src/view/AccountPanel.cpp:737 src/view/PreferencesPanel.cpp:408 -#: src/view/PreferencesPanel.cpp:782 +#: src/view/AccountPanel.cpp:1114 +msgid "Any account updated !" +msgstr "" + +#: src/view/ImportPanel.cpp:175 src/view/ExportPanel.cpp:138 +msgid "Any engine can process this file !" +msgstr "" + +#: src/view/PreferencesPanel.cpp:839 src/view/AccountPanel.cpp:874 msgid "Are you sure want to delete " msgstr "" -#: src/view/PreferencesPanel.cpp:657 src/view/grid/GridAccount.cpp:828 +#: src/view/grid/GridAccount.cpp:859 msgid "Are you sure want to delete : \n" msgstr "" -#: src/view/PreferencesPanel.cpp:343 +#: src/view/ImportPanel.cpp:367 +msgid "Are you sure want to integrate these operations ?" +msgstr "" + +#: src/view/PreferencesPanel.cpp:369 msgid "Ascending" msgstr "" -#: src/view/PreferencesPanel.cpp:267 +#: src/view/PreferencesPanel.cpp:293 msgid "Background color" msgstr "" -#: src/view/PreferencesPanel.cpp:187 +#: src/view/PreferencesPanel.cpp:200 msgid "Blocked" msgstr "" -#: src/view/GenerateDialog.cpp:60 src/view/PasswordDialog.cpp:56 -#: src/view/UsersDialog.cpp:51 +#: src/view/UsersDialog.cpp:52 src/view/GenerateDialog.cpp:61 +#: src/view/PasswordDialog.cpp:56 msgid "Cancel" msgstr "" -#: src/view/grid/GridAccount.cpp:1259 src/view/grid/GridAccount.cpp:1266 +#: src/view/grid/GridAccount.cpp:1294 src/view/grid/GridAccount.cpp:1301 msgid "Cannot group these operations" msgstr "" -#: src/view/grid/GridAccount.cpp:1385 src/view/grid/GridAccount.cpp:1391 +#: src/view/grid/GridAccount.cpp:1420 src/view/grid/GridAccount.cpp:1426 msgid "Cannot ungroup these operations" msgstr "" -#: src/view/PreferencesPanel.cpp:60 +#: src/controller/KissCount.cpp:363 +msgid "Car" +msgstr "" + +#: src/view/PreferencesPanel.cpp:61 msgid "Categories" msgstr "" -#: src/view/SearchPanel.cpp:88 src/view/grid/GridAccount.cpp:67 +#: src/view/SearchBanner.cpp:81 src/view/grid/GridAccount.cpp:63 msgid "Category" msgstr "" -#: src/view/PreferencesPanel.cpp:677 src/view/PreferencesPanel.cpp:696 +#: src/view/PreferencesPanel.cpp:727 src/view/PreferencesPanel.cpp:746 msgid "Category " msgstr "" -#: src/view/PreferencesPanel.cpp:77 -msgid "Change Name" +#: src/view/ImportPanel.cpp:82 +msgid "Category name" msgstr "" #: src/view/PreferencesPanel.cpp:78 +msgid "Change Name" +msgstr "" + +#: src/view/PreferencesPanel.cpp:79 msgid "Change Password" msgstr "" +#: src/view/SearchPanel.cpp:59 +msgid "Change account" +msgstr "" + +#: src/view/SearchPanel.cpp:60 +msgid "Change category" +msgstr "" + #: src/view/PasswordDialog.cpp:29 msgid "Change password" msgstr "" -#: src/view/ButtonPanel.cpp:73 -msgid "Change user" +#: src/view/AccountPanel.cpp:128 +msgid "Check" msgstr "" -#: src/view/AccountPanel.cpp:122 -msgid "Check mode" -msgstr "" - -#: src/view/SearchPanel.cpp:76 +#: src/view/SearchBanner.cpp:70 msgid "Checked" msgstr "" +#: src/view/ImportPanel.cpp:133 +msgid "Choose a database to open" +msgstr "" + +#: src/view/SearchPanel.cpp:154 +msgid "Choose a new account" +msgstr "" + +#: src/view/SearchPanel.cpp:196 +msgid "Choose a new category" +msgstr "" + #: src/view/PasswordDialog.cpp:46 msgid "Confirm password " msgstr "" -#: src/view/AccountPanel.cpp:116 src/view/StatsPanel.cpp:132 +#: src/view/StatsPanel.cpp:142 src/view/AccountPanel.cpp:122 msgid "Cost repartition" msgstr "" -#: src/view/grid/GridAccount.cpp:67 +#: src/view/ImportPanel.cpp:187 src/view/ImportPanel.cpp:217 +#: src/view/ImportPanel.cpp:260 src/view/ImportPanel.cpp:269 +#: src/view/ImportPanel.cpp:297 src/view/ImportPanel.cpp:315 +msgid "Create one" +msgstr "" + +#: src/view/grid/GridAccount.cpp:63 msgid "Credit" msgstr "" -#: src/view/AccountPanel.cpp:202 +#: src/view/AccountPanel.cpp:234 msgid "Cur Credit" msgstr "" -#: src/view/AccountPanel.cpp:203 +#: src/view/AccountPanel.cpp:235 msgid "Cur Debit" msgstr "" -#: src/view/AccountPanel.cpp:108 +#: src/view/AccountPanel.cpp:114 msgid "Current value" msgstr "" -#: src/view/grid/GridAccount.cpp:67 +#: src/view/grid/GridAccount.cpp:63 msgid "Date" msgstr "" -#: src/view/SearchPanel.cpp:49 +#: src/view/SearchBanner.cpp:41 msgid "Date from" msgstr "" -#: src/view/SearchPanel.cpp:50 +#: src/view/SearchBanner.cpp:42 msgid "Date to" msgstr "" -#: src/view/grid/GridAccount.cpp:67 +#: src/view/grid/GridAccount.cpp:63 msgid "Debit" msgstr "" -#: src/view/PreferencesPanel.cpp:186 +#: src/view/PreferencesPanel.cpp:198 msgid "Default" msgstr "" -#: src/view/AccountPanel.cpp:596 src/view/PreferencesPanel.cpp:188 -#: src/view/PreferencesPanel.cpp:270 +#: src/view/PreferencesPanel.cpp:201 src/view/PreferencesPanel.cpp:296 +#: src/view/AccountPanel.cpp:733 msgid "Delete" msgstr "" -#: src/view/PreferencesPanel.cpp:344 +#: src/view/PreferencesPanel.cpp:370 msgid "Descending" msgstr "" -#: src/view/SearchPanel.cpp:85 src/view/grid/GridAccount.cpp:67 +#: src/view/SearchBanner.cpp:78 src/view/grid/GridAccount.cpp:63 msgid "Description" msgstr "" -#: src/model/Database.cpp:38 src/model/Database.cpp:55 -#: src/model/Database.cpp:95 src/model/Database.cpp:102 -#: src/model/Database.cpp:120 src/model/Database.cpp:147 -#: src/model/Database.cpp:155 src/model/Database.cpp:176 -#: src/view/AccountPanel.cpp:733 src/view/PasswordDialog.cpp:72 -#: src/view/PasswordDialog.cpp:78 src/view/PreferencesPanel.cpp:404 -#: src/view/PreferencesPanel.cpp:456 src/view/PreferencesPanel.cpp:476 -#: src/view/PreferencesPanel.cpp:677 src/view/PreferencesPanel.cpp:696 -#: src/view/PreferencesPanel.cpp:727 src/view/PreferencesPanel.cpp:733 -#: src/view/SearchPanel.cpp:142 src/view/SearchPanel.cpp:152 -#: src/view/SearchPanel.cpp:166 src/view/SearchPanel.cpp:177 -#: src/view/UsersDialog.cpp:88 src/view/UsersDialog.cpp:118 -#: src/view/grid/GridAccount.cpp:1259 src/view/grid/GridAccount.cpp:1266 -#: src/view/grid/GridAccount.cpp:1385 src/view/grid/GridAccount.cpp:1391 -#: src/view/grid/wxGridCellFormulaEditor.cpp:69 +#: src/view/SearchPanel.cpp:238 +msgid "Enter a new description" +msgstr "" + +#: src/view/UsersDialog.cpp:95 src/view/UsersDialog.cpp:125 +#: src/view/SearchBanner.cpp:140 src/view/SearchBanner.cpp:150 +#: src/view/SearchBanner.cpp:163 src/view/SearchBanner.cpp:172 +#: src/view/PreferencesPanel.cpp:436 src/view/PreferencesPanel.cpp:496 +#: src/view/PreferencesPanel.cpp:516 src/view/PreferencesPanel.cpp:727 +#: src/view/PreferencesPanel.cpp:746 src/view/PreferencesPanel.cpp:784 +#: src/view/PreferencesPanel.cpp:790 src/view/PasswordDialog.cpp:72 +#: src/view/PasswordDialog.cpp:78 src/view/AccountPanel.cpp:870 +#: src/view/grid/GridAccount.cpp:1294 src/view/grid/GridAccount.cpp:1301 +#: src/view/grid/GridAccount.cpp:1420 src/view/grid/GridAccount.cpp:1426 +#: src/view/grid/wxGridCellFormulaEditor.cpp:69 src/model/Database.cpp:45 +#: src/model/Database.cpp:52 src/model/Database.cpp:70 +#: src/model/Database.cpp:106 src/model/Database.cpp:112 +#: src/model/Database.cpp:118 src/model/Database.cpp:126 +#: src/model/Database.cpp:147 src/model/Database_Update.cpp:23 msgid "Error" msgstr "" -#: src/model/Database.cpp:176 +#: src/model/Database.cpp:147 msgid "Error creating original database" msgstr "" -#: src/view/AccountPanel.cpp:109 +#: src/view/ExportPanel.cpp:55 src/view/ExportPanel.cpp:89 +msgid "Export" +msgstr "" + +#: src/view/ExportPanel.cpp:146 +msgid "Failed to save operations" +msgstr "" + +#: src/view/ImportPanel.cpp:73 +msgid "File account" +msgstr "" + +#: src/view/ImportPanel.cpp:81 +msgid "File category" +msgstr "" + +#: src/view/AccountPanel.cpp:115 msgid "Final value" msgstr "" -#: src/controller/KissCount.cpp:359 src/model/Database.cpp:331 -#: src/view/SearchPanel.cpp:76 src/view/grid/GridAccount.cpp:326 +#: src/view/SearchBanner.cpp:70 src/view/grid/GridAccount.cpp:340 +#: src/controller/KissCount.cpp:363 msgid "Fix" msgstr "" -#: src/view/PreferencesPanel.cpp:269 +#: src/view/PreferencesPanel.cpp:295 msgid "Font" msgstr "" -#: src/view/PreferencesPanel.cpp:268 +#: src/view/PreferencesPanel.cpp:294 msgid "Foreground color" msgstr "" -#: src/view/StatsPanel.cpp:64 +#: src/view/StatsPanel.cpp:65 msgid "From" msgstr "" -#: src/view/GenerateDialog.cpp:45 +#: src/view/GenerateDialog.cpp:46 msgid "From " msgstr "" -#: src/view/AccountPanel.cpp:593 src/view/GenerateDialog.cpp:31 +#: src/view/GenerateDialog.cpp:31 src/view/AccountPanel.cpp:730 msgid "Generate month" msgstr "" -#: src/controller/KissCount.cpp:361 +#: src/model/import/GrisbiImportEngine.cpp:201 +msgid "Grisbi files (*.gsb)|*.gsb" +msgstr "" + +#: src/controller/KissCount.cpp:363 msgid "Groceries" msgstr "" -#: src/view/AccountPanel.cpp:126 +#: src/view/AccountPanel.cpp:134 msgid "Group" msgstr "" @@ -276,23 +360,39 @@ msgstr "" msgid "Hobbies" msgstr "" -#: src/view/AccountPanel.cpp:107 +#: src/view/ImportPanel.cpp:123 +msgid "Import" +msgstr "" + +#: src/view/AccountPanel.cpp:113 msgid "Initial value" msgstr "" -#: src/view/SearchPanel.cpp:152 +#: src/view/ImportPanel.cpp:57 +msgid "Integrate operations" +msgstr "" + +#: src/view/ImportPanel.cpp:75 +msgid "Internal account" +msgstr "" + +#: src/view/ImportPanel.cpp:83 +msgid "Internal category" +msgstr "" + +#: src/view/SearchBanner.cpp:150 msgid "Invalid amount from" msgstr "" -#: src/view/SearchPanel.cpp:177 +#: src/view/SearchBanner.cpp:172 msgid "Invalid amount range" msgstr "" -#: src/view/SearchPanel.cpp:166 +#: src/view/SearchBanner.cpp:163 msgid "Invalid amount to" msgstr "" -#: src/view/SearchPanel.cpp:142 +#: src/view/SearchBanner.cpp:140 msgid "Invalid date range" msgstr "" @@ -300,7 +400,7 @@ msgstr "" msgid "Invalid formula !" msgstr "" -#: src/view/PreferencesPanel.cpp:727 +#: src/view/PreferencesPanel.cpp:784 msgid "Invalid name" msgstr "" @@ -308,53 +408,73 @@ msgstr "" msgid "Invalid old password" msgstr "" -#: src/view/UsersDialog.cpp:88 +#: src/view/UsersDialog.cpp:95 msgid "Invalid password" msgstr "" -#: src/view/StatsPanel.cpp:347 +#: src/view/StatsPanel.cpp:403 msgid "Invalide date range" msgstr "" -#: src/view/PreferencesPanel.cpp:404 +#: src/view/PreferencesPanel.cpp:436 msgid "It must be at least one account !" msgstr "" -#: src/view/AccountPanel.cpp:733 +#: src/view/AccountPanel.cpp:870 msgid "It must be at least one month !" msgstr "" -#: src/view/PreferencesPanel.cpp:79 +#: src/view/PreferencesPanel.cpp:80 msgid "Kill me" msgstr "" -#: src/view/PreferencesPanel.cpp:767 src/view/PreferencesPanel.cpp:770 -#: src/view/StatsPanel.cpp:347 +#: src/view/StatsPanel.cpp:403 src/view/PreferencesPanel.cpp:824 +#: src/view/PreferencesPanel.cpp:827 src/model/Database_Update.cpp:140 msgid "KissCount" msgstr "" -#: src/view/PreferencesPanel.cpp:61 +#: src/view/ExportPanel.cpp:152 +msgid "KissCount - Export" +msgstr "" + +#: src/view/ImportPanel.cpp:128 +msgid "KissCount - Import" +msgstr "" + +#: src/model/import/XMLImportEngine.cpp:268 +msgid "KissCount xml files (*.xml)|*.xml" +msgstr "" + +#: src/view/PreferencesPanel.cpp:62 msgid "Language" msgstr "" -#: src/view/PreferencesPanel.cpp:770 +#: src/view/PreferencesPanel.cpp:827 msgid "Language not changed" msgstr "" -#: src/view/PreferencesPanel.cpp:767 +#: src/view/PreferencesPanel.cpp:824 msgid "Language successfully changed, please go to another panel" msgstr "" -#: src/view/PreferencesPanel.cpp:71 src/view/PreferencesPanel.cpp:184 -#: src/view/PreferencesPanel.cpp:266 +#: src/view/ImportPanel.cpp:54 +msgid "Load operations" +msgstr "" + +#: src/view/AccountPanel.cpp:129 +msgid "Mode" +msgstr "" + +#: src/view/PreferencesPanel.cpp:72 src/view/PreferencesPanel.cpp:196 +#: src/view/PreferencesPanel.cpp:292 msgid "Name" msgstr "" -#: src/view/PreferencesPanel.cpp:739 +#: src/view/PreferencesPanel.cpp:796 msgid "Name changed" msgstr "" -#: src/view/UsersDialog.cpp:52 src/view/UsersDialog.cpp:107 +#: src/view/UsersDialog.cpp:53 src/view/UsersDialog.cpp:114 msgid "New User" msgstr "" @@ -362,30 +482,48 @@ msgstr "" msgid "New password " msgstr "" -#: src/model/Database.cpp:135 +#: src/model/Database.cpp:91 msgid "" "No database found, would you like to create a new one ?\n" "\n" msgstr "" -#: src/view/SearchPanel.cpp:232 +#: src/view/SearchPanel.cpp:119 src/view/ExportPanel.cpp:111 msgid "No entry found" msgstr "" -#: src/view/SearchPanel.cpp:76 +#: src/view/ImportPanel.cpp:351 +msgid "No operation found into this file" +msgstr "" + +#: src/view/ExportPanel.cpp:124 +msgid "No operation to save" +msgstr "" + +#: src/view/StatsPanel.cpp:117 src/view/SearchBanner.cpp:70 +#: src/view/AccountPanel.cpp:237 msgid "Non fix" msgstr "" -#: src/view/SearchPanel.cpp:76 +#: src/view/SearchPanel.cpp:149 src/view/SearchPanel.cpp:191 +#: src/view/PreferencesPanel.cpp:441 src/view/PreferencesPanel.cpp:702 +msgid "None" +msgstr "" + +#: src/view/SearchBanner.cpp:70 msgid "Not checked" msgstr "" -#: src/view/PreferencesPanel.cpp:185 +#: src/view/PreferencesPanel.cpp:197 msgid "Number" msgstr "" -#: src/view/GenerateDialog.cpp:59 src/view/PasswordDialog.cpp:55 -#: src/view/UsersDialog.cpp:50 +#: src/model/import/OFXImportEngine.cpp:135 +msgid "OFX files (*.ofx)|*.ofx" +msgstr "" + +#: src/view/UsersDialog.cpp:51 src/view/GenerateDialog.cpp:60 +#: src/view/PasswordDialog.cpp:55 msgid "OK" msgstr "" @@ -393,23 +531,27 @@ msgstr "" msgid "Old password " msgstr "" -#: src/controller/KissCount.cpp:365 -msgid "Operating exepense" -msgstr "" - -#: src/view/PreferencesPanel.cpp:62 +#: src/view/PreferencesPanel.cpp:63 msgid "Operation order" msgstr "" -#: src/view/ButtonPanel.cpp:69 src/view/SearchPanel.cpp:89 +#: src/view/SearchBanner.cpp:82 src/view/AccountPanel.cpp:186 msgid "Operations" msgstr "" -#: src/controller/KissCount.cpp:369 +#: src/view/ImportPanel.cpp:421 +msgid "Operations successfully imported" +msgstr "" + +#: src/view/ExportPanel.cpp:144 +msgid "Operations successfuly saved" +msgstr "" + +#: src/controller/KissCount.cpp:364 msgid "Other" msgstr "" -#: src/view/UsersDialog.cpp:43 +#: src/view/UsersDialog.cpp:45 msgid "Password " msgstr "" @@ -417,7 +559,7 @@ msgstr "" msgid "Password changed" msgstr "" -#: src/view/ButtonPanel.cpp:105 +#: src/view/wxUI.cpp:249 msgid "" "Personal accounting software\n" "\n" @@ -425,153 +567,192 @@ msgid "" "\n" "Licenced under GNU GPL v3\n" "\n" -"Copyright (C) 2010 Grégory Soutadé" +"Copyright (C) 2010-2011 Grégory Soutadé" msgstr "" #: src/view/PasswordDialog.cpp:78 msgid "Please retype new password" msgstr "" -#: src/view/ButtonPanel.cpp:72 src/view/PreferencesPanel.cpp:741 -#: src/view/PreferencesPanel.cpp:775 +#: src/view/PreferencesPanel.cpp:184 src/view/PreferencesPanel.cpp:798 +#: src/view/PreferencesPanel.cpp:832 msgid "Preferences" msgstr "" -#: src/model/Database.cpp:55 -msgid "Query failed !\n" -msgstr "" - -#: src/view/ButtonPanel.cpp:75 -msgid "Quit" -msgstr "" - -#: src/view/ButtonPanel.cpp:112 +#: src/view/wxUI.cpp:256 msgid "Quit KissCount ?" msgstr "" -#: src/view/AccountPanel.cpp:204 +#: src/view/AccountPanel.cpp:128 +msgid "Real" +msgstr "" + +#: src/view/AccountPanel.cpp:236 msgid "Remains" msgstr "" -#: src/view/ButtonPanel.cpp:71 src/view/SearchPanel.cpp:83 -#: src/view/SearchPanel.cpp:243 +#: src/view/SearchPanel.cpp:61 +msgid "Rename" +msgstr "" + +#: src/view/ExportPanel.cpp:128 +msgid "Save as" +msgstr "" + +#: src/view/ImportPanel.cpp:60 +msgid "Save import patterns" +msgstr "" + +#: src/view/SearchPanel.cpp:48 src/view/SearchPanel.cpp:97 +#: src/view/SearchPanel.cpp:254 src/view/ExportPanel.cpp:44 msgid "Search" msgstr "" -#: src/view/AccountPanel.cpp:90 src/view/StatsPanel.cpp:124 +#: src/view/StatsPanel.cpp:134 src/view/AccountPanel.cpp:95 msgid "Serie 1" msgstr "" -#: src/view/PreferencesPanel.cpp:63 +#: src/view/PreferencesPanel.cpp:64 msgid "Shared with" msgstr "" -#: src/view/ButtonPanel.cpp:70 src/view/StatsPanel.cpp:328 +#: src/view/StatsPanel.cpp:172 src/view/StatsPanel.cpp:384 msgid "Statistics" msgstr "" -#: src/view/StatsPanel.cpp:71 +#: src/view/StatsPanel.cpp:70 msgid "To" msgstr "" -#: src/view/GenerateDialog.cpp:52 +#: src/view/GenerateDialog.cpp:53 msgid "To " msgstr "" -#: src/view/AccountPanel.cpp:186 +#: src/view/AccountPanel.cpp:210 msgid "Total Credit" msgstr "" -#: src/view/AccountPanel.cpp:187 +#: src/view/AccountPanel.cpp:211 msgid "Total Debit" msgstr "" -#: src/view/AccountPanel.cpp:127 +#: src/view/AccountPanel.cpp:135 msgid "UnGroup" msgstr "" -#: src/model/Database.cpp:95 src/model/Database.cpp:102 -#: src/model/Database.cpp:120 src/model/Database.cpp:155 +#: src/model/Database.cpp:112 src/model/Database.cpp:118 +msgid "Unable to Create " +msgstr "" + +#: src/model/Database.cpp:45 src/model/Database.cpp:52 +#: src/model/Database.cpp:70 src/model/Database.cpp:126 msgid "Unable to open Database" msgstr "" -#: src/controller/KissCount.cpp:367 +#: src/controller/KissCount.cpp:364 msgid "Unexpected" msgstr "" -#: src/model/User.cpp:52 src/model/User.cpp:72 src/model/User.cpp:120 +#: src/view/SearchBanner.cpp:66 src/view/SearchBanner.cpp:74 +#: src/view/AccountPanel.cpp:529 src/view/AccountPanel.cpp:565 +#: src/view/AccountPanel.cpp:603 src/model/User.cpp:59 src/model/User.cpp:79 +#: src/model/User.cpp:127 msgid "Unknown" msgstr "" -#: src/model/Database.cpp:38 -msgid "Update failed !\n" +#: src/view/ImportPanel.cpp:42 +msgid "Unresolved accounts" msgstr "" -#: src/view/PreferencesPanel.cpp:58 +#: src/view/ImportPanel.cpp:43 +msgid "Unresolved categories" +msgstr "" + +#: src/view/AccountPanel.cpp:136 +msgid "Update next months" +msgstr "" + +#: src/view/PreferencesPanel.cpp:59 msgid "User" msgstr "" -#: src/view/PreferencesPanel.cpp:733 src/view/UsersDialog.cpp:38 -#: src/view/UsersDialog.cpp:118 +#: src/view/UsersDialog.cpp:40 src/view/UsersDialog.cpp:125 +#: src/view/PreferencesPanel.cpp:790 msgid "User " msgstr "" -#: src/view/UsersDialog.cpp:30 +#: src/view/UsersDialog.cpp:31 msgid "Users" msgstr "" -#: src/view/grid/GridAccount.cpp:1036 +#: src/view/PreferencesPanel.cpp:199 src/view/AccountPanel.cpp:128 +msgid "Virtual" +msgstr "" + +#: src/view/grid/GridAccount.cpp:1071 msgid "Warning" msgstr "" -#: src/view/grid/GridAccount.cpp:1036 +#: src/view/PreferencesPanel.cpp:446 +msgid "Wich account will replace this one ?" +msgstr "" + +#: src/view/PreferencesPanel.cpp:707 +msgid "Wich category will replace this one ?" +msgstr "" + +#: src/model/export/XMLExportEngine.cpp:29 +msgid "XML files (*.xml)|*.xml" +msgstr "" + +#: src/view/grid/GridAccount.cpp:1071 msgid "You made a debit on a blocked account" msgstr "" -#: src/view/wxUI.cpp:99 +#: src/view/wxUI.cpp:125 msgid "april" msgstr "" -#: src/view/wxUI.cpp:103 +#: src/view/wxUI.cpp:129 msgid "august" msgstr "" -#: src/view/wxUI.cpp:107 +#: src/view/wxUI.cpp:133 msgid "december" msgstr "" -#: src/view/wxUI.cpp:97 +#: src/view/wxUI.cpp:123 msgid "february" msgstr "" -#: src/view/wxUI.cpp:96 +#: src/view/wxUI.cpp:122 msgid "january" msgstr "" -#: src/view/wxUI.cpp:102 +#: src/view/wxUI.cpp:128 msgid "july" msgstr "" -#: src/view/wxUI.cpp:101 +#: src/view/wxUI.cpp:127 msgid "june" msgstr "" -#: src/view/wxUI.cpp:98 +#: src/view/wxUI.cpp:124 msgid "march" msgstr "" -#: src/view/wxUI.cpp:100 +#: src/view/wxUI.cpp:126 msgid "may" msgstr "" -#: src/view/wxUI.cpp:106 +#: src/view/wxUI.cpp:132 msgid "november" msgstr "" -#: src/view/wxUI.cpp:105 +#: src/view/wxUI.cpp:131 msgid "october" msgstr "" -#: src/view/wxUI.cpp:104 +#: src/view/wxUI.cpp:130 msgid "september" msgstr "" diff --git a/src/ParseExp.cpp b/src/ParseExp.cpp index 81f7e37..e6162b7 100644 --- a/src/ParseExp.cpp +++ b/src/ParseExp.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/ParseExp.h b/src/ParseExp.h index 00c09c5..41bb170 100644 --- a/src/ParseExp.h +++ b/src/ParseExp.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/controller/KissCount.cpp b/src/controller/KissCount.cpp index 4113bc4..54793d7 100644 --- a/src/controller/KissCount.cpp +++ b/src/controller/KissCount.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -19,6 +19,9 @@ #include "KissCount.h" +std::vector * KissCount::_importEngines; +std::vector * KissCount::_exportEngines; + KissCount::KissCount(const char* bdd_filename) : _user(NULL) { wxRect rect = wxDisplay().GetGeometry(); @@ -40,7 +43,7 @@ KissCount::KissCount(const char* bdd_filename) : _user(NULL) _wxUI->Close(true); throw s; } - + _wxUI->ChangeUser(); _wxUI->Enable(); } @@ -49,6 +52,9 @@ KissCount::~KissCount() { delete _db; delete _wxUI; + delete _importEngines; + delete _exportEngines; + if (_user) delete _user; } @@ -93,23 +99,32 @@ double KissCount::GetAccountAmount(const wxString& id, int month, int year) return _db->GetAccountAmount(id, month, year); } -void KissCount::UpdateOperation(Operation& op) +double KissCount::CalcAccountAmount(const wxString& id, int month, int year, bool* had_values) { - // Unlink - op.transfert = wxT(""); - _user->LinkOrUnlinkOperation(op); - - _db->UpdateOperation(op); - - // Link - _user->LinkOrUnlinkOperation(op); + return _db->CalcAccountAmount(id, month, year, had_values); } -wxString KissCount::AddOperation(Operation& op) +void KissCount::UpdateOperation(Operation& op, bool checkTransfert) { - wxString ret = _db->AddOperation(_user, op); + // Unlink + if (checkTransfert) + { + op.transfert = wxT(""); + _user->LinkOrUnlinkOperation(op); + } - if (op.transfert.Length()) + _db->UpdateOperation(_user, op, checkTransfert); + + // Link + if (checkTransfert) + _user->LinkOrUnlinkOperation(op); +} + +wxString KissCount::AddOperation(Operation& op, bool checkTransfert) +{ + wxString ret = _db->AddOperation(_user, op, checkTransfert); + + if (checkTransfert && op.transfert.Length()) _user->LinkOrUnlinkOperation(op); return ret; @@ -122,7 +137,7 @@ void KissCount::DeleteOperation(Operation& op) op.transfert = wxT(""); _user->LinkOrUnlinkOperation(op); } - _db->DeleteOperation(op); + _db->DeleteOperation(_user, op); } void KissCount::DeleteOperations(int month, int year) @@ -148,9 +163,9 @@ double KissCount::MetaPositiveAmount(const wxString& id) return _db->MetaPositiveAmount(id); } -void KissCount::SetAccountAmount(int month, int year, const wxString& accountId, double amount) +void KissCount::SetAccountAmount(const wxString& accountId, int month, int year, double amount) { - _db->SetAccountAmount(month, year, accountId, amount); + _db->SetAccountAmount(accountId, month, year, amount); } wxString KissCount::AddAccount(Account& ac) @@ -162,19 +177,11 @@ wxString KissCount::AddAccount(Account& ac) curDate.SetToCurrent(); - SetAccountAmount((int)curDate.GetMonth(), curDate.GetYear(), ac.id, 0.0); + SetAccountAmount(ac.id, (int)curDate.GetMonth(), curDate.GetYear(), 0.0); return ac.id; } -bool sortAccounts(Account ac1, Account ac2) -{ - if (ac1._default) return true; - if (ac2._default) return false; - - return (ac1.name.Cmp(ac2.name) < 0); -} - void KissCount::UpdateAccount(Account& ac) { std::vector::iterator it; @@ -189,18 +196,24 @@ void KissCount::UpdateAccount(Account& ac) std::sort(_user->_accounts.begin(), _user->_accounts.end(), sortAccounts); } -void KissCount::DeleteAccount(Account& ac) +void KissCount::DeleteAccount(Account& ac, const wxString& replacement) { std::vector::iterator it; int i; + std::map >* >::iterator it2; - _db->DeleteAccount(_user, ac); + _db->DeleteAccount(_user, ac, replacement); for (i=0, it=_user->_accounts.begin(); it !=_user->_accounts.end(); it++, i++) if (it->id == ac.id) { _user->_accounts.erase(_user->_accounts.begin()+i); break; } + + for (it2= _user->_operations.begin(); + it2 != _user->_operations.end(); + it2++) + LoadYear(it2->first, true); } void KissCount::AddSharedAccount(Account& ac, const wxString& granted) @@ -230,6 +243,7 @@ wxString KissCount::AddCategory(Category& category) category.id = id; _user->_categories.push_back(category); + _user->_categoriesFonts.push_back(ExtractFont(wxT(""))); return id; } @@ -247,16 +261,24 @@ void KissCount::UpdateCategory(Category& category) } } -void KissCount::DeleteCategory(Category& category) +void KissCount::DeleteCategory(Category& category, const wxString& replacement) { - _db->DeleteCategory(_user, category); + std::map >* >::iterator it; + + _db->DeleteCategory(_user, category, replacement); for (int i=0; i<_user->GetCategoriesNumber();i++) if (_user->_categories[i].id == category.id) { _user->_categories.erase(_user->_categories.begin()+i); + _user->_categoriesFonts.erase(_user->_categoriesFonts.begin()+i); break; } + + for (it= _user->_operations.begin(); + it != _user->_operations.end(); + it++) + LoadYear(it->first, true); } std::map > KissCount::GetAllOperations() @@ -290,6 +312,7 @@ void KissCount::GenerateMonth(int monthFrom, int yearFrom, int monthTo, int year op.checked = false; op.id = AddOperation(op); op.childs.clear(); + op._virtual = false; if (op.meta) meta[it->id] = op.id; (*_user->_operations[yearTo])[monthTo].push_back(op); @@ -335,6 +358,12 @@ void KissCount::ChangeName(const wxString& name) _user->_name = name; } +// To enable translation during xgettext +wxString default_cats[] = { + _("Fix"), _("Groceries"), _("Hobbies"), _("Car"), + _("Unexpected"), _("Other") +}; + void KissCount::NewUser(const wxString& name) { wxDateTime curDate; @@ -356,17 +385,17 @@ void KissCount::NewUser(const wxString& name) AddAccount(ac); - cat.parent = wxT("0") ; cat.name = _("Fix") ; cat.backcolor = OWN_YELLOW ; cat.forecolor = *wxBLACK; + cat.parent = wxT("0") ; cat.name = wxT("Fix") ; cat.backcolor = OWN_YELLOW ; cat.forecolor = *wxBLACK; cat.fix_cost = true; AddCategory(cat); - cat.parent = wxT("0") ; cat.name = _("Groceries") ; cat.backcolor = OWN_GREEN; cat.forecolor = *wxBLACK; + cat.parent = wxT("0") ; cat.name = wxT("Groceries") ; cat.backcolor = OWN_GREEN; cat.forecolor = *wxBLACK; cat.fix_cost = false; AddCategory(cat); - cat.parent = wxT("0") ; cat.name = _("Hobbies") ; cat.backcolor = OWN_GREEN; cat.forecolor = *wxBLACK; + cat.parent = wxT("0") ; cat.name = wxT("Hobbies") ; cat.backcolor = OWN_GREEN; cat.forecolor = *wxBLACK; cat.fix_cost = false; AddCategory(cat); - cat.parent = wxT("0") ; cat.name = _("Operating exepense") ; cat.backcolor = OWN_GREEN; cat.forecolor = *wxBLACK; + cat.parent = wxT("0") ; cat.name = wxT("Car") ; cat.backcolor = OWN_GREEN; cat.forecolor = *wxBLACK; cat.fix_cost = false; AddCategory(cat); - cat.parent = wxT("0") ; cat.name = _("Unexpected") ; cat.backcolor = OWN_GREEN; cat.forecolor = *wxBLACK; + cat.parent = wxT("0") ; cat.name = wxT("Unexpected") ; cat.backcolor = OWN_GREEN; cat.forecolor = *wxBLACK; cat.fix_cost = false; AddCategory(cat); - cat.parent = wxT("0") ; cat.name = _("Other") ; cat.backcolor = OWN_GREEN; cat.forecolor = *wxBLACK; + cat.parent = wxT("0") ; cat.name = wxT("Other") ; cat.backcolor = OWN_GREEN; cat.forecolor = *wxBLACK; cat.fix_cost = false; AddCategory(cat); SetOperationOrder(wxT("ASC")); @@ -398,6 +427,11 @@ void KissCount::SetOperationOrder(const wxString& order) _db->UpdatePreference(_user, wxT("operation_order")); } +const wxString& KissCount::GetOperationOrder() +{ + return _user->_preferences[wxT("operation_order")] ; +} + std::vector* KissCount::Search(wxString* description, wxDateTime* dateFrom, wxDateTime* dateTo, wxString* amountFrom, wxString* amountTo, std::vector categories, int types, std::vector accounts) @@ -406,7 +440,7 @@ std::vector* KissCount::Search(wxString* description, wxDateTime* dat return _db->Search(_user, description, dateFrom, dateTo, amountFrom, amountTo, categories, types, accounts, true); } -bool KissCount::SearchPreviousOperation(Operation* res, Operation& op, int month, int year) +bool KissCount::SearchPreviousOperation(Operation* res, Operation& op, int month, int year, bool limitToType) { std::vector* operations; wxDateTime* date ; @@ -423,7 +457,11 @@ bool KissCount::SearchPreviousOperation(Operation* res, Operation& op, int month date = new wxDateTime(0, (wxDateTime::Month)month, year); - operations = _db->Search(_user, &op.description, date, NULL, NULL, NULL, v, op.fix_cost ? FIX_OP : NON_FIX_OP, v, false); + if (limitToType) + operations = _db->Search(_user, &op.description, date, NULL, NULL, NULL, v, op.fix_cost ? FIX_OP : NON_FIX_OP, v, false); + else + operations = _db->Search(_user, &op.description, date, NULL, NULL, NULL, v, ALL_OP, v, false); + delete date; @@ -474,6 +512,11 @@ std::map* KissCount::GetNotChecked(int month, int year) return _db->GetNotChecked(_user, month, year); } +std::map* KissCount::GetVirtualAmount(int month, int year) +{ + return _db->GetVirtualAmount(_user, month, year); +} + wxFont KissCount::ExtractFont(wxString strFont) { long int pos, pointSize, family, style, weight; @@ -529,3 +572,120 @@ wxString KissCount::CompactFont(const wxFont& font) return res; } + +void KissCount::UnRegisterImportEngine(ImportEngine* engine) +{ + std::vector::iterator it; + std::vector* importEngines = KissCount::GetImportEngines(); + + for(it=importEngines->begin(); it!=importEngines->end(); it++) + if (*it == engine) + { + importEngines->erase(it); + break; + } +} + +void KissCount::RegisterImportEngine(ImportEngine* engine) +{ + std::vector* importEngines = KissCount::GetImportEngines(); + + importEngines->push_back(engine); +} + +wxString KissCount::GetImportEngineExtensions() +{ + wxString res; + std::vector::iterator it; + int i; + std::vector* importEngines = KissCount::GetImportEngines(); + + for(i=0; i<(int)importEngines->size()-1; i++) + res = res + (*importEngines)[i]->GetFileExt() + wxT("|") ; + + if (importEngines->size()) + res = res + (*importEngines)[i]->GetFileExt(); + + return res; +} + +ImportEngine* KissCount::GetImportEngine(wxString path) +{ + std::vector::iterator it; + std::vector* importEngines = KissCount::GetImportEngines(); + + for(it=importEngines->begin(); it!=importEngines->end(); it++) + if ((*it)->HandleFile(path, _user, _db, this)) + return *it; + + return NULL; +} + +void KissCount::UpdateImportPattern() +{ + _db->UpdateImportPattern(_user); +} + +void KissCount::UnRegisterExportEngine(ExportEngine* engine) +{ + std::vector::iterator it; + std::vector* exportEngines = KissCount::GetExportEngines(); + + for(it=exportEngines->begin(); it!=exportEngines->end(); it++) + if (*it == engine) + { + exportEngines->erase(it); + break; + } +} + +void KissCount::RegisterExportEngine(ExportEngine* engine) +{ + std::vector* exportEngines = KissCount::GetExportEngines(); + + exportEngines->push_back(engine); +} + +wxString KissCount::GetExportEngineExtensions() +{ + wxString res; + std::vector::iterator it; + int i; + std::vector* exportEngines = KissCount::GetExportEngines(); + + for(i=0; i<(int)exportEngines->size()-1; i++) + res = res + (*exportEngines)[i]->GetFileExt() + wxT("|") ; + + if (exportEngines->size()) + res = res + (*exportEngines)[i]->GetFileExt(); + + return res; +} + +ExportEngine* KissCount::GetExportEngine(wxString path) +{ + std::vector::iterator it; + std::vector* exportEngines = KissCount::GetExportEngines(); + + for(it=exportEngines->begin(); it!=exportEngines->end(); it++) + if ((*it)->HandleFile(path, _user, _db, this)) + return *it; + + return NULL; +} + +std::vector* KissCount::GetImportEngines() +{ + if (!_importEngines) + _importEngines = new std::vector; + + return _importEngines; +} + +std::vector* KissCount::GetExportEngines() +{ + if (!_exportEngines) + _exportEngines = new std::vector; + + return _exportEngines; +} diff --git a/src/controller/KissCount.h b/src/controller/KissCount.h index 7eb3a0a..a8d0c42 100644 --- a/src/controller/KissCount.h +++ b/src/controller/KissCount.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -24,15 +24,30 @@ #include #include +#include +#include #include #include #include -#define APP_VERSION "v0.1" +#define APP_VERSION "0.2" +#define ESCAPE_CHARS(s) { \ + if (s.Find(wxT("\\\"")) == wxNOT_FOUND) \ + s.Replace(wxT("\""), wxT("\\\""), true); \ + if (s.Find(wxT("\\\'")) == wxNOT_FOUND) \ + s.Replace(wxT("\'"), wxT("\\\'"), true); \ + } + +#define UNESCAPE_CHARS(s) { \ + s.Replace(wxT("\\\""), wxT("\""), true); \ + s.Replace(wxT("\\\'"), wxT("\'"), true); \ + } class wxUI; class Database; +class ImportEngine; +class ExportEngine; class KissCount { @@ -51,18 +66,20 @@ public: void LoadYear(int year, bool force=false); - wxString AddOperation(Operation& op); - void UpdateOperation(Operation& op); + wxString AddOperation(Operation& op, bool checkTransfert=true); + void UpdateOperation(Operation& op, bool checkTransfert=true); void DeleteOperation(Operation& op); void DeleteOperations(int month, int year); double MetaAmount(const wxString& id); double MetaPositiveAmount(const wxString& id); double GetAccountAmount(const wxString& id, int month, int year); - void SetAccountAmount(int month, int year, const wxString& accountId, double value); + void SetAccountAmount(const wxString& accountId, int month, int year, double value); + double CalcAccountAmount(const wxString& id, int month, int year, bool* had_values); + wxString AddAccount(Account& ac); void UpdateAccount(Account& ac); - void DeleteAccount(Account& ac); + void DeleteAccount(Account& ac, const wxString& replacement); void AddSharedAccount(Account& ac, const wxString& granted); void RemoveSharedAccount(Account& ac, const wxString& granted); std::map getSharedAccountOwners(const wxString& account); @@ -70,7 +87,7 @@ public: wxString AddCategory(Category& category); void UpdateCategory(Category& category); - void DeleteCategory(Category& category); + void DeleteCategory(Category& category, const wxString& replacement); std::map > GetAllOperations(); @@ -79,12 +96,13 @@ public: void SetLanguage(wxLanguage language); void SetOperationOrder(const wxString& order); + const wxString& GetOperationOrder(); std::vector* Search(wxString* description, wxDateTime* dateFrom, wxDateTime* dateTo, wxString* amountFrom, wxString* amountTo, std::vector categories, int types, std::vector accounts); - bool SearchPreviousOperation(Operation* res, Operation& op, int month, int year); + bool SearchPreviousOperation(Operation* res, Operation& op, int month, int year, bool limitToType); void GetStats(int monthFrom, int yearFrom, int monthTo, int yearTo, std::map > >* accountAmounts, @@ -95,14 +113,35 @@ public: std::map* categories); std::map* GetNotChecked(int month, int year); + std::map* GetVirtualAmount(int month, int year); static wxFont ExtractFont(wxString strFont); static wxString CompactFont(const wxFont& font); + static void RegisterImportEngine(ImportEngine* engine); + static void UnRegisterImportEngine(ImportEngine* engine); + + wxString GetImportEngineExtensions(); + ImportEngine* GetImportEngine(wxString path); + + static void RegisterExportEngine(ExportEngine* engine); + static void UnRegisterExportEngine(ExportEngine* engine); + + wxString GetExportEngineExtensions(); + ExportEngine* GetExportEngine(wxString path); + + void UpdateImportPattern(); + private: wxUI* _wxUI; Database* _db; User* _user; + + static std::vector *GetImportEngines(); + static std::vector *GetExportEngines(); + + static std::vector *_importEngines; + static std::vector *_exportEngines; }; #endif diff --git a/src/main.cpp b/src/main.cpp index 4061e71..97c33f4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/main.h b/src/main.h index 7563d00..68b9370 100644 --- a/src/main.h +++ b/src/main.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/model/Account.cpp b/src/model/Account.cpp new file mode 100644 index 0000000..b207df9 --- /dev/null +++ b/src/model/Account.cpp @@ -0,0 +1,34 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#include "Account.h" + +bool sortAccounts(Account ac1, Account ac2) +{ + if (ac1._default) return true; + if (ac2._default) return false; + + if (!ac1.blocked && ac2.blocked) return true; + if (ac1.blocked && !ac2.blocked) return false; + + if (!ac1._virtual && ac2._virtual) return true; + if (ac1._virtual && !ac2._virtual) return false; + + return (ac1.name.Cmp(ac2.name) < 0); +} diff --git a/src/model/Account.h b/src/model/Account.h index 7c56b71..3cf953c 100644 --- a/src/model/Account.h +++ b/src/model/Account.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -33,6 +33,9 @@ public: bool blocked; bool _default; bool is_owner; + bool _virtual; }; +bool sortAccounts(Account ac1, Account ac2); + #endif diff --git a/src/model/AccountAmount.h b/src/model/AccountAmount.h new file mode 100644 index 0000000..79bd690 --- /dev/null +++ b/src/model/AccountAmount.h @@ -0,0 +1,44 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#ifndef ACCOUNTAMOUNT_H +#define ACCOUNTAMOUNT_H + +class AccountAmount { +public: + wxString account; + int month; + int year; + bool operator()(const AccountAmount& x, const AccountAmount& y) const + { + long x1, y1; + + if (x.account != y.account) + { + x.account.ToLong(&x1); + y.account.ToLong(&y1); + + return x1 < y1; + } + + return (x.year < y.year || ((x.year == y.year) && x.month < y.month)); + } +}; + +#endif diff --git a/src/model/Category.h b/src/model/Category.h index af2f29a..7eec57a 100644 --- a/src/model/Category.h +++ b/src/model/Category.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -29,6 +29,7 @@ public: wxColour backcolor; wxColour forecolor; wxString font; + bool fix_cost; }; #endif diff --git a/src/model/Database.cpp b/src/model/Database.cpp index ea9b9d0..9f6ec23 100644 --- a/src/model/Database.cpp +++ b/src/model/Database.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -19,59 +19,6 @@ #include "Database.h" -// if (!_db.CheckSyntax(req)) -// { -// wxString s = req; -// std::cout << s.mb_str() << " is invalid !\n" ; -// code_if_syntax_fail; -// return return_value; -// } - -#define EXECUTE_SQL_UPDATE_WITH_CODE(req, return_value, code_if_fail, code_if_syntax_fail) \ - do{ \ - try \ - { \ - _db.ExecuteUpdate(req); \ - } \ - catch (wxSQLite3Exception e) \ - { \ - wxMessageBox(_("Update failed !\n") + req, _("Error"), wxICON_ERROR | wxOK); \ - std::cerr << __FUNCTION__ << "\n" ; \ - std::cerr << req.mb_str() << "\n" ; \ - std::cerr << e.GetMessage().mb_str() << "\n" ; \ - code_if_fail; \ - return return_value; \ - } \ - } while(0); - -#define EXECUTE_SQL_QUERY_WITH_CODE(req, res, return_value, code_if_fail, code_if_syntax_fail) \ - do{ \ - try \ - { \ - res = _db.ExecuteQuery(req); \ - } \ - catch (wxSQLite3Exception e) \ - { \ - wxMessageBox(_("Query failed !\n") + req, _("Error"), wxICON_ERROR | wxOK); \ - std::cerr << __FUNCTION__ << "\n" ; \ - std::cerr << req.mb_str() << "\n" ; \ - std::cerr << e.GetMessage().mb_str() << "\n" ; \ - code_if_fail; \ - return return_value; \ - } \ - } while(0); - -#define EXECUTE_SQL_QUERY(req, res, return_value) EXECUTE_SQL_QUERY_WITH_CODE(req, res, return_value, {}, {}) - -#define EXECUTE_SQL_UPDATE(req, return_value) EXECUTE_SQL_UPDATE_WITH_CODE(req, return_value, {}, {}) - -#define ESCAPE_CHARS(s) { \ - if (s.Find(wxT("\\\"")) == wxNOT_FOUND) \ - s.Replace(wxT("\""), wxT("\\\""), true); \ - if (s.Find(wxT("\\\'")) == wxNOT_FOUND) \ - s.Replace(wxT("\'"), wxT("\\\'"), true); \ - } - static inline wxString DoubleToString(double d) { wxString res; @@ -85,6 +32,9 @@ static inline wxString DoubleToString(double d) Database::Database(const char* filename, KissCount* kiss) : _kiss(kiss) { std::ifstream bdd_file; + + std::string sPath = std::string(wxGetHomeDir().mb_str()) + std::string(BDD_FILE); + wxString wPath = wxGetHomeDir() + wxT(BDD_FILE); if (filename) { @@ -106,7 +56,7 @@ Database::Database(const char* filename, KissCount* kiss) : _kiss(kiss) else { // If default BDD file, assume this can be the first load - bdd_file.open(BDD_FILE, std::ifstream::in); + bdd_file.open(sPath.c_str(), std::ifstream::in); if (!bdd_file.good()) { @@ -114,16 +64,18 @@ Database::Database(const char* filename, KissCount* kiss) : _kiss(kiss) } else { - _db.Open(wxT(BDD_FILE)); + _db.Open(wPath); if (!_db.IsOpen()) { wxMessageBox(_("Unable to open Database"), _("Error"), wxICON_ERROR | wxOK ); - throw std::string("Unable to open ") + BDD_FILE; + throw std::string("Unable to open ") + sPath; } } } bdd_file.close(); + + CheckDatabaseVersion(); } void Database::CreateDatabase() @@ -131,14 +83,21 @@ void Database::CreateDatabase() std::ifstream init_script; std::string line; wxString wxline; - + std::string sPath = std::string(wxGetHomeDir().mb_str()) + std::string(BDD_FILE); + wxString wPath = wxGetHomeDir() + wxT(BDD_FILE); + wxFileName dirname( wxGetHomeDir() +wxT("/.kisscount/"), wxPATH_UNIX); + wxFileName filename (wPath); + wxFile file; wxString message = _("No database found, would you like to create a new one ?\n\n"); message += _("!! Warning !! If there was a bug, the old database will be suppressed !"); - wxMessageDialog dialog(NULL, message, wxT("KissCount"), wxYES_NO); + if (dirname.DirExists()) + { + wxMessageDialog dialog(NULL, message, wxT("KissCount"), wxYES_NO); - if (dialog.ShowModal() == wxID_NO) - throw std::string("No database") ; + if (dialog.ShowModal() == wxID_NO) + throw std::string("No database") ; + } init_script.open(INIT_SCRIPT); @@ -148,12 +107,24 @@ void Database::CreateDatabase() throw "init.sql not found, aborting"; } - _db.Open(wxT(BDD_FILE)); + if (!dirname.DirExists() && !dirname.Mkdir()) + { + wxMessageBox(_("Unable to Create ") + wxGetHomeDir() +wxT("/.kisscount/"), _("Error"), wxICON_ERROR | wxOK ); + throw std::string("Unable to create ") + std::string(wxGetHomeDir().mb_str()) + std::string("/.kisscount/"); + } + + if (!filename.FileExists() && !file.Create(wPath, false, wxS_IRUSR|wxS_IWUSR|wxS_IXUSR)) + { + wxMessageBox(_("Unable to Create ") + wPath, _("Error"), wxICON_ERROR | wxOK ); + throw std::string("Unable to create ") + sPath; + } + + _db.Open(wPath); if (!_db.IsOpen()) { wxMessageBox(_("Unable to open Database"), _("Error"), wxICON_ERROR | wxOK ); - throw std::string("Unable to open ") + BDD_FILE; + throw std::string("Unable to open ") + sPath; } do @@ -174,7 +145,7 @@ void Database::CreateDatabase() catch (...) { wxMessageBox(_("Error creating original database"), _("Error"), wxICON_ERROR | wxOK ); - remove(BDD_FILE); + remove(sPath.c_str()); throw line; } } while (init_script); @@ -241,6 +212,7 @@ User* Database::LoadUser(const wxString& name) User* user; Account account; Category category; + ImportPattern importPattern; std::vector::iterator it; @@ -261,7 +233,7 @@ User* Database::LoadUser(const wxString& name) set.Finalize(); - req = wxT("SELECT * FROM account WHERE user='") + user->_id + wxT("' ORDER BY default_account DESC, name ASC"); + req = wxT("SELECT * FROM account WHERE user='") + user->_id + wxT("' ORDER BY default_account DESC, virtual, blocked, name ASC"); EXECUTE_SQL_QUERY_WITH_CODE(req, set, NULL, {delete user;}, {delete user;}); @@ -273,12 +245,13 @@ User* Database::LoadUser(const wxString& name) account.shared = set.GetBool(wxT("shared")); account.blocked = set.GetBool(wxT("blocked")); account._default = set.GetBool(wxT("default_account")); + account._virtual = set.GetBool(wxT("virtual")); account.is_owner = true; user->_accounts.push_back(account); } set.Finalize(); - req = wxT("SELECT * FROM account WHERE id IN (SELECT account FROM shared_account WHERE user='") + user->_id + wxT("') ORDER BY name ASC"); + req = wxT("SELECT * FROM account WHERE id IN (SELECT account FROM shared_account WHERE user='") + user->_id + wxT("') ORDER BY name, blocked, virtual ASC"); EXECUTE_SQL_QUERY_WITH_CODE(req, set, NULL, {delete user;}, {delete user;}); @@ -290,6 +263,7 @@ User* Database::LoadUser(const wxString& name) account.shared = set.GetBool(wxT("shared")); account.blocked = set.GetBool(wxT("blocked")); account._default = set.GetBool(wxT("default_account")); + account._virtual = set.GetBool(wxT("virtual")); account.is_owner = false; user->_accounts.push_back(account); } @@ -317,7 +291,7 @@ User* Database::LoadUser(const wxString& name) set.Finalize(); } - req = wxT("SELECT * FROM category WHERE user='") + user->_id + wxT("' ORDER BY name ASC"); + req = wxT("SELECT * FROM category WHERE user='") + user->_id + wxT("' ORDER BY fix_cost DESC, name ASC"); EXECUTE_SQL_QUERY_WITH_CODE(req, set, NULL, {delete user;}, {delete user;}); while (set.NextRow()) @@ -328,16 +302,10 @@ User* Database::LoadUser(const wxString& name) category.backcolor = wxColour(set.GetAsString(wxT("backcolor"))); category.forecolor = wxColour(set.GetAsString(wxT("forecolor"))); category.font = set.GetAsString(wxT("font")); - if (category.name != _("Fix")) - { - user->_categories.push_back(category); - user->_categoriesFonts.push_back(_kiss->ExtractFont(category.font)); - } - else - { - user->_categories.insert(user->_categories.begin(), category); - user->_categoriesFonts.insert(user->_categoriesFonts.begin(), _kiss->ExtractFont(category.font)); - } + category.fix_cost = set.GetBool(wxT("fix_cost")); + + user->_categories.push_back(category); + user->_categoriesFonts.push_back(_kiss->ExtractFont(category.font)); } set.Finalize(); @@ -350,6 +318,20 @@ User* Database::LoadUser(const wxString& name) set.Finalize(); + req = wxT("SELECT * FROM import_pattern WHERE user='") + user->_id + wxT("'"); + EXECUTE_SQL_QUERY_WITH_CODE(req, set, NULL, {delete user;}, {delete user;}); + + while (set.NextRow()) + { + importPattern.pattern = set.GetAsString(wxT("pattern")); + importPattern.account = set.GetAsString(wxT("account")); + importPattern.category = set.GetAsString(wxT("category")); + + user->_importPatterns[set.GetAsString(wxT("description"))] = importPattern; + } + + set.Finalize(); + return user; } @@ -396,6 +378,7 @@ void Database::LoadYear(User* user, int year) op.transfert = set.GetAsString(wxT("transfert")); op.formula = set.GetAsString(wxT("formula")); op.meta = set.GetBool(wxT("meta")); + op._virtual = set.GetBool(wxT("virtual")); (*user->_operations[op.year])[op.month].push_back(op); } @@ -420,7 +403,7 @@ double Database::GetAccountAmount(const wxString& id, int month, int year) res = set.GetDouble(wxT("amount")); else { - SetAccountAmount(month, year, id, 0.0); + SetAccountAmount(id, month, year, 0.0); res = 0.0; } @@ -429,6 +412,38 @@ double Database::GetAccountAmount(const wxString& id, int month, int year) return res; } +double Database::CalcAccountAmount(const wxString& id, int month, int year, bool* had_values) +{ + wxSQLite3ResultSet set; + wxString req; + double res; + + req = wxT("SELECT SUM(id) AS id, SUM(amount) AS amount FROM operation WHERE account='") + id ; + req += wxT("' AND month='") + wxString::Format(wxT("%d"), month); + req += wxT("' AND year='") + wxString::Format(wxT("%d"), year); + req += wxT("'"); + req += wxT(" AND meta='0'"); + + EXECUTE_SQL_QUERY(req , set, 0.0); + + if (set.NextRow()) + { + res = set.GetDouble(wxT("amount")); + if (had_values) + *had_values = set.GetInt(wxT("id")) > 0 ; + } + else + { + res=0.0; + if (had_values) + *had_values = false; + } + + set.Finalize(); + + return res; +} + bool Database::GetOperation(const wxString& id, Operation* op) { wxSQLite3ResultSet set; @@ -452,26 +467,30 @@ bool Database::GetOperation(const wxString& id, Operation* op) op->fix_cost = set.GetBool(wxT("fix_cost")); op->checked = set.GetBool(wxT("checked")); op->transfert = set.GetAsString(wxT("transfert")); - op->formula = set.GetAsString(wxT("formula")); + op->formula = set.GetAsString(wxT("formula")); op->meta = set.GetBool(wxT("meta")); + op->_virtual = set.GetBool(wxT("virtual")); return true; } -void Database::LinkOrUnlinkOperation(Operation& op) +void Database::LinkOrUnlinkOperation(User* user, Operation& op) { Operation linked; wxString req; wxSQLite3ResultSet set; + Account account, account2; + bool _virtual; if (op.transfert.Length()) { // No one or not linked if (!GetOperation(op.transfert, &linked) || op.description != linked.description || op.amount != -linked.amount || op.account == linked.account) { - req = wxT("UPDATE operation SET transfert='' where id='") + op.id + wxT("'") ; + req = wxT("UPDATE operation SET transfert='' virtual='0' WHERE id='") + op.id + wxT("'") ; EXECUTE_SQL_UPDATE(req, ); op.transfert = wxT(""); + op._virtual = 0; return; } } @@ -484,11 +503,11 @@ void Database::LinkOrUnlinkOperation(Operation& op) if (set.NextRow()) { - req = wxT("UPDATE operation SET transfert='' where id='") + set.GetAsString(wxT("id")) + wxT("'") ; + req = wxT("UPDATE operation SET transfert='', virtual='0' WHERE id='") + set.GetAsString(wxT("id")) + wxT("'") ; EXECUTE_SQL_UPDATE(req, ); } - req = wxT("SELECT id FROM operation WHERE description=\"") + op.description + wxT("\""); + req = wxT("SELECT id, account FROM operation WHERE description=\"") + op.description + wxT("\""); req += wxT(" AND month='") + wxString::Format(wxT("%d"), op.month) + wxT("'"); req += wxT(" AND year='") + wxString::Format(wxT("%d"), op.year) + wxT("'"); req += wxT(" AND amount='") + DoubleToString(-op.amount) + wxT("'"); @@ -498,6 +517,8 @@ void Database::LinkOrUnlinkOperation(Operation& op) EXECUTE_SQL_QUERY(req, set, ); + op._virtual = false; + // Don't need to link if (!set.NextRow()) return ; @@ -506,19 +527,38 @@ void Database::LinkOrUnlinkOperation(Operation& op) op.transfert = linked.id; - req = wxT("UPDATE operation SET transfert='") + linked.id + wxT("' where id='") + op.id + wxT("'") ; + account = user->GetAccount(op.account); + account2 = user->GetAccount(set.GetAsString(wxT("account"))); + + _virtual = account._virtual || account2._virtual; + + req = wxT("UPDATE operation SET transfert='") + linked.id + wxT("'"); + if (_virtual) + req += wxT(", virtual='1'"); + else + req += wxT(", virtual='0'"); + req += wxT(" WHERE id='") + op.id + wxT("'") ; + EXECUTE_SQL_UPDATE(req, ); - req = wxT("UPDATE operation SET transfert='") + op.id + wxT("' where id='") + linked.id + wxT("'") ; + req = wxT("UPDATE operation SET transfert='") + op.id + wxT("'") ; + if (_virtual) + req += wxT(", virtual='1'"); + else + req += wxT(", virtual='0'"); + req += wxT(" WHERE id='") + linked.id + wxT("'") ; EXECUTE_SQL_UPDATE(req, ); + + op._virtual = _virtual; } } -void Database::UpdateOperation(Operation& op) +void Database::UpdateOperation(User* user, Operation& op, bool checkTransfert) { wxString req; - LinkOrUnlinkOperation(op); + if (checkTransfert) + LinkOrUnlinkOperation(user, op); ESCAPE_CHARS(op.description); @@ -531,6 +571,10 @@ void Database::UpdateOperation(Operation& op) req += wxT(", amount='") + DoubleToString(op.amount) + wxT("'"); req += wxT(", description=\"") + op.description + wxT("\""); req += wxT(", category='") + op.category + wxT("'"); + if (op.fix_cost) + req += wxT(", fix_cost='1'"); + else + req += wxT(", fix_cost='0'"); if (op.checked) req += wxT(", checked='1'"); else @@ -540,22 +584,27 @@ void Database::UpdateOperation(Operation& op) req += wxT(", meta='1'"); else req += wxT(", meta='0'"); + if (op._virtual) + req += wxT(", virtual='1'"); + else + req += wxT(", virtual='0'"); req += wxT(", formula='") + op.formula + wxT("'"); req += wxT(" WHERE id='") + op.id + wxT("'"); EXECUTE_SQL_UPDATE(req, ); - LinkOrUnlinkOperation(op); + if (checkTransfert) + LinkOrUnlinkOperation(user, op); } -wxString Database::AddOperation(User* user, Operation& op) +wxString Database::AddOperation(User* user, Operation& op, bool checkTransfert) { wxString req, res; wxSQLite3ResultSet set; ESCAPE_CHARS(op.description); - req = wxT("INSERT INTO operation ('user', 'parent', 'account', 'year', 'month', 'day', 'amount', 'description', 'category', 'fix_cost', 'formula', 'transfert', 'meta') VALUES ('") ; + req = wxT("INSERT INTO operation ('user', 'parent', 'account', 'year', 'month', 'day', 'amount', 'description', 'category', 'fix_cost', 'formula', 'transfert', 'meta', 'virtual') VALUES ('") ; req += user->_id + wxT("'"); req += wxT(", '") + op.parent + wxT("'"); req += wxT(", '") + op.account + wxT("'"); @@ -575,6 +624,10 @@ wxString Database::AddOperation(User* user, Operation& op) req += wxT(", '1'") ; else req += wxT(", '0'") ; + if (op._virtual) + req += wxT(", '1'") ; + else + req += wxT(", '0'") ; req += wxT(")"); EXECUTE_SQL_UPDATE(req, wxT("0")); @@ -583,19 +636,20 @@ wxString Database::AddOperation(User* user, Operation& op) op.id = res; - LinkOrUnlinkOperation(op); + if (checkTransfert) + LinkOrUnlinkOperation(user, op); return res; } -void Database::DeleteOperation(Operation& op) +void Database::DeleteOperation(User* user, Operation& op) { wxString req; req = wxT("DELETE FROM operation WHERE id='") + op.id + wxT("'"); EXECUTE_SQL_UPDATE(req, ); - LinkOrUnlinkOperation(op); + LinkOrUnlinkOperation(user, op); } void Database::DeleteOperations(User* user, int month, int year) @@ -659,8 +713,9 @@ bool Database::LoadOperation(User* user, const wxString& id) op.fix_cost = set.GetBool(wxT("fix_cost")); op.checked = set.GetBool(wxT("checked")); op.transfert = set.GetAsString(wxT("transfert")); - op.formula = set.GetAsString(wxT("formula")); + op.formula = set.GetAsString(wxT("formula")); op.meta = set.GetBool(wxT("meta")); + op._virtual = set.GetBool(wxT("virtual")); if (user->_operations[op.year]) { @@ -734,7 +789,7 @@ double Database::MetaPositiveAmount(const wxString& id) return res; } -void Database::SetAccountAmount(int month, int year, const wxString& accountId, double amount) +void Database::SetAccountAmount(const wxString& accountId, int month, int year, double amount) { wxString req; req = wxT("UPDATE account_amount SET ") ; @@ -768,7 +823,7 @@ wxString Database::AddAccount(User* user, Account& ac) { wxString req; - req = wxT("INSERT INTO account ('user', 'name', 'number', 'shared', 'blocked', 'default_account') VALUES ('") ; + req = wxT("INSERT INTO account ('user', 'name', 'number', 'shared', 'blocked', 'default_account', 'virtual') VALUES ('") ; req += user->_id + wxT("'"); req += wxT(", '") + ac.name + wxT("'"); req += wxT(", '") + ac.number + wxT("'"); @@ -784,6 +839,10 @@ wxString Database::AddAccount(User* user, Account& ac) req += wxT(", '1'") ; else req += wxT(", '0'") ; + if (ac._virtual) + req += wxT(", '1'") ; + else + req += wxT(", '0'") ; req += wxT(")"); EXECUTE_SQL_UPDATE(req, wxT("0")); @@ -809,6 +868,10 @@ void Database::UpdateAccount(Account& ac) req += wxT(", default_account='1'"); else req += wxT(", default_account='0'"); + if (ac._virtual) + req += wxT(", virtual='1'"); + else + req += wxT(", virtual='0'"); req += wxT(" WHERE id='") + ac.id + wxT("'"); EXECUTE_SQL_UPDATE(req, ); @@ -821,7 +884,7 @@ void Database::UpdateAccount(Account& ac) } } -void Database::DeleteAccount(User* user, Account& ac) +void Database::DeleteAccount(User* user, Account& ac, const wxString& replacement) { wxString req; @@ -840,6 +903,12 @@ void Database::DeleteAccount(User* user, Account& ac) req = wxT("DELETE FROM account_amount WHERE account='") + ac.id + wxT("'"); EXECUTE_SQL_UPDATE(req, ); + + req = wxT("UPDATE operation SET account='") + replacement + wxT("'"); + // req += wxT(", transfert='0'"); + req += wxT(" WHERE account='") + ac.id + wxT("'"); + + EXECUTE_SQL_UPDATE(req, ); } else RemoveSharedAccount(ac, user->_id); @@ -901,13 +970,17 @@ wxString Database::AddCategory(User* user, Category& category) forecolor += wxString::Format(wxT("%02X"), category.forecolor.Green()); forecolor += wxString::Format(wxT("%02X"), category.forecolor.Blue()); - req = wxT("INSERT INTO category ('user', 'parent', 'name', 'backcolor', 'forecolor', font) VALUES ('") ; + req = wxT("INSERT INTO category ('user', 'parent', 'name', 'backcolor', 'forecolor', 'font', 'fix_cost') VALUES ('") ; req += user->_id + wxT("'"); req += wxT(", '") + category.parent + wxT("'"); req += wxT(", '") + category.name + wxT("'"); req += wxT(", '") + backcolor + wxT("'"); req += wxT(", '") + forecolor + wxT("'"); req += wxT(", '") + category.font + wxT("'"); + if (category.fix_cost) + req += wxT(", '1'"); + else + req += wxT(", '0'"); req += wxT(")"); EXECUTE_SQL_UPDATE(req, wxT("0")); @@ -936,12 +1009,16 @@ void Database::UpdateCategory(Category& category) req += wxT(", backcolor='") + backcolor + wxT("'"); req += wxT(", forecolor='") + forecolor + wxT("'"); req += wxT(", font='") + category.font + wxT("'"); + if (category.fix_cost) + req += wxT(", fix_cost='1'"); + else + req += wxT(", fix_cost='0'"); req += wxT(" WHERE id='") + category.id + wxT("'"); EXECUTE_SQL_UPDATE(req, ); } -void Database::DeleteCategory(User* user, Category& category) +void Database::DeleteCategory(User* user, Category& category, const wxString& replacement) { wxString req; @@ -954,6 +1031,20 @@ void Database::DeleteCategory(User* user, Category& category) req += wxT(" WHERE parent='") + category.id + wxT("'"); EXECUTE_SQL_UPDATE(req, ); + + req = wxT("UPDATE operation SET category='") + replacement + wxT("'"); + req += wxT(" WHERE category='") + category.id + wxT("'"); + req += wxT(" AND user='") + user->_id + wxT("'"); + + EXECUTE_SQL_UPDATE(req, ); + + if (replacement == user->_categories[0].id) + { + req = wxT("UPDATE operation SET fix_cost='1'"); + req += wxT(" WHERE category='") + replacement + wxT("'"); + + EXECUTE_SQL_UPDATE(req, ); + } } bool Database::LoadCategory(const wxString& id, const wxString& name, Category& category) @@ -977,6 +1068,7 @@ bool Database::LoadCategory(const wxString& id, const wxString& name, Category& category.backcolor = wxColour(set.GetAsString(wxT("backcolor"))); category.forecolor = wxColour(set.GetAsString(wxT("forecolor"))); category.font = set.GetAsString(wxT("font")); + category.fix_cost = set.GetBool(wxT("fix_cost")); ret = true; } @@ -1185,7 +1277,7 @@ void Database::KillMe(User* user) if (!user->_accounts.empty()) { for (it = user->_accounts.begin(); it != user->_accounts.end(); it++) - DeleteAccount(user, *it); + DeleteAccount(user, *it, wxT("0")); it = user->_accounts.begin(); if (it->is_owner) @@ -1280,7 +1372,7 @@ std::vector* Database::Search(User* user, wxString* description, wxDa ESCAPE_CHARS(desc); if (wildcards) - req += wxT("description LIKE '%") + desc + wxT("%'"); + req += wxT("UPPER(description) LIKE UPPER('%") + desc + wxT("%')"); else req += wxT("description=\"") + desc + wxT("\""); firstCond = true; @@ -1369,8 +1461,8 @@ std::vector* Database::Search(User* user, wxString* description, wxDa for (; it != accounts.end(); it++) req += wxT("', '") + *it ; - req += wxT("')"); - req += wxT(" OR user ='") + user->_id + wxT("')"); + req += wxT("'))"); + // req += wxT(" OR user ='") + user->_id + wxT("')"); } else { @@ -1410,8 +1502,9 @@ std::vector* Database::Search(User* user, wxString* description, wxDa op.fix_cost = set.GetBool(wxT("fix_cost")); op.checked = set.GetBool(wxT("checked")); op.transfert = set.GetAsString(wxT("transfert")); - op.formula = set.GetAsString(wxT("formula")); + op.formula = set.GetAsString(wxT("formula")); op.meta = set.GetBool(wxT("meta")); + op._virtual = set.GetBool(wxT("virtual")); res->push_back(op); } @@ -1588,6 +1681,32 @@ std::map* Database::GetNotChecked(User* user, int month, int y return res; } +std::map* Database::GetVirtualAmount(User* user, int month, int year) +{ + std::vector::iterator accountIt; + std::map* res = new std::map; + wxSQLite3ResultSet set; + wxString req; + + for (accountIt = user->_accounts.begin() ;accountIt != user->_accounts.end(); accountIt++) + { + req = wxT("SELECT SUM(amount) AS amount FROM operation WHERE account='") + accountIt->id + wxT("'"); + req += wxT(" AND virtual='1'"); + req += wxT(" AND meta='0'"); + req += wxT(" AND (year < '") + wxString::Format(wxT("%d"), year) + wxT("'") ; + req += wxT(" OR (year == '") + wxString::Format(wxT("%d"), year) + wxT("'") ; + req += wxT(" AND month < '") + wxString::Format(wxT("%d"), month) + wxT("'") ; + req += wxT("))"); + + EXECUTE_SQL_QUERY_WITH_CODE(req, set, NULL, delete res, delete res); + + if (set.NextRow()) + (*res)[accountIt->id] = set.GetDouble(wxT("amount")); + } + + return res; +} + std::map Database::getSharedAccountOwners(const wxString& account) { std::map res; @@ -1628,3 +1747,45 @@ wxString Database::getSharedAccountOwner(const wxString& account) return set2.GetAsString(wxT("name")); } + +void Database::UpdateImportPattern(User* user) +{ + std::map::iterator it; + wxString req, key; + + for (it = user->_importPatterns.begin(); + it != user->_importPatterns.end(); + it++) + { + if (it->second.pattern == NULL_IMPORT_PATTERN) continue; + + key = ImportEngine::RemoveUnused(it->first); + + req = wxT("UPDATE import_pattern SET ") ; + req += wxT("pattern='") + it->second.pattern + wxT("'"); + req += wxT(", account='") + it->second.account + wxT("'"); + req += wxT(", category='") + it->second.category + wxT("'"); + req += wxT(" WHERE description='") + key + wxT("'"); + + try + { + if (!_db.ExecuteUpdate(req)) + { + req = wxT("INSERT INTO import_pattern ('user', 'description', 'pattern', 'account', 'category') VALUES ('") ; + req += user->_id + wxT("'"); + req += wxT(" ,'") + key + wxT("'"); + req += wxT(" ,'") + it->second.pattern + wxT("'"); + req += wxT(" ,'") + it->second.account + wxT("'"); + req += wxT(" ,'") + it->second.category + wxT("'"); + req += wxT(")"); + EXECUTE_SQL_UPDATE(req, ); + } + } + catch (wxSQLite3Exception e) + { + std::cerr << req.mb_str() << "\n" ; + std::cerr << e.GetMessage().mb_str() << "\n" ; + return ; + } + } +} diff --git a/src/model/Database.h b/src/model/Database.h index 0a2d090..e741ef1 100644 --- a/src/model/Database.h +++ b/src/model/Database.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -25,18 +25,70 @@ #include #include #include +#include +#include +#include #include +#include #include "model.h" -#include "../controller/KissCount.h" -#define BDD_FILE "kc.bdd" -#define INIT_SCRIPT "init.sql" +#define DATABASE_VERSION 2 + +#define BDD_FILE "/.kisscount/kc.bdd" +#define INIT_SCRIPT RESSOURCES_ROOT "init.sql" #define FIX_OP (1 << 0) #define NON_FIX_OP (1 << 1) #define CHECKED_OP (1 << 2) #define NOT_CHECKED_OP (1 << 3) +#define ALL_OP (~0) + +// if (!_db.CheckSyntax(req)) +// { +// wxString s = req; +// std::cout << s.mb_str() << " is invalid !\n" ; +// code_if_syntax_fail; +// return return_value; +// } + +#define EXECUTE_SQL_UPDATE_WITH_CODE(req, return_value, code_if_fail, code_if_syntax_fail) \ + do{ \ + try \ + { \ + _db.ExecuteUpdate(req); \ + } \ + catch (wxSQLite3Exception e) \ + { \ + wxMessageBox(_("Update failed !\n") + req, _("Error"), wxICON_ERROR | wxOK); \ + std::cerr << __FUNCTION__ << "\n" ; \ + std::cerr << req.mb_str() << "\n" ; \ + std::cerr << e.GetMessage().mb_str() << "\n" ; \ + code_if_fail; \ + return return_value; \ + } \ + } while(0); + +#define EXECUTE_SQL_QUERY_WITH_CODE(req, res, return_value, code_if_fail, code_if_syntax_fail) \ + do{ \ + try \ + { \ + res = _db.ExecuteQuery(req); \ + } \ + catch (wxSQLite3Exception e) \ + { \ + wxMessageBox(_("Query failed !\n") + req, _("Error"), wxICON_ERROR | wxOK); \ + std::cerr << __FUNCTION__ << "\n" ; \ + std::cerr << req.mb_str() << "\n" ; \ + std::cerr << e.GetMessage().mb_str() << "\n" ; \ + code_if_fail; \ + return return_value; \ + } \ + } while(0); + +#define EXECUTE_SQL_QUERY(req, res, return_value) EXECUTE_SQL_QUERY_WITH_CODE(req, res, return_value, {}, {}) + +#define EXECUTE_SQL_UPDATE(req, return_value) EXECUTE_SQL_UPDATE_WITH_CODE(req, return_value, {}, {}) class KissCount; class User; @@ -52,26 +104,27 @@ public: User* LoadUser(const wxString& name); void LoadYear(User* user, int year); - void UpdateOperation(Operation& op); - wxString AddOperation(User* user, Operation& op); - void DeleteOperation(Operation& op); + void UpdateOperation(User* user, Operation& op, bool checkTransfert=true); + wxString AddOperation(User* user, Operation& op, bool checkTransfert=true); + void DeleteOperation(User* user, Operation& op); void DeleteOperations(User* user, int month, int year); bool LoadOperation(User* user, const wxString& id); double MetaAmount(const wxString& id); double MetaPositiveAmount(const wxString& id); double GetAccountAmount(const wxString& id, int month, int year); - void SetAccountAmount(int month, int year, const wxString& accountId, double amount); + void SetAccountAmount(const wxString& accountId, int month, int year, double amount); + double CalcAccountAmount(const wxString& id, int month, int year, bool* had_values); wxString AddAccount(User* user, Account& ac); void UpdateAccount(Account& ac); - void DeleteAccount(User* user, Account& ac); + void DeleteAccount(User* user, Account& ac, const wxString& replacement); void AddSharedAccount(Account& ac, const wxString& granted); void RemoveSharedAccount(Account& ac, const wxString& granted); wxString AddCategory(User* user, Category& category); void UpdateCategory(Category& category); - void DeleteCategory(User* user, Category& category); + void DeleteCategory(User* user, Category& category, const wxString& replacement); bool LoadCategory(const wxString& id, const wxString& name, Category& category); std::map > GetAllOperations(User* user); @@ -102,6 +155,13 @@ public: wxString getSharedAccountOwner(const wxString& account); std::map* GetNotChecked(User* user, int month, int year); + std::map* GetVirtualAmount(User* user, int month, int year); + + void UpdateImportPattern(User* user); + +/* Database Update */ + + void CheckDatabaseVersion(); private: KissCount* _kiss; @@ -109,7 +169,7 @@ private: void CreateDatabase(); wxString HashPassword(const wxString& password); - void LinkOrUnlinkOperation(Operation& op); + void LinkOrUnlinkOperation(User* user, Operation& op); }; #endif diff --git a/src/model/Database_Update.cpp b/src/model/Database_Update.cpp new file mode 100644 index 0000000..dbfc113 --- /dev/null +++ b/src/model/Database_Update.cpp @@ -0,0 +1,143 @@ +/* + Copyright 2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#include "Database.h" + +#define ON_ERROR(m) \ + wxMessageBox(wxT(m), _("Error"), wxICON_ERROR | wxOK); \ + throw std::string(m); + +typedef void (*update_func)(wxSQLite3Database& _db) ; + +#define UPDATE_TABLE(from, to, step) EXECUTE_SQL_UPDATE_WITH_CODE(req, , throw std::string("Error while upgrading from version " from " to version " to ", step " step);, }); + +static void Version_1_to_2(wxSQLite3Database& _db) +{ + wxString req ; + wxSQLite3ResultSet set; + + /* Category */ + req = wxT("ALTER TABLE category ADD fix_cost CHAR(1)"); + + UPDATE_TABLE("1", "2", "1"); + + req = wxT("UPDATE category SET fix_cost='0'"); + + UPDATE_TABLE("1", "2", "2"); + + req = wxT("UPDATE category SET fix_cost='1' WHERE id IN ("); + req += wxT("SELECT MIN(category.id) FROM category, user WHERE category.user = user.id"); + req += wxT(")"); + + UPDATE_TABLE("1", "2", "3"); + + /* Account */ + req = wxT("ALTER TABLE account ADD virtual CHAR(1)"); + + UPDATE_TABLE("1", "2", "4"); + + req = wxT("UPDATE account SET virtual='0'"); + + UPDATE_TABLE("1", "2", "5"); + + /* Operation */ + req = wxT("ALTER TABLE operation ADD virtual CHAR(1)"); + + UPDATE_TABLE("1", "2", "6"); + + req = wxT("UPDATE operation SET virtual='0'"); + + UPDATE_TABLE("1", "2", "7"); + + /* Import Pattern */ + req = wxT("CREATE TABLE import_pattern(id INTEGER PRIMARY KEY, user REFERENCES user(id), description VARCHAR(255), pattern VARCHAR(255), account REFERENCES account(id), category REFERENCES category(id))"); + + UPDATE_TABLE("1", "2", "8"); +} + +static update_func updates[] = { + Version_1_to_2 +}; + +void Database::CheckDatabaseVersion() +{ + wxString req ; + wxSQLite3ResultSet set; + long int version; + int i; + + req = wxT("SELECT db_version FROM kisscount"); + + EXECUTE_SQL_QUERY_WITH_CODE(req, set, , ON_ERROR("Unable to get database version"), {}); + + if (!set.GetAsString(wxT("db_version")).ToLong(&version)) + { + ON_ERROR("Invalid database version"); + } + + if (version == DATABASE_VERSION) return; + + if (version > DATABASE_VERSION) + { + ON_ERROR("Your current version of KissCount is too old and can't load this database. Please upgrade KissCount"); + } + + version--; + + // Maybe one update is impossible + for(i=version; i. +*/ + +#include "Operation.h" + +bool sortOperations(Operation op1, Operation op2) +{ + if (!op1.fix_cost && op2.fix_cost) return false; + if (op1.fix_cost && !op2.fix_cost) return true; + + if (op1.year < op2.year) + return true; + else if (op1.year == op2.year) + { + if (op1.month < op2.month) + return true; + else if (op1.month == op2.month) + { + if (op1.day < op2.day) + return true; + else if (op1.day == op2.day) + return (op1.description.Cmp(op2.description) < 0); + } + } + + return false; +} + +bool reverseSortOperations(Operation op1, Operation op2) +{ + return !sortOperations(op1, op2); +} diff --git a/src/model/Operation.h b/src/model/Operation.h index 7fe7202..842ca47 100644 --- a/src/model/Operation.h +++ b/src/model/Operation.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -20,6 +20,9 @@ #ifndef OPERATION_H #define OPERATION_H +#include +#include + class Operation { public: wxString id; @@ -36,7 +39,11 @@ public: wxString transfert; wxString formula; bool meta; + bool _virtual; std::vector childs; } ; +bool sortOperations(Operation op1, Operation op2); +bool reverseSortOperations(Operation op1, Operation op2); + #endif diff --git a/src/model/User.cpp b/src/model/User.cpp index 8a905f5..1b6110f 100644 --- a/src/model/User.cpp +++ b/src/model/User.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -23,6 +23,11 @@ User::User(Database* db) : _db(db) {} User::~User() +{ + InvalidateOperations(); +} + +void User::InvalidateOperations() { std::map >* >::iterator it; @@ -33,9 +38,11 @@ User::~User() delete it->second; } } + + _operations.clear(); } -Category User::GetCategory(wxString& catId) +Category User::GetCategory(const wxString& catId) { Category cat; std::vector::iterator it; @@ -57,7 +64,7 @@ Category User::GetCategory(wxString& catId) return cat; } -wxString User::GetCategoryName(wxString& catId) +wxString User::GetCategoryName(const wxString& catId) { Category cat; std::vector::iterator it; @@ -72,13 +79,13 @@ wxString User::GetCategoryName(wxString& catId) return _("Unknown") ; } -wxString User::GetCategoryId(wxString& catName) +wxString User::GetCategoryId(const wxString& catName) { std::vector::iterator it; Category cat; for (it=_categories.begin(); it !=_categories.end(); it++) - if (it->name == catName) + if (wxGetTranslation(it->name) == catName) return it->id; if ( _db->LoadCategory(wxT(""), catName, cat)) @@ -87,7 +94,7 @@ wxString User::GetCategoryId(wxString& catName) return wxT("0") ; } -const wxFont User::GetCategoryFont(wxString& catId) +const wxFont User::GetCategoryFont(const wxString& catId) { wxFont f; Category cat; @@ -120,7 +127,7 @@ wxString User::GetAccountName(const wxString& accountId) return _("Unknown") ; } -wxString User::GetAccountId(wxString& accountName) +wxString User::GetAccountId(const wxString& accountName) { std::vector::iterator it; for (it=_accounts.begin(); it !=_accounts.end(); it++) @@ -161,6 +168,13 @@ wxLanguage User::GetLanguage() void User::LinkOrUnlinkOperation(Operation& op) { std::vector::iterator it; + Account account, account2; + + if (!_operations[op.year]) + _db->LoadYear(this, op.year); + + if (!_operations[op.year]) + return; // Not Linked if (!op.transfert.Length()) @@ -170,6 +184,7 @@ void User::LinkOrUnlinkOperation(Operation& op) if (it->id != op.id && it->transfert == op.id) { it->transfert = wxT(""); + it->_virtual = false; return; } } @@ -181,11 +196,16 @@ void User::LinkOrUnlinkOperation(Operation& op) { if (it->id != op.id && it->id == op.transfert) { + account = GetAccount(it->account); + account2 = GetAccount(op.account); it->transfert = op.id; + it->_virtual = account._virtual || account2._virtual; + op._virtual = account._virtual || account2._virtual; return; } } op.transfert = wxT(""); + op._virtual = false; } } diff --git a/src/model/User.h b/src/model/User.h index 509186d..dbc89cc 100644 --- a/src/model/User.h +++ b/src/model/User.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -29,14 +29,17 @@ #include "Account.h" #include "Operation.h" #include "Database.h" +#include "import/ImportEngine.h" class Database; +class ImportPattern; class User { public: User(Database* db); ~User(); + void InvalidateOperations(); wxString _id; wxString _name; @@ -46,15 +49,16 @@ public: std::vector _categories; std::vector _categoriesFonts; std::map _preferences; + std::map _importPatterns; - Category GetCategory(wxString& catId); - wxString GetCategoryName(wxString& catId); - wxString GetCategoryId(wxString& catName); - const wxFont GetCategoryFont(wxString& catId); + Category GetCategory(const wxString& catId); + wxString GetCategoryName(const wxString& catId); + wxString GetCategoryId(const wxString& catName); + const wxFont GetCategoryFont(const wxString& catId); Account GetAccount(const wxString& accountId); wxString GetAccountName(const wxString& accountId); - wxString GetAccountId(wxString& accountName); + wxString GetAccountId(const wxString& accountName); int GetCategoriesNumber(); int GetAccountsNumber(); @@ -69,6 +73,7 @@ public: void UnGroup(const Operation& op); void ResolveGroups(int year); + void UpdateImportPattern(User* user); private: Database* _db; }; diff --git a/src/model/export/ExportEngine.cpp b/src/model/export/ExportEngine.cpp new file mode 100644 index 0000000..db65e10 --- /dev/null +++ b/src/model/export/ExportEngine.cpp @@ -0,0 +1,107 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#include "ExportEngine.h" + +ExportEngine::ExportEngine() +{ +} + +ExportEngine::~ExportEngine() +{ +} + +bool ExportEngine::HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss) +{ + _path = path; + _user = user; + _db = db; + _kiss = kiss; + + return path.EndsWith(_shortExt) || path.EndsWith(_shortExt.Upper()); +} + +bool ExportEngine::SaveFile(std::vector* operations) +{ + int i; + wxString account, category; + AccountAmount accountAmount; + int minMonth = -1, minYear = -1; + unsigned int maxMonth = -1, maxYear = -1; + unsigned int month, year; + std::map::iterator it; + + if (!operations) return NULL; + + _accounts.clear(); + _categories.clear(); + _accountAmounts.clear(); + + for(i=0; i<(int)operations->size(); i++) + { + account = (*operations)[i].account; + category = (*operations)[i].category; + + if (minYear == -1 || (int)(*operations)[i].year < minYear) + maxYear = minYear = (*operations)[i].year; + + if (minMonth == -1 || ((int)(*operations)[i].month < minMonth && (int)(*operations)[i].year == minYear)) + maxMonth = minMonth = (*operations)[i].month; + + if ((*operations)[i].year > maxYear) + { + maxYear = (*operations)[i].year; + maxMonth = (*operations)[i].month; + } + + if ((*operations)[i].month > maxMonth && (*operations)[i].year == maxYear) + maxMonth = (*operations)[i].month; + + if (account.Length() && !_accounts.count(account)) + _accounts[account]++; + + if (category.Length() && !_categories.count(category)) + _categories[category]++; + } + + for(it=_accounts.begin(); it!=_accounts.end(); it++) + { + month = minMonth; + for (year = minYear; year <= maxYear; year++) + { + for (; !(month > maxMonth && year == maxYear) && month < 12; month++) + { + accountAmount.account = it->first; + accountAmount.month = month; + accountAmount.year = year; + + _accountAmounts[accountAmount] = _kiss->GetAccountAmount(accountAmount.account, accountAmount.month, accountAmount.year); + } + month = 0; + } + } + + return true; +} + +wxString ExportEngine::GetFileExt() +{ + return wxGetTranslation(_longExt); +} + diff --git a/src/model/export/ExportEngine.h b/src/model/export/ExportEngine.h new file mode 100644 index 0000000..50976f9 --- /dev/null +++ b/src/model/export/ExportEngine.h @@ -0,0 +1,58 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#ifndef EXPORTENGINE_H +#define EXPORTENGINE_H + +#include +#include +#include + +class KissCount; + +class ExportEngine { +public: + ExportEngine(); + ~ExportEngine(); + + // Get supported file extension. Example : + // "OFX files (*.ofx)|*.ofx" + virtual wxString GetFileExt(); + + // Handle the file + virtual bool HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss)=0; + + // Save operations (ExportEngin pre fill _accounts, _categories and _accountAmounts) + virtual bool SaveFile(std::vector* operations)=0; + +protected: + wxString _path; + Database* _db; + User* _user; + KissCount* _kiss; + + wxString _shortExt; + wxString _longExt; + + std::map _accounts; + std::map _categories; + std::map _accountAmounts; +}; + +#endif diff --git a/src/model/export/XMLExportEngine.cpp b/src/model/export/XMLExportEngine.cpp new file mode 100644 index 0000000..be78faf --- /dev/null +++ b/src/model/export/XMLExportEngine.cpp @@ -0,0 +1,181 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#include "XMLExportEngine.h" + +static XMLExportEngine xmlExportEngine; + +XMLExportEngine::XMLExportEngine() +{ + KissCount::RegisterExportEngine(this); + + _shortExt = wxT("xml"); + _longExt = _("XML files (*.xml)|*.xml"); +} + +XMLExportEngine::~XMLExportEngine() +{ +} + +bool XMLExportEngine::HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss) +{ + return ExportEngine::HandleFile(path, user, db, kiss); +} + +bool XMLExportEngine::SaveAccounts() +{ + Account account; + std::map::iterator it; + + for(it=_accounts.begin(); it!=_accounts.end(); it++) + { + account = _user->GetAccount(it->first); + + ESCAPE_CHARS(account.name); + ESCAPE_CHARS(account.number); + + xmlTextWriterStartElement(_writer, (const xmlChar*) "account"); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "id", (const xmlChar*) account.id.utf8_str().data()); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "name", (const xmlChar*) account.name.utf8_str().data()); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "number", (const xmlChar*) account.number.utf8_str().data()); + // xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "shared", (const xmlChar*) (account.shared ? "1" : "0")); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "blocked", (const xmlChar*) (account.blocked ? "1" : "0")); + // xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "default", (const xmlChar*) (account._default ? "1" : "0")); + // xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "is_owner", (const xmlChar*) (account.is_owner ? "1" : "0")); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "virtual", (const xmlChar*) (account._virtual ? "1" : "0")); + xmlTextWriterEndElement(_writer); + } + + return true; +} + +bool XMLExportEngine::SaveAccountAmounts() +{ + std::map::iterator it; + + for(it=_accountAmounts.begin(); it!=_accountAmounts.end(); it++) + { + xmlTextWriterStartElement(_writer, (const xmlChar*) "account_amount"); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "account", (const xmlChar*) it->first.account.utf8_str().data()); + xmlTextWriterWriteFormatAttribute(_writer, (const xmlChar*) "month", "%d", it->first.month); + xmlTextWriterWriteFormatAttribute(_writer, (const xmlChar*) "year", "%d", it->first.year); + xmlTextWriterWriteFormatAttribute(_writer, (const xmlChar*) "amount", "%.2lf", it->second); + xmlTextWriterEndElement(_writer); + } + + return true; +} + +bool XMLExportEngine::SaveCategories() +{ + Category category; + std::map::iterator it; + int rgb; + + for(it=_categories.begin(); it!=_categories.end(); it++) + { + category = _user->GetCategory(it->first); + + ESCAPE_CHARS(category.name); + + xmlTextWriterStartElement(_writer, (const xmlChar*) "category"); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "id", (const xmlChar*) category.id.utf8_str().data()); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "name", (const xmlChar*) category.name.utf8_str().data()); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "font", (const xmlChar*) category.font.utf8_str().data()); + rgb = category.backcolor.Blue(); + rgb |= category.backcolor.Green() << 8; + rgb |= category.backcolor.Red() << 16; + xmlTextWriterWriteFormatAttribute(_writer, (const xmlChar*) "backcolor", "0x%08X", rgb); + rgb = category.forecolor.Blue(); + rgb |= category.forecolor.Green() << 8; + rgb |= category.forecolor.Red() << 16; + xmlTextWriterWriteFormatAttribute(_writer, (const xmlChar*) "forecolor", "0x%08X", rgb); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "fix_cost", (const xmlChar*) (category.fix_cost ? "1" : "0")); + xmlTextWriterEndElement(_writer); + } + + return true; +} + +bool XMLExportEngine::SaveOperations(std::vector* operations) +{ + std::vector::iterator it; + + for(it=operations->begin(); it!=operations->end(); it++) + { + ESCAPE_CHARS(it->description); + + xmlTextWriterStartElement(_writer, (const xmlChar*) "operation"); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "id", (const xmlChar*) it->id.utf8_str().data()); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "parent", (const xmlChar*) it->parent.utf8_str().data()); + xmlTextWriterWriteFormatAttribute(_writer, (const xmlChar*) "day", "%d", it->day); + xmlTextWriterWriteFormatAttribute(_writer, (const xmlChar*) "month", "%d", it->month); + xmlTextWriterWriteFormatAttribute(_writer, (const xmlChar*) "year", "%d", it->year); + xmlTextWriterWriteFormatAttribute(_writer, (const xmlChar*) "amount", "%.2lf", it->amount); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "description", (const xmlChar*) it->description.utf8_str().data()); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "category", (const xmlChar*) it->category.utf8_str().data()); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "fix_cost", (const xmlChar*) (it->fix_cost ? "1" : "0")); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "account", (const xmlChar*) it->account.utf8_str().data()); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "checked", (const xmlChar*) (it->checked ? "1" : "0")); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "transfert", (const xmlChar*) it->transfert.utf8_str().data()); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "formula", (const xmlChar*) it->formula.utf8_str().data()); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "meta", (const xmlChar*) (it->meta ? "1" : "0")); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "virtual", (const xmlChar*) (it->_virtual ? "1" : "0")); + xmlTextWriterEndElement(_writer); + } + + return true; +} + +bool XMLExportEngine::SaveFile(std::vector* operations) +{ + int rc; + + rc = ExportEngine::SaveFile(operations); + + if (!rc) return false; + + _writer = xmlNewTextWriterFilename(_path.mb_str(), 0); + + if (!_writer) + { + std::cout << "Error can't open the file" << std::endl; + return false; + } + + rc = xmlTextWriterStartDocument(_writer, NULL, "UTF-8", NULL); + + if (rc < 0) return false; + + xmlTextWriterStartElement(_writer, (const xmlChar*) "kisscount"); + xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "version", (const xmlChar*) "1"); + + SaveAccounts(); + SaveAccountAmounts(); + SaveCategories(); + SaveOperations(operations); + + xmlTextWriterEndElement(_writer); + + xmlTextWriterEndDocument(_writer); + + xmlFreeTextWriter(_writer); + + return true; +} diff --git a/src/model/export/XMLExportEngine.h b/src/model/export/XMLExportEngine.h new file mode 100644 index 0000000..f91c0f8 --- /dev/null +++ b/src/model/export/XMLExportEngine.h @@ -0,0 +1,47 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#ifndef XMLEXPORTENGINE_H +#define XMLEXPORTENGINE_H + +#include +#include + +#include +#include +#include "ExportEngine.h" + +class XMLExportEngine : public ExportEngine { +public: + XMLExportEngine(); + ~XMLExportEngine(); + + bool HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss); + bool SaveFile(std::vector* operations); + +private: + xmlTextWriterPtr _writer; + + bool SaveAccounts(); + bool SaveAccountAmounts(); + bool SaveCategories(); + bool SaveOperations(std::vector* operations); +}; + +#endif diff --git a/src/model/import/GrisbiImportEngine.cpp b/src/model/import/GrisbiImportEngine.cpp new file mode 100644 index 0000000..9fb267d --- /dev/null +++ b/src/model/import/GrisbiImportEngine.cpp @@ -0,0 +1,227 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#include "GrisbiImportEngine.h" + +static GrisbiImportEngine grisbiImportEngine; + +void GrisbiImportEngine::LoadAccount(GrisbiImportEngine* _this, const char** attrs) +{ + int i; + wxString account_number, name, id, key; + Account ac; + + for (i=0; attrs[i]; i+=2) + { + if (!strcmp(attrs[i], "Name")) + name = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "Number")) + id = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "Bank_account_number")) + account_number = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "Key")) + key = wxString(attrs[i+1], wxConvUTF8); + } + + account_number += key; + + UNESCAPE_CHARS(name); + UNESCAPE_CHARS(account_number); + + for (i=0; i<(int)_this->_user->_accounts.size(); i++) + { + if (_this->_user->_accounts[i].number == account_number) + { + _this->_accounts[id] = _this->_user->_accounts[i].id; + return; + } + } + + _this->_accounts[id] = wxT("unknown-") + account_number; + ac.number = account_number; + ac.name = name; + ac.shared = false; + ac.blocked = false; + ac._default = false; + ac.is_owner = true; + ac._virtual = false; + _this->_unresolvedAccounts.push_back(ac); +} + +void GrisbiImportEngine::LoadCategory(GrisbiImportEngine* _this, const char** attrs) +{ + wxString name, id; + int i; + Category cat; + + for (i=0; attrs[i]; i+=2) + { + if (!strcmp(attrs[i], "Na")) + name = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "Nb")) + id = wxString(attrs[i+1], wxConvUTF8); + } + + UNESCAPE_CHARS(name); + + for (i=0; i<(int)_this->_user->_categories.size(); i++) + { + if (_this->_user->_categories[i].name == name) + { + _this->_categories[id] = _this->_user->_categories[i].id; + return; + } + } + + _this->_categories[id] = wxT("unknown-") + name; + cat.id = id; + cat.name = name; + cat.parent = wxT("0"); + cat.backcolor = OWN_GREEN ; + cat.forecolor = *wxBLACK; + cat.fix_cost = false; + _this->_unresolvedCategories.push_back(cat); +} + +void GrisbiImportEngine::LoadOperation(GrisbiImportEngine* _this, const char** attrs) +{ + int i; + static int id=0; + Operation op; + wxString amount; + wxDateTime date; + + op.id = wxString::Format(wxT("%d"), ++id); + op.parent = wxT(""); + op.account = wxT("unknwon-0"); + op.category = wxT("unknwon-0"); + op.fix_cost = false; + op.checked = false; + op.transfert = wxT(""); + op.formula = wxT(""); + op.meta = false; + op._virtual = false; + + for (i=0; attrs[i]; i+=2) + { + if (!strcmp(attrs[i], "Ac")) + op.account = _this->_accounts[wxString(attrs[i+1], wxConvUTF8)]; + + else if (!strcmp(attrs[i], "Dt")) + { + date.ParseFormat(wxString(attrs[i+1], wxConvUTF8), wxT("%m/%d/%Y")); + op.day = date.GetDay(); + op.month = date.GetMonth(); + op.year = date.GetYear(); + } + + else if (!strcmp(attrs[i], "Am")) + { + amount = wxString(attrs[i+1], wxConvUTF8); + amount.ToDouble(&op.amount); + } + + else if (!strcmp(attrs[i], "Ca")) + op.category = _this->_categories[wxString(attrs[i+1], wxConvUTF8)]; + + else if (!strcmp(attrs[i], "No")) + op.description = wxString(attrs[i+1], wxConvUTF8); + } + + UNESCAPE_CHARS(op.description); + + _this->_operations.push_back(op); + _this->_descriptions[op.id] = op.description; + + _this->MatchPattern(op.description, op); +} + +void GrisbiImportEngine::GrisbiStartElement(void* user_data, const xmlChar* name_, const xmlChar** attrs_) +{ + GrisbiImportEngine* _this = (GrisbiImportEngine*) user_data; + static char first = 0; + int i; + const char** attrs = (const char**) attrs_; + const char* name = (const char*) name_; + + if (!first && strcmp(name, "Grisbi")) + { + throw "Invalid file !"; + } + else + first = 1; + + if (!strcmp(name, "General")) + { + for (i=0; attrs[i]; i+=2) + { + if (!strcmp(attrs[i], "File_version") && strcmp(attrs[i+1], "0.6.0") < 0) + throw "Unsupported version !"; + + else if (!strcmp(attrs[i], "Crypt_file") && !strcmp(attrs[i+1], "1")) + throw "Crypted file !"; + } + } + + else if (!strcmp(name, "Account")) + LoadAccount(_this, attrs); + + else if (!strcmp(name, "Category")) + LoadCategory(_this, attrs); + + else if (!strcmp(name, "Transaction")) + LoadOperation(_this, attrs); +} + +GrisbiImportEngine::GrisbiImportEngine() +{ + KissCount::RegisterImportEngine(this); + + _shortExt = wxT("gsb"); + _longExt = _("Grisbi files (*.gsb)|*.gsb"); + + _sax.startElement = GrisbiStartElement ; +} + +GrisbiImportEngine::~GrisbiImportEngine() +{ +} + +bool GrisbiImportEngine::HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss) +{ + int res = -1; + + if (!ImportEngine::HandleFile(path, user, db, kiss)) return false; + + try + { + res = xmlSAXUserParseFile(&_sax, this, path.mb_str()); + } + catch (const char* s) + { + std::cout << "GrisbiImportEngine :: " << s << std::endl; + res = -1; + } + + return res >= 0; +} diff --git a/src/model/import/GrisbiImportEngine.h b/src/model/import/GrisbiImportEngine.h new file mode 100644 index 0000000..ec238ea --- /dev/null +++ b/src/model/import/GrisbiImportEngine.h @@ -0,0 +1,43 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#ifndef GRISBIIMPORTENGINE_H +#define GRISBIIMPORTENGINE_H + +#include + +#include "ImportEngine.h" + +class GrisbiImportEngine : public ImportEngine { +public: + GrisbiImportEngine(); + ~GrisbiImportEngine(); + + virtual bool HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss); + +private: + xmlSAXHandler _sax; + + static void GrisbiStartElement(void* user_data, const xmlChar* name_, const xmlChar** attrs_); + static void LoadAccount(GrisbiImportEngine* _this, const char** attrs); + static void LoadCategory(GrisbiImportEngine* _this, const char** attrs); + static void LoadOperation(GrisbiImportEngine* _this, const char** attrs); +}; + +#endif diff --git a/src/model/import/ImportEngine.cpp b/src/model/import/ImportEngine.cpp new file mode 100644 index 0000000..11fc6ca --- /dev/null +++ b/src/model/import/ImportEngine.cpp @@ -0,0 +1,315 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#include "ImportEngine.h" + +ImportEngine::ImportEngine() +{ +} + +ImportEngine::~ImportEngine() +{ +} + +bool ImportEngine::HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss) +{ + _path = path; + _user = user; + _db = db; + _kiss = kiss; + + _accounts.clear(); + _unresolvedAccounts.clear(); + _operations.clear(); + _descriptions.clear(); + _accountAmounts.clear(); + + return path.EndsWith(_shortExt) || path.EndsWith(_shortExt.Upper()); +} + +wxString ImportEngine::GetFileExt() +{ + return wxGetTranslation(_longExt); +} + +std::vector ExplodeString(wxString& s) +{ + wxString tmp = s, cur = wxT(""); + std::vector res; + + while (tmp.Len()) + { + if (tmp.StartsWith(wxT(" ")) || + tmp.StartsWith(wxT("\t")) || + tmp.StartsWith(wxT("\n")) || + tmp.StartsWith(wxT("\r"))) + { + if (cur.Len()) + { + res.push_back(cur); + cur = wxT(""); + } + tmp = tmp.Mid(1); + continue; + } + cur += tmp.SubString(0, 0); + tmp = tmp.Mid(1); + } + + if (cur.Len()) + res.push_back(cur); + + return res; +} + +/* + Remove : + - head spaces + - tail spaces + - every word starting by a number + */ +wxString ImportEngine::RemoveUnused(const wxString& s) +{ + wxString tmp = s, tmp2; + wxString res; + + tmp = tmp.Trim(true); + tmp = tmp.Trim(false); + + while (tmp.Len()) + { + tmp2 = tmp.SubString(0, 0); + if (tmp2.IsNumber()) + { + do + { + tmp = tmp.Mid(1); + tmp2 = tmp.SubString(0, 0); + } while (tmp.Len() && tmp2 != wxT(" ") && + tmp2 != wxT("\t") && + tmp2 != wxT("\r") && + tmp2 != wxT("\n")); + } + res += tmp2; + tmp = tmp.Mid(1); + } + + return res; +} + +/* + Find a pattern between the two strings: + %mX : lower string of orig[X] + %MX : upper string of orig[X] + %fX : First case in upper case of orig[X] + %wX : word orig[X] + other : constants + */ +wxString ImportEngine::FindPattern(wxString& orig, wxString& dest) +{ + wxString pattern, cur_pat; + int i, a; + std::vector tok1; + std::vector tok2; + int size1, size2; + + if (orig == dest) return NULL_IMPORT_PATTERN; + + tok1 = ExplodeString(orig); + tok2 = ExplodeString(dest); + size1 = tok1.size(); + size2 = tok2.size(); + + for(i=0; i tok1; + std::vector tok2; + int size1, i; + long pos; + + op.account = pattern.account; + op.category = pattern.category; + + if (pattern.pattern == NULL_IMPORT_PATTERN) return; + + tok1 = ExplodeString(pattern.pattern); + tok2 = ExplodeString(op.description); + size1 = tok1.size(); + + op.description = wxT(""); + + for(i=0; i_importPatterns[key1] = pattern; + + for(i=pos+1; i<(int)_operations.size(); i++) + { + key2 = RemoveUnused(_descriptions[_operations[i].id]); + + if (key1 == key2) + { + ApplyPattern(_user->_importPatterns[key2], _operations[i]); + nbOpUpdated++; + } + } + + return nbOpUpdated; +} + +void ImportEngine::MatchPattern(wxString& originalKey, Operation& op) +{ + wxString key1; + ImportPattern pattern; + + if (!_user) return; + + key1 = RemoveUnused(originalKey); + + if (!_user->_importPatterns.count(key1)) + { + pattern.pattern = FindPattern(originalKey, op.description); + pattern.account = op.account; + pattern.category = op.category; + + _user->_importPatterns[key1] = pattern; + + // std::cout << "New pattern " << key1.mb_str() << "\t" << pattern.pattern.mb_str() << std::endl; + } + else + { + op.description = _descriptions[op.id]; + ApplyPattern(_user->_importPatterns[key1], op); + } +} + +void ImportEngine::ParseFile(std::vector& accounts, std::vector& categories) +{ + accounts = _unresolvedAccounts; + categories = _unresolvedCategories; +} + +std::vector* ImportEngine::GetOperations(std::map& accounts, std::map& categories) +{ + int i; + + for(i=0; i<(int)_operations.size(); i++) + { + if (_operations[i].account.StartsWith(wxT("unknown-"))) + _operations[i].account = accounts[_operations[i].account.Mid(8)]; + if (_operations[i].category.StartsWith(wxT("unknown-"))) + _operations[i].category = categories[_operations[i].category.Mid(8)]; + } + + if (_kiss->GetOperationOrder() == wxT("ASC")) + std::sort(_operations.begin(), _operations.end(), sortOperations); + else + std::sort(_operations.begin(), _operations.end(), reverseSortOperations); + + return &_operations; +} + +const std::map& ImportEngine::GetAccountAmounts() +{ + return _accountAmounts; +} diff --git a/src/model/import/ImportEngine.h b/src/model/import/ImportEngine.h new file mode 100644 index 0000000..9db452e --- /dev/null +++ b/src/model/import/ImportEngine.h @@ -0,0 +1,84 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#ifndef IMPORTENGINE_H +#define IMPORTENGINE_H + +#include +#include +#include + +class KissCount; + +class ImportPattern { +public: + wxString pattern; + wxString account; + wxString category; +} ; + +#define NULL_IMPORT_PATTERN wxT("(nil)") + +class ImportEngine { +public: + ImportEngine(); + ~ImportEngine(); + + // Get supported file extension. Example : + // "OFX files (*.ofx)|*.ofx" + virtual wxString GetFileExt(); + + // Handle the file + virtual bool HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss)=0; + + // Parse the file and return accounts that doesn't match + virtual void ParseFile(std::vector& accounts, std::vector& categories); + + // Final Step + virtual std::vector* GetOperations(std::map& accounts, std::map& categories); + + const std::map& GetAccountAmounts(); + + void MatchPattern(wxString& key, Operation& op); + int UpdatePattern(int pos); + + static wxString RemoveUnused(const wxString& s); + +protected: + Database* _db; + User* _user; + wxString _path; + KissCount* _kiss; + + wxString _shortExt; + wxString _longExt; + + std::map _accounts; + std::map _categories; + std::vector _unresolvedAccounts; + std::vector _unresolvedCategories; + std::vector _operations; + std::map _descriptions; + std::map _accountAmounts; + + void ApplyPattern(ImportPattern& pattern, Operation& op); + wxString FindPattern(wxString& s1, wxString& s2); +}; + +#endif diff --git a/src/model/import/OFXImportEngine.cpp b/src/model/import/OFXImportEngine.cpp new file mode 100644 index 0000000..25e6073 --- /dev/null +++ b/src/model/import/OFXImportEngine.cpp @@ -0,0 +1,149 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#include "OFXImportEngine.h" + +static OFXImportEngine ofxImportEngine; + +int OFXImportEngine::account_cb(const struct OfxAccountData data, void * account_data) +{ + int i; + OFXImportEngine* _this = (OFXImportEngine*) account_data; + wxString account_number = wxString(data.account_number, wxConvUTF8); + Account ac; + + _this->_curAccount = wxT(""); + + UNESCAPE_CHARS(account_number); + + for (i=0; i<(int)_this->_user->_accounts.size(); i++) + { + if (_this->_user->_accounts[i].number == account_number) + { + _this->_accounts[account_number] = _this->_user->_accounts[i].id; + _this->_curAccount = _this->_user->_accounts[i].id; + // std::cout << "Account " << data.account_number << " is " << i << std::endl; + //_this->_unresolvedAccounts.push_back(account_number); + break; + } + } + + if (!_this->_curAccount.Len()) + { + _this->_curAccount = _this->_accounts[account_number] = wxT("unknown-") + account_number; + ac.number = account_number; + ac.shared = false; + ac.blocked = false; + ac._default = false; + ac.is_owner = true; + ac._virtual = false; + _this->_unresolvedAccounts.push_back(ac); + } + + return 0; +} + +int OFXImportEngine::transaction_cb(const struct OfxTransactionData data, void * transaction_data) +{ + static int id=0; + OFXImportEngine* _this = (OFXImportEngine*) transaction_data; + Operation op; + struct tm t; + + if (!data.amount_valid || + (!data.date_posted_valid && !data.date_initiated_valid)) return 1; + + op.id = wxString::Format(wxT("%d"), ++id); + op.parent = wxT(""); + op.category = wxT("0"); + op.fix_cost = false; + op.account = _this->_curAccount; + op.checked = false; + op.transfert = wxT(""); + op.formula = wxT(""); + op.meta = false; + op._virtual = false; + + op.amount = data.amount; + + if (data.date_initiated_valid) + gmtime_r(&data.date_initiated, &t); + else + gmtime_r(&data.date_posted, &t); + + op.day = t.tm_mday; + op.month = t.tm_mon; + op.year = t.tm_year+1900; + + if (data.name_valid) + op.description = wxString(data.name, wxConvUTF8); + + if (data.memo_valid) + { + if (op.description.Len()) + op.description += wxT(" "); + op.description += wxString(data.memo, wxConvUTF8); + } + + UNESCAPE_CHARS(op.description); + + _this->_operations.push_back(op); + _this->_descriptions[op.id] = op.description; + + _this->MatchPattern(op.description, op); + + return 0; +} + +int OFXImportEngine::account_balance_cb(const struct OfxStatementData data, void * statement_data) +{ + // OFXImportEngine* _this = (OFXImportEngine*) statement_data; + + return 0; +} + +OFXImportEngine::OFXImportEngine() +{ + KissCount::RegisterImportEngine(this); + + _ctx = libofx_get_new_context(); + + if (!_ctx) + throw std::string("Unable to initialize libofx"); + + ofx_set_account_cb(_ctx, account_cb, this); + ofx_set_transaction_cb(_ctx, transaction_cb, this); + ofx_set_statement_cb(_ctx, account_balance_cb, this); + + _shortExt = wxT("ofx"); + _longExt = _("OFX files (*.ofx)|*.ofx"); +} + +OFXImportEngine::~OFXImportEngine() +{ + if (_ctx) + libofx_free_context(_ctx); +} + +bool OFXImportEngine::HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss) +{ + if (!ImportEngine::HandleFile(path, user, db, kiss)) return false; + + return !libofx_proc_file(_ctx, path.mb_str(), AUTODETECT); +} diff --git a/src/model/import/OFXImportEngine.h b/src/model/import/OFXImportEngine.h new file mode 100644 index 0000000..3c3324f --- /dev/null +++ b/src/model/import/OFXImportEngine.h @@ -0,0 +1,44 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#ifndef OFXIMPORTENGINE_H +#define OFXIMPORTENGINE_H + +#include "ImportEngine.h" +#include + +class OFXImportEngine : public ImportEngine { +public: + OFXImportEngine(); + ~OFXImportEngine(); + + virtual bool HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss); + /* virtual std::vector ParseFile(); */ + /* virtual std::vector* GetOperations(std::map& accounts); */ + +private: + LibofxContextPtr _ctx; + wxString _curAccount; + + static int account_cb(const struct OfxAccountData data, void * account_data); + static int transaction_cb(const struct OfxTransactionData data, void * transaction_data); + static int account_balance_cb(const struct OfxStatementData data, void * statement_data); +}; + +#endif diff --git a/src/model/import/XMLImportEngine.cpp b/src/model/import/XMLImportEngine.cpp new file mode 100644 index 0000000..692b587 --- /dev/null +++ b/src/model/import/XMLImportEngine.cpp @@ -0,0 +1,294 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#include "XMLImportEngine.h" + +static XMLImportEngine xmlImportEngine; + +void XMLImportEngine::LoadAccount(XMLImportEngine* _this, const char** attrs) +{ + int i; + wxString account_number, name, id; + Account ac; + + for (i=0; attrs[i]; i+=2) + { + if (!strcmp(attrs[i], "name")) + ac.name = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "id")) + ac.id = id = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "number")) + ac.number = account_number = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "blocked")) + ac.blocked = (wxString(attrs[i+1], wxConvUTF8) == wxT("1")); + + else if (!strcmp(attrs[i], "virtual")) + ac._virtual = (wxString(attrs[i+1], wxConvUTF8) == wxT("1")); + } + + UNESCAPE_CHARS(ac.name); + UNESCAPE_CHARS(ac.number); + + ac._default = false; + ac.shared = false; + ac.is_owner = true; + + if (account_number.Length()) + { + for (i=0; i<(int)_this->_user->_accounts.size(); i++) + { + if (_this->_user->_accounts[i].number == account_number) + { + _this->_accounts[id] = _this->_user->_accounts[i].id; + return; + } + } + } + + _this->_accounts[id] = wxT("unknown-") + account_number; + _this->_unresolvedAccounts.push_back(ac); +} + +void XMLImportEngine::LoadAccountAmount(XMLImportEngine* _this, const char** attrs) +{ + AccountAmount accountAmount; + int i; + long v; + double amount; + + for (i=0; attrs[i]; i+=2) + { + if (!strcmp(attrs[i], "account")) + accountAmount.account = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "month")) + { + wxString(attrs[i+1], wxConvUTF8).ToLong(&v); + accountAmount.month = v; + } + + else if (!strcmp(attrs[i], "year")) + { + wxString(attrs[i+1], wxConvUTF8).ToLong(&v); + accountAmount.year = v; + } + + else if (!strcmp(attrs[i], "amount")) + wxString(attrs[i+1], wxConvUTF8).ToDouble(&amount); + } + + _this->_accountAmounts[accountAmount] = amount; +} + +void XMLImportEngine::LoadCategory(XMLImportEngine* _this, const char** attrs) +{ + wxString name, id; + int i; + long rgb; + Category cat; + + for (i=0; attrs[i]; i+=2) + { + if (!strcmp(attrs[i], "name")) + cat.name = name = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "id")) + cat.id = id = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "font")) + cat.font = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "backcolor")) + { + wxString(attrs[i+1], wxConvUTF8).ToLong(&rgb, 16); + cat.backcolor = wxColour((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF); + } + + else if (!strcmp(attrs[i], "forecolor")) + { + wxString(attrs[i+1], wxConvUTF8).ToLong(&rgb, 16); + cat.forecolor = wxColour((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF); + } + + else if (!strcmp(attrs[i], "fix_cost")) + cat.fix_cost = (wxString(attrs[i+1], wxConvUTF8) == wxT("1")); + } + + UNESCAPE_CHARS(cat.name); + + for (i=0; i<(int)_this->_user->_categories.size(); i++) + { + if (_this->_user->_categories[i].name == name) + { + _this->_categories[id] = _this->_user->_categories[i].id; + return; + } + } + + _this->_categories[id] = wxT("unknown-") + name; + _this->_unresolvedCategories.push_back(cat); +} + +void XMLImportEngine::LoadOperation(XMLImportEngine* _this, const char** attrs) +{ + int i; + Operation op; + long v; + double amount; + wxString id; + + for (i=0; attrs[i]; i+=2) + { + if (!strcmp(attrs[i], "id")) + op.id = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "parent")) + op.parent = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "day")) + { + wxString(attrs[i+1], wxConvUTF8).ToLong(&v); + op.day = v; + } + + else if (!strcmp(attrs[i], "month")) + { + wxString(attrs[i+1], wxConvUTF8).ToLong(&v); + op.month = v; + } + + else if (!strcmp(attrs[i], "year")) + { + wxString(attrs[i+1], wxConvUTF8).ToLong(&v); + op.year = v; + } + + else if (!strcmp(attrs[i], "amount")) + { + wxString(attrs[i+1], wxConvUTF8).ToDouble(&amount); + op.amount = amount; + } + + else if (!strcmp(attrs[i], "description")) + op.description = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "category")) + op.category = _this->_categories[wxString(attrs[i+1], wxConvUTF8)]; + + else if (!strcmp(attrs[i], "fix_cost")) + op.fix_cost = (wxString(attrs[i+1], wxConvUTF8) == wxT("1")); + + else if (!strcmp(attrs[i], "account")) + op.account = _this->_accounts[wxString(attrs[i+1], wxConvUTF8)]; + + else if (!strcmp(attrs[i], "checked")) + op.checked = (wxString(attrs[i+1], wxConvUTF8) == wxT("1")); + + else if (!strcmp(attrs[i], "transfert")) + op.transfert = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "formula")) + op.formula = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "meta")) + op.meta = (wxString(attrs[i+1], wxConvUTF8) == wxT("1")); + + else if (!strcmp(attrs[i], "virtual")) + op._virtual = (wxString(attrs[i+1], wxConvUTF8) == wxT("1")); + } + + UNESCAPE_CHARS(op.description); + + _this->_operations.push_back(op); + _this->_descriptions[op.id] = op.description; + + _this->MatchPattern(op.description, op); +} + +void XMLImportEngine::XmlStartElement(void* user_data, const xmlChar* name_, const xmlChar** attrs_) +{ + XMLImportEngine* _this = (XMLImportEngine*) user_data; + int i; + const char** attrs = (const char**) attrs_; + const char* name = (const char*) name_; + + // if (!first && strcmp(name, "Xml")) + // { + // throw "Invalid file !"; + // } + // else + // first = 1; + + if (!strcmp(name, "kisscount")) + { + for (i=0; attrs[i]; i+=2) + { + if (!strcmp(attrs[i], "version") && strcmp(attrs[i+1], "1")) + throw "Unsupported version !"; + } + } + + else if (!strcmp(name, "account")) + LoadAccount(_this, attrs); + + else if (!strcmp(name, "account_amount")) + LoadAccountAmount(_this, attrs); + + else if (!strcmp(name, "category")) + LoadCategory(_this, attrs); + + else if (!strcmp(name, "operation")) + LoadOperation(_this, attrs); +} + +XMLImportEngine::XMLImportEngine() +{ + KissCount::RegisterImportEngine(this); + + _shortExt = wxT("xml"); + _longExt = _("KissCount xml files (*.xml)|*.xml"); + + _sax.startElement = XmlStartElement ; +} + +XMLImportEngine::~XMLImportEngine() +{ +} + +bool XMLImportEngine::HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss) +{ + int res = -1 ; + + if (!ImportEngine::HandleFile(path, user, db, kiss)) return false; + + try + { + res = xmlSAXUserParseFile(&_sax, this, path.mb_str()) >= 0; + } + catch (const char* s) + { + std::cout << "XMLImportEngine :: " << s << std::endl; + res = -1; + } + + return res >= 0; +} diff --git a/src/model/import/XMLImportEngine.h b/src/model/import/XMLImportEngine.h new file mode 100644 index 0000000..2ab355b --- /dev/null +++ b/src/model/import/XMLImportEngine.h @@ -0,0 +1,44 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#ifndef XMLIMPORTENGINE_H +#define XMLIMPORTENGINE_H + +#include + +#include "ImportEngine.h" + +class XMLImportEngine : public ImportEngine { +public: + XMLImportEngine(); + ~XMLImportEngine(); + + virtual bool HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss); + +private: + xmlSAXHandler _sax; + + static void XmlStartElement(void* user_data, const xmlChar* name_, const xmlChar** attrs_); + static void LoadAccount(XMLImportEngine* _this, const char** attrs); + static void LoadAccountAmount(XMLImportEngine* _this, const char** attrs); + static void LoadCategory(XMLImportEngine* _this, const char** attrs); + static void LoadOperation(XMLImportEngine* _this, const char** attrs); +}; + +#endif diff --git a/src/model/model.h b/src/model/model.h index 9b1ab4e..4d11dc9 100644 --- a/src/model/model.h +++ b/src/model/model.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -25,9 +25,9 @@ class Database; class Account; class Operation; -#include "User.h" -#include "Database.h" -#include "Account.h" -#include "Operation.h" +#include +#include +#include +#include #endif diff --git a/src/sha1.cpp b/src/sha1.cpp index 012ff77..8930673 100644 --- a/src/sha1.cpp +++ b/src/sha1.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/sha1.h b/src/sha1.h index c99cd9f..7448b70 100644 --- a/src/sha1.h +++ b/src/sha1.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/AccountPanel.cpp b/src/view/AccountPanel.cpp index f90a83c..25c216a 100644 --- a/src/view/AccountPanel.cpp +++ b/src/view/AccountPanel.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -20,8 +20,10 @@ #include "AccountPanel.h" enum {ACCOUNT_NUMBER, ACCOUNT_NAME, ACCOUNT_INIT, ACCOUNT_CUR, ACCOUNT_FINAL, NUMBER_COLS_ACCOUNTS}; -enum {CUR_CREDIT, CUR_DEBIT, TOTAL_CREDIT, TOTAL_DEBIT, REMAINS, STATS_ROW, CATS_STATS}; -enum {CALENDAR_TREE_ID=1, OPS_GRID_ID, CALENDAR_ID, ACCOUNTS_GRID_ID, MENU_GENERATE_ID, MENU_DELETE_ID, CHECK_MODE_ID, GROUP_ID, UNGROUP_ID}; +enum {CUR_CREDIT, CUR_DEBIT, TOTAL_CREDIT, TOTAL_DEBIT, REMAINS, STATS_ROW, CATS_STATS, NON_FIX}; +enum {CALENDAR_TREE_ID=1, OPS_GRID_ID, CALENDAR_ID, ACCOUNTS_GRID_ID, MENU_GENERATE_ID, MENU_DELETE_ID, DISPLAY_MODE_ID, GROUP_ID, UNGROUP_ID, UPDATE_NEXT_MONTHS_ID}; + +enum {VIRTUAL_MODE=0, REAL_MODE, CHECK_MODE}; BEGIN_EVENT_TABLE(AccountPanel, wxPanel) EVT_GRID_CMD_CELL_CHANGE(OPS_GRID_ID, AccountPanel::OnOperationModified) @@ -33,12 +35,13 @@ EVT_MENU(MENU_GENERATE_ID, AccountPanel::OnMenuGenerate) EVT_MENU(MENU_DELETE_ID, AccountPanel::OnMenuDelete) EVT_SHOW(AccountPanel::OnShow) EVT_CALENDAR_SEL_CHANGED(CALENDAR_ID, AccountPanel::OnCalendarChange) -EVT_CHECKBOX(CHECK_MODE_ID, AccountPanel::OnCheckMode) +EVT_RADIOBOX(DISPLAY_MODE_ID, AccountPanel::OnModeChange) EVT_BUTTON(GROUP_ID, AccountPanel::OnGroup) EVT_BUTTON(UNGROUP_ID, AccountPanel::OnUnGroup) +EVT_BUTTON(UPDATE_NEXT_MONTHS_ID, AccountPanel::OnUpdateNextMonths) END_EVENT_TABLE() -AccountPanel::AccountPanel(KissCount* kiss, wxUI *parent) : wxScrolledWindow(&(*parent)), _curMonth(-1), _curYear(-1), _kiss(kiss), _wxUI(parent), _tree(this, CALENDAR_TREE_ID, wxDefaultPosition, wxDefaultSize, wxTR_HIDE_ROOT) +AccountPanel::AccountPanel(KissCount* kiss, wxUI *parent) : KissPanel(kiss, parent), _curMonth(-1), _curYear(-1), _tree(this, CALENDAR_TREE_ID, wxDefaultPosition, wxDefaultSize, wxTR_HIDE_ROOT) { wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *hbox2 = new wxBoxSizer(wxHORIZONTAL); @@ -52,6 +55,7 @@ AccountPanel::AccountPanel(KissCount* kiss, wxUI *parent) : wxScrolledWindow(&(* std::vector::iterator categoryIt; DEFAULT_FONT(font); wxRect rect = wxDisplay().GetGeometry(); + int nbCategories; SetSizer(hbox); @@ -77,24 +81,26 @@ AccountPanel::AccountPanel(KissCount* kiss, wxUI *parent) : wxScrolledWindow(&(* categoryIt != user->_categories.end(); categoryIt++, i++) { - _categories[i] = categoryIt->name ; + _categories[i] = wxGetTranslation(categoryIt->name) ; _categoriesIndexes[categoryIt->name] = i; } - - _dataset = new CategorySimpleDataset(_categories, user->GetCategoriesNumber()); + + nbCategories = (user->GetCategoriesNumber() <= MAX_CATEGORY) ? user->GetCategoriesNumber() : MAX_CATEGORY; _categoriesValues = new double[user->GetCategoriesNumber()]; for(i=0; iGetCategoriesNumber(); i++) - _categoriesValues[i] = 0.0; + _categoriesValues[i] = 0.0; - _dataset->AddSerie(_("Serie 1"), _categoriesValues, user->GetCategoriesNumber()); + _dataset = new CategorySimpleDataset(_categories, nbCategories); + _dataset->AddSerie(_("Serie 1"), _categoriesValues, nbCategories); + _dataset->SetRenderer(new CategoryRenderer(*colorScheme)); _pie->SetDataset(_dataset); _pie->SetColorScheme(colorScheme); _pie->SetLegend(new Legend(wxBOTTOM, wxCENTER)); - _grid = new GridAccount(_kiss, this, OPS_GRID_ID); + _grid = new GridAccount(_kiss, this, OPS_GRID_ID, true, true, true); _accountsGrid = new wxGrid(this, ACCOUNTS_GRID_ID); _accountsGrid->CreateGrid(0, NUMBER_COLS_ACCOUNTS); @@ -117,41 +123,41 @@ AccountPanel::AccountPanel(KissCount* kiss, wxUI *parent) : wxScrolledWindow(&(* chart->Fit(); chart->Layout(); chart->SetMinSize(// chart->GetSize() - wxSize(200,250)); + wxSize(200,300)); - _checkCheckMode = new wxCheckBox(this, CHECK_MODE_ID, _("Check mode")); + wxString modes[3] = {_("Virtual"), _("Real"), _("Check")}; + _radioMode = new wxRadioBox(this, DISPLAY_MODE_ID, _("Mode"), wxDefaultPosition, wxDefaultSize, + 3, modes); _tree.SetIndent(5); wxButton* buttonGroup = new wxButton(this, GROUP_ID, _("Group")); wxButton* buttonUnGroup = new wxButton(this, UNGROUP_ID, _("UnGroup")); + wxButton* buttonUpdateNextMonths = new wxButton(this, UPDATE_NEXT_MONTHS_ID, _("Update next months")); - vbox3->Add(&_tree, 0); - vbox3->Add(-1, 30); - vbox3->Add(buttonGroup, 0); - vbox3->Add(-1, 10); - vbox3->Add(buttonUnGroup, 0); + vbox3->Add(&_tree, 0, wxGROW|wxALL, 2); + vbox3->Add(buttonUpdateNextMonths, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 10); + vbox3->Add(buttonGroup, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 10); + vbox3->Add(buttonUnGroup, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 10); + vbox3->Add(_radioMode, 0, wxALIGN_CENTER_HORIZONTAL|wxUP, 50); - hbox->Add(vbox3, 0); - hbox2->Add(_accountsGrid, 0); - hbox2->Add(_calendar, 0); + hbox->Add(vbox3, 0, wxALL, 2); + // hbox->Add(vbox3, 0, wxGROW|wxALL, 2); + hbox2->Add(_accountsGrid, 0, wxGROW|wxALL, 2); + hbox2->Add(_calendar, 0, wxALL, 2); vbox2->Add(hbox2, 0); - vbox2->Add(-1, 10); - vbox2->Add(_grid, 0); - hbox->Add(vbox2, 0); - vbox->Add(_statsGrid, 0); - vbox->Add(-1, 10); - vbox->Add(chart, 0); - hbox->Add(-1, 10); - vbox->Add(_checkCheckMode, 0); - hbox->Add(-1, 10); - hbox->Add(vbox, 0); + vbox2->Add(_grid, 0, wxGROW|wxALL, 2); + hbox->Add(vbox2, 0, wxGROW|wxALL, 2); + vbox->Add(_statsGrid, 0, wxGROW); + vbox->Add(chart, 0, wxALIGN_CENTER_HORIZONTAL|wxUP, 10); + hbox->Add(vbox, 0, wxGROW|wxALL, 2); ChangeUser(); Fit(); - SetMinSize(wxSize(rect.width-rect.x, rect.height-rect.y-128)); + SetMinSize(wxSize(rect.width-rect.x-15, rect.height-rect.y-128-25)); + SetMaxSize(wxSize(rect.width-rect.x-15, rect.height-rect.y-128-25)); SetScrollbars(10, 10, 100/10, 100/10); } @@ -162,15 +168,33 @@ AccountPanel::~AccountPanel() delete[] _accounts; } +KissPanel* AccountPanel::CreatePanel() +{ + return new AccountPanel(_kiss, _wxUI); +} + +wxBitmapButton* AccountPanel::GetButton(int id) +{ + if (!_KissButton) + _KissButton = new wxBitmapButton(_wxUI, id, wxBitmap(wxT(ACCOUNT_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128)); + + return _KissButton; +} + +wxString AccountPanel::GetToolTip() +{ + return _("Operations"); +} + void AccountPanel::InitStatsGrid(User* user) { int i; - + int nb_categories = user->GetCategoriesNumber(); DEFAULT_FONT(font); if (!_statsGrid->GetNumberRows()) { - _statsGrid->CreateGrid(user->GetCategoriesNumber()+6, 2); + _statsGrid->CreateGrid(nb_categories+CATS_STATS+1, 2); // Headers + blank + categories + non fix _statsGrid->SetColLabelSize(0); _statsGrid->SetRowLabelSize(0); _statsGrid->EnableEditing(false); @@ -178,7 +202,7 @@ void AccountPanel::InitStatsGrid(User* user) else { _statsGrid->DeleteRows(0, _statsGrid->GetNumberRows()); - _statsGrid->InsertRows(0, user->GetCategoriesNumber()+6); + _statsGrid->InsertRows(0, nb_categories+CATS_STATS); } _statsGrid->SetDefaultCellFont(font); @@ -186,10 +210,18 @@ void AccountPanel::InitStatsGrid(User* user) _statsGrid->SetCellValue(TOTAL_CREDIT, 0, _("Total Credit")); _statsGrid->SetCellValue(TOTAL_DEBIT, 0, _("Total Debit")); - for(i=0; iGetCategoriesNumber(); i++) + for(i=0; iSetCellValue(CATS_STATS+i, 0, _categories[i]); - _statsGrid->SetCellAlignment(CATS_STATS+i, 1, wxALIGN_RIGHT, wxALIGN_CENTRE); + if (i) + { + _statsGrid->SetCellValue(CATS_STATS+i+1, 0, _categories[i]); + _statsGrid->SetCellAlignment(CATS_STATS+i+1, 1, wxALIGN_RIGHT, wxALIGN_CENTRE); + } + else + { + _statsGrid->SetCellValue(CATS_STATS+i, 0, _categories[i]); + _statsGrid->SetCellAlignment(CATS_STATS+i, 1, wxALIGN_RIGHT, wxALIGN_CENTRE); + } } _statsGrid->AutoSizeColumn(0, false); @@ -202,12 +234,14 @@ void AccountPanel::InitStatsGrid(User* user) _statsGrid->SetCellValue(CUR_CREDIT, 0, _("Cur Credit")); _statsGrid->SetCellValue(CUR_DEBIT, 0, _("Cur Debit")); _statsGrid->SetCellValue(REMAINS, 0, _("Remains")); + _statsGrid->SetCellValue(NON_FIX, 0, _("Non fix")); _statsGrid->SetCellAlignment(CUR_DEBIT, 1, wxALIGN_RIGHT, wxALIGN_CENTRE); _statsGrid->SetCellAlignment(CUR_CREDIT, 1, wxALIGN_RIGHT, wxALIGN_CENTRE); _statsGrid->SetCellAlignment(TOTAL_DEBIT, 1, wxALIGN_RIGHT, wxALIGN_CENTRE); _statsGrid->SetCellAlignment(TOTAL_CREDIT, 1, wxALIGN_RIGHT, wxALIGN_CENTRE); _statsGrid->SetCellAlignment(REMAINS, 1, wxALIGN_RIGHT, wxALIGN_CENTRE); + _statsGrid->SetCellAlignment(NON_FIX, 1, wxALIGN_RIGHT, wxALIGN_CENTRE); } void AccountPanel::ChangeUser() @@ -346,7 +380,7 @@ void AccountPanel::ShowMonth(int month, int year) // Operations are ordered _curOperations = &((*user->_operations[year])[month]); - _grid->LoadOperations(_curOperations, true, true, _curMonth, _curYear); + _grid->LoadOperations(_curOperations, _curMonth, _curYear); InitAccountsGrid(user, month, year); @@ -419,18 +453,22 @@ void AccountPanel::UpdateStats() std::map::iterator intIt; std::vector::iterator accountIt; unsigned int day; - bool checkMode = _checkCheckMode->IsChecked(); + int mode; std::map* notChecked = NULL; + std::map* virtuals = NULL; Account account; Operation op; bool blocked_account ; + mode = _radioMode->GetSelection(); + curCredit = curDebit = totalCredit = totalDebit = percents = 0.0; - if (checkMode) - { + if (mode == CHECK_MODE) notChecked = _kiss->GetNotChecked(_curMonth, _curYear); - } + + if (mode == REAL_MODE || mode == CHECK_MODE) + virtuals = _kiss->GetVirtualAmount(_curMonth, _curYear); day = _calendar->GetDate().GetDay()-1; @@ -441,6 +479,12 @@ void AccountPanel::UpdateStats() { curAccountAmount[doubleIt->first] = _accountsInitValues[doubleIt->first]; finalAccountAmount[doubleIt->first] = _accountsInitValues[doubleIt->first]; + + if (mode == REAL_MODE || mode == CHECK_MODE) + { + curAccountAmount[doubleIt->first] += -(*virtuals)[doubleIt->first]; + finalAccountAmount[doubleIt->first] += -(*virtuals)[doubleIt->first]; + } } for (it=_curOperations->begin(); it!=_curOperations->end(); it++) @@ -463,41 +507,119 @@ void AccountPanel::UpdateStats() } } - if (op.amount >= 0) + + switch(mode) { - if (!op.transfert.Length()) - totalCredit += op.amount; - - if (day >= op.day) + case VIRTUAL_MODE: + if (op.amount >= 0) { - if (!op.transfert.Length()) - curCredit += op.amount; - if (!checkMode || (checkMode && op.checked)) - curAccountAmount[op.account] += op.amount; + if (!op.transfert.Length()) + totalCredit += op.amount; + + if (day >= op.day) + { + if (!op.transfert.Length()) + curCredit += op.amount; + curAccountAmount[op.account] += op.amount; + } + finalAccountAmount[op.account] += op.amount; } - if (!checkMode || (checkMode && op.checked)) - finalAccountAmount[op.account] += op.amount; - } - else - { - if (!op.transfert.Length()) - _categoriesValues[_categoriesIndexes[user->GetCategoryName(op.category)]] += -op.amount ; - - if (!op.transfert.Length()) - totalDebit += -op.amount; - - if (blocked_account) - op.amount = -op.amount; - - if (day >= op.day) + else { - if (!op.transfert.Length()) - curDebit += -op.amount; - if (!checkMode || (checkMode && op.checked)) - curAccountAmount[op.account] += op.amount; + if (!op.transfert.Length() && user->GetCategoryName(op.category) != _("Unknown")) + _categoriesValues[_categoriesIndexes[user->GetCategoryName(op.category)]] += -op.amount ; + + if (!op.transfert.Length() || op._virtual) + totalDebit += -op.amount; + + if (blocked_account) + op.amount = -op.amount; + + if (day >= op.day) + { + if (!op.transfert.Length() || op._virtual) + curDebit += -op.amount; + curAccountAmount[op.account] += op.amount; + } + finalAccountAmount[op.account] += op.amount; } - if (!checkMode || (checkMode && op.checked)) - finalAccountAmount[op.account] += op.amount; + break; + case REAL_MODE: + if (op.amount >= 0) + { + if (!op.transfert.Length()) + totalCredit += op.amount; + + if (day >= op.day) + { + if (!op.transfert.Length()) + curCredit += op.amount; + if (!op._virtual) + curAccountAmount[op.account] += op.amount; + } + if (!op._virtual) + finalAccountAmount[op.account] += op.amount; + } + else + { + if (!op.transfert.Length() && user->GetCategoryName(op.category) != _("Unknown")) + _categoriesValues[_categoriesIndexes[user->GetCategoryName(op.category)]] += -op.amount ; + + if (!op.transfert.Length() && !op._virtual) + totalDebit += -op.amount; + + if (blocked_account) + op.amount = -op.amount; + + if (day >= op.day) + { + if (!op.transfert.Length() && !op._virtual) + curDebit += -op.amount; + if (!op._virtual) + curAccountAmount[op.account] += op.amount; + } + if (!op._virtual) + finalAccountAmount[op.account] += op.amount; + } + break; + case CHECK_MODE: + if (op.amount >= 0) + { + if (!op.transfert.Length() && !op._virtual) + totalCredit += op.amount; + + if (day >= op.day) + { + if (!op.transfert.Length() && !op._virtual) + curCredit += op.amount; + if (op.checked && !op._virtual) + curAccountAmount[op.account] += op.amount; + } + if (op.checked && !op._virtual) + finalAccountAmount[op.account] += op.amount; + } + else + { + if (!op.transfert.Length() && user->GetCategoryName(op.category) != _("Unknown")) + _categoriesValues[_categoriesIndexes[user->GetCategoryName(op.category)]] += -op.amount ; + + if (!op.transfert.Length() && !op._virtual) + totalDebit += -op.amount; + + if (blocked_account) + op.amount = -op.amount; + + if (day >= op.day) + { + if (!op.transfert.Length() && !op._virtual) + curDebit += -op.amount; + if (op.checked && !op._virtual) + curAccountAmount[op.account] += op.amount; + } + if (op.checked && !op._virtual) + finalAccountAmount[op.account] += op.amount; + } + break; } } @@ -516,14 +638,26 @@ void AccountPanel::UpdateStats() percents = ((double) (_categoriesValues[i]*100))/totalDebit; else percents = 0.0; - _statsGrid->SetCellValue(CATS_STATS+i, 1, wxString::Format(wxT("%.2lf (%02d %%)"), _categoriesValues[i], (int)percents)); + if (i) + _statsGrid->SetCellValue(CATS_STATS+i+1, 1, wxString::Format(wxT("%.2lf (%02d %%)"), _categoriesValues[i], (int)percents)); + else + _statsGrid->SetCellValue(CATS_STATS+i, 1, wxString::Format(wxT("%.2lf (%02d %%)"), _categoriesValues[i], (int)percents)); } + value = totalDebit - _categoriesValues[0]; + if (totalDebit != 0) + percents = ((double) (value*100))/totalDebit; + else + percents = 0.0; + _statsGrid->SetCellValue(NON_FIX, 1, wxString::Format(wxT("%.2lf (%02d %%)"), value, (int)percents)); + for (i=0, accountIt=user->_accounts.begin(); accountIt!=user->_accounts.end(); accountIt++, i++) { - if (!checkMode || !notChecked) + if (mode != CHECK_MODE) { value = _accountsInitValues[accountIt->id]; + if (mode == REAL_MODE) + value -= (*virtuals)[accountIt->id]; _accountsGrid->SetCellValue(i, ACCOUNT_INIT, wxString::Format(wxT("%.2lf"), value)); value = curAccountAmount[accountIt->id]; _accountsGrid->SetCellValue(i, ACCOUNT_CUR, wxString::Format(wxT("%.2lf"), value)); @@ -533,7 +667,7 @@ void AccountPanel::UpdateStats() } else { - value = _accountsInitValues[accountIt->id]; + value = _accountsInitValues[accountIt->id] - (*virtuals)[accountIt->id]; value2 = (*notChecked)[accountIt->id]; _accountsGrid->SetCellValue(i, ACCOUNT_INIT, wxString::Format(wxT("%.2lf (%.2lf)"), value, value-value2)); @@ -550,6 +684,7 @@ void AccountPanel::UpdateStats() _accountsGrid->AutoSizeColumn(ACCOUNT_FINAL, true); if (notChecked) delete notChecked; + if (virtuals) delete virtuals; _statsGrid->AutoSizeColumn(1, true); @@ -561,6 +696,8 @@ void AccountPanel::UpdateStats() void AccountPanel::OnOperationModified(wxGridEvent& event) { UpdateStats(); + + Fit(); } void AccountPanel::OnAccountModified(wxGridEvent& event) @@ -578,7 +715,7 @@ void AccountPanel::OnAccountModified(wxGridEvent& event) _accountsGrid->GetCellValue(row, event.GetCol()).ToDouble(&amount); - _kiss->SetAccountAmount(_curMonth, _curYear, id, amount); + _kiss->SetAccountAmount(id, _curMonth, _curYear, amount); _accountsInitValues[id] = amount; UpdateStats(); @@ -769,6 +906,7 @@ void AccountPanel::OnMenuDelete(wxCommandEvent& event) month = ops[year][0]; ShowMonth(month, year); } + _wxUI->NeedReload(); } void AccountPanel::GenerateMonth(int month, int year) @@ -841,6 +979,7 @@ void AccountPanel::GenerateMonth(int month, int year) _tree.SelectItem(node, true); ShowMonth(month, year); + _wxUI->NeedReload(); } void AccountPanel::OnShow(wxShowEvent& event) @@ -856,7 +995,7 @@ void AccountPanel::OnCalendarChange(wxCalendarEvent& event) UpdateStats(); } -void AccountPanel::OnCheckMode(wxCommandEvent& event) +void AccountPanel::OnModeChange(wxCommandEvent& event) { UpdateStats(); } @@ -870,3 +1009,110 @@ void AccountPanel::OnUnGroup(wxCommandEvent& event) { _grid->UnGroup(); } + +void AccountPanel::OnUpdateNextMonths(wxCommandEvent& event) +{ + double* deltas, *cur_amounts, amount; + int i, a; + User* user = _kiss->GetUser(); + bool had_values, accounts_updated = false; + int last_month = 0, last_year = 0, account_updated = 0; + std::map > operations; + + deltas = new double[user->_accounts.size()] ; + cur_amounts = new double[user->_accounts.size()] ; + + operations = _kiss->GetAllOperations(); + + if (_curMonth == 11) + { + last_month = 0; + last_year = _curYear+1; + } + else + { + last_month = _curMonth+1; + last_year = _curYear; + } + + for (i=0; i<(int)user->_accounts.size(); i++) + { + deltas[i] = _kiss->GetAccountAmount(user->_accounts[i].id, _curMonth, _curYear); + cur_amounts[i] = deltas[i] += _kiss->CalcAccountAmount(user->_accounts[i].id, _curMonth, _curYear, &had_values); + + for (a=0; a<(int)operations[last_year].size(); a++) + if (operations[last_year][a] == last_month) break; + + if (a == (int)operations[last_year].size()) + { + deltas[i] = 0; + continue; + } + + amount = _kiss->GetAccountAmount(user->_accounts[i].id, last_month, last_year); + + deltas[i] -= amount; + + account_updated++; + } + + if (!account_updated) + goto end; + + last_month = _curMonth; + last_year = _curYear; + + while (1) + { + account_updated = 0; + + if (last_month == 11) + { + last_month = 0; + last_year = last_year+1; + } + else + last_month++; + + for (i=0; i<(int)user->_accounts.size(); i++) + { + if (deltas[i] == 0.0) continue; + + amount = _kiss->GetAccountAmount(user->_accounts[i].id, last_month, last_year); + if ((cur_amounts[i] - amount) != deltas[i]) continue; + + cur_amounts[i] = amount + deltas[i]; + _kiss->SetAccountAmount(user->_accounts[i].id, last_month, last_year, cur_amounts[i]); + cur_amounts[i] += _kiss->CalcAccountAmount(user->_accounts[i].id, last_month, last_year, &had_values); + + account_updated++; + } + + if (!account_updated) break; + + accounts_updated = true; + } + + if (last_month == 0) + { + last_month = 11; + last_year--; + } + else + last_month--; + +end: + if (accounts_updated) + { + wxString message = _("Accounts updated until ") + months[last_month]; + + message += wxT(" ") + wxString::Format(wxT("%d"), last_year); + + wxMessageBox(message, wxT("KissCount"), wxICON_INFORMATION | wxOK); + } + else + wxMessageBox(_("Any account updated !"), wxT("KissCount"), wxICON_INFORMATION | wxOK); + + delete[] deltas; + delete[] cur_amounts; +} diff --git a/src/view/AccountPanel.h b/src/view/AccountPanel.h index 09d0928..a96dd4e 100644 --- a/src/view/AccountPanel.h +++ b/src/view/AccountPanel.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -25,26 +25,28 @@ #include #include #include -#include +#include #include "grid/CalendarEditor.h" #include "grid/wxGridCellBitmapRenderer.h" #include "view.h" #include -#include "wxUI.h" #include #include "grid/GridAccount.h" #include -class wxUI; -class KissCount; - -class AccountPanel: public wxScrolledWindow +class AccountPanel: public KissPanel { public: AccountPanel(KissCount* kiss, wxUI *parent); ~AccountPanel(); + + KissPanel* CreatePanel(); + wxBitmapButton* GetButton(int id); + wxString GetToolTip(); + void OnShow(wxShowEvent& event); + void ChangeUser(); void LoadYear(int year, bool showMonth=true); void ShowMonth(int month, int year); @@ -56,24 +58,22 @@ public: void OnTreeChange(wxTreeEvent& event); void OnMenuGenerate(wxCommandEvent& event); void OnMenuDelete(wxCommandEvent& event); - void OnShow(wxShowEvent& event); void OnCalendarChange(wxCalendarEvent& event); - void OnCheckMode(wxCommandEvent& event); + void OnModeChange(wxCommandEvent& event); void OnGroup(wxCommandEvent& event); void OnUnGroup(wxCommandEvent& event); + void OnUpdateNextMonths(wxCommandEvent& event); int _curMonth, _curYear; private: - KissCount* _kiss; - wxUI* _wxUI; wxTreeCtrl _tree; wxCalendarCtrl* _calendar; GridAccount* _grid; wxGrid *_statsGrid, *_accountsGrid; PiePlot* _pie; double *_categoriesValues; - wxCheckBox *_checkCheckMode; + wxRadioBox *_radioMode; std::map _categoriesIndexes; std::vector* _curOperations; wxString* _categories, *_accounts; diff --git a/src/view/ButtonPanel.cpp b/src/view/ButtonPanel.cpp deleted file mode 100644 index b8cfa22..0000000 --- a/src/view/ButtonPanel.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - Copyright 2010 Grégory Soutadé - - This file is part of KissCount. - - KissCount is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - KissCount is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with KissCount. If not, see . -*/ - -#include "ButtonPanel.h" - -enum {BUTTON_ACCOUNT_ID=1, BUTTON_STATS_ID, BUTTON_SEARCH_ID, BUTTON_PREFS_ID, BUTTON_CHANGE_USER_ID, BUTTON_ABOUT_ID, BUTTON_QUIT_ID}; - -BEGIN_EVENT_TABLE(ButtonPanel, wxPanel) -EVT_BUTTON(BUTTON_ACCOUNT_ID, ButtonPanel::OnButtonAccount) -EVT_BUTTON(BUTTON_STATS_ID, ButtonPanel::OnButtonStats) -EVT_BUTTON(BUTTON_SEARCH_ID, ButtonPanel::OnButtonSearch) -EVT_BUTTON(BUTTON_PREFS_ID, ButtonPanel::OnButtonPreferences) -EVT_BUTTON(BUTTON_CHANGE_USER_ID, ButtonPanel::OnButtonChangeUser) -EVT_BUTTON(BUTTON_ABOUT_ID, ButtonPanel::OnButtonAbout) -EVT_BUTTON(BUTTON_QUIT_ID, ButtonPanel::OnButtonQuit) -END_EVENT_TABLE() - -ButtonPanel::ButtonPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*parent)), _kiss(kiss), _wxUI(parent) -{ - wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL); - _account = new wxBitmapButton(this, BUTTON_ACCOUNT_ID, wxBitmap(wxT(ACCOUNT_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128)); - _stats = new wxBitmapButton(this, BUTTON_STATS_ID, wxBitmap(wxT(STATS_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128)); - _search = new wxBitmapButton(this, BUTTON_SEARCH_ID, wxBitmap(wxT(SEARCH_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128)); - _prefs = new wxBitmapButton(this, BUTTON_PREFS_ID, wxBitmap(wxT(PREFS_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128)); - _changeUser = new wxBitmapButton(this, BUTTON_CHANGE_USER_ID, wxBitmap(wxT(CHANGE_USER_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128)); - _about = new wxBitmapButton(this, BUTTON_ABOUT_ID, wxBitmap(wxT(ABOUT_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128)); - _quit = new wxBitmapButton(this, BUTTON_QUIT_ID, wxBitmap(wxT(QUIT_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128)); - - SetSizer(hbox); - - hbox->Add(_account); - hbox->Add(_stats); - hbox->Add(_search); - hbox->Add(_prefs); - hbox->Add(_changeUser); - hbox->Add(_about); - hbox->Add(_quit); - - Fit(); - SetMinSize(GetSize()); -} - -ButtonPanel::~ButtonPanel() -{ - delete _account; - delete _stats; - delete _prefs; - delete _changeUser; -} - -void ButtonPanel::SetToolTips() -{ - _account->SetToolTip(_("Operations")); - _stats->SetToolTip(_("Statistics")); - _search->SetToolTip(_("Search")); - _prefs->SetToolTip(_("Preferences")); - _changeUser->SetToolTip(_("Change user")); - _about->SetToolTip(_("About")); - _quit->SetToolTip(_("Quit")); -} - -void ButtonPanel::OnButtonAccount(wxCommandEvent& event) -{ - _wxUI->ShowAccount(); -} - -void ButtonPanel::OnButtonStats(wxCommandEvent& event) -{ - _wxUI->ShowStats(); -} - -void ButtonPanel::OnButtonSearch(wxCommandEvent& event) -{ - _wxUI->ShowSearch(); -} - -void ButtonPanel::OnButtonPreferences(wxCommandEvent& event) -{ - _wxUI->ShowPreferences(); -} - -void ButtonPanel::OnButtonChangeUser(wxCommandEvent& event) -{ - _wxUI->ChangeUser(); -} - -void ButtonPanel::OnButtonAbout(wxCommandEvent& event) -{ - wxMessageBox( _("Personal accounting software\n\nhttp://indefero.soutade.fr/p/kisscount/\n\nLicenced under GNU GPL v3\n\nCopyright (C) 2010 Grégory Soutadé"), - wxT("KissCount " APP_VERSION "\n\n"), - wxOK | wxICON_INFORMATION, _wxUI ); -} - -void ButtonPanel::OnButtonQuit(wxCommandEvent& event) -{ - wxMessageDialog dialog(_wxUI, _("Quit KissCount ?"), wxT("KissCount"), wxYES_NO); - if (dialog.ShowModal() == wxID_NO) - { - return; - } - _wxUI->Close(true); -} diff --git a/src/view/ButtonPanel.h b/src/view/ButtonPanel.h deleted file mode 100644 index 6e6e171..0000000 --- a/src/view/ButtonPanel.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright 2010 Grégory Soutadé - - This file is part of KissCount. - - KissCount is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - KissCount is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with KissCount. If not, see . -*/ - -#ifndef BUTTONPANEL_H -#define BUTTONPANEL_H - -#include -#include - -#include -#include "wxUI.h" - -#include "view.h" - -class KissCount; -class wxUI; - -class ButtonPanel: public wxPanel -{ -public: - - ButtonPanel(KissCount* kiss, wxUI *parent); - ~ButtonPanel(); - - void SetToolTips(); - - void OnButtonAccount(wxCommandEvent& event); - void OnButtonStats(wxCommandEvent& event); - void OnButtonSearch(wxCommandEvent& event); - void OnButtonPreferences(wxCommandEvent& event); - void OnButtonChangeUser(wxCommandEvent& event); - void OnButtonAbout(wxCommandEvent& event); - void OnButtonQuit(wxCommandEvent& event); - -private: - KissCount* _kiss; - wxUI* _wxUI; - wxBitmapButton* _account; - wxBitmapButton* _stats; - wxBitmapButton* _search; - wxBitmapButton* _prefs; - wxBitmapButton* _changeUser; - wxBitmapButton* _about; - wxBitmapButton* _quit; - - DECLARE_EVENT_TABLE(); -}; - -#endif diff --git a/src/view/ExportPanel.cpp b/src/view/ExportPanel.cpp new file mode 100644 index 0000000..efa0499 --- /dev/null +++ b/src/view/ExportPanel.cpp @@ -0,0 +1,153 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#include "ExportPanel.h" + +enum {EXPORT_ID=1, SEARCH_ID, GRID_ID}; + +BEGIN_EVENT_TABLE(ExportPanel, wxPanel) +EVT_BUTTON(EXPORT_ID, ExportPanel::OnButtonExport) +EVT_BUTTON(SEARCH_ID, ExportPanel::OnButtonSearch) +EVT_SHOW(ExportPanel::OnShow) +END_EVENT_TABLE() + +ExportPanel::ExportPanel(KissCount* kiss, wxUI *parent) : KissPanel(kiss, parent), _operations(NULL) +{ + DEFAULT_FONT(font); + std::vector::iterator accountIt; + std::vector::iterator categoryIt; + wxDateTime firstOfMonth; + wxRect rect = wxDisplay().GetGeometry(); + + wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *vbox2 = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL); + + SetSizer(vbox); + + _searchButton = new wxButton(this, SEARCH_ID, _("Search")); + + _banner = new SearchBanner(kiss, this, this, OnEnter); + + vbox->Add(_banner, 0, wxGROW|wxALL, 5); + vbox->Add(_searchButton, 0, wxALL, 5); + + _grid = new GridAccount(_kiss, this, GRID_ID, false, false, false); + + hbox->Add(_grid, 0, wxGROW|wxALL, 5); + + _exportButton = new wxButton(this, EXPORT_ID, _("Export")); + + vbox2->Add(_exportButton, wxALL, 15); + + hbox->Add(vbox2, 0, wxALL, 15); + + vbox->Add(hbox, 0, wxGROW|wxALL, 5); + + Fit(); + + SetMinSize(wxSize(rect.width-rect.x-15, rect.height-rect.y-128-25)); + SetMaxSize(wxSize(rect.width-rect.x-15, rect.height-rect.y-128-25)); + SetScrollbars(10, 10, 100/10, 100/10); +} + +ExportPanel::~ExportPanel() +{ +} + +KissPanel* ExportPanel::CreatePanel() +{ + return new ExportPanel(_kiss, _wxUI); +} + +wxBitmapButton* ExportPanel::GetButton(int id) +{ + if (!_KissButton) + _KissButton = new wxBitmapButton(_wxUI, id, wxBitmap(wxT(EXPORT_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128)); + + return _KissButton; +} + +wxString ExportPanel::GetToolTip() +{ + return _("Export"); +} + +void ExportPanel::OnEnter(void* caller, wxCommandEvent& event) +{ + ExportPanel* _this = (ExportPanel*) caller; + + _this->OnButtonExport(event); +} + +void ExportPanel::OnButtonSearch(wxCommandEvent& event) +{ + _operations = _banner->Search(); + + if (!_operations) return; + + if (_operations->size() > 1) + wxMessageBox(wxString::Format(wxT("%d"), _operations->size()) + _(" entries found"), wxT("KissCount"), wxICON_INFORMATION | wxOK); + else if (_operations->size() == 1) + wxMessageBox(_("1 entry found"), wxT("KissCount"), wxICON_INFORMATION | wxOK); + else + { + wxMessageBox(_("No entry found"), wxT("KissCount"), wxICON_INFORMATION | wxOK); + return; + } + + _grid->LoadOperations(_operations, 0, 0); + + _wxUI->Layout(); +} + +void ExportPanel::OnButtonExport(wxCommandEvent& event) +{ + if (!_operations || !_operations->size()) + { + wxMessageBox(_("No operation to save"), wxT("Error"), wxICON_ERROR | wxOK); + return; + } + + wxFileDialog saveFileDialog(this, _("Save as"), wxT(""), wxT(""), + _kiss->GetExportEngineExtensions(), wxFD_SAVE|wxFD_OVERWRITE_PROMPT); + + if (saveFileDialog.ShowModal() == wxID_CANCEL) + return; + + _exportEngine = _kiss->GetExportEngine(saveFileDialog.GetPath()); + + if (!_exportEngine) + { + wxMessageBox(_("Any engine can process this file !"), wxT("KissCount"), wxICON_INFORMATION | wxOK); + + return ; + } + + if (_exportEngine->SaveFile(_operations)) + wxMessageBox(_("Operations successfuly saved"), wxT("KissCount"), wxICON_INFORMATION | wxOK); + else + wxMessageBox(_("Failed to save operations"), wxT("Error"), wxICON_ERROR | wxOK); + +} + +void ExportPanel::OnShow(wxShowEvent& event) +{ + _wxUI->SetTitle(_("KissCount - Export")); +} diff --git a/src/view/ExportPanel.h b/src/view/ExportPanel.h new file mode 100644 index 0000000..67c2be8 --- /dev/null +++ b/src/view/ExportPanel.h @@ -0,0 +1,65 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#ifndef EXPORTPANEL_H +#define EXPORTPANEL_H + +#include +#include +#include +#include +#include "view.h" +#include "grid/CalendarEditor.h" +#include "grid/wxGridCellBitmapRenderer.h" +#include "grid/GridAccount.h" +#include "SearchBanner.h" +#include +#include + +class GridAccount; +class SearchBanner; +class ExportEngine; + +class ExportPanel: public KissPanel +{ +public: + ExportPanel(KissCount* kiss, wxUI *parent); + ~ExportPanel(); + + KissPanel* CreatePanel(); + wxBitmapButton* GetButton(int id); + wxString GetToolTip(); + void OnShow(wxShowEvent& event); + + void OnButtonSearch(wxCommandEvent& event); + void OnButtonExport(wxCommandEvent& event); + +private: + std::vector *_operations; + SearchBanner* _banner; + GridAccount *_grid; + wxButton* _searchButton, *_exportButton; + ExportEngine* _exportEngine; + + static void OnEnter(void* caller, wxCommandEvent& event); + + DECLARE_EVENT_TABLE(); +}; + +#endif diff --git a/src/view/GenerateDialog.cpp b/src/view/GenerateDialog.cpp index d1fb549..3d5d8ef 100644 --- a/src/view/GenerateDialog.cpp +++ b/src/view/GenerateDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -38,10 +38,11 @@ GenerateDialog::GenerateDialog(KissCount* kiss, wxUI *parent, int month, int yea wxCommandEvent event; std::vector::iterator monthIt; - curDate.SetToCurrent(); - + wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL); gridBagSizer = new wxGridBagSizer(4, 5); + curDate.SetToCurrent(); + label = new wxStaticText(this, wxID_ANY, _("From ")); gridBagSizer->Add(label, wxGBPosition(0, 0)); _yearFrom = new wxChoice(this, YEAR_FROM_ID); @@ -133,9 +134,10 @@ GenerateDialog::GenerateDialog(KissCount* kiss, wxUI *parent, int month, int yea } } - SetSizer(gridBagSizer); + hbox->Add(gridBagSizer, 0, wxGROW|wxALL, 10); + SetSizer(hbox); - Layout(); + Fit(); Center(); } diff --git a/src/view/GenerateDialog.h b/src/view/GenerateDialog.h index 8187ddc..f3c2d4a 100644 --- a/src/view/GenerateDialog.h +++ b/src/view/GenerateDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/ImportPanel.cpp b/src/view/ImportPanel.cpp new file mode 100644 index 0000000..92d246c --- /dev/null +++ b/src/view/ImportPanel.cpp @@ -0,0 +1,448 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#include "ImportPanel.h" + +enum {OPEN_FILE_ID=1, BUTTON_OPEN_ID, NAME_ID, BUTTON_LOAD_ID, BUTTON_INTEGRATE_ID, CHECK_SAVE_ID, OPS_GRID_ID}; + +BEGIN_EVENT_TABLE(ImportPanel, wxPanel) +EVT_GRID_CMD_CELL_CHANGE(OPS_GRID_ID, ImportPanel::OnOperationModified) +EVT_BUTTON(BUTTON_OPEN_ID, ImportPanel::OnFile) +EVT_BUTTON(BUTTON_INTEGRATE_ID, ImportPanel::OnIntegrate) +EVT_TEXT_ENTER(OPEN_FILE_ID, ImportPanel::OnFileEnter) +EVT_BUTTON(BUTTON_LOAD_ID, ImportPanel::OnLoadOperations) +EVT_SHOW(ImportPanel::OnShow) +END_EVENT_TABLE() + +ImportPanel::ImportPanel(KissCount* kiss, wxUI *parent) : KissPanel(kiss, parent) +{ + wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *vbox2 = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL); + _hbox = new wxBoxSizer(wxHORIZONTAL); + wxButton* buttonOpen; + wxRect rect = wxDisplay().GetGeometry(); + int w, h; + wxStaticBox* staticAccount = new wxStaticBox(this, wxID_ANY, _("Unresolved accounts")); + wxStaticBox* staticCategory = new wxStaticBox(this, wxID_ANY, _("Unresolved categories")); + + SetSizer(vbox); + + _fileTxt = new wxTextCtrl(this, OPEN_FILE_ID); + _fileTxt->SetWindowStyle(_fileTxt->GetWindowStyle() | wxTE_PROCESS_ENTER); + _fileTxt->GetSize(&w, &h); + wxSize size(rect.width/3, h); + _fileTxt->SetMinSize(size); + buttonOpen = new wxButton(this, BUTTON_OPEN_ID, wxT("...")); + + _buttonLoadOperations = new wxButton(this, BUTTON_LOAD_ID, _("Load operations")); + _buttonLoadOperations->Disable(); + + _buttonIntegrate = new wxButton(this, BUTTON_INTEGRATE_ID, _("Integrate operations")); + _buttonIntegrate->Disable(); + + _checkSaveImportPatterns = new wxCheckBox(this, CHECK_SAVE_ID, _("Save import patterns")); + + hbox->Add(_fileTxt, 0, wxGROW|wxALL, 5); + hbox->Add(buttonOpen, 0, wxALL, 5); + hbox->Add(_buttonLoadOperations, 0, wxALL, 5); + hbox->Add(_buttonIntegrate, 0, wxALL, 5); + hbox->Add(_checkSaveImportPatterns, 0, wxALL, 5); + + vbox->Add(hbox, 0); + + _accountsGrid = new wxGrid(this, wxID_ANY); + _accountsGrid->CreateGrid(0, 3); + _accountsGrid->SetRowLabelSize(0); + _accountsGrid->SetColLabelValue(0, _("File account")); + _accountsGrid->SetColLabelValue(1, _("Account name")); + _accountsGrid->SetColLabelValue(2, _("Internal account")); + _accountsGrid->Fit(); + + _categoriesGrid = new wxGrid(this, wxID_ANY); + _categoriesGrid->CreateGrid(0, 3); + _categoriesGrid->SetRowLabelSize(0); + _categoriesGrid->SetColLabelValue(0, _("File category")); + _categoriesGrid->SetColLabelValue(1, _("Category name")); + _categoriesGrid->SetColLabelValue(2, _("Internal category")); + _categoriesGrid->Fit(); + + wxStaticBoxSizer* staticBoxSizer = new wxStaticBoxSizer (staticAccount, wxVERTICAL); + staticBoxSizer->Add(_accountsGrid, 0, wxGROW|wxALL, 2); + vbox2->Add(staticBoxSizer, wxGROW|wxALL); + + staticBoxSizer = new wxStaticBoxSizer (staticCategory, wxVERTICAL); + staticBoxSizer->Add(_categoriesGrid, 0, wxGROW|wxALL, 2); + vbox2->Add(staticBoxSizer, wxGROW|wxALL); + + _operationsGrid = new GridAccount(kiss, this, OPS_GRID_ID, false, false, false); + + _hbox->Add(vbox2, 0, wxGROW|wxALL, 15); + _hbox->Add(_operationsGrid, 0, wxGROW|wxALL, 15); + + vbox->Add(_hbox, wxGROW); + + Fit(); + + SetMinSize(wxSize(rect.width-rect.x-15, rect.height-rect.y-128-25)); + SetMaxSize(wxSize(rect.width-rect.x-15, rect.height-rect.y-128-25)); + SetScrollbars(10, 10, 100/10, 100/10); +} + +KissPanel* ImportPanel::CreatePanel() +{ + return new ImportPanel(_kiss, _wxUI); +} + +wxBitmapButton* ImportPanel::GetButton(int id) +{ + if (!_KissButton) + _KissButton = new wxBitmapButton(_wxUI, id, wxBitmap(wxT(IMPORT_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128)); + + return _KissButton; +} + +wxString ImportPanel::GetToolTip() +{ + return _("Import"); +} + +void ImportPanel::OnShow(wxShowEvent& event) +{ + _wxUI->SetTitle(_("KissCount - Import")); +} + +void ImportPanel::OnFile(wxCommandEvent& WXUNUSED(event)) +{ + wxFileDialog openFileDialog(this, _("Choose a database to open"), wxT(""), wxT(""), + _kiss->GetImportEngineExtensions(), wxFD_OPEN|wxFD_FILE_MUST_EXIST); + + if (openFileDialog.ShowModal() == wxID_CANCEL) + return; + + _fileTxt->Clear(); + + *_fileTxt << openFileDialog.GetPath(); + + ProcessFile(); +} + +void ImportPanel::OnFileEnter(wxCommandEvent& WXUNUSED(event)) +{ + ProcessFile(); +} + +void ImportPanel::ProcessFile() +{ + User* user = _kiss->GetUser(); + int i; + wxGridCellChoiceEditor* accountEditor; + wxString* userAccounts; + std::map resolvedAccounts; + wxGridCellChoiceEditor* categoryEditor; + wxString* userCategories; + std::map resolvedCategories; + wxCommandEvent event; + + wxString path = _fileTxt->GetLineText(0); + + _buttonLoadOperations->Disable(); + _buttonIntegrate->Disable(); + _accountsGrid->ClearGrid(); + _categoriesGrid->ClearGrid(); + _operationsGrid->ClearGrid(); + + _importEngine = _kiss->GetImportEngine(path); + + if (!_importEngine) + { + wxMessageBox(_("Any engine can process this file !"), wxT("KissCount"), wxICON_INFORMATION | wxOK); + + return ; + } + + _importEngine->ParseFile(_unresolvedAccounts, _unresolvedCategories); + + if (_unresolvedAccounts.size()) + { + int nb_accounts = user->GetAccountsNumber(); + userAccounts = new wxString[nb_accounts+1]; + + userAccounts[0] = _("Create one"); + + for(i=0; i_accounts[i].name; + + accountEditor = new wxGridCellChoiceEditor(nb_accounts+1, userAccounts, false); + + _buttonLoadOperations->Enable(); + + _accountsGrid->AppendRows(_unresolvedAccounts.size()); + + for (i=0; i<(int)_unresolvedAccounts.size(); i++) + { + _accountsGrid->SetCellValue(i, 0, _unresolvedAccounts[i].number); + _accountsGrid->SetReadOnly(i, 0); + _accountsGrid->SetCellValue(i, 1, _unresolvedAccounts[i].name); + _accountsGrid->SetCellValue(i, 2, userAccounts[0]); + + _accountsGrid->SetCellEditor(i, 2, accountEditor); + } + + _accountsGrid->AutoSize(); + _accountsGrid->Layout(); + } + + if (_unresolvedCategories.size()) + { + int nb_categories = user->GetCategoriesNumber(); + userCategories = new wxString[nb_categories+1]; + + userCategories[0] = _("Create one"); + + for(i=0; i_categories[i].name; + + categoryEditor = new wxGridCellChoiceEditor(nb_categories+1, userCategories, false); + + _buttonLoadOperations->Enable(); + + _categoriesGrid->AppendRows(_unresolvedCategories.size()); + + for (i=0; i<(int)_unresolvedCategories.size(); i++) + { + _categoriesGrid->SetCellValue(i, 0, _unresolvedCategories[i].name); + _categoriesGrid->SetReadOnly(i, 0); + _categoriesGrid->SetCellValue(i, 2, userCategories[0]); + + _categoriesGrid->SetCellEditor(i, 2, categoryEditor); + } + + _categoriesGrid->AutoSize(); + _categoriesGrid->Layout(); + } + + if (!_unresolvedAccounts.size() && !_unresolvedCategories.size()) + { + OnLoadOperations(event); + } + Layout(); +} + +void ImportPanel::OnLoadOperations(wxCommandEvent& WXUNUSED(event)) +{ + int i, nbAccounts=0, nbCategories=0; + User* user = _kiss->GetUser(); + Account account; + Category category; + std::map accounts; + std::map categories; + wxString oldid; + + for(i=0; i<_accountsGrid->GetNumberRows(); i++) + { + if (_accountsGrid->GetCellValue(i, 2) == _("Create one")) + nbAccounts++; + else + accounts[_accountsGrid->GetCellValue(i, 0)] = + user->GetAccountId(_accountsGrid->GetCellValue(i, 1)); + } + + for(i=0; i<_categoriesGrid->GetNumberRows(); i++) + { + if (_categoriesGrid->GetCellValue(i, 2) == _("Create one")) + nbCategories++; + else + categories[_categoriesGrid->GetCellValue(i, 0)] = + user->GetAccountId(_categoriesGrid->GetCellValue(i, 1)); + } + + if (nbAccounts || nbCategories) + { + wxString message; + + if (nbAccounts) + { + message += wxString::Format(_("%d accounts"), nbAccounts); + if (nbCategories) message += _(" and "); + } + + if (nbCategories) + message += wxString::Format(_("%d categories"), nbCategories); + + message += _(" will be created, is it ok ?"); + + wxMessageDialog dialog(_wxUI, message, wxT("KissCount"), wxYES_NO); + if (dialog.ShowModal() == wxID_NO) + return; + + for(i=0; i<_accountsGrid->GetNumberRows(); i++) + { + if (_accountsGrid->GetCellValue(i, 2) == _("Create one")) + { + account = _unresolvedAccounts[i] ; + if (_accountsGrid->GetCellValue(i, 1).Length()) + account.name = _accountsGrid->GetCellValue(i, 1); + else + account.name = _accountsGrid->GetCellValue(i, 0); + account.number = _accountsGrid->GetCellValue(i, 0); + + oldid = account.id; + _resolvedAccounts[oldid] = accounts[_accountsGrid->GetCellValue(i, 0)] = _kiss->AddAccount(account); + } + } + + _accountsGrid->DeleteRows(0, _accountsGrid->GetNumberRows ()); + + for(i=0; i<_categoriesGrid->GetNumberRows(); i++) + { + if (_categoriesGrid->GetCellValue(i, 2) == _("Create one")) + { + category = _unresolvedCategories[i] ; + if (_categoriesGrid->GetCellValue(i, 1).Length()) + category.name = _categoriesGrid->GetCellValue(i, 1); + else + category.name = _categoriesGrid->GetCellValue(i, 0); + + oldid = category.id; + _resolvedCategories[oldid] = categories[_categoriesGrid->GetCellValue(i, 0)] = category.id = _kiss->AddCategory(category); + } + } + + _categoriesGrid->DeleteRows(0, _categoriesGrid->GetNumberRows ()); + + _wxUI->NeedReload(); + } + + _operations = _importEngine->GetOperations(accounts, categories); + + if (_operations->size()) + { + _hbox->Detach(_operationsGrid); + delete _operationsGrid; + _operationsGrid = new GridAccount(_kiss, this, OPS_GRID_ID, false, false, false); + _hbox->Add(_operationsGrid, 0, wxGROW|wxALL, 15); + + _operationsGrid->LoadOperations(_operations, 0, 0); + _buttonIntegrate->Enable(); + + _buttonLoadOperations->Disable(); + + Fit(); + } + else + { + wxMessageBox(_("No operation found into this file"), wxT("KissCount"), wxICON_INFORMATION | wxOK); + } +} + +void ImportPanel::OnIntegrate(wxCommandEvent& WXUNUSED(event)) +{ + int i; + std::map mapid; + wxString oldid, account; + bool update; + std::map accountAmounts; + std::map::iterator it; + double amount; + + if (!_operations->size()) return; + + wxMessageDialog dialog(_wxUI, _("Are you sure want to integrate these operations ?"), wxT("KissCount"), wxYES_NO); + if (dialog.ShowModal() == wxID_NO) + return; + + _buttonIntegrate->Disable(); + + for(i=0; i<(int)_operations->size(); i++) + { + oldid = (*_operations)[i].id; + _kiss->AddOperation((*_operations)[i], false); + mapid[oldid] = (*_operations)[i].id; + } + + for(i=0; i<(int)_operations->size(); i++) + { + update = false; + + if ((*_operations)[i].parent.Length()) + { + (*_operations)[i].parent = mapid[(*_operations)[i].parent]; + update = true; + } + + if ((*_operations)[i].transfert.Length()) + { + (*_operations)[i].transfert = mapid[(*_operations)[i].transfert]; + update = true; + } + + if (update) + _kiss->UpdateOperation((*_operations)[i], false); + } + + accountAmounts = _importEngine->GetAccountAmounts(); + + for(it=accountAmounts.begin(); it!=accountAmounts.end(); it++) + { + account = it->first.account; + + if (_resolvedAccounts.count(account)) + account = _resolvedAccounts[account]; + + amount = _kiss->GetAccountAmount(account, it->first.month, it->first.year); + + if (!amount) + _kiss->SetAccountAmount(account, it->first.month, it->first.year, it->second); + } + + if (_checkSaveImportPatterns->IsChecked()) + _kiss->UpdateImportPattern(); + + _operations->clear(); + _operationsGrid->ClearGrid(); + + wxMessageBox(_("Operations successfully imported"), wxT("KissCount"), wxICON_INFORMATION | wxOK); + + _wxUI->NeedReload(); +} + +void ImportPanel::OnOperationModified(wxGridEvent& event) +{ + int col = event.GetCol(); + int row; + static bool update = false; + + if (col != DESCRIPTION && col != CATEGORY && col != ACCOUNT) return ; + + if (update) return; + + update = true; + + row = event.GetRow(); + + _operationsGrid->ClearGrid(); + + if (_importEngine->UpdatePattern(row-1) > 1) + _operationsGrid->LoadOperations(_operations, 0, 0); + + Fit(); + + update = false; +} diff --git a/src/view/ImportPanel.h b/src/view/ImportPanel.h new file mode 100644 index 0000000..8ef365c --- /dev/null +++ b/src/view/ImportPanel.h @@ -0,0 +1,74 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#ifndef IMPORTPANEL_H +#define IMPORTPANEL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "view.h" +#include +#include + +class ImportPanel: public KissPanel +{ +public: + ImportPanel(KissCount* kiss, wxUI *parent); + + KissPanel* CreatePanel(); + wxBitmapButton* GetButton(int id); + wxString GetToolTip(); + void OnShow(wxShowEvent& event); + + void OnFile(wxCommandEvent& WXUNUSED(event)); + void OnFileEnter(wxCommandEvent& WXUNUSED(event)); + void OnLoadOperations(wxCommandEvent& WXUNUSED(event)); + void OnIntegrate(wxCommandEvent& WXUNUSED(event)); + void OnOperationModified(wxGridEvent& event); + +private: + wxBoxSizer *_hbox; + wxGrid* _accountsGrid, *_categoriesGrid; + wxTextCtrl* _fileTxt; + GridAccount* _operationsGrid; + ImportEngine* _importEngine; + wxButton* _buttonLoadOperations, *_buttonIntegrate; + wxCheckBox *_checkSaveImportPatterns; + std::vector* _operations; + + std::vector _unresolvedAccounts; + std::vector _unresolvedCategories; + std::map _resolvedAccounts; + std::map _resolvedCategories; + + void ProcessFile(); + + DECLARE_EVENT_TABLE(); +}; + +#endif diff --git a/src/view/KissPanel.h b/src/view/KissPanel.h new file mode 100644 index 0000000..339638b --- /dev/null +++ b/src/view/KissPanel.h @@ -0,0 +1,51 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#ifndef KISSPANEL_H +#define KISSPANEL_H + +#include +#include +#include + +class wxUI; +class KissCount; + +class KissPanel: public wxScrolledWindow +{ +public: + KissPanel(KissCount* kiss, wxUI* parent) : + wxScrolledWindow((wxFrame*)parent), + _kiss(kiss), + _wxUI(parent), + _KissButton(NULL) + {Hide();} + + virtual void OnShow(wxShowEvent& event)=0; + virtual KissPanel* CreatePanel()=0; + virtual wxBitmapButton* GetButton(int id) {return NULL;} + virtual wxString GetToolTip() {return wxT("");} + +protected: + KissCount* _kiss; + wxUI* _wxUI; + wxBitmapButton* _KissButton; +}; + +#endif diff --git a/src/view/PasswordDialog.cpp b/src/view/PasswordDialog.cpp index 5d4a21b..4071f2a 100644 --- a/src/view/PasswordDialog.cpp +++ b/src/view/PasswordDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/PasswordDialog.h b/src/view/PasswordDialog.h index e52d468..cbd1a7d 100644 --- a/src/view/PasswordDialog.h +++ b/src/view/PasswordDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/PreferencesPanel.cpp b/src/view/PreferencesPanel.cpp index 7aae234..3e97df8 100644 --- a/src/view/PreferencesPanel.cpp +++ b/src/view/PreferencesPanel.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -19,7 +19,7 @@ #include "PreferencesPanel.h" -enum {ACCOUNT_NAME, ACCOUNT_NUMBER, ACCOUNT_DEFAULT, ACCOUNT_BLOCKED, ACCOUNT_DELETE, NUMBER_COLS_ACCOUNT}; +enum {ACCOUNT_NAME, ACCOUNT_NUMBER, ACCOUNT_DEFAULT, ACCOUNT_VIRTUAL, ACCOUNT_BLOCKED, ACCOUNT_DELETE, NUMBER_COLS_ACCOUNT}; enum {CATEGORY_NAME, CATEGORY_BACKGROUND_COLOR, CATEGORY_FOREGROUND_COLOR, CATEGORY_FONT, CATEGORY_DELETE, NUMBER_COLS_CATEGORY}; enum {CATEGORIES_GRID_ID=1, ACCOUNTS_GRID_ID, NAME_ID, CHANGE_NAME_ID, CHANGE_PASSWORD_ID, KILL_ME_ID, LANGUAGE_ID, @@ -38,7 +38,7 @@ EVT_CHECKLISTBOX(SHARED_WITH_ID, PreferencesPanel::OnSharedChange) EVT_SHOW(PreferencesPanel::OnShow) END_EVENT_TABLE() -PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*parent)), _kiss(kiss), _wxUI(parent), _sharedWith(NULL), _curAccountRow(-1) +PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent) : KissPanel(kiss, parent), _sharedWith(NULL), _curAccountRow(-1) { wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); wxBoxSizer *hbox1 = new wxBoxSizer(wxHORIZONTAL); @@ -52,6 +52,7 @@ PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*p wxStaticBoxSizer * staticBoxSizer; std::list users; std::list::iterator it; + wxRect rect = wxDisplay().GetGeometry(); SetSizer(vbox); @@ -65,8 +66,8 @@ PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*p // User staticBoxSizer = new wxStaticBoxSizer (staticUser, wxVERTICAL); - gridBagSizer = new wxGridBagSizer(10, 10); - staticBoxSizer->Add(gridBagSizer); + gridBagSizer = new wxGridBagSizer(2, 3); + staticBoxSizer->Add(gridBagSizer, 0, wxGROW|wxALL, 2); label = new wxStaticText(this, wxID_ANY, _("Name")); gridBagSizer->Add(label, wxGBPosition(0, 0)); @@ -82,8 +83,7 @@ PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*p gridBagSizer->Add(buttonChangePassword, wxGBPosition(1, 1)); gridBagSizer->Add(killMe, wxGBPosition(1, 2)); - vbox->Add(staticBoxSizer); - vbox->Add(-1, 20); + vbox->Add(staticBoxSizer, 0, wxALL, 10); // Account staticBoxSizer = new wxStaticBoxSizer (staticAccount, wxVERTICAL); @@ -96,15 +96,14 @@ PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*p InitAccounts(user); - staticBoxSizer->Add(_accountsGrid); + staticBoxSizer->Add(_accountsGrid, 0, wxGROW|wxALL, 2); - hbox1->Add(staticBoxSizer); - hbox1->Add(-1, 20); + hbox1->Add(staticBoxSizer, 0, wxALL); staticBoxSizer = new wxStaticBoxSizer (staticSharedWith, wxVERTICAL); _sharedWith = new wxCheckListBox(this, SHARED_WITH_ID); - staticBoxSizer->Add(_sharedWith); + staticBoxSizer->Add(_sharedWith, 0, wxGROW); users = _kiss->GetUsers(); @@ -114,8 +113,8 @@ PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*p _sharedWith->Enable(false); - hbox1->Add(staticBoxSizer); - hbox1->Add(-1, 20); + hbox1->Add(staticBoxSizer, 0, wxLEFT, 5); + vbox->Add(hbox1, 0, wxGROW|wxALL, 10); // Categories staticBoxSizer = new wxStaticBoxSizer (staticCategories, wxVERTICAL); @@ -126,15 +125,11 @@ PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*p _categoriesGrid = new wxMyGrid(this, CATEGORIES_GRID_ID, clicks, 4); } - staticBoxSizer->Add(_categoriesGrid); + staticBoxSizer->Add(_categoriesGrid, 0, wxGROW|wxALL, 2); InitCategories(user); - vbox->Add(hbox1); - vbox->Add(-1, 20); - - vbox->Add(staticBoxSizer); - vbox->Add(-1, 20); + vbox->Add(staticBoxSizer, 0, wxALL, 10); // Operation Order staticBoxSizer = new wxStaticBoxSizer (staticOperationOrder, wxVERTICAL); @@ -142,10 +137,9 @@ PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*p _operationOrder = new wxComboBox(this, OPERATION_ORDER_ID); _operationOrder->SetWindowStyle(wxCB_READONLY); - staticBoxSizer->Add(_operationOrder); + staticBoxSizer->Add(_operationOrder, 0, wxGROW|wxALL, 2); - hbox2->Add(staticBoxSizer); - hbox2->Add(-1, 20); + hbox2->Add(staticBoxSizer, 0); InitOperationOrder(user); @@ -155,21 +149,39 @@ PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*p _language = new wxBitmapComboBox(this, LANGUAGE_ID); _language->SetWindowStyle(wxCB_READONLY); - staticBoxSizer->Add(_language); + staticBoxSizer->Add(_language, 0, wxGROW|wxALL, 2); - hbox2->Add(staticBoxSizer); - hbox2->Add(-1, 20); + hbox2->Add(staticBoxSizer, 0, wxLEFT, 10); InitLanguage(user); _language->Fit(); - vbox->Add(hbox2); + vbox->Add(hbox2, 0, wxALL, 10); Fit(); - SetMinSize(GetSize()); - Hide(); + SetMinSize(wxSize(rect.width-rect.x-15, rect.height-rect.y-128-25)); + SetMaxSize(wxSize(rect.width-rect.x-15, rect.height-rect.y-128-25)); + SetScrollbars(10, 10, 100/10, 100/10); +} + +KissPanel* PreferencesPanel::CreatePanel() +{ + return new PreferencesPanel(_kiss, _wxUI); +} + +wxBitmapButton* PreferencesPanel::GetButton(int id) +{ + if (!_KissButton) + _KissButton = new wxBitmapButton(_wxUI, id, wxBitmap(wxT(PREFS_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128)); + + return _KissButton; +} + +wxString PreferencesPanel::GetToolTip() +{ + return _("Preferences"); } void PreferencesPanel::InitAccounts(User* user) @@ -184,6 +196,7 @@ void PreferencesPanel::InitAccounts(User* user) _accountsGrid->SetColLabelValue(ACCOUNT_NAME, _("Name")); _accountsGrid->SetColLabelValue(ACCOUNT_NUMBER, _("Number")); _accountsGrid->SetColLabelValue(ACCOUNT_DEFAULT, _("Default")); + _accountsGrid->SetColLabelValue(ACCOUNT_VIRTUAL, _("Virtual")); _accountsGrid->SetColLabelValue(ACCOUNT_BLOCKED, _("Blocked")); _accountsGrid->SetColLabelValue(ACCOUNT_DELETE, _("Delete")); _accountsGrid->SetDefaultCellFont(font); @@ -212,14 +225,18 @@ void PreferencesPanel::AddAccount(int line, Account ac) _accountsGrid->SetCellEditor(line, ACCOUNT_NUMBER, new wxGridCellStarEditor ()); _accountsGrid->SetCellRenderer(line, ACCOUNT_DEFAULT, new wxGridCellBoolRenderer ()); _accountsGrid->SetCellEditor(line, ACCOUNT_DEFAULT, new wxGridCellFastBoolEditor ()); + _accountsGrid->SetCellRenderer(line, ACCOUNT_VIRTUAL, new wxGridCellBoolRenderer ()); + _accountsGrid->SetCellEditor(line, ACCOUNT_VIRTUAL, new wxGridCellFastBoolEditor ()); _accountsGrid->SetCellRenderer(line, ACCOUNT_BLOCKED, new wxGridCellBoolRenderer ()); _accountsGrid->SetCellEditor(line, ACCOUNT_BLOCKED, new wxGridCellFastBoolEditor ()); _accountsGrid->SetCellRenderer(line, ACCOUNT_DELETE, new wxGridCellBoolRenderer ()); _accountsGrid->SetCellEditor(line, ACCOUNT_DELETE, new wxGridCellBoolEditor ()); _accountsGrid->SetCellValue(line, ACCOUNT_DEFAULT, (ac._default)?wxT("1"):wxT("0")); + _accountsGrid->SetCellValue(line, ACCOUNT_VIRTUAL, (ac._virtual)?wxT("1"):wxT("0")); _accountsGrid->SetCellValue(line, ACCOUNT_BLOCKED, (ac.blocked)?wxT("1"):wxT("0")); _accountsGrid->SetCellAlignment(line, ACCOUNT_DEFAULT, wxALIGN_CENTRE, wxALIGN_CENTRE); + _accountsGrid->SetCellAlignment(line, ACCOUNT_VIRTUAL, wxALIGN_CENTRE, wxALIGN_CENTRE); _accountsGrid->SetCellAlignment(line, ACCOUNT_BLOCKED, wxALIGN_CENTRE, wxALIGN_CENTRE); _accountsGrid->SetCellAlignment(line, ACCOUNT_DELETE, wxALIGN_CENTRE, wxALIGN_CENTRE); @@ -228,12 +245,21 @@ void PreferencesPanel::AddAccount(int line, Account ac) _accountsGrid->SetReadOnly(line, ACCOUNT_NAME, true); _accountsGrid->SetReadOnly(line, ACCOUNT_NUMBER, true); _accountsGrid->SetReadOnly(line, ACCOUNT_DEFAULT, true); + _accountsGrid->SetReadOnly(line, ACCOUNT_VIRTUAL, true); _accountsGrid->SetReadOnly(line, ACCOUNT_BLOCKED, true); } + else + { + _accountsGrid->SetReadOnly(line, ACCOUNT_DEFAULT, false); + _accountsGrid->SetReadOnly(line, ACCOUNT_VIRTUAL, false); + _accountsGrid->SetReadOnly(line, ACCOUNT_BLOCKED, false); + _accountsGrid->SetReadOnly(line, ACCOUNT_DELETE, false); + } } else { _accountsGrid->SetReadOnly(line, ACCOUNT_DEFAULT, true); + _accountsGrid->SetReadOnly(line, ACCOUNT_VIRTUAL, true); _accountsGrid->SetReadOnly(line, ACCOUNT_BLOCKED, true); _accountsGrid->SetReadOnly(line, ACCOUNT_DELETE, true); @@ -289,7 +315,7 @@ void PreferencesPanel::AddCategory(int line, Category cat) if (cat.id != wxT("0")) { - _categoriesGrid->SetCellValue(line, CATEGORY_NAME, cat.name); + _categoriesGrid->SetCellValue(line, CATEGORY_NAME, wxGetTranslation(cat.name)); SET_ROW_COLOR(line, cat.backcolor, cat.forecolor); if (line) { @@ -358,7 +384,7 @@ void PreferencesPanel::OnAccountModified(wxGridEvent& event) int row = event.GetRow(); int col = event.GetCol(); static bool inModification = false ; - int i; + int i, a; if (inModification) return; @@ -384,6 +410,12 @@ void PreferencesPanel::OnAccountModified(wxGridEvent& event) else new_account._default = false; + value = _accountsGrid->GetCellValue(row, ACCOUNT_VIRTUAL); + if (value.Length() && value != wxT("0")) + new_account._virtual = true; + else + new_account._virtual = false; + value = _accountsGrid->GetCellValue(row, ACCOUNT_BLOCKED); if (value.Length() && value != wxT("0")) new_account.blocked = true; @@ -405,15 +437,23 @@ void PreferencesPanel::OnAccountModified(wxGridEvent& event) _accountsGrid->SetCellValue(row, col, wxT("0")); return; } - wxMessageDialog dialog(_wxUI, _("Are you sure want to delete ")+new_account.name, wxT("KissCount"), wxYES_NO); - if (dialog.ShowModal() == wxID_NO) + wxString *accounts = new wxString[user->GetAccountsNumber()]; + accounts[0] = _("None"); + a = 0; + for(i=0; i < user->GetAccountsNumber(); i++) + if (user->_accounts[i].id != new_account.id) + accounts[++a] = user->_accounts[i].name; + wxSingleChoiceDialog dialog(_wxUI, _("Wich account will replace this one ?"), wxT("KissCount"), user->GetAccountsNumber(), accounts); + + if (dialog.ShowModal() == wxID_CANCEL) { - _accountsGrid->SetCellValue(row, col, wxT("0")); + _accountsGrid->SetCellValue(row, col, wxT("0")); } - else + else { - _accountsGrid->DeleteRows(row, 1); - _kiss->DeleteAccount(new_account); + _accountsGrid->DeleteRows(row, 1); + i = dialog.GetSelection(); + _kiss->DeleteAccount(new_account, (!i) ? wxT("0") : user->GetAccountId(accounts[i])); } if (user->_accounts.size() == 1) @@ -423,7 +463,7 @@ void PreferencesPanel::OnAccountModified(wxGridEvent& event) _accountsGrid->SetCellValue(0, ACCOUNT_DEFAULT, wxT("1")); } - _wxUI->Layout(); + Fit(); inModification = false; _wxUI->NeedReload(); return; @@ -480,6 +520,8 @@ void PreferencesPanel::OnAccountModified(wxGridEvent& event) new_account.shared = false; new_account.blocked = false; + new_account.is_owner = true; + new_account._virtual = false; AddAccount(row, new_account); _kiss->AddAccount(new_account); @@ -487,10 +529,10 @@ void PreferencesPanel::OnAccountModified(wxGridEvent& event) _accountsGrid->AppendRows(); new_account.id = wxT("0"); - AddAccount(row, new_account); + AddAccount(row+1, new_account); } - _wxUI->Layout(); + Fit(); _wxUI->NeedReload(); inModification = false; } @@ -572,6 +614,7 @@ void PreferencesPanel::OnCategoryModified(wxGridEvent& event) int col = event.GetCol(); static bool inModification = false ; Category new_cat, cat_tmp; + int i, a; if (inModification) return; @@ -652,21 +695,28 @@ void PreferencesPanel::OnCategoryModified(wxGridEvent& event) if (user->GetCategoriesNumber() && row < user->GetCategoriesNumber()) { new_cat.id = user->_categories[row].id; + new_cat.fix_cost = user->_categories[row].fix_cost; if (col == CATEGORY_DELETE) { - wxMessageDialog dialog(_wxUI, _("Are you sure want to delete : \n")+new_cat.name, wxT("KissCount"), wxYES_NO); - if (dialog.ShowModal() == wxID_NO) + wxString *categories = new wxString[user->GetCategoriesNumber()]; + categories[0] = _("None"); + a = 0; + for(i=0; i < user->GetCategoriesNumber(); i++) + if (user->_categories[i].id != new_cat.id) + categories[++a] = wxGetTranslation(user->_categories[i].name); + wxSingleChoiceDialog dialog(_wxUI, _("Wich category will replace this one ?"), wxT("KissCount"), user->GetCategoriesNumber(), categories); + if (dialog.ShowModal() == wxID_CANCEL) { - _categoriesGrid->SetCellValue(row, col, wxT("0")); + _categoriesGrid->SetCellValue(row, col, wxT("0")); } - else + else { - _categoriesGrid->DeleteRows(row, 1); - _kiss->DeleteCategory(user->_categories[row]); + _categoriesGrid->DeleteRows(row, 1); + i = dialog.GetSelection(); + _kiss->DeleteCategory(user->_categories[row], (!i) ? wxT("0") : user->GetCategoryId(categories[i])); + Fit(); + _wxUI->NeedReload(); } - - _wxUI->Layout(); - _wxUI->NeedReload(); inModification = false; return; } @@ -697,16 +747,23 @@ void PreferencesPanel::OnCategoryModified(wxGridEvent& event) inModification = false; return ; } + + new_cat.fix_cost = false; _kiss->AddCategory(new_cat); AddCategory(row, new_cat); + _categoriesGrid->SetReadOnly(row, CATEGORY_BACKGROUND_COLOR, false); + _categoriesGrid->SetReadOnly(row, CATEGORY_FOREGROUND_COLOR, false); + _categoriesGrid->SetReadOnly(row, CATEGORY_FONT, false); + _categoriesGrid->SetReadOnly(row, CATEGORY_DELETE, false); + new_cat.id = wxT("0"); _categoriesGrid->AppendRows(); AddCategory(++row, new_cat); } - _wxUI->Layout(); + Fit(); _wxUI->NeedReload(); inModification = false; diff --git a/src/view/PreferencesPanel.h b/src/view/PreferencesPanel.h index e45437c..e7632bc 100644 --- a/src/view/PreferencesPanel.h +++ b/src/view/PreferencesPanel.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -31,20 +31,22 @@ #include #include -#include -#include "wxUI.h" +#include "view.h" #include #include "PasswordDialog.h" #include "SupportedLanguages.h" #include "wxGridCellStarEditor.h" -class wxUI; -class KissCount; - -class PreferencesPanel: public wxPanel +class PreferencesPanel: public KissPanel { public: PreferencesPanel(KissCount* kiss, wxUI *parent); + + KissPanel* CreatePanel(); + wxBitmapButton* GetButton(int id); + wxString GetToolTip(); + void OnShow(wxShowEvent& event); + void ChangeUser(); void OnAccountModified(wxGridEvent& event); @@ -55,12 +57,9 @@ public: void OnChangePassword(wxCommandEvent& event); void OnOperationOrderChange(wxCommandEvent& event); void OnLanguageChange(wxCommandEvent& event); - void OnShow(wxShowEvent& event); void OnKillMe(wxCommandEvent& event); private: - KissCount* _kiss; - wxUI* _wxUI; wxGrid* _accountsGrid; wxGrid* _categoriesGrid; wxTextCtrl* _name; diff --git a/src/view/SearchBanner.cpp b/src/view/SearchBanner.cpp new file mode 100644 index 0000000..4ee0d91 --- /dev/null +++ b/src/view/SearchBanner.cpp @@ -0,0 +1,220 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#include "SearchBanner.h" + +enum {DESCRIPTION_ID=1, CALENDAR_FROM_ID, CALENDAR_TO_ID}; + +BEGIN_EVENT_TABLE(SearchBanner, wxPanel) +EVT_CALENDAR_SEL_CHANGED(CALENDAR_TO_ID, SearchBanner::OnCalendarToChange) +EVT_TEXT_ENTER(DESCRIPTION_ID, SearchBanner::OnEnter) +END_EVENT_TABLE() + +SearchBanner::SearchBanner(KissCount* kiss, wxPanel *parent, void* caller, OnButtonEnter enterCallback) : wxPanel(parent), _kiss(kiss), _caller(caller), _enterCallback(enterCallback), _operations(NULL) +{ + DEFAULT_FONT(font); + User* user = _kiss->GetUser(); + std::vector::iterator accountIt; + std::vector::iterator categoryIt; + wxDateTime firstOfMonth; + + wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); + + SetSizer(vbox); + + _checkDateFrom = new wxCheckBox(this, wxID_ANY, _("Date from")); + _checkDateTo = new wxCheckBox(this, wxID_ANY, _("Date to")); + + _checkDateFrom->SetValue(wxT("1")); + _checkDateTo->SetValue(wxT("1")); + + wxGridBagSizer *gridBagSizer = new wxGridBagSizer(3, 10); + + firstOfMonth.SetToCurrent(); + firstOfMonth.SetDay(1); + _calendarFrom = new wxCalendarCtrl(this, CALENDAR_FROM_ID, firstOfMonth, wxDefaultPosition, wxDefaultSize, + wxCAL_MONDAY_FIRST); + _calendarTo = new wxCalendarCtrl(this, CALENDAR_TO_ID, wxDefaultDateTime, wxDefaultPosition, wxDefaultSize, + wxCAL_MONDAY_FIRST); + + + _description = new wxTextCtrl(this, DESCRIPTION_ID); + _description->SetWindowStyle(_description->GetWindowStyle() | wxTE_PROCESS_ENTER); + wxSize size = _description->GetSize(); + size.SetWidth(size.GetWidth()*2); + _description->SetMinSize(size); + _amountFrom = new wxTextCtrl(this, wxID_ANY); + _amountTo = new wxTextCtrl(this, wxID_ANY); + + _category = new wxCheckListBox(this, wxID_ANY); + _category->Append(_("Unknown")); + for(categoryIt = user->_categories.begin(); categoryIt != user->_categories.end(); categoryIt++) + _category->Append(wxGetTranslation(categoryIt->name)); + + wxString stypes[] = {_("Fix"), _("Non fix"), _("Checked"), _("Not checked")}; + _optype = new wxCheckListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 4, stypes); + + _account = new wxCheckListBox(this, wxID_ANY); + _account->Append(_("Unknown")); + for(accountIt = user->_accounts.begin(); accountIt != user->_accounts.end(); accountIt++) + _account->Append(accountIt->name); + + wxStaticText* labelDescription = new wxStaticText(this, wxID_ANY, _("Description")); + wxStaticText* labelAmountFrom = new wxStaticText(this, wxID_ANY, _("Amount from")); + wxStaticText* labelAmountTo = new wxStaticText(this, wxID_ANY, _("Amount to")); + wxStaticText* labelCategory = new wxStaticText(this, wxID_ANY, _("Category")); + wxStaticText* labelOperations = new wxStaticText(this, wxID_ANY, _("Operations")); + wxStaticText* labelAccount = new wxStaticText(this, wxID_ANY, _("Account")); + + gridBagSizer->Add(labelDescription, wxGBPosition(0, 0)); + gridBagSizer->Add(_description, wxGBPosition(1, 0)); + gridBagSizer->Add(_checkDateFrom, wxGBPosition(0, 1)); + gridBagSizer->Add(_calendarFrom, wxGBPosition(1, 1)); + gridBagSizer->Add(_checkDateTo, wxGBPosition(0, 2)); + gridBagSizer->Add(_calendarTo, wxGBPosition(1, 2)); + gridBagSizer->Add(labelAmountFrom, wxGBPosition(0, 3)); + gridBagSizer->Add(_amountFrom, wxGBPosition(1, 3)); + gridBagSizer->Add(labelAmountTo, wxGBPosition(0, 4)); + gridBagSizer->Add(_amountTo, wxGBPosition(1, 4)); + gridBagSizer->Add(labelCategory, wxGBPosition(0, 5)); + gridBagSizer->Add(_category, wxGBPosition(1, 5)); + gridBagSizer->Add(labelOperations, wxGBPosition(0, 6)); + gridBagSizer->Add(_optype, wxGBPosition(1, 6)); + gridBagSizer->Add(labelAccount, wxGBPosition(0, 7)); + gridBagSizer->Add(_account, wxGBPosition(1, 7)); + + vbox->Add(gridBagSizer, 0, wxGROW|wxALL, 5); +} + +SearchBanner::~SearchBanner() +{ + if (_operations) delete _operations; +} + +std::vector * SearchBanner::Search() +{ + wxString *description=NULL, *amountFrom=NULL, *amountTo=NULL; + std::vector categories, accounts; + wxDateTime *dateFrom=NULL, *dateTo=NULL; + User* user= _kiss->GetUser(); + int i, types=0; + std::vector::iterator it; + double af, at; + + if (_operations) + { + delete _operations; + _operations = NULL; + } + + if (_checkDateFrom->IsChecked()) + { + dateFrom = new wxDateTime; + *dateFrom = _calendarFrom->GetDate(); + } + + if (_checkDateTo->IsChecked()) + { + dateTo = new wxDateTime; + *dateTo = _calendarTo->GetDate(); + } + + if (dateFrom && dateTo && *dateFrom > *dateTo) + { + wxMessageBox(_("Invalid date range"), _("Error"), wxICON_ERROR | wxOK); + goto end; + } + + if (_amountFrom->GetLineText(0).Length()) + { + amountFrom = new wxString; + *amountFrom = _amountFrom->GetLineText(0); + if (!amountFrom->ToDouble(&af)) + { + wxMessageBox(_("Invalid amount from"), _("Error"), wxICON_ERROR | wxOK); + goto end; + } + + if (af < 0) af *= -1; + } + + if (_amountTo->GetLineText(0).Length()) + { + amountTo = new wxString; + *amountTo = _amountTo->GetLineText(0); + if (!amountTo->ToDouble(&at)) + { + wxMessageBox(_("Invalid amount to"), _("Error"), wxICON_ERROR | wxOK); + goto end; + } + + if (at < 0) at *= -1; + } + + if (amountFrom && amountTo && af > at) + { + wxMessageBox(_("Invalid amount range"), _("Error"), wxICON_ERROR | wxOK); + goto end; + } + + if (_description->GetLineText(0).Length()) + { + description = new wxString; + *description = _description->GetLineText(0); + } + + for(i=0; iGetCategoriesNumber(); i++) + if (_category->IsChecked(i)) + categories.push_back((i) ? user->_categories[i-1].id : wxT("0")); + + types |= (_optype->IsChecked(0)) ? FIX_OP : 0; + types |= (_optype->IsChecked(1)) ? NON_FIX_OP : 0; + types |= (_optype->IsChecked(2)) ? CHECKED_OP : 0; + types |= (_optype->IsChecked(3)) ? NOT_CHECKED_OP : 0; + + for(i=0; iGetAccountsNumber(); i++) + if (_account->IsChecked(i)) + accounts.push_back((i) ? user->_accounts[i-1].id : wxT("0")); + + _operations = _kiss->Search(description, dateFrom, dateTo, amountFrom, amountTo, categories,types, accounts); + +end: + delete dateFrom; + delete dateTo; + delete amountFrom; + delete amountTo; + + return _operations; +} + +void SearchBanner::OnEnter(wxCommandEvent& event) +{ + if (_enterCallback) + _enterCallback(_caller, event); +} + +void SearchBanner::OnCalendarFromChange(wxCalendarEvent& event) +{ + _checkDateFrom->SetValue(true); +} + +void SearchBanner::OnCalendarToChange(wxCalendarEvent& event) +{ + _checkDateTo->SetValue(true); +} diff --git a/src/view/SearchBanner.h b/src/view/SearchBanner.h new file mode 100644 index 0000000..bf3bc7b --- /dev/null +++ b/src/view/SearchBanner.h @@ -0,0 +1,60 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#ifndef SEARCHBANNER_H +#define SEARCHBANNER_H + +#include +#include +#include +#include +#include "view.h" +#include "grid/CalendarEditor.h" + +#include + +typedef void (*OnButtonEnter)(void* caller, wxCommandEvent& event); + +class SearchBanner: public wxPanel +{ +public: + SearchBanner(KissCount* kiss, wxPanel* parent, void* caller=NULL, OnButtonEnter enterCallback=NULL); + ~SearchBanner(); + + void OnEnter(wxCommandEvent& event); + void OnButtonSearch(wxCommandEvent& event); + void OnCalendarFromChange(wxCalendarEvent& event); + void OnCalendarToChange(wxCalendarEvent& event); + + std::vector * Search(); + +private: + KissCount* _kiss; + void* _caller; + OnButtonEnter _enterCallback; + + std::vector *_operations; + wxCalendarCtrl* _calendarFrom, *_calendarTo; + wxCheckBox *_checkDateFrom, *_checkDateTo; + wxTextCtrl* _description, *_amountFrom, *_amountTo; + wxCheckListBox* _category, *_account, *_optype; + DECLARE_EVENT_TABLE(); +}; + +#endif diff --git a/src/view/SearchPanel.cpp b/src/view/SearchPanel.cpp index 1318a0c..dca32ee 100644 --- a/src/view/SearchPanel.cpp +++ b/src/view/SearchPanel.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -19,209 +19,96 @@ #include "AccountPanel.h" -enum {SEARCH_ID, GRID_ID, CALENDAR_FROM_ID, CALENDAR_TO_ID}; +enum {SEARCH_ID=1, GRID_ID, + CHANGE_ACCOUNT_ID, CHANGE_CATEGORY_ID, RENAME_ID}; BEGIN_EVENT_TABLE(SearchPanel, wxPanel) EVT_BUTTON(SEARCH_ID, SearchPanel::OnButtonSearch) -EVT_CALENDAR_SEL_CHANGED(CALENDAR_FROM_ID, SearchPanel::OnCalendarFromChange) -EVT_CALENDAR_SEL_CHANGED(CALENDAR_TO_ID, SearchPanel::OnCalendarToChange) EVT_GRID_CMD_CELL_CHANGE(GRID_ID, SearchPanel::OnOperationModified) +EVT_BUTTON(CHANGE_ACCOUNT_ID, SearchPanel::OnButtonChangeAccount) +EVT_BUTTON(CHANGE_CATEGORY_ID, SearchPanel::OnButtonChangeCategory) +EVT_BUTTON(RENAME_ID, SearchPanel::OnButtonRename) EVT_SHOW(SearchPanel::OnShow) END_EVENT_TABLE() -#define UNESCAPE_CHARS(s) { \ - s.Replace(wxT("\\\""), wxT("\""), true); \ - s.Replace(wxT("\\\'"), wxT("\'"), true); \ - } - -SearchPanel::SearchPanel(KissCount* kiss, wxUI *parent) : wxScrolledWindow(&(*parent)), _kiss(kiss), _wxUI(parent), _operations(NULL) +SearchPanel::SearchPanel(KissCount* kiss, wxUI *parent) : KissPanel(kiss, parent), _operations(NULL) { DEFAULT_FONT(font); - User* user = _kiss->GetUser(); std::vector::iterator accountIt; std::vector::iterator categoryIt; wxDateTime firstOfMonth; wxRect rect = wxDisplay().GetGeometry(); wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *vbox2 = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL); + SetSizer(vbox); - - _checkDateFrom = new wxCheckBox(this, wxID_ANY, _("Date from")); - _checkDateTo = new wxCheckBox(this, wxID_ANY, _("Date to")); - - _checkDateFrom->SetValue(wxT("1")); - _checkDateTo->SetValue(wxT("1")); - - wxGridBagSizer *gridBagSizer = new wxGridBagSizer(3, 10); - - firstOfMonth.SetToCurrent(); - firstOfMonth.SetDay(1); - _calendarFrom = new wxCalendarCtrl(this, CALENDAR_FROM_ID, firstOfMonth, wxDefaultPosition, wxDefaultSize, - wxCAL_MONDAY_FIRST); - _calendarTo = new wxCalendarCtrl(this, CALENDAR_TO_ID, wxDefaultDateTime, wxDefaultPosition, wxDefaultSize, - wxCAL_MONDAY_FIRST); - - _description = new wxTextCtrl(this, wxID_ANY); - wxSize size = _description->GetSize(); - size.SetWidth(size.GetWidth()*2); - _description->SetMinSize(size); - _amountFrom = new wxTextCtrl(this, wxID_ANY); - _amountTo = new wxTextCtrl(this, wxID_ANY); - - _category = new wxCheckListBox(this, wxID_ANY); - for(categoryIt = user->_categories.begin(); categoryIt != user->_categories.end(); categoryIt++) - _category->Append(categoryIt->name); - - wxString stypes[] = {_("Fix"), _("Non fix"), _("Checked"), _("Not checked")}; - _optype = new wxCheckListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 4, stypes); - - _account = new wxCheckListBox(this, wxID_ANY); - for(accountIt = user->_accounts.begin(); accountIt != user->_accounts.end(); accountIt++) - _account->Append(accountIt->name); - _searchButton = new wxButton(this, SEARCH_ID, _("Search")); - wxStaticText* labelDescription = new wxStaticText(this, wxID_ANY, _("Description")); - wxStaticText* labelAmountFrom = new wxStaticText(this, wxID_ANY, _("Amount from")); - wxStaticText* labelAmountTo = new wxStaticText(this, wxID_ANY, _("Amount to")); - wxStaticText* labelCategory = new wxStaticText(this, wxID_ANY, _("Category")); - wxStaticText* labelOperations = new wxStaticText(this, wxID_ANY, _("Operations")); - wxStaticText* labelAccount = new wxStaticText(this, wxID_ANY, _("Account")); + _banner = new SearchBanner(kiss, this, this, OnEnter); - gridBagSizer->Add(labelDescription, wxGBPosition(0, 0)); - gridBagSizer->Add(_description, wxGBPosition(1, 0)); - gridBagSizer->Add(_checkDateFrom, wxGBPosition(0, 1)); - gridBagSizer->Add(_calendarFrom, wxGBPosition(1, 1)); - gridBagSizer->Add(_checkDateTo, wxGBPosition(0, 2)); - gridBagSizer->Add(_calendarTo, wxGBPosition(1, 2)); - gridBagSizer->Add(labelAmountFrom, wxGBPosition(0, 3)); - gridBagSizer->Add(_amountFrom, wxGBPosition(1, 3)); - gridBagSizer->Add(labelAmountTo, wxGBPosition(0, 4)); - gridBagSizer->Add(_amountTo, wxGBPosition(1, 4)); - gridBagSizer->Add(labelCategory, wxGBPosition(0, 5)); - gridBagSizer->Add(_category, wxGBPosition(1, 5)); - gridBagSizer->Add(labelOperations, wxGBPosition(0, 6)); - gridBagSizer->Add(_optype, wxGBPosition(1, 6)); - gridBagSizer->Add(labelAccount, wxGBPosition(0, 7)); - gridBagSizer->Add(_account, wxGBPosition(1, 7)); - gridBagSizer->Add(_searchButton, wxGBPosition(2, 0)); + vbox->Add(_banner, 0, wxGROW|wxALL, 5); + vbox->Add(_searchButton, 0, wxALL, 5); - vbox->Add(gridBagSizer); - vbox->Add(-1, 20); + _grid = new GridAccount(_kiss, this, GRID_ID, false, false, true); - _grid = new GridAccount(_kiss, this, GRID_ID); + hbox->Add(_grid, 0, wxGROW|wxALL, 5); - vbox->Add(_grid); + _changeAccountButton = new wxButton(this, CHANGE_ACCOUNT_ID, _("Change account")); + _changeCategoryButton = new wxButton(this, CHANGE_CATEGORY_ID, _("Change category")); + _renameButton = new wxButton(this, RENAME_ID, _("Rename")); + + vbox2->Add(_changeAccountButton, wxALL, 15); + vbox2->Add(_changeCategoryButton, wxALL, 15); + vbox2->Add(_renameButton, wxALL, 15); + + hbox->Add(vbox2, 0, wxALL, 15); + + vbox->Add(hbox, 0, wxGROW|wxALL, 5); Fit(); - SetMinSize(wxSize(rect.width-rect.x, rect.height-rect.y-128)); + SetMinSize(wxSize(rect.width-rect.x-15, rect.height-rect.y-128-25)); + SetMaxSize(wxSize(rect.width-rect.x-15, rect.height-rect.y-128-25)); SetScrollbars(10, 10, 100/10, 100/10); - - Hide(); } SearchPanel::~SearchPanel() { - if (_operations) delete _operations; +} + +KissPanel* SearchPanel::CreatePanel() +{ + return new SearchPanel(_kiss, _wxUI); +} + +wxBitmapButton* SearchPanel::GetButton(int id) +{ + if (!_KissButton) + _KissButton = new wxBitmapButton(_wxUI, id, wxBitmap(wxT(SEARCH_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128)); + + return _KissButton; +} + +wxString SearchPanel::GetToolTip() +{ + return _("Search"); +} + +void SearchPanel::OnEnter(void* caller, wxCommandEvent& event) +{ + SearchPanel* _this = (SearchPanel*) caller; + + _this->OnButtonSearch(event); } void SearchPanel::OnButtonSearch(wxCommandEvent& event) { - wxString *description=NULL, *amountFrom=NULL, *amountTo=NULL; - std::vector categories, accounts; - wxDateTime *dateFrom=NULL, *dateTo=NULL; - User* user= _kiss->GetUser(); - int i, types=0; - std::vector::iterator it; - double af, at; + _operations = _banner->Search(); - if (_calendarFrom->GetDate() > _calendarTo->GetDate()) - { - wxMessageBox(_("Invalid date range"), _("Error"), wxICON_ERROR | wxOK); - return; - } - - if (_amountFrom->GetLineText(0).Length()) - { - amountFrom = new wxString; - *amountFrom = _amountFrom->GetLineText(0); - if (!amountFrom->ToDouble(&af)) - { - wxMessageBox(_("Invalid amount from"), _("Error"), wxICON_ERROR | wxOK); - delete amountFrom; - return; - } - - if (af < 0) af *= -1; - } - - if (_amountTo->GetLineText(0).Length()) - { - amountTo = new wxString; - *amountTo = _amountTo->GetLineText(0); - if (!amountTo->ToDouble(&at)) - { - wxMessageBox(_("Invalid amount to"), _("Error"), wxICON_ERROR | wxOK); - delete amountFrom; - delete amountTo; - return; - } - - if (at < 0) at *= -1; - } - - if (amountFrom && amountTo && af > at) - { - wxMessageBox(_("Invalid amount range"), _("Error"), wxICON_ERROR | wxOK); - delete amountFrom; - delete amountTo; - return; - } - - _grid->DeleteRows(1, _grid->GetNumberRows()-1); - - if (_description->GetLineText(0).Length()) - { - description = new wxString; - *description = _description->GetLineText(0); - } - - if (_checkDateFrom->IsChecked()) - { - dateFrom = new wxDateTime; - *dateFrom = _calendarFrom->GetDate(); - } - - if (_checkDateTo->IsChecked()) - { - dateTo = new wxDateTime; - *dateTo = _calendarTo->GetDate(); - } - - if (dateFrom && dateTo && *dateFrom > *dateTo) - { - ; - } - - for(i=0; iGetCategoriesNumber(); i++) - if (_category->IsChecked(i)) - categories.push_back(user->_categories[i].id); - - types |= (_optype->IsChecked(0)) ? FIX_OP : 0; - types |= (_optype->IsChecked(1)) ? NON_FIX_OP : 0; - types |= (_optype->IsChecked(2)) ? CHECKED_OP : 0; - types |= (_optype->IsChecked(3)) ? NOT_CHECKED_OP : 0; - - for(i=0; iGetAccountsNumber(); i++) - if (_account->IsChecked(i)) - accounts.push_back(user->_accounts[i].id); - - if (_operations) - delete _operations; - - _operations = _kiss->Search(description, dateFrom, dateTo, amountFrom, amountTo, categories,types, accounts); + if (!_operations) return; if (_operations->size() > 1) wxMessageBox(wxString::Format(wxT("%d"), _operations->size()) + _(" entries found"), wxT("KissCount"), wxICON_INFORMATION | wxOK); @@ -233,26 +120,140 @@ void SearchPanel::OnButtonSearch(wxCommandEvent& event) return; } - _grid->LoadOperations(_operations, false, false, 0, 0); + _grid->LoadOperations(_operations, 0, 0); _wxUI->Layout(); } +static void ChangeAccount(Operation* op, void** params) +{ + wxString* account = (wxString*) params[0]; + + op->account = *account; +} + +void SearchPanel::OnButtonChangeAccount(wxCommandEvent& event) +{ + int i, a; + std::vector rows; + User* user = _kiss->GetUser(); + wxString *accounts = new wxString[user->GetAccountsNumber()+1]; + std::vector::iterator it; + wxString account; + void * params[] = {&account}; + + if (!_operations) return; + + _grid->GetSelectedOperations(&rows); + + accounts[0] = _("None"); + a = 0; + for(i=0; i < user->GetAccountsNumber(); i++) + accounts[++a] = user->_accounts[i].name; + + wxSingleChoiceDialog dialog(_wxUI, _("Choose a new account"), wxT("KissCount"), user->GetAccountsNumber()+1, accounts); + + if (dialog.ShowModal() == wxID_CANCEL) + return; + + a = dialog.GetSelection(); + account = (a) ? user->_accounts[a-1].id : wxT("0"); + + _grid->MassUpdate(rows, ChangeAccount, params); + + _wxUI->NeedReload(); +} + +static void ChangeCategory(Operation* op, void** params) +{ + wxString* category = (wxString*) params[0]; + bool* fix = (bool*) params[1]; + + op->category = *category; + op->fix_cost = * fix; +} + +void SearchPanel::OnButtonChangeCategory(wxCommandEvent& event) +{ + int i, a; + std::vector rows; + User* user = _kiss->GetUser(); + wxString *categories = new wxString[user->GetCategoriesNumber()+1]; + std::vector::iterator it; + wxString category; + bool fix; + void * params[] = {&category, &fix}; + + if (!_operations) return; + + _grid->GetSelectedOperations(&rows); + + categories[0] = _("None"); + a = 0; + for(i=0; i < user->GetCategoriesNumber(); i++) + categories[++a] = wxGetTranslation(user->_categories[i].name); + + wxSingleChoiceDialog dialog(_wxUI, _("Choose a new category"), wxT("KissCount"), user->GetCategoriesNumber()+1, categories); + + if (dialog.ShowModal() == wxID_CANCEL) + return; + + a = dialog.GetSelection(); + + if (a) + { + category = user->_categories[a-1].id ; + fix = user->_categories[a-1].fix_cost; + } + else + { + category = wxT("0"); + fix = false; + } + + _grid->MassUpdate(rows, ChangeCategory, params); + + _wxUI->NeedReload(); +} + +static void ChangeName(Operation* op, void** params) +{ + wxString* description = (wxString*) params[0]; + + op->description = *description; +} + +void SearchPanel::OnButtonRename(wxCommandEvent& event) +{ + std::vector rows; + std::vector::iterator it; + wxString category; + wxString description; + void * params[] = {&description}; + + if (!_operations) return; + + _grid->GetSelectedOperations(&rows); + + wxTextEntryDialog u(this, wxT(""), _("Enter a new description")); + + if (u.ShowModal() == wxID_CANCEL) + return; + + description = u.GetValue(); + + if (!description.size()) return; + + _grid->MassUpdate(rows, ChangeName, params); + + _wxUI->NeedReload(); +} + void SearchPanel::OnShow(wxShowEvent& event) { _wxUI->SetTitle(_kiss->GetUser()->_name + wxT(" - ") + _("Search")); } -void SearchPanel::OnCalendarFromChange(wxCalendarEvent& event) -{ - _checkDateFrom->SetValue(true); -} - -void SearchPanel::OnCalendarToChange(wxCalendarEvent& event) -{ - _checkDateTo->SetValue(true); -} - void SearchPanel::OnOperationModified(wxGridEvent& event) { _wxUI->NeedReload(); diff --git a/src/view/SearchPanel.h b/src/view/SearchPanel.h index d6476a4..b60ee0b 100644 --- a/src/view/SearchPanel.h +++ b/src/view/SearchPanel.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -24,42 +24,45 @@ #include #include #include +#include "view.h" #include "grid/CalendarEditor.h" #include "grid/wxGridCellBitmapRenderer.h" -#include "AccountPanel.h" #include "grid/GridAccount.h" -#include "view.h" +#include "AccountPanel.h" +#include "SearchBanner.h" -#include -#include "wxUI.h" #include -class wxUI; -class KissCount; class GridAccount; +class SearchBanner; -class SearchPanel: public wxScrolledWindow +class SearchPanel: public KissPanel { public: SearchPanel(KissCount* kiss, wxUI *parent); ~SearchPanel(); + KissPanel* CreatePanel(); + wxBitmapButton* GetButton(int id); + wxString GetToolTip(); + void OnShow(wxShowEvent& event); + + /* void OnEnter(wxCommandEvent& event); */ void OnButtonSearch(wxCommandEvent& event); void OnOperationModified(wxGridEvent& event); - void OnShow(wxShowEvent& event); - void OnCalendarFromChange(wxCalendarEvent& event); - void OnCalendarToChange(wxCalendarEvent& event); + + void OnButtonChangeAccount(wxCommandEvent& event); + void OnButtonChangeCategory(wxCommandEvent& event); + void OnButtonRename(wxCommandEvent& event); private: - KissCount* _kiss; - wxUI* _wxUI; std::vector *_operations; - wxCalendarCtrl* _calendarFrom, *_calendarTo; + SearchBanner* _banner; GridAccount *_grid; - wxCheckBox *_checkDateFrom, *_checkDateTo; - wxTextCtrl* _description, *_amountFrom, *_amountTo; - wxCheckListBox* _category, *_account, *_optype; - wxButton* _searchButton; + wxButton* _searchButton, *_renameButton, *_changeAccountButton, *_changeCategoryButton; + + static void OnEnter(void* caller, wxCommandEvent& event); + DECLARE_EVENT_TABLE(); }; diff --git a/src/view/StatsPanel.cpp b/src/view/StatsPanel.cpp index f285d2f..1925d1e 100644 --- a/src/view/StatsPanel.cpp +++ b/src/view/StatsPanel.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -26,11 +26,11 @@ EVT_CHOICE(RANGE_ID, StatsPanel::OnRangeChange) EVT_CHECKLISTBOX(ACCOUNTS_ID, StatsPanel::OnAccountsChange) END_EVENT_TABLE() -StatsPanel::StatsPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*parent)), _kiss(kiss), _wxUI(parent), _plot(NULL), _chart(NULL) +StatsPanel::StatsPanel(KissCount* kiss, wxUI *parent) : KissPanel(kiss, parent), _plot(NULL), _chart(NULL) { wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL); - _hbox2 = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); + _hbox2 = new wxBoxSizer(wxHORIZONTAL); _vbox2 = new wxBoxSizer(wxVERTICAL); int i; User* user = _kiss->GetUser(); @@ -38,6 +38,7 @@ StatsPanel::StatsPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*parent)), _ki std::vector::iterator categoryIt; std::map > operations; std::map >::iterator it; + int nbCategories; SetSizer(vbox); @@ -62,16 +63,13 @@ StatsPanel::StatsPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*parent)), _ki _monthTo->Select(11); wxStaticText* label = new wxStaticText(this, wxID_ANY, _("From")); - hbox->Add(label); - hbox->Add(-1, 10); - hbox->Add(_monthFrom); - hbox->Add(_yearFrom); + hbox->Add(label, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); + hbox->Add(_monthFrom, 0, wxRIGHT, 5); + hbox->Add(_yearFrom, 0, wxRIGHT, 20); - hbox->Add(-1, 30); label = new wxStaticText(this, wxID_ANY, _("To")); - hbox->Add(label); - hbox->Add(-1, 10); - hbox->Add(_monthTo); + hbox->Add(label, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5); + hbox->Add(_monthTo, 0, wxRIGHT, 5); hbox->Add(_yearTo); _account = new wxCheckListBox(this, ACCOUNTS_ID); @@ -87,14 +85,14 @@ StatsPanel::StatsPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*parent)), _ki categoryIt++, i++) { _categoriesIndexes[categoryIt->id] = i; - _categories[i] = categoryIt->name ; + _categories[i] = wxGetTranslation(categoryIt->name) ; } DEFAULT_FONT(font); _statsGrid = new wxGrid(this, wxID_ANY); - _statsGrid->CreateGrid(user->GetCategoriesNumber(), 2); + _statsGrid->CreateGrid(user->GetCategoriesNumber()+1, 2); _statsGrid->SetColLabelSize(0); _statsGrid->SetRowLabelSize(0); _statsGrid->EnableEditing(false); @@ -104,49 +102,74 @@ StatsPanel::StatsPanel(KissCount* kiss, wxUI *parent) : wxPanel(&(*parent)), _ki for(i=0; iGetCategoriesNumber(); i++) { - _statsGrid->SetCellValue(i, 0, _categories[i]); - _statsGrid->SetCellAlignment(i, 1, wxALIGN_RIGHT, wxALIGN_CENTRE); + if (i) + { + _statsGrid->SetCellValue(i+1, 0, _categories[i]); + _statsGrid->SetCellAlignment(i+1, 1, wxALIGN_RIGHT, wxALIGN_CENTRE); + } + else + { + _statsGrid->SetCellValue(i, 0, _categories[i]); + _statsGrid->SetCellAlignment(i, 1, wxALIGN_RIGHT, wxALIGN_CENTRE); + } } - _vbox2->Add(_account); - _vbox2->Add(-1, 10); - _vbox2->Add(_statsGrid); + _statsGrid->SetCellValue(1, 0, _("Non fix")); + _statsGrid->SetCellAlignment(1, 1, wxALIGN_RIGHT, wxALIGN_CENTRE); + + _vbox2->Add(_account, 0, wxGROW|wxALL, 5); + _vbox2->Add(_statsGrid, 0, wxALIGN_CENTER_HORIZONTAL|wxGROW|wxALL, 5); _pie = new PiePlot(); - _dataset = new CategorySimpleDataset(_categories, user->GetCategoriesNumber()); + nbCategories = (user->GetCategoriesNumber() <= MAX_CATEGORY) ? user->GetCategoriesNumber() : MAX_CATEGORY; + + _dataset = new CategorySimpleDataset(_categories, nbCategories); ColorScheme* colorScheme = new ColorScheme(categoryColors, WXSIZEOF(categoryColors)); _categoriesValues = new double[user->GetCategoriesNumber()]; for(i=0; iGetCategoriesNumber(); i++) _categoriesValues[i] = 0.0; - _dataset->AddSerie(_("Serie 1"), _categoriesValues, user->GetCategoriesNumber()); + _dataset->AddSerie(_("Serie 1"), _categoriesValues, nbCategories); _dataset->SetRenderer(new CategoryRenderer(*colorScheme)); _pie->SetDataset(_dataset); _pie->SetColorScheme(colorScheme); _pie->SetLegend(new Legend(wxBOTTOM, wxCENTER)); - wxChartPanel* chart = new wxChartPanel(this); - chart->SetChart(new Chart(_pie, _("Cost repartition"))); - chart->Fit(); - chart->Layout(); - chart->SetMinSize(// chart->GetSize() + _chartCategories = new wxChartPanel(this); + _chartCategories->SetChart(new Chart(_pie, _("Cost repartition"))); + _chartCategories->Fit(); + _chartCategories->Layout(); + _chartCategories->SetMinSize(// chart->GetSize() wxSize(200,250)); - _vbox2->Add(-1, 10); - _vbox2->Add(chart); - - vbox->Add(hbox); - vbox->Add(_hbox2); + vbox->Add(hbox, 0, wxALIGN_CENTER_VERTICAL|wxGROW|wxALL, 5); + vbox->Add(_hbox2, 0, wxGROW|wxALL, 5); wxCommandEvent event ; OnRangeChange(event); Fit(); +} - Hide(); +KissPanel* StatsPanel::CreatePanel() +{ + return new StatsPanel(_kiss, _wxUI); +} + +wxBitmapButton* StatsPanel::GetButton(int id) +{ + if (!_KissButton) + _KissButton = new wxBitmapButton(_wxUI, id, wxBitmap(wxT(STATS_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128)); + + return _KissButton; +} + +wxString StatsPanel::GetToolTip() +{ + return _("Statistics"); } void StatsPanel::UpdateStats(int monthFrom, int yearFrom, int monthTo, int yearTo) @@ -157,17 +180,19 @@ void StatsPanel::UpdateStats(int monthFrom, int yearFrom, int monthTo, int yearT std::vector::iterator accountIt; std::map::iterator categoriesIt; std::map >::iterator accountYearIt; - double total; + double total, non_fix; int account, size, i, a, b, percents, nbDays; double *amounts; wxString value; User* user = _kiss->GetUser(); wxDateTime date; + bool failed; if (_chart) { _hbox2->Detach(_chart); _hbox2->Detach(_vbox2); + _hbox2->Detach(_chartCategories); delete _chart; } @@ -245,18 +270,36 @@ void StatsPanel::UpdateStats(int monthFrom, int yearFrom, int monthTo, int yearT } size = accountAmounts[accountIt->id].size(); - amounts = new double[size*12*2]; + amounts = new double[size*13*2]; size = 0; for(a = 0, accountYearIt = accountAmounts[accountIt->id].begin(); accountYearIt != accountAmounts[accountIt->id].end(); accountYearIt++, a++) { - for(b = 0; b<12; b++) + for(b = 0; b<=12; b++) { - if (!accountAmounts[accountIt->id][accountYearIt->first].count(b)) - continue; amounts[size*2+0] = a*12+b; - amounts[size*2+1] = accountAmounts[accountIt->id][accountYearIt->first][b]; + if (!accountAmounts[accountIt->id][accountYearIt->first].count(b)) + { + /* + If previously afiled, continue to avoid to set + account to 0 (only for display) + */ + if (!b || failed) continue; + /* + Compute cur month value (if there are operations) + as next month value + */ + amounts[size*2+1] = + accountAmounts[accountIt->id][accountYearIt->first][b-1] + + _kiss->CalcAccountAmount(accountIt->id, b-1, accountYearIt->first, NULL); + failed = true; + } + else + { + amounts[size*2+1] = accountAmounts[accountIt->id][accountYearIt->first][b]; + failed = false; + } size++; } } @@ -296,13 +339,13 @@ void StatsPanel::UpdateStats(int monthFrom, int yearFrom, int monthTo, int yearT _chart->SetMinSize(// chart->GetSize() wxSize(750,550)); - _hbox2->Add(_chart); + _hbox2->Add(_chart, 0, wxGROW|wxALL, 5); total = 0.0; for(categoriesIt = categories.begin(); categoriesIt != categories.end(); categoriesIt++) total += categoriesIt->second; - for(categoriesIt = categories.begin(); categoriesIt != categories.end(); categoriesIt++) + for(i=0, categoriesIt = categories.begin(); categoriesIt != categories.end(); categoriesIt++, i++) { _categoriesValues[_categoriesIndexes[categoriesIt->first]] = categoriesIt->second; if (total) @@ -310,15 +353,28 @@ void StatsPanel::UpdateStats(int monthFrom, int yearFrom, int monthTo, int yearT else percents = 0; value = wxString::Format(wxT("%0.2lf (%02d%%)"), categoriesIt->second, percents); - _statsGrid->SetCellValue(_categoriesIndexes[categoriesIt->first], 1, value); + if (i) + _statsGrid->SetCellValue(_categoriesIndexes[categoriesIt->first]+1, 1, value); + else + _statsGrid->SetCellValue(_categoriesIndexes[categoriesIt->first], 1, value); } + non_fix = total - _categoriesValues[0]; + + if (total) + percents = ((double) (non_fix*100))/total; + else + percents = 0; + value = wxString::Format(wxT("%0.2lf (%02d%%)"), non_fix, percents); + _statsGrid->SetCellValue(1, 1, value); + _statsGrid->AutoSizeColumn(0, true); _statsGrid->AutoSizeColumn(1, true); _pie->DatasetChanged(_dataset); - _hbox2->Add(_vbox2); + _hbox2->Add(_vbox2, 0, wxGROW|wxALL, 5); + _hbox2->Add(_chartCategories, 0, wxGROW|wxALL, 10); Layout(); } diff --git a/src/view/StatsPanel.h b/src/view/StatsPanel.h index b4fd2af..1e2596b 100644 --- a/src/view/StatsPanel.h +++ b/src/view/StatsPanel.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -34,24 +34,20 @@ #include #include -#include "wxUI.h" -#include #include "view.h" +#include -class wxUI; -class KissCount; - -class StatsPanel: public wxPanel //public wxScrolledWindow +class StatsPanel: public KissPanel { public: StatsPanel(KissCount* kiss, wxUI *parent); - //~StatsPanel(); + KissPanel* CreatePanel(); + wxBitmapButton* GetButton(int id); + wxString GetToolTip(); void OnShow(wxShowEvent& event); private: - KissCount* _kiss; - wxUI* _wxUI; wxCalendarCtrl* _calendarFrom, *_calendarTo; wxChoice* _monthFrom, *_yearFrom, *_monthTo, *_yearTo; wxGrid *_statsGrid; @@ -62,7 +58,7 @@ private: wxString* _categories; std::map _categoriesIndexes; wxBoxSizer *_hbox2, *_vbox2; - wxChartPanel* _chart; + wxChartPanel* _chart, *_chartCategories; wxCheckListBox* _account; void UpdateStats(int monthFrom, int yearFrom, int monthTo, int yearTo); diff --git a/src/view/SupportedLanguages.cpp b/src/view/SupportedLanguages.cpp index dde3792..44062ca 100644 --- a/src/view/SupportedLanguages.cpp +++ b/src/view/SupportedLanguages.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/SupportedLanguages.h b/src/view/SupportedLanguages.h index 44019c0..2df9936 100644 --- a/src/view/SupportedLanguages.h +++ b/src/view/SupportedLanguages.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -29,7 +29,7 @@ typedef struct { } language ; #define NB_SUPPORTED_LANGUAGES 2 -#define ICONS_PATH "./ressources/icons" +#define ICONS_PATH RESSOURCES_ROOT "icons/" extern language languages[NB_SUPPORTED_LANGUAGES]; diff --git a/src/view/UsersDialog.cpp b/src/view/UsersDialog.cpp index ce60891..5a43486 100644 --- a/src/view/UsersDialog.cpp +++ b/src/view/UsersDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -19,12 +19,13 @@ #include "UsersDialog.h" -enum {BUTTON_OK_ID=1, BUTTON_CANCEL_ID, BUTTON_NEW_USER_ID}; +enum {TEXT_PASSWORD_ID=1, BUTTON_OK_ID, BUTTON_CANCEL_ID, BUTTON_NEW_USER_ID}; BEGIN_EVENT_TABLE(UsersDialog, wxDialog) EVT_BUTTON(BUTTON_OK_ID, UsersDialog::OnOK) EVT_BUTTON(BUTTON_CANCEL_ID, UsersDialog::OnCancel) EVT_BUTTON(BUTTON_NEW_USER_ID, UsersDialog::OnNewUser) +EVT_TEXT_ENTER(TEXT_PASSWORD_ID, UsersDialog::OnEnter) END_EVENT_TABLE() UsersDialog::UsersDialog(KissCount* kiss, wxUI *parent) : wxDialog(&(*parent), -1, _("Users")), _kiss(kiss), _wxUI(parent) @@ -33,20 +34,20 @@ UsersDialog::UsersDialog(KissCount* kiss, wxUI *parent) : wxDialog(&(*parent), - wxStaticText* label; wxCommandEvent event; + wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL); gridBagSizer = new wxGridBagSizer(4, 4); - label = new wxStaticText(this, -1, _("User ")); + label = new wxStaticText(this, wxID_ANY, _("User ")); gridBagSizer->Add(label, wxGBPosition(0, 0)); _users = new wxChoice(this, wxID_ANY); gridBagSizer->Add(_users, wxGBPosition(0, 1)); - label = new wxStaticText(this, -1, _("Password ")); + label = new wxStaticText(this, wxID_ANY, _("Password ")); gridBagSizer->Add(label, wxGBPosition(1, 0)); - _password = new wxTextCtrl(this, wxID_ANY); + _password = new wxTextCtrl(this, TEXT_PASSWORD_ID); + _password->SetWindowStyle(_password->GetWindowStyle() | wxTE_PASSWORD | wxTE_PROCESS_ENTER); gridBagSizer->Add(_password, wxGBPosition(1, 1)); - _password->SetWindowStyle(wxTE_PASSWORD); - wxButton* ok = new wxButton(this, BUTTON_OK_ID, _("OK")); wxButton* cancel = new wxButton(this, BUTTON_CANCEL_ID, _("Cancel")); wxButton* newUser = new wxButton(this, BUTTON_NEW_USER_ID, _("New User")); @@ -63,10 +64,11 @@ UsersDialog::UsersDialog(KissCount* kiss, wxUI *parent) : wxDialog(&(*parent), - _users->Select(0); - SetSizer(gridBagSizer); + hbox->Add(gridBagSizer, 0, wxGROW|wxALL, 10); + SetSizer(hbox); _users->SetFocus(); - Layout(); + Fit(); Center(); if (users_list.size() == 0) @@ -75,6 +77,11 @@ UsersDialog::UsersDialog(KissCount* kiss, wxUI *parent) : wxDialog(&(*parent), - ShowModal(); } +void UsersDialog::OnEnter(wxCommandEvent& event) +{ + OnOK(event); +} + void UsersDialog::OnOK(wxCommandEvent& event) { // No users in database diff --git a/src/view/UsersDialog.h b/src/view/UsersDialog.h index 4799826..d753628 100644 --- a/src/view/UsersDialog.h +++ b/src/view/UsersDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -37,6 +37,7 @@ class UsersDialog : public wxDialog public: UsersDialog(KissCount* kiss, wxUI *parent); + void OnEnter(wxCommandEvent& event); void OnOK(wxCommandEvent& event); void OnCancel(wxCommandEvent& event); void OnNewUser(wxCommandEvent& event); diff --git a/src/view/grid/CalendarEditor.cpp b/src/view/grid/CalendarEditor.cpp index a799e5e..c2b3cca 100644 --- a/src/view/grid/CalendarEditor.cpp +++ b/src/view/grid/CalendarEditor.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/CalendarEditor.h b/src/view/grid/CalendarEditor.h index 2bd33c0..544ef98 100644 --- a/src/view/grid/CalendarEditor.h +++ b/src/view/grid/CalendarEditor.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/GridAccount.cpp b/src/view/grid/GridAccount.cpp index c81a9b1..a07d7d4 100644 --- a/src/view/grid/GridAccount.cpp +++ b/src/view/grid/GridAccount.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -25,28 +25,24 @@ SetCellTextColour(row, i, forecolor); \ } -#define SET_ROW_FONT(row, font) for(int i=0; i_categories.end(); categoryIt++, i++) { - _categories[i] = categoryIt->name ; + _categories[i] = wxGetTranslation(categoryIt->name) ; } Connect(id, wxEVT_GRID_CELL_CHANGE, wxGridEventHandler(GridAccount::OnOperationModified), NULL, this); @@ -120,6 +116,9 @@ wxPen GridAccount::GetRowGridLinePen (int row) { row == _week4) return wxPen(*wxBLACK, 1, wxSOLID); } + else + if (row == 0) + return wxPen(*wxBLACK, 1, wxSOLID); return GetCellBackgroundColour(row, 0); } @@ -156,7 +155,8 @@ void GridAccount::UpdateOperation(Operation& op) for(i=0; i < (int)_operations->size(); i++) if ((*_operations)[i].id == op.id) { - _kiss->UpdateOperation(op); + if (_databaseSynchronization) + _kiss->UpdateOperation(op); (*_operations)[i] = op; break; } @@ -171,7 +171,14 @@ int GridAccount::GetDisplayedRow(const wxString& id) return -1; } -void GridAccount::LoadOperations(std::vector* operations, bool canAddOperation, bool setWeek, int month, int year) +void GridAccount::ClearGrid() +{ + std::vector operations; + + LoadOperations(&operations, 0, 0); +} + +void GridAccount::LoadOperations(std::vector* operations, int month, int year) { std::vector::iterator it; User* user = _kiss->GetUser(); @@ -182,22 +189,23 @@ void GridAccount::LoadOperations(std::vector* operations, bool canAdd _loadOperations = true; _operations = operations; - _canAddOperation = canAddOperation; _curMonth = month; _curYear = year; _displayedOperations.clear(); _displayedOperations.push_back(NULLop); // Header _fixCosts = 0; - it = _operations->begin(); + if (GetNumberRows () > 1) + DeleteRows(1, GetNumberRows ()-1); + if (_canAddOperation) { for (;it != _operations->end() && it->fix_cost; it++) { if (it->parent.Length()) continue; - if (setWeek) + if (_setWeek) InsertOperationWithWeek(user, *it, ++curLine, true, it->month, it->year); else InsertOperation(user, *it, ++curLine, true, it->month, it->year); @@ -209,7 +217,7 @@ void GridAccount::LoadOperations(std::vector* operations, bool canAdd { if (it->parent.Length()) continue; - if (setWeek) + if (_setWeek) InsertOperationWithWeek(user, *it, ++curLine, false, it->month, it->year); else InsertOperation(user, *it, ++curLine, false, it->month, it->year); @@ -318,7 +326,13 @@ void GridAccount::InsertOperation(User* user, Operation& op, int line, bool fix, SetCellEditor(line, CREDIT, new wxGridCellFloatEditor(wxID_ANY, 2)); wxGridCellChoiceEditor* accountEditor = new wxGridCellChoiceEditor(user->GetAccountsNumber(), _accounts, false); SetCellEditor(line, ACCOUNT, accountEditor); - wxGridCellChoiceEditor* categoryEditor = new wxGridCellChoiceEditor(user->GetCategoriesNumber()-1, _categories+1, false); + wxGridCellChoiceEditor* categoryEditor ; + + if (_canAddOperation) + categoryEditor = new wxGridCellChoiceEditor(user->GetCategoriesNumber()-1, _categories+1, false); + else + categoryEditor = new wxGridCellChoiceEditor(user->GetCategoriesNumber(), _categories, false); + SetCellEditor(line, CATEGORY, categoryEditor); if (fix) @@ -349,7 +363,7 @@ void GridAccount::InsertOperation(User* user, Operation& op, int line, bool fix, if (!op.meta) SetCellValue(line, ACCOUNT, user->GetAccountName(op.account)); if (!fix && !op.meta) - SetCellValue(line, CATEGORY, cat.name); + SetCellValue(line, CATEGORY, wxGetTranslation(cat.name)); SetCellRenderer(line, OP_DELETE, new wxGridCellBoolRenderer ()); SetCellEditor(line, OP_DELETE, new wxGridCellBoolEditor ()); SetCellRenderer(line, CHECKED, new wxGridCellBoolRenderer ()); @@ -542,13 +556,13 @@ void GridAccount::InsertIntoGrid(Operation& op) InsertOperationWithWeek(user, (*_operations)[a], i, op.fix_cost, _curMonth, _curYear); } -void GridAccount::RemoveMeta(Operation op, int line, bool removeRoot, bool deleteOp) +int GridAccount::RemoveMeta(Operation op, int line, bool removeRoot, bool deleteOp) { std::vector::iterator it, it2; wxGridCellTreeButtonRenderer* treeRenderer; - int i; + int i, deletedOperations = 0; Operation op2; - + treeRenderer = (wxGridCellTreeButtonRenderer*) GetCellRenderer(line, TREE); for(i=0; i<(int)op.childs.size(); i++) @@ -561,6 +575,7 @@ void GridAccount::RemoveMeta(Operation op, int line, bool removeRoot, bool delet if (treeRenderer->IsCollapsed()) { DeleteRows(line+1, 1); + deletedOperations++; if (op2.fix_cost) _fixCosts--; _displayedOperations.erase(_displayedOperations.begin()+line+1); } @@ -568,7 +583,8 @@ void GridAccount::RemoveMeta(Operation op, int line, bool removeRoot, bool delet if (deleteOp) { DeleteOperation(op2); - _kiss->DeleteOperation(op2); + if (_databaseSynchronization) + _kiss->DeleteOperation(op2); } } } @@ -583,11 +599,15 @@ void GridAccount::RemoveMeta(Operation op, int line, bool removeRoot, bool delet if (deleteOp) { DeleteOperation(op); - _kiss->DeleteOperation(op); + if (_databaseSynchronization) + _kiss->DeleteOperation(op); } + deletedOperations++; } treeRenderer->DecRef(); + + return deletedOperations; } void GridAccount::CheckMeta(Operation& op, int line, bool check) @@ -640,7 +660,7 @@ void GridAccount::OnOperationModified(wxGridEvent& event) int op_complete = 6, i, last_day; wxString value ; wxDateTime date; - bool need_insertion = false, fix_op=false; + bool need_insertion = false; static bool inModification = false ; wxColour color ; unsigned char r, g, b; @@ -652,7 +672,7 @@ void GridAccount::OnOperationModified(wxGridEvent& event) Category cat ; // Avoid recursives calls - if (inModification) return; + if (inModification || _loadOperations) return; inModification = true ; @@ -667,7 +687,7 @@ void GridAccount::OnOperationModified(wxGridEvent& event) for (i=1, it=op.childs.begin(); it!=op.childs.end(); it++, i++) { op2 = GetOperation(*it); - InsertOperationWithWeek(user, op2, row+i, op2.fix_cost, _curMonth, _curYear); + InsertOperationWithWeek(user, op2, row+i, op2.fix_cost, op2.month, op2.year); } } else @@ -679,6 +699,7 @@ void GridAccount::OnOperationModified(wxGridEvent& event) ComputeWeeks(); inModification = false; + _parent->Fit(); return; } @@ -704,6 +725,32 @@ void GridAccount::OnOperationModified(wxGridEvent& event) op_complete--; } + if (col == DESCRIPTION && + (!GetCellValue(row, CATEGORY).Length() || + !GetCellValue(row, ACCOUNT).Length())) + { + new_op.fix_cost = (row <= _fixCosts); + if (_kiss->SearchPreviousOperation(&op_tmp, new_op, _curMonth, _curYear, _canAddOperation)) + { + if (!GetCellValue(row, CATEGORY).Length()) + { + new_op.category = op_tmp.category; + SetCellValue(row, CATEGORY, wxGetTranslation(user->GetCategoryName(new_op.category))); + op_complete--; + } + + if (!GetCellValue(row, ACCOUNT).Length()) + { + new_op.account = op_tmp.account; + SetCellValue(row, ACCOUNT, user->GetAccountName(new_op.account)); + op_complete--; + } + + col = CATEGORY; + new_op.fix_cost = (new_op.category == user->GetCategoryId(wxT("Fix"))); + } + } + value = GetCellValue(row, DEBIT); if (value.Length()) { @@ -735,7 +782,6 @@ void GridAccount::OnOperationModified(wxGridEvent& event) pEditor->DecRef(); } - value = GetCellValue(row, CATEGORY); if (value.Length()) { @@ -757,21 +803,6 @@ void GridAccount::OnOperationModified(wxGridEvent& event) new_op.checked = false; op_complete--; - if (col == DESCRIPTION && - (!GetCellValue(row, CATEGORY).Length() || - !GetCellValue(row, ACCOUNT).Length())) - { - new_op.fix_cost = row <= _fixCosts; - if (_kiss->SearchPreviousOperation(&op_tmp, new_op, _curMonth, _curYear)) - { - new_op.category = op_tmp.category; - new_op.account = op_tmp.account; - SetCellValue(row, CATEGORY, user->GetCategoryName(new_op.category)); - SetCellValue(row, ACCOUNT, user->GetAccountName(new_op.account)); - op_complete -= 2; - } - } - cur_op = (_displayedOperations)[row] ; if (col == CHECKED || col == CATEGORY) @@ -835,7 +866,7 @@ void GridAccount::OnOperationModified(wxGridEvent& event) } // Modify a fix operation - if (row < _fixCosts) + if (row < _fixCosts || !_canAddOperation) { if (col == OP_DELETE) { @@ -859,7 +890,8 @@ void GridAccount::OnOperationModified(wxGridEvent& event) DeleteRows(row, 1); DeleteOperation(cur_op); - _kiss->DeleteOperation(cur_op); + if (_databaseSynchronization) + _kiss->DeleteOperation(cur_op); _displayedOperations.erase(_displayedOperations.begin()+row); if (cur_op.parent.Length() && op_tmp.childs.size() < 2) @@ -875,7 +907,8 @@ void GridAccount::OnOperationModified(wxGridEvent& event) row = GetDisplayedRow(cur_op.parent); DeleteRows(row, 1); DeleteOperation(op_tmp); - _kiss->DeleteOperation(op_tmp); + if (_databaseSynchronization) + _kiss->DeleteOperation(op_tmp); _displayedOperations.erase(_displayedOperations.begin()+row); _fixCosts--; } @@ -893,6 +926,7 @@ void GridAccount::OnOperationModified(wxGridEvent& event) new_op.meta = cur_op.meta; new_op.parent = cur_op.parent; new_op.childs = cur_op.childs; + new_op._virtual = cur_op._virtual; if (cur_op.day != new_op.day) { @@ -907,8 +941,6 @@ void GridAccount::OnOperationModified(wxGridEvent& event) UpdateOperation(new_op); (_displayedOperations)[row] = new_op; } - - fix_op = true; } // Add a fixCost else if (row == _fixCosts) @@ -918,9 +950,9 @@ void GridAccount::OnOperationModified(wxGridEvent& event) return ; } need_insertion = true; - fix_op = true; new_op.fix_cost = true; new_op.meta = false; + new_op._virtual = false; for(i=0; iDeleteOperation(cur_op); + if (_databaseSynchronization) + _kiss->DeleteOperation(cur_op); if (cur_op.parent.Length() && op_tmp.childs.size() <= 1) { @@ -983,7 +1017,8 @@ void GridAccount::OnOperationModified(wxGridEvent& event) row = GetDisplayedRow(cur_op.parent); DeleteRows(row, 1); DeleteOperation(op_tmp); - _kiss->DeleteOperation(op_tmp); + if (_databaseSynchronization) + _kiss->DeleteOperation(op_tmp); _displayedOperations.erase(_displayedOperations.begin()+row); } @@ -1015,9 +1050,9 @@ void GridAccount::OnOperationModified(wxGridEvent& event) return ; } need_insertion = true; - fix_op = false; new_op.fix_cost = false; new_op.meta = false; + new_op._virtual = false; for(i=0; i* rows) if (*it == row) break; - if (it != rows->end()) continue; + if (it != rows->end() || !row) continue; rows->push_back(row); } @@ -1217,7 +1252,7 @@ void GridAccount::GetSelectedOperations(std::vector* rows) if (*it == c.GetRow()) break; - if (it != rows->end()) continue; + if (it != rows->end() || !c.GetRow()) continue; rows->push_back(c.GetRow()); } @@ -1473,3 +1508,57 @@ removeLastGroup: ComputeWeeks(); } + +void GridAccount::MassUpdate(std::vector& rows, updateOperationFunc func, void** params) +{ + int i, b; + std::vector::iterator it; + Operation op, op2; + + _parent->Disable(); + + _parent->SetCursor(wxCursor(wxCURSOR_WAIT)); + + _parent->Update(); + + if (rows.size()) + { + for(i=0; i<(int)rows.size(); i++) + { + op = _displayedOperations[rows[i]]; + + func (&op, params); + + UpdateOperation(op); + + if (op.meta) + { + for(b=0; b<(int)op.childs.size(); b++) + { + op2 = GetOperation(op.childs[b]); + func (&op2, params); + UpdateOperation(op2); + } + } + } + } + else + { + for(it=_operations->begin(); it!=_operations->end(); it++) + { + func (&(*it), params); + if (_databaseSynchronization) + _kiss->UpdateOperation(*it); + } + } + + ClearGrid(); + + LoadOperations(_operations, 0, 0); + + Layout(); + + _parent->Enable(); + + _parent->SetCursor(wxNullCursor); +} diff --git a/src/view/grid/GridAccount.h b/src/view/grid/GridAccount.h index 2a2a89f..c1c6926 100644 --- a/src/view/grid/GridAccount.h +++ b/src/view/grid/GridAccount.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -35,19 +36,29 @@ class KissCount; +enum {TREE, DESCRIPTION, OP_DATE, DEBIT, CREDIT, CATEGORY, ACCOUNT, OP_DELETE, CHECKED, NUMBER_COLS_OPS}; + +typedef void (*updateOperationFunc)(Operation* op, void** params); + class GridAccount : public wxGrid { public: - GridAccount(KissCount* kiss, wxWindow *parent, wxWindowID id); + GridAccount(KissCount* kiss, wxWindow *parent, wxWindowID id, + bool canAddOperation, bool setWeek, bool synchronizeWithDatabase); ~GridAccount(); wxPen GetColGridLinePen (int col); wxPen GetRowGridLinePen (int row); - void LoadOperations(std::vector* operations, bool canAddOperation, bool setWeek, int month, int year); + virtual void ClearGrid(); + void LoadOperations(std::vector* operations, int month, int year); void InsertOperationWithWeek(User* user, Operation& op, int line, bool fix, int month, int year) ; void InsertOperation(User* user, Operation& op, int line, bool fix, int month, int year) ; + void GetSelectedOperations(std::vector* rows); + + void MassUpdate(std::vector& rows, updateOperationFunc func, void** params); + void Group(); void UnGroup(); @@ -59,11 +70,14 @@ public: std::vector _displayedOperations; private: + wxWindow* _parent; KissCount* _kiss; bool _displayLines; + bool _canAddOperation, _setWeek; + bool _databaseSynchronization; wxString* _categories, *_accounts; std::vector* _operations; - bool _canAddOperation, _loadOperations; + bool _loadOperations; int _curMonth, _curYear; void SetWeek(int week, int line); @@ -73,13 +87,12 @@ private: void InsertIntoGrid(Operation& op); void DeleteOperation(const Operation& op); void UpdateMeta(Operation& op); - void RemoveMeta(Operation op, int line, bool removeRoot, bool deleteOp); + int RemoveMeta(Operation op, int line, bool removeRoot, bool deleteOp); void CheckMeta(Operation& op, int line, bool check); Operation& GetOperation(const wxString& id); void UpdateOperation(Operation& op); int GetDisplayedRow(const wxString& id); - void GetSelectedOperations(std::vector* rows); DECLARE_EVENT_TABLE(); }; diff --git a/src/view/grid/wxGridCellBitmapRenderer.cpp b/src/view/grid/wxGridCellBitmapRenderer.cpp index e48319f..84149e0 100644 --- a/src/view/grid/wxGridCellBitmapRenderer.cpp +++ b/src/view/grid/wxGridCellBitmapRenderer.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/wxGridCellBitmapRenderer.h b/src/view/grid/wxGridCellBitmapRenderer.h index d0f97ee..782c9ce 100644 --- a/src/view/grid/wxGridCellBitmapRenderer.h +++ b/src/view/grid/wxGridCellBitmapRenderer.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/wxGridCellButtonEditor.cpp b/src/view/grid/wxGridCellButtonEditor.cpp index b1836a8..4d85bc8 100644 --- a/src/view/grid/wxGridCellButtonEditor.cpp +++ b/src/view/grid/wxGridCellButtonEditor.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/wxGridCellButtonEditor.h b/src/view/grid/wxGridCellButtonEditor.h index d57f841..21c6b6b 100644 --- a/src/view/grid/wxGridCellButtonEditor.h +++ b/src/view/grid/wxGridCellButtonEditor.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/wxGridCellButtonRenderer.cpp b/src/view/grid/wxGridCellButtonRenderer.cpp index 1278e63..98951fe 100644 --- a/src/view/grid/wxGridCellButtonRenderer.cpp +++ b/src/view/grid/wxGridCellButtonRenderer.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/wxGridCellButtonRenderer.h b/src/view/grid/wxGridCellButtonRenderer.h index 32ef204..4a41a23 100644 --- a/src/view/grid/wxGridCellButtonRenderer.h +++ b/src/view/grid/wxGridCellButtonRenderer.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/wxGridCellFormulaEditor.cpp b/src/view/grid/wxGridCellFormulaEditor.cpp index a5d08ae..619023e 100644 --- a/src/view/grid/wxGridCellFormulaEditor.cpp +++ b/src/view/grid/wxGridCellFormulaEditor.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/wxGridCellFormulaEditor.h b/src/view/grid/wxGridCellFormulaEditor.h index 636a677..50ee5aa 100644 --- a/src/view/grid/wxGridCellFormulaEditor.h +++ b/src/view/grid/wxGridCellFormulaEditor.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/wxGridCellTabStringRenderer.cpp b/src/view/grid/wxGridCellTabStringRenderer.cpp index ee9eedd..3c4d514 100644 --- a/src/view/grid/wxGridCellTabStringRenderer.cpp +++ b/src/view/grid/wxGridCellTabStringRenderer.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/wxGridCellTabStringRenderer.h b/src/view/grid/wxGridCellTabStringRenderer.h index d57ecab..53d7715 100644 --- a/src/view/grid/wxGridCellTabStringRenderer.h +++ b/src/view/grid/wxGridCellTabStringRenderer.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/wxGridCellTreeButtonEditor.cpp b/src/view/grid/wxGridCellTreeButtonEditor.cpp index 4fb6215..fb13875 100644 --- a/src/view/grid/wxGridCellTreeButtonEditor.cpp +++ b/src/view/grid/wxGridCellTreeButtonEditor.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/wxGridCellTreeButtonEditor.h b/src/view/grid/wxGridCellTreeButtonEditor.h index 992008e..3109f18 100644 --- a/src/view/grid/wxGridCellTreeButtonEditor.h +++ b/src/view/grid/wxGridCellTreeButtonEditor.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/wxGridCellTreeButtonRenderer.cpp b/src/view/grid/wxGridCellTreeButtonRenderer.cpp index 846cc5f..828b837 100644 --- a/src/view/grid/wxGridCellTreeButtonRenderer.cpp +++ b/src/view/grid/wxGridCellTreeButtonRenderer.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/grid/wxGridCellTreeButtonRenderer.h b/src/view/grid/wxGridCellTreeButtonRenderer.h index 13f29e8..33c4f6a 100644 --- a/src/view/grid/wxGridCellTreeButtonRenderer.h +++ b/src/view/grid/wxGridCellTreeButtonRenderer.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/view.h b/src/view/view.h index b6ae0ae..5360adb 100644 --- a/src/view/view.h +++ b/src/view/view.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -28,14 +28,20 @@ #define DEFAULT_FONT_SIZE 12 #define DEFAULT_FONT(font_name) wxFont font_name(DEFAULT_FONT_SIZE, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, DEFAULT_FONT_NAME); -#define DELETE_ICON "./ressources/icons/process-stop.png" -#define CHECKED_ICON "./ressources/icons/tick-icon.png" -#define ACCOUNT_ICON "./ressources/icons/administrator-icon.png" -#define STATS_ICON "./ressources/icons/chart-icon.png" -#define SEARCH_ICON "./ressources/icons/Search-icon.png" -#define PREFS_ICON "./ressources/icons/options-icon.png" -#define CHANGE_USER_ICON "./ressources/icons/Clients-icon.png" -#define ABOUT_ICON "./ressources/icons/windows-users-icon.png" -#define QUIT_ICON "./ressources/icons/system-log-out.png" +#define DELETE_ICON RESSOURCES_ROOT "icons/process-stop.png" +#define CHECKED_ICON RESSOURCES_ROOT "icons/tick-icon.png" +#define ACCOUNT_ICON RESSOURCES_ROOT "icons/administrator-icon.png" +#define STATS_ICON RESSOURCES_ROOT "icons/chart-icon.png" +#define SEARCH_ICON RESSOURCES_ROOT "icons/Search-icon.png" +#define PREFS_ICON RESSOURCES_ROOT "icons/options-icon.png" +#define IMPORT_ICON RESSOURCES_ROOT "icons/import-icon.png" +#define EXPORT_ICON RESSOURCES_ROOT "icons/export-icon.png" +#define CHANGE_USER_ICON RESSOURCES_ROOT "icons/Clients-icon.png" +#define ABOUT_ICON RESSOURCES_ROOT "icons/windows-users-icon.png" +#define QUIT_ICON RESSOURCES_ROOT "icons/system-log-out.png" + +#define LANG_ROOT RESSOURCES_ROOT "po/" + +#include "KissPanel.h" #endif diff --git a/src/view/wxGridCellStarEditor.cpp b/src/view/wxGridCellStarEditor.cpp index 1db025e..0642f0f 100644 --- a/src/view/wxGridCellStarEditor.cpp +++ b/src/view/wxGridCellStarEditor.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/wxGridCellStarEditor.h b/src/view/wxGridCellStarEditor.h index 1937348..9709c4e 100644 --- a/src/view/wxGridCellStarEditor.h +++ b/src/view/wxGridCellStarEditor.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. diff --git a/src/view/wxUI.cpp b/src/view/wxUI.cpp index 8410771..99798d2 100644 --- a/src/view/wxUI.cpp +++ b/src/view/wxUI.cpp @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -19,40 +19,66 @@ #include "wxUI.h" +enum {BUTTON_CHANGE_USER_ID=-4, BUTTON_ABOUT_ID=-5, BUTTON_QUIT_ID=-6}; + +BEGIN_EVENT_TABLE(wxUI, wxFrame) +EVT_BUTTON(BUTTON_CHANGE_USER_ID, wxUI::OnButtonChangeUser) +EVT_BUTTON(BUTTON_ABOUT_ID, wxUI::OnButtonAbout) +EVT_BUTTON(BUTTON_QUIT_ID, wxUI::OnButtonQuit) +END_EVENT_TABLE() + wxString months[12] ; -wxColour categoryColors[12] = {wxColour(0x00, 0x45, 0x86), - wxColour(0xFF, 0x3E, 0x0E), - wxColour(0xFF, 0xD3, 0x20), - wxColour(0x58, 0x9D, 0x1B), - wxColour(0x7E, 0x00, 0x21), - wxColour(0x83, 0xCC, 0xFF), - wxColour(0x31, 0x40, 0x04), - wxColour(0xB0, 0xCF, 0x00), - wxColour(0x4B, 0x1F, 0x6F), - wxColour(0xFF, 0x93, 0x0E), - wxColour(0xC5, 0x00, 0x0D), - wxColour(0x00, 0x84, 0xD1)}; +wxColour categoryColors[MAX_CATEGORY] = {wxColour(0x00, 0x45, 0x86), + wxColour(0xFF, 0x3E, 0x0E), + wxColour(0xFF, 0xD3, 0x20), + wxColour(0x58, 0x9D, 0x1B), + wxColour(0x7E, 0x00, 0x21), + wxColour(0x83, 0xCC, 0xFF), + wxColour(0x31, 0x40, 0x04), + wxColour(0xB0, 0xCF, 0x00), + wxColour(0x4B, 0x1F, 0x6F), + wxColour(0xFF, 0x93, 0x0E), + wxColour(0xC5, 0x00, 0x0D), + wxColour(0x00, 0x84, 0xD1)}; wxUI::wxUI(KissCount* kiss, const wxString& title, const wxPoint& pos, const wxSize& size) - : wxFrame(NULL, wxID_ANY, title, pos, size), _kiss(kiss), _buttonPanel(NULL), _accountPanel(NULL), _statsPanel(NULL), - _searchPanel(NULL), _preferencesPanel(NULL), _curPanel(NULL), _locale(NULL), _needReload(false) + : wxFrame(NULL, -1, title, pos, size), _kiss(kiss), + _curPanel(NULL), _locale(NULL), _needReload(false) { - wxInitAllImageHandlers(); + wxInitAllImageHandlers(); - _hbox = new wxBoxSizer(wxVERTICAL); - _buttonPanel = new ButtonPanel(_kiss, this); + _vbox = new wxBoxSizer(wxVERTICAL); + _buttonsBox = new wxBoxSizer(wxHORIZONTAL); + // ButtonPanel* buttons = new ButtonPanel(_kiss, this); + // wxMenu *menuFile = new wxMenu; - SetSizer(_hbox); + // menuFile->Append( ID_About, wxT("&About...") ); + // menuFile->AppendSeparator(); + // menuFile->Append( ID_Quit, wxT("E&xit") ); - _hbox->Add(_buttonPanel); + // wxMenuBar *menuBar = new wxMenuBar; + // menuBar->Append( menuFile, wxT("&File") ); + + // SetMenuBar( menuBar ); + + // CreateStatusBar(); + // SetStatusText( wxT("Welcome to wxWidgets!") ); + _buttonsBox->Add(new wxBitmapButton(this, BUTTON_CHANGE_USER_ID, wxBitmap(wxT(CHANGE_USER_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128))); + _buttonsBox->Add(new wxBitmapButton(this, BUTTON_ABOUT_ID, wxBitmap(wxT(ABOUT_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128))); + _buttonsBox->Add(new wxBitmapButton(this, BUTTON_QUIT_ID, wxBitmap(wxT(QUIT_ICON), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxSize(128, 128))); + + SetSizer(_vbox); + + _vbox->Add(_buttonsBox, 0, wxGROW); } wxUI::~wxUI() { - if (_accountPanel) delete _accountPanel; - if (_preferencesPanel) delete _preferencesPanel; - if (_searchPanel) delete _searchPanel; - if (_statsPanel) delete _statsPanel; + int i; + + for (i=0; i<(int)_panels.size(); i++) + Disconnect(i, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxUI::OnButtonClicked), _panels[i], this); + if (_locale) delete _locale; } @@ -66,31 +92,31 @@ bool wxUI::SetLanguage(long language) // load language if possible, fall back to english otherwise if(wxLocale::IsAvailable(language)) { - _locale = new wxLocale( language, wxLOCALE_CONV_ENCODING ); + _locale = new wxLocale( language, wxLOCALE_CONV_ENCODING ); #ifdef __WXGTK__ - _locale->AddCatalogLookupPathPrefix(wxT("./ressources/po")); + _locale->AddCatalogLookupPathPrefix(wxT(LANG_ROOT)); #endif - _locale->AddCatalog(wxT("french")); - _locale->AddCatalog(wxT("kisscount")); + _locale->AddCatalog(wxT("french")); + _locale->AddCatalog(wxT("kisscount")); - _language = (wxLanguage) language; + _language = (wxLanguage) language; } if (_locale == NULL || !_locale->IsOk()) { - if (_locale) delete _locale; - _locale = new wxLocale(); + if (_locale) delete _locale; + _locale = new wxLocale(); #ifdef __WXGTK__ - _locale->AddCatalogLookupPathPrefix(wxT("./ressources/po")); + _locale->AddCatalogLookupPathPrefix(wxT(LANG_ROOT)); #endif - _locale->AddCatalog(wxT("kisscount")); + _locale->AddCatalog(wxT("kisscount")); - _language = wxLANGUAGE_ENGLISH; - res = false; + _language = wxLANGUAGE_ENGLISH; + res = false; } months[0] = _("january"); @@ -106,30 +132,111 @@ bool wxUI::SetLanguage(long language) months[10] = _("november"); months[11] = _("december") ; - if (_buttonPanel) - _buttonPanel->SetToolTips(); - return res; } -void wxUI::ShowAccount() +#define ADD_PANEL(panelName, id) \ + panel = new panelName(_kiss, this); \ + button = panel->GetButton(id); \ + button->SetToolTip(panel->GetToolTip()); \ + _buttonsBox->Insert(id, button); \ + _buttons.insert(_buttons.begin()+id, button); \ + _panels.push_back(panel); \ + Connect(id, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxUI::OnButtonClicked), panel, this); + +void wxUI::InitPanels() { - ShowPanel(_accountPanel); + KissPanel* panel; + wxBitmapButton* button; + _panels.clear(); + + ADD_PANEL(AccountPanel, 0); + ADD_PANEL(StatsPanel, 1); + ADD_PANEL(SearchPanel, 2); + ADD_PANEL(PreferencesPanel, 3); + ADD_PANEL(ImportPanel, 4); + ADD_PANEL(ExportPanel, 5); } -void wxUI::ShowSearch() +void wxUI::LoadPanels() { - ShowPanel(_searchPanel); + std::vector::iterator it; + KissPanel* temp; + int i; + + if (_curPanel) + { + _vbox->Detach(_curPanel); + _curPanel = NULL; + } + + if (_panels.size()) + { + for (i=0; i<(int)_panels.size(); i++) + { + temp = _panels[i]->CreatePanel(); + Disconnect(i, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxUI::OnButtonClicked), _panels[i], this); + _panels[i] = temp; + Connect(i, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(wxUI::OnButtonClicked), temp, this); + _buttons[i]->SetToolTip(temp->GetToolTip()); + } + } + else + InitPanels(); } -void wxUI::ShowStats() +void wxUI::LoadUser() { - ShowPanel(_statsPanel); + User* user = _kiss->GetUser(); + LoadPanels(); + + if (user->_preferences[wxT("language")] != wxT("")) + SetLanguage(user->GetLanguage()); + + if (_panels.size()) + ShowPanel(_panels[0]); } -void wxUI::ShowPreferences() +void wxUI::ShowPanel(KissPanel* panel) { - ShowPanel(_preferencesPanel); + wxShowEvent event; + int i; + User* user = _kiss->GetUser(); + + if (!panel) return; + + if (_curPanel) + { + _vbox->Detach(_curPanel); + _curPanel->Hide(); + } + + if (_needReload) + { + user->InvalidateOperations(); + for(i=0; i<(int)_panels.size(); i++) + if (_panels[i] == panel) break; + LoadPanels(); + _needReload = false; + _curPanel = _panels[i]; + } + else + _curPanel = panel; + + _curPanel->OnShow(event); + _vbox->Add(_curPanel); + _curPanel->Show(); + Layout(); +} + +void wxUI::OnButtonClicked(wxCommandEvent& event) +{ + ShowPanel(_panels[event.GetId()]); +} + +void wxUI::OnButtonChangeUser(wxCommandEvent& event) +{ + ChangeUser(); } void wxUI::ChangeUser() @@ -137,151 +244,47 @@ void wxUI::ChangeUser() UsersDialog u(_kiss, this); } -void wxUI::LoadUser() +void wxUI::OnButtonAbout(wxCommandEvent& event) { - User* user = _kiss->GetUser(); - wxShowEvent event; - - if (_curPanel) - { - _hbox->Detach(_curPanel); - _curPanel = NULL; - } - - if (_accountPanel) - delete _accountPanel; - - if (_preferencesPanel) - delete _preferencesPanel; - - if (_searchPanel) - delete _searchPanel; - - if (_statsPanel) - delete _statsPanel; - - if (user->_preferences[wxT("language")].Length()) - SetLanguage(user->GetLanguage()); - - _accountPanel = new AccountPanel(_kiss, this); - _statsPanel = new StatsPanel(_kiss, this); - _searchPanel = new SearchPanel(_kiss, this); - _preferencesPanel = new PreferencesPanel(_kiss, this); - - ShowPanel(_accountPanel); - _accountPanel->OnShow(event); + wxMessageBox( _("Personal accounting software\n\nhttp://indefero.soutade.fr/p/kisscount/\n\nLicenced under GNU GPL v3\n\nCopyright (C) 2010-2011 Grégory Soutadé"), + wxT("KissCount " APP_VERSION "\n\n"), + wxOK | wxICON_INFORMATION, this ); } -void wxUI::ShowPanel(wxPanel* panel) +void wxUI::OnButtonQuit(wxCommandEvent& event) { - int month, year=-1, account=0, preferences=0, search=0, stats=0; - wxShowEvent event; - - if (!panel || (panel == _curPanel && !_needReload)) return; - - if (_curPanel) + wxMessageDialog dialog(this, _("Quit KissCount ?"), wxT("KissCount"), wxYES_NO); + if (dialog.ShowModal() == wxID_NO) { - _hbox->Detach(_curPanel); - _curPanel->Hide(); + return; } - if (_needReload) - { - if (panel == _accountPanel) - { - account = 1; - month = _accountPanel->_curMonth; - year = _accountPanel->_curYear; - } - - if (panel == _preferencesPanel) - { - preferences = 1; - } - - if (panel == _searchPanel) - { - search = 1; - } - - if (panel == _statsPanel) - { - stats = 1; - } - - delete _accountPanel; - delete _preferencesPanel; - delete _searchPanel; - delete _statsPanel; - - _accountPanel = new AccountPanel(_kiss, this); - if (year != -1) - { - _kiss->LoadYear(year, true); - _accountPanel->ShowMonth(month, year); - } - _preferencesPanel = new PreferencesPanel(_kiss, this); - _searchPanel = new SearchPanel(_kiss, this); - _statsPanel = new StatsPanel(_kiss, this); - - if (account) - { - _accountPanel->OnShow(event); - panel = _accountPanel; - } - else if (preferences) - { - _preferencesPanel->OnShow(event); - panel = _preferencesPanel; - } - else if (search) - { - _searchPanel->OnShow(event); - panel = _searchPanel; - } - else if (stats) - { - _statsPanel->OnShow(event); - panel = _statsPanel; - } - - _hbox->Detach(_accountPanel); - _accountPanel->Hide(); - - _needReload = false; - } - - _curPanel = panel; - _hbox->Add(panel); - _curPanel->Show(); - Layout(); + Close(true); } void wxUI::GenerateMonth(int month, int year) { - _accountPanel->GenerateMonth(month, year); + ((AccountPanel*)_panels[0])->GenerateMonth(month, year); } void wxUI::KillMe() { + std::vector::iterator it; + if (_curPanel) { - _hbox->Detach(_curPanel); - _curPanel = NULL; + _vbox->Detach(_curPanel); + _curPanel = NULL; } - if (_accountPanel) - delete _accountPanel; + for (it=_panels.begin(); it!= _panels.end(); it++) + { + if (*it) delete *it; + _buttonsBox->Remove(0); + _buttons.erase(_buttons.begin()); + } - if (_preferencesPanel) - delete _preferencesPanel; - - if (_searchPanel) - delete _searchPanel; - - _accountPanel = NULL; - _preferencesPanel = NULL; - _searchPanel = NULL; + _panels.clear(); } void wxUI::NeedReload() diff --git a/src/view/wxUI.h b/src/view/wxUI.h index fab5882..921ee6b 100644 --- a/src/view/wxUI.h +++ b/src/view/wxUI.h @@ -1,5 +1,5 @@ /* - Copyright 2010 Grégory Soutadé + Copyright 2010-2011 Grégory Soutadé This file is part of KissCount. @@ -20,14 +20,17 @@ #ifndef WXUI_H #define WXUI_H +class ImportEngine; + #include #include "AccountPanel.h" -#include "ButtonPanel.h" #include "PreferencesPanel.h" #include "UsersDialog.h" #include "GenerateDialog.h" #include "SearchPanel.h" #include "StatsPanel.h" +#include "ImportPanel.h" +#include "ExportPanel.h" #include #include "grid/wxMyGrid.h" #include "grid/wxGridCellFastBoolEditor.h" @@ -40,14 +43,14 @@ #endif class KissCount; -class ButtonPanel; class AccountPanel; class PreferencesPanel; -class StatsPanel; extern wxString months[12]; extern wxColour categoryColors[12]; +#define MAX_CATEGORY 12 + class wxUI: public wxFrame { public: @@ -61,29 +64,35 @@ public: void LoadUser(); void ShowAccount(); - void ShowStats(); void ShowSearch(); void ShowPreferences(); void GenerateMonth(int month, int year); void KillMe(); - void ShowPanel(wxPanel* panel); + void ShowPanel(KissPanel* panel); void NeedReload(); wxLanguage _language; + void OnButtonClicked(wxCommandEvent& event); + void OnButtonChangeUser(wxCommandEvent& event); + void OnButtonAbout(wxCommandEvent& event); + void OnButtonQuit(wxCommandEvent& event); + private: KissCount *_kiss; - wxBoxSizer *_hbox; - ButtonPanel *_buttonPanel; - AccountPanel *_accountPanel; - StatsPanel *_statsPanel; - SearchPanel *_searchPanel; - PreferencesPanel *_preferencesPanel; - wxPanel *_curPanel; + wxBoxSizer *_vbox, *_buttonsBox; + KissPanel *_curPanel; + std::vector _panels; + std::vector _buttons; wxLocale *_locale; bool _needReload; + + void InitPanels(); + void LoadPanels(); + + DECLARE_EVENT_TABLE(); }; #endif diff --git a/tools/launch_kc.sh b/tools/launch_kc.sh deleted file mode 100755 index 854f4aa..0000000 --- a/tools/launch_kc.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -export LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH -./kc $* diff --git a/tools/package.sh b/tools/package.sh index 3649c25..cfc153d 100755 --- a/tools/package.sh +++ b/tools/package.sh @@ -1,11 +1,17 @@ #!/bin/bash DATE=`date +%d.%m.%Y` -[ -z "$ARCH" ] && ARCH=`${PREFIX}gcc -dumpmachine | cut -d- -f1` +[ -z "$ARCH" ] && ARCH=`${HOST}gcc -dumpmachine | cut -d- -f1` VERSION=`cat src/controller/KissCount.h | grep APP_VERSION | cut -d\" -f2` DIR="KissCount_build_${VERSION}_${DATE}_${ARCH}" FILE="$DIR.tar.bz2" +# Debian packaging +DEB_DIR="kisscount-${VERSION}" +DEBEMAIL=soutade@gmail.com +DEBFULLNAME="Grégory Soutadé" +export DEBEMAIL DEBFULLNAME + rm -f "$FILE" rm -rf "$DIR" if [ "$1" == "clean" ] ; then @@ -14,8 +20,33 @@ fi make || (echo "Compilation failed" ; exit 1) mkdir -p "$DIR"/lib cp -r lib/freechart/lib/*.so* lib/wxsqlite3-1.9.9/lib/*.so* "$DIR"/lib -cp -r kc init.sql ressources tools/launch_kc.sh TODO CONTRIBUTORS COPYING README README.fr www "$DIR" -find "$DIR" -type f -executable -exec ${PREFIX}strip \{\} \; +cp -r kc ressources TODO CONTRIBUTORS COPYING README* www "$DIR" +find "$DIR" -type f -executable -exec ${HOST}strip \{\} \; tar -jcf "$FILE" "$DIR" rm -rf "$DIR" -echo "Packaged into $FILE !" \ No newline at end of file + +# Debian packaging +if [ -d "debian" ] ; then +DEB_FILE="kisscount_${VERSION}-1_${ARCH}.deb" +rm -rf "$DEB_DIR" "$DEB_FILE" +mkdir -p "$DEB_DIR/lib" +cp -r lib/freechart/lib/*.so* lib/wxsqlite3-1.9.9/lib/*.so* "$DEB_DIR/lib" +cp -r kc.1 kc debian README* ChangeLog TODO CONTRIBUTORS ressources "$DEB_DIR" +${HOST}strip "$DEB_DIR/lib/*" kc +#tar -zcf "$DEB_FILE" "$DEB_DIR" +cd "$DEB_DIR" +mv debian/Makefile . +ln -s ../../src +#sed -i s/i686/$ARCH/g debian/control +#dh_make -f "../$DEB_FILE" --copyright gpl3 --email $DEBEMAIL -s +[ "${ARCH}" == "x86_64" ] && ARCH="amd64" +[ "${ARCH}" == "i686" ] && ARCH="i386" +debuild -us -uc -b -i -a${ARCH} +cd - +fi + +if [ -z "$DEB_FILE" ] ; then + echo "Packaged into $FILE !" +else + echo "Packaged into $FILE and $DEB_FILE !" +fi diff --git a/www/User.php b/www/User.php old mode 100644 new mode 100755 index 87eea4f..888ec77 --- a/www/User.php +++ b/www/User.php @@ -1,6 +1,6 @@ id = $row["id"]; - $result = $db->query("SELECT * FROM account WHERE user='$user->id' ORDER BY default_account DESC, name ASC"); + $result = $db->query("SELECT * FROM account WHERE user='$user->id' ORDER BY default_account DESC, virtual, blocked, name ASC"); $user->accounts = array(); @@ -96,7 +96,7 @@ function LoadUser($name) array_push($user->accounts, $row); // Shared accounts - $result = $db->query("SELECT * FROM account WHERE id IN (SELECT account FROM shared_account WHERE user='$user->id') ORDER BY name ASC"); + $result = $db->query("SELECT * FROM account WHERE id IN (SELECT account FROM shared_account WHERE user='$user->id') ORDER BY name, blocked, virtual ASC"); while ($row = $result->fetchArray()) array_push($user->accounts, $row); @@ -152,9 +152,19 @@ function GetAccountAmount($id, $month, $year) return 0; } +function array_insert($array,$pos,$val) +{ + $array2 = array_splice($array,$pos); + $array[] = $val; + $array = array_merge($array,$array2); + + return $array; +} + function LoadMonth($user, $month, $year) { global $db; + $res = array(); if (!isset($user->accounts[0])) return; @@ -170,7 +180,37 @@ function LoadMonth($user, $month, $year) $req .= " ORDER BY fix_cost DESC, year, month ASC, day "; $req .= $user->preferences["operation_order"]; - return $db->query($req); + $result = $db->query($req); + + // Pack operations and their sub operations + while ($row = $result->fetchArray()) + { + $inserted = 0; + foreach($res as $i => $value) + { + if ($value["parent"] == $row["id"]) + { + $res = array_insert($res, $i, $row); + $inserted = 1; + break; + } + + if ($row["parent"] == $value["id"]) + { + $res = array_insert($res, $i+1, $row); + $inserted = 1; + break; + } + } + + // Append + if ($inserted == 0) + { + $res = array_insert($res, $i+1, $row); + } + } + + return $res; } function MetaPositiveAmount($id) @@ -250,4 +290,21 @@ function GetAllOperations($user, &$last_year, &$last_month) return $res; } +function GetSubOperations($parent) +{ + $res = "["; + global $db; + + $req = "SELECT id FROM operation WHERE parent=\"" . $parent . "\""; + + $result = $db->query($req); + + while ($row = $result->fetchArray()) + $res .= $row["id"] . ", "; + + if (strlen($res) > 1) + $res = substr($res, 0, strlen($res)-2); + + return $res . "]"; +} ?> \ No newline at end of file diff --git a/www/index.php b/www/index.php old mode 100644 new mode 100755 index aad419e..3fbf024 --- a/www/index.php +++ b/www/index.php @@ -1,6 +1,6 @@ \n"; echo "\n"; echo "

\n"; - echo "
KissCount © 2010 Grégory Soutadé
\n"; + echo "
KissCount © 2010-2011 Grégory Soutadé
\n"; die(); } else @@ -86,16 +86,13 @@ else $_SESSION["cur_month"] = $_POST["month"]; } -if (isset($_POST["expand"])) - $_SESSION["expand"] = "1"; -else - $_SESSION["expand"] = "0"; - $operations = LoadMonth($_SESSION["user"], $_SESSION["cur_month"], $_SESSION["cur_year"]); $cur_date = mktime(0, 0, 0, date("m") , date("d"), date("Y")); $total_incomes = $total_outcomes = $cur_incomes = $cur_outcomes = 0; +$categories = array(); -while($operation = $operations->fetchArray()) +// Statistics +foreach($operations as $i => $operation) { if ($operation["meta"] == "1") continue; @@ -151,6 +148,29 @@ function changeMonths() ?> } } +function toggleOperations(parent, operations) +{ + var obj = document.getElementById(operations[0]); + var obj2 = document.getElementById(parent); + var visibility; + + if (obj.style.display == "none") + { + obj2.value = "-"; + visibility = "table-row"; + } + else + { + obj2.value = "+"; + visibility = "none"; + } + + for (var i=0; i
- >Expand groups
Disconnect @@ -208,23 +227,13 @@ function changeMonths()

- + fetchArray()) -{ - if ($_SESSION["expand"] == "1") - { - if ($operation["meta"] == "1") - continue; - } - else - { - if ($operation["parent"] != "") - continue; - } +foreach($operations as $i => $operation) +{ $category = $_SESSION["user"]->GetCategory($operation["category"]); if ($operation["fix_cost"] == "0") { @@ -237,7 +246,15 @@ while($operation = $operations->fetchArray()) else $tr_class = ""; } - echo ""; + if ($operation["meta"] == "1") + echo ""; + else + { + if ($operation["parent"] == "") + echo ""; + else + echo ""; + } echo ""; if ($operation["meta"] == "1" && $operation["amount"] == 0) { @@ -252,7 +269,7 @@ while($operation = $operations->fetchArray()) else echo ""; } - if ($operation["meta"] != "1") + if ($operation["meta"] != "1" && $category["id"] > 1) echo ""; else echo "\n"; + } + + if ($total_outcomes == 0) + $percent = 0; + else + { + $percent = ($categories[$category["id"]] * 100) / $total_outcomes; + $percent = round($percent, 0); + $percent = ($percent < 10) ? "0$percent" : "$percent"; + } + echo "\n"; } ?> @@ -292,6 +331,6 @@ while($operation = $operations->fetchArray())

-
KissCount © 2010 Grégory Soutadé
+
KissCount © 2010-2011 Grégory Soutadé
\ No newline at end of file diff --git a/www/kisscount.css b/www/kisscount.css old mode 100644 new mode 100755 diff --git a/www/kisscount.php b/www/kisscount.php old mode 100644 new mode 100755 index 5e15ca7..bd0f32d --- a/www/kisscount.php +++ b/www/kisscount.php @@ -1,6 +1,6 @@
DescriptionDateDebitCreditCategoryAccount
DescriptionDateDebitCreditCategoryAccount
" . $operation["description"] . "
" . $operation["description"] . "
" . $operation["description"] . "
Non fix" . number_format($value, 2) . " ($percent %)
" . $category["name"]. "" . number_format($categories[$category["id"]], 2) . " ($percent %)