From a590409f8f05fc124957186ad369abb2dc8ccfd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Sun, 19 Feb 2012 17:14:36 +0100 Subject: [PATCH] Work on import engines : remove libxml dependency in favor of QtXml --- Makefile | 6 +- src/model/import/GrisbiImportEngine.cpp | 144 ++++------ src/model/import/GrisbiImportEngine.hpp | 16 +- src/model/import/XMLImportEngine.cpp | 338 +++++++++--------------- src/model/import/XMLImportEngine.hpp | 18 +- 5 files changed, 208 insertions(+), 314 deletions(-) diff --git a/Makefile b/Makefile index da3541f..5aff438 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,9 @@ SHARE_DIR=$(DESTDIR)$(ROOT_DIR)"/share/kisscount/" DOC_DIR=$(DESTDIR)$(ROOT_DIR)"/share/doc/kisscount/" BIN_DIR=$(DESTDIR)$(ROOT_DIR)"/bin/" -QT_PACKAGES="QtCore QtGui QtSql" -CXXFLAGS=`pkg-config --cflags $(QT_PACKAGES)` -Wall -Isrc -ggdb -fPIC +QT_PACKAGES="QtCore QtGui QtSql QtXml" +CXXFLAGS=`pkg-config --cflags $(QT_PACKAGES)` +CXXFLAGS+=-Wall -Isrc -ggdb -fPIC CXXFLAGS+=-I/usr/include/libxml2 CXXFLAGS+=-DRESSOURCES_ROOT="\"$(SHARE_DIR)\"" # For developpers @@ -13,7 +14,6 @@ CXXFLAGS+=-DRESSOURCES_ROOT="\"$(SHARE_DIR)\"" LDFLAGS=`pkg-config --libs $(QT_PACKAGES)` LDFLAGS+=-lofx -LDFLAGS+=-lxml2 CXX=$(HOST)g++ diff --git a/src/model/import/GrisbiImportEngine.cpp b/src/model/import/GrisbiImportEngine.cpp index 8287760..e903f0c 100644 --- a/src/model/import/GrisbiImportEngine.cpp +++ b/src/model/import/GrisbiImportEngine.cpp @@ -24,42 +24,32 @@ static GrisbiImportEngine grisbiImportEngine; -void GrisbiImportEngine::LoadAccount(GrisbiImportEngine* _this, const char** attrs) +void GrisbiImportEngine::LoadAccount(const QXmlAttributes& attrs) { - int i, id; + int id, i; QString account_number, name, key; Account ac; - for (i=0; attrs[i]; i+=2) - { - if (!strcmp(attrs[i], "Name")) - name = attrs[i+1]; - - else if (!strcmp(attrs[i], "Number")) - id = QString(attrs[i+1]).toInt(); - - else if (!strcmp(attrs[i], "Bank_account_number")) - account_number = attrs[i+1]; - - else if (!strcmp(attrs[i], "Key")) - key = attrs[i+1]; - } + name = attrs.value("name"); + id = attrs.value("Number").toInt(); + account_number = attrs.value("Bank_account_number"); + key = attrs.value("Key"); account_number += key; UNESCAPE_CHARS(name); UNESCAPE_CHARS(account_number); - for (i=0; i<(int)_this->_user->_accounts.size(); i++) + for (i=0; i<(int)_user->_accounts.size(); i++) { - if (_this->_user->_accounts[i].number == account_number) + if (_user->_accounts[i].number == account_number) { - _this->_accounts[id] = _this->_user->_accounts[i].id; + _accounts[id] = _user->_accounts[i].id; return; } } - _this->_accounts[id] = 0; + _accounts[id] = 0; ac.number = account_number; ac.name = name; ac.shared = false; @@ -68,48 +58,41 @@ void GrisbiImportEngine::LoadAccount(GrisbiImportEngine* _this, const char** att ac.is_owner = true; ac._virtual = false; ac.hidden = false; - _this->_unresolvedAccounts.push_back(ac); + _unresolvedAccounts.push_back(ac); } -void GrisbiImportEngine::LoadCategory(GrisbiImportEngine* _this, const char** attrs) +void GrisbiImportEngine::LoadCategory(const QXmlAttributes& attrs) { QString name; - int i, id; + int id, i; Category cat; - for (i=0; attrs[i]; i+=2) - { - if (!strcmp(attrs[i], "Na")) - name = attrs[i+1]; - - else if (!strcmp(attrs[i], "Nb")) - id = QString(attrs[i+1]).toInt(); - } + name = attrs.value("Na"); + id = attrs.value("Nb").toInt(); UNESCAPE_CHARS(name); - for (i=0; i<(int)_this->_user->_categories.size(); i++) + for (i=0; i<(int)_user->_categories.size(); i++) { - if (_this->_user->_categories[i].name == name) + if (_user->_categories[i].name == name) { - _this->_categories[id] = _this->_user->_categories[i].id; + _categories[id] = _user->_categories[i].id; return; } } - _this->_categories[id] = 0; + _categories[id] = 0; cat.id = id; cat.name = name; cat.parent = 0; cat.backcolor = view::OWN_GREEN ; cat.forecolor = Qt::black; cat.fix_cost = false; - _this->_unresolvedCategories.push_back(cat); + _unresolvedCategories.push_back(cat); } -void GrisbiImportEngine::LoadOperation(GrisbiImportEngine* _this, const char** attrs) +void GrisbiImportEngine::LoadOperation(const QXmlAttributes& attrs) { - int i; static int id=0; Operation op; QDate date; @@ -125,74 +108,54 @@ void GrisbiImportEngine::LoadOperation(GrisbiImportEngine* _this, const char** a op.meta = false; op._virtual = false; - for (i=0; attrs[i]; i+=2) - { - if (!strcmp(attrs[i], "Ac")) - op.account = _this->_accounts[QString(attrs[i+1]).toInt()]; + op.account = _accounts[attrs.value("Ac").toInt()]; - else if (!strcmp(attrs[i], "Dt")) - { - date.fromString(attrs[i+1], "MM/%dd/%yyyy"); - op.day = date.day(); - op.month = date.month(); - op.year = date.year(); - } - - else if (!strcmp(attrs[i], "Am")) - { - op.amount = QString(attrs[i+1]).toDouble(); - } - - else if (!strcmp(attrs[i], "Ca")) - op.category = _this->_categories[QString(attrs[i+1]).toInt()]; - - else if (!strcmp(attrs[i], "No")) - op.description = attrs[i+1]; - } + date.fromString(attrs.value("Dt"), "MM/%dd/%yyyy"); + op.day = date.day(); + op.month = date.month(); + op.year = date.year(); + op.amount = attrs.value("Am").toDouble(); + op.category = _categories[attrs.value("Ca").toInt()]; + op.description = attrs.value("No"); UNESCAPE_CHARS(op.description); - _this->_operations.push_back(op); - _this->_descriptions[op.id] = op.description; + _operations.push_back(op); + _descriptions[op.id] = op.description; - _this->MatchPattern(op.description, op); + MatchPattern(op.description, op); } -void GrisbiImportEngine::GrisbiStartElement(void* user_data, const xmlChar* name_, const xmlChar** attrs_) +bool GrisbiImportEngine::startElement (const QString& namespaceURI, const QString& localName, const QString& qName, const QXmlAttributes& 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")) + if (!first && qName != "Grisbi") { throw "Invalid file !"; } else first = 1; - if (!strcmp(name, "General")) + if (qName == "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 !"; + if (attrs.value("File_version") < "0.6.0") + throw "Unsupported version !"; - else if (!strcmp(attrs[i], "Crypt_file") && !strcmp(attrs[i+1], "1")) - throw "Crypted file !"; - } + if (attrs.value("Crypt_file") == "1") + throw "Crypted file !"; } - else if (!strcmp(name, "Account")) - LoadAccount(_this, attrs); + else if (qName == "Account") + LoadAccount(attrs); - else if (!strcmp(name, "Category")) - LoadCategory(_this, attrs); + else if (qName == "Category") + LoadCategory(attrs); - else if (!strcmp(name, "Transaction")) - LoadOperation(_this, attrs); + else if (qName == "Transaction") + LoadOperation(attrs); + + return true; } GrisbiImportEngine::GrisbiImportEngine() @@ -202,7 +165,7 @@ GrisbiImportEngine::GrisbiImportEngine() _shortExt = ".gsb"; _longExt = _("Grisbi files (*.gsb)"); - _sax.startElement = GrisbiStartElement ; + _sax.setContentHandler(this); } GrisbiImportEngine::~GrisbiImportEngine() @@ -211,19 +174,22 @@ GrisbiImportEngine::~GrisbiImportEngine() bool GrisbiImportEngine::HandleFile(const QString& path, User* user, Database* db, KissCount* kiss) { - int res = -1; + bool res = false; + QFile file(path); if (!ImportEngine::HandleFile(path, user, db, kiss)) return false; try { - res = xmlSAXUserParseFile(&_sax, this, path.toStdString().c_str()); + res = _sax.parse(&file); } catch (const char* s) { std::cout << "GrisbiImportEngine :: " << s << std::endl; - res = -1; + res = false; } - return res >= 0; + file.close(); + + return res; } diff --git a/src/model/import/GrisbiImportEngine.hpp b/src/model/import/GrisbiImportEngine.hpp index c80e9c1..a4ed9ae 100644 --- a/src/model/import/GrisbiImportEngine.hpp +++ b/src/model/import/GrisbiImportEngine.hpp @@ -20,11 +20,12 @@ #ifndef GRISBIIMPORTENGINE_H #define GRISBIIMPORTENGINE_H -#include +#include +#include #include "ImportEngine.hpp" -class GrisbiImportEngine : public ImportEngine { +class GrisbiImportEngine : public ImportEngine, public QXmlDefaultHandler { public: GrisbiImportEngine(); ~GrisbiImportEngine(); @@ -32,12 +33,13 @@ public: virtual bool HandleFile(const QString& path, User* user, Database* db, KissCount* kiss); private: - xmlSAXHandler _sax; + QXmlSimpleReader _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); + bool startElement (const QString& namespaceURI, const QString& localName, const QString& qName, const QXmlAttributes& atts); + + void LoadAccount(const QXmlAttributes& atts); + void LoadCategory(const QXmlAttributes& atts); + void LoadOperation(const QXmlAttributes& atts); }; #endif diff --git a/src/model/import/XMLImportEngine.cpp b/src/model/import/XMLImportEngine.cpp index 366f8b5..9daef56 100644 --- a/src/model/import/XMLImportEngine.cpp +++ b/src/model/import/XMLImportEngine.cpp @@ -18,39 +18,93 @@ */ #include +#include #include "XMLImportEngine.hpp" static XMLImportEngine xmlImportEngine; -void XMLImportEngine::LoadAccount(XMLImportEngine* _this, const char** attrs) +XMLImportEngine::XMLImportEngine() { - int i, id; + KissCount::RegisterImportEngine(this); + + _shortExt = ".xml"; + _longExt = _("KissCount xml files (*.xml)"); + + _sax.setContentHandler(this); +} + +XMLImportEngine::~XMLImportEngine() +{ +} + +bool XMLImportEngine::HandleFile(const QString& path, User* user, Database* db, KissCount* kiss) +{ + bool res = false; + QFile file(path); + + if (!ImportEngine::HandleFile(path, user, db, kiss)) return false; + + if (!file.open(QIODevice::ReadOnly)) + { + std::cout << "Error can't open the file " << path.toStdString() << std::endl; + return false; + } + + try + { + res = _sax.parse(&file); + } + catch (const char* s) + { + std::cout << "XMLImportEngine :: " << s << std::endl; + res = false; + } + + file.close(); + + return res; +} + +bool XMLImportEngine::startElement (const QString& namespaceURI, const QString& localName, const QString& qName, const QXmlAttributes& attrs) +{ + if (qName == "kisscount") + { + if (attrs.value("version") != "1") + throw "Unsupported version !"; + } + + else if (qName == "account") + LoadAccount(attrs); + + else if (qName == "account_amount") + LoadAccountAmount(attrs); + + else if (qName == "category") + LoadCategory(attrs); + + else if (qName == "operation") + LoadOperation(attrs); + else + std::cout << "Unknown element : '" << qName.toStdString() << "'" << std::endl; + return true; +} + +void XMLImportEngine::LoadAccount(const QXmlAttributes& attrs) +{ + int id; QString account_number, name; Account ac; static int unknownAccount = 0; ac.id = 0; - for (i=0; attrs[i]; i+=2) - { - if (!strcmp(attrs[i], "name")) - ac.name = attrs[i+1]; - else if (!strcmp(attrs[i], "id")) - ac.id = id = QString(attrs[i+1]).toInt(); - - else if (!strcmp(attrs[i], "number")) - ac.number = account_number = attrs[i+1]; - - else if (!strcmp(attrs[i], "blocked")) - ac.blocked = (QString(attrs[i+1]) == "1"); - - else if (!strcmp(attrs[i], "virtual")) - ac._virtual = (QString(attrs[i+1]) == "1"); - - else if (!strcmp(attrs[i], "hidden")) - ac.hidden = (QString(attrs[i+1]) == "1"); - } + ac.name = attrs.value("name"); + ac.id = id = attrs.value("id").toInt(); + ac.number = account_number = attrs.value("number"); + ac.blocked = (attrs.value("blocked") == "1"); + ac._virtual = (attrs.value("virtual") == "1"); + ac.hidden = (attrs.value("hidden") == "1"); UNESCAPE_CHARS(ac.name); UNESCAPE_CHARS(ac.number); @@ -62,7 +116,7 @@ void XMLImportEngine::LoadAccount(XMLImportEngine* _this, const char** attrs) if (account_number.size()) { try { - _this->_accounts[ac.id] = _this->_user->GetAccountIdFromAccountNumber(account_number); + _accounts[ac.id] = _user->GetAccountIdFromAccountNumber(account_number); return; } catch (User::AccountNotFound) @@ -70,224 +124,94 @@ void XMLImportEngine::LoadAccount(XMLImportEngine* _this, const char** attrs) } } - _this->_accounts[id] = --unknownAccount; - _this->_unresolvedAccounts.push_back(ac); + _accounts[id] = --unknownAccount; + _unresolvedAccounts.push_back(ac); } -void XMLImportEngine::LoadAccountAmount(XMLImportEngine* _this, const char** attrs) +void XMLImportEngine::LoadAccountAmount(const QXmlAttributes& attrs) { AccountAmount accountAmount; - int i; double amount; - for (i=0; attrs[i]; i+=2) - { - if (!strcmp(attrs[i], "account")) - accountAmount.account = QString(attrs[i+1]).toInt(); + accountAmount.account = attrs.value("account").toInt(); + accountAmount.month = attrs.value("month").toInt(); + accountAmount.year = attrs.value("year").toInt(); + amount = attrs.value("amount").toDouble(); - else if (!strcmp(attrs[i], "month")) - { - accountAmount.month = QString(attrs[i+1]).toInt(); - } - - else if (!strcmp(attrs[i], "year")) - { - accountAmount.year = QString(attrs[i+1]).toInt(); - } - - else if (!strcmp(attrs[i], "amount")) - amount = QString(attrs[i+1]).toDouble(); - } - - _this->_accountAmounts[accountAmount] = amount; + _accountAmounts[accountAmount] = amount; } -void XMLImportEngine::LoadCategory(XMLImportEngine* _this, const char** attrs) +void XMLImportEngine::LoadCategory(const QXmlAttributes& attrs) { QString name; - int i, id; + int id; long rgb; Category cat; static int unknownCategory = 0; - for (i=0; attrs[i]; i+=2) + cat.fix_cost = false; + + cat.name = name = attrs.value("name"); + cat.id = id = attrs.value("id").toInt(); + cat.parent = attrs.value("parent").toInt(); + cat.font = attrs.value("font"); + + if (attrs.value("backcolor") != "") { - if (!strcmp(attrs[i], "name")) - cat.name = name = QString(attrs[i+1]); - - else if (!strcmp(attrs[i], "id")) - cat.id = id = QString(attrs[i+1]).toInt(); - - else if (!strcmp(attrs[i], "parent")) - cat.parent = id = QString(attrs[i+1]).toInt(); - - else if (!strcmp(attrs[i], "font")) - cat.font = QString(attrs[i+1]); - - else if (!strcmp(attrs[i], "backcolor")) - { - rgb = QString(attrs[i+1]).toInt(0, 16); - cat.backcolor = QColor((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF); - } - - else if (!strcmp(attrs[i], "forecolor")) - { - rgb = QString(attrs[i+1]).toInt(0, 16); - cat.forecolor = QColor((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF); - } - - else if (!strcmp(attrs[i], "fix_cost")) - cat.fix_cost = (QString(attrs[i+1]) == "1"); + rgb = attrs.value("backcolor").toInt(0, 16); + cat.backcolor = QColor((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF); } + else + cat.backcolor = view::OWN_GREEN; + + if (attrs.value("forecolor") != "") + { + rgb = attrs.value("forecolor").toInt(0, 16); + cat.forecolor = QColor((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF); + } + else + cat.forecolor = Qt::black; + + cat.fix_cost = (attrs.value("fix_cost") == "1"); UNESCAPE_CHARS(cat.name); - int catId = _this->_user->GetCategoryId(name); + int catId = _user->GetCategoryId(name); if (catId) { - _this->_categories[id] = catId; + _categories[id] = catId; return; } - _this->_categories[id] = --unknownCategory; - _this->_unresolvedCategories.push_back(cat); + _categories[id] = --unknownCategory; + _unresolvedCategories.push_back(cat); } -void XMLImportEngine::LoadOperation(XMLImportEngine* _this, const char** attrs) +void XMLImportEngine::LoadOperation(const QXmlAttributes& attrs) { - int i; Operation op; - for (i=0; attrs[i]; i+=2) - { - if (!strcmp(attrs[i], "id")) - op.id = QString(attrs[i+1]).toInt(); - - else if (!strcmp(attrs[i], "parent")) - op.parent = QString(attrs[i+1]).toInt(); - - else if (!strcmp(attrs[i], "day")) - { - op.day = QString(attrs[i+1]).toInt(); - } - - else if (!strcmp(attrs[i], "month")) - { - op.month = QString(attrs[i+1]).toInt(); - } - - else if (!strcmp(attrs[i], "year")) - { - op.year = QString(attrs[i+1]).toInt(); - } - - else if (!strcmp(attrs[i], "amount")) - { - op.amount = QString(attrs[i+1]).toDouble(); - } - - else if (!strcmp(attrs[i], "description")) - op.description = QString(attrs[i+1]); - - else if (!strcmp(attrs[i], "category")) - op.category = _this->_categories[QString(attrs[i+1]).toInt()]; - - else if (!strcmp(attrs[i], "fix_cost")) - op.fix_cost = (QString(attrs[i+1]) == "1"); - - else if (!strcmp(attrs[i], "account")) - op.account = _this->_accounts[QString(attrs[i+1]).toInt()]; - - else if (!strcmp(attrs[i], "checked")) - op.checked = (QString(attrs[i+1]) == "1"); - - else if (!strcmp(attrs[i], "transfert")) - op.transfert = QString(attrs[i+1]).toInt(); - - else if (!strcmp(attrs[i], "formula")) - op.formula = QString(attrs[i+1]); - - else if (!strcmp(attrs[i], "meta")) - op.meta = (QString(attrs[i+1]) == "1"); - - else if (!strcmp(attrs[i], "virtual")) - op._virtual = (QString(attrs[i+1]) == "1"); - } + op.id = attrs.value("id").toInt(); + op.parent = attrs.value("parent").toInt(); + op.day = attrs.value("day").toInt(); + op.month = attrs.value("month").toInt(); + op.year = attrs.value("year").toInt(); + op.amount = attrs.value("amount").toDouble(); + op.description = attrs.value("description"); + op.category = attrs.value("category").toInt(); + op.category = (attrs.value("fix_cost") == "1"); + op.account = attrs.value("account").toInt(); + op.checked = (attrs.value("checked") == "1"); + op.transfert = attrs.value("transfert").toInt(); + op.formula = attrs.value("formula"); + op.meta = (attrs.value("meta") == "1"); + op._virtual = (attrs.value("virtual") == "1"); UNESCAPE_CHARS(op.description); - _this->_operations.push_back(op); - _this->_descriptions[op.id] = op.description; + _operations.push_back(op); + _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 = ".xml"; - _longExt = _("KissCount xml files (*.xml)"); - - _sax.startElement = XmlStartElement ; -} - -XMLImportEngine::~XMLImportEngine() -{ -} - -bool XMLImportEngine::HandleFile(const QString& 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.toStdString().c_str()) >= 0; - } - catch (const char* s) - { - std::cout << "XMLImportEngine :: " << s << std::endl; - res = -1; - } - - return res >= 0; + MatchPattern(op.description, op); } diff --git a/src/model/import/XMLImportEngine.hpp b/src/model/import/XMLImportEngine.hpp index 6b657b7..55eb00f 100644 --- a/src/model/import/XMLImportEngine.hpp +++ b/src/model/import/XMLImportEngine.hpp @@ -20,11 +20,12 @@ #ifndef XMLIMPORTENGINE_H #define XMLIMPORTENGINE_H -#include +#include +#include #include "ImportEngine.hpp" -class XMLImportEngine : public ImportEngine { +class XMLImportEngine : public ImportEngine, public QXmlDefaultHandler { public: XMLImportEngine(); ~XMLImportEngine(); @@ -32,13 +33,14 @@ public: virtual bool HandleFile(const QString& path, User* user, Database* db, KissCount* kiss); private: - xmlSAXHandler _sax; + QXmlSimpleReader _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); + bool startElement (const QString& namespaceURI, const QString& localName, const QString& qName, const QXmlAttributes& atts); + + void LoadAccount(const QXmlAttributes& atts); + void LoadAccountAmount(const QXmlAttributes& atts); + void LoadCategory(const QXmlAttributes& atts); + void LoadOperation(const QXmlAttributes& atts); }; #endif