File indexing completed on 2024-05-19 16:08:03

0001 /* This file is part of the KDE project
0002    Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "Database.h"
0021 
0022 #include <QString>
0023 
0024 #include <KoXmlNS.h>
0025 #include <KoXmlWriter.h>
0026 
0027 #include "DatabaseSource.h"
0028 #include "Filter.h"
0029 #include "FilterPopup.h"
0030 #include "Map.h"
0031 #include "Region.h"
0032 #include "odf/SheetsOdf.h"
0033 
0034 using namespace Calligra::Sheets;
0035 
0036 class Sort;
0037 class SubtotalRules;
0038 
0039 class Q_DECL_HIDDEN Database::Private : public QSharedData
0040 {
0041 public:
0042     Private()
0043             : source(0)
0044             , sort(0)
0045             , filter(new Filter())
0046             , subtotalRules(0)
0047             , isSelection(false)
0048             , onUpdateKeepStyles(false)
0049             , onUpdateKeepSize(true)
0050             , hasPersistentData(true)
0051             , orientation(Row)
0052             , containsHeader(true)
0053             , displayFilterButtons(false)
0054             , refreshDelay(0) {
0055     }
0056 
0057     Private(const Private& other)
0058             : QSharedData(other)
0059             , source(/*other.source ? new DatabaseSource(*other.source) : */0)
0060             , sort(/*other.sort ? new Sort(*other.sort) : */0)
0061             , filter(other.filter ? new Filter(*other.filter) : 0)
0062             , subtotalRules(/*other.subtotalRules ? new SubtotalRules(*other.subtotalRules) : */0)
0063             , name(other.name)
0064             , isSelection(other.isSelection)
0065             , onUpdateKeepStyles(other.onUpdateKeepStyles)
0066             , onUpdateKeepSize(other.onUpdateKeepSize)
0067             , hasPersistentData(other.hasPersistentData)
0068             , orientation(other.orientation)
0069             , containsHeader(other.containsHeader)
0070             , displayFilterButtons(other.displayFilterButtons)
0071             , targetRangeAddress(other.targetRangeAddress)
0072             , refreshDelay(other.refreshDelay) {
0073     }
0074 
0075     virtual ~Private() {
0076 //         delete source;
0077 //         delete sort;
0078         delete filter;
0079 //         delete subtotalRules;
0080     }
0081 
0082     DatabaseSource* source;
0083     Sort* sort;
0084     Filter* filter;
0085     SubtotalRules* subtotalRules;
0086     QString name;
0087     bool isSelection                    : 1;
0088     bool onUpdateKeepStyles             : 1;
0089     bool onUpdateKeepSize               : 1;
0090     bool hasPersistentData              : 1;
0091     enum { Row, Column } orientation    : 1;
0092     bool containsHeader                 : 1;
0093     bool displayFilterButtons           : 1;
0094     Region targetRangeAddress;
0095     int refreshDelay;
0096 
0097 private:
0098     void operator=(const Private&);
0099 };
0100 
0101 Database::Database()
0102         : d(new Private)
0103 {
0104 }
0105 
0106 Database::Database(const QString& name)
0107         : d(new Private)
0108 {
0109     d->name = name;
0110 }
0111 
0112 Database::Database(const Database& other)
0113         : d(other.d)
0114 {
0115 }
0116 
0117 Database::~Database()
0118 {
0119 }
0120 
0121 bool Database::isEmpty() const
0122 {
0123     return d->name.isNull(); // it may be empty though
0124 }
0125 
0126 const QString& Database::name() const
0127 {
0128     return d->name;
0129 }
0130 
0131 void Database::setName(const QString& name)
0132 {
0133     d->name = name;
0134 }
0135 
0136 Qt::Orientation Database::orientation() const
0137 {
0138     return d->orientation == Private::Row ? Qt::Vertical : Qt::Horizontal;
0139 }
0140 
0141 bool Database::containsHeader() const
0142 {
0143     return d->containsHeader;
0144 }
0145 
0146 void Database::setContainsHeader(bool enable)
0147 {
0148     d->containsHeader = enable;
0149 }
0150 
0151 bool Database::displayFilterButtons() const
0152 {
0153     return d->displayFilterButtons;
0154 }
0155 
0156 void Database::setDisplayFilterButtons(bool enable)
0157 {
0158     d->displayFilterButtons = enable;
0159 }
0160 
0161 const Calligra::Sheets::Region& Database::range() const
0162 {
0163     return d->targetRangeAddress;
0164 }
0165 
0166 void Database::setRange(const Region& region)
0167 {
0168     Q_ASSERT(region.isContiguous());
0169     d->targetRangeAddress = region;
0170 }
0171 
0172 const Filter& Database::filter() const
0173 {
0174     return *d->filter;
0175 }
0176 
0177 void Database::setFilter(const Filter& filter)
0178 {
0179     if (*d->filter == filter)
0180         return;
0181     delete d->filter;
0182     d->filter = new Filter(filter);
0183 }
0184 
0185 bool Database::loadOdf(const KoXmlElement& element, const Map* map)
0186 {
0187     if (element.hasAttributeNS(KoXmlNS::table, "name"))
0188         d->name = element.attributeNS(KoXmlNS::table, "name", QString());
0189     if (element.hasAttributeNS(KoXmlNS::table, "is-selection")) {
0190         if (element.attributeNS(KoXmlNS::table, "is-selection", "false") == "true")
0191             d->isSelection = true;
0192         else
0193             d->isSelection = false;
0194     }
0195     if (element.hasAttributeNS(KoXmlNS::table, "on-update-keep-styles")) {
0196         if (element.attributeNS(KoXmlNS::table, "on-update-keep-styles", "false") == "true")
0197             d->onUpdateKeepStyles = true;
0198         else
0199             d->onUpdateKeepStyles = false;
0200     }
0201     if (element.hasAttributeNS(KoXmlNS::table, "on-update-keep-size")) {
0202         if (element.attributeNS(KoXmlNS::table, "on-update-keep-size", "true") == "false")
0203             d->onUpdateKeepSize = false;
0204         else
0205             d->onUpdateKeepSize = true;
0206     }
0207     if (element.hasAttributeNS(KoXmlNS::table, "has-persistent-data")) {
0208         if (element.attributeNS(KoXmlNS::table, "has-persistent-data", "true") == "false")
0209             d->hasPersistentData = false;
0210         else
0211             d->hasPersistentData = true;
0212     }
0213     if (element.hasAttributeNS(KoXmlNS::table, "orientation")) {
0214         if (element.attributeNS(KoXmlNS::table, "orientation", "row") == "column")
0215             d->orientation = Private::Column;
0216         else
0217             d->orientation = Private::Row;
0218     }
0219     if (element.hasAttributeNS(KoXmlNS::table, "contains-header")) {
0220         if (element.attributeNS(KoXmlNS::table, "contains-header", "true") == "false")
0221             d->containsHeader = false;
0222         else
0223             d->containsHeader = true;
0224     }
0225     if (element.hasAttributeNS(KoXmlNS::table, "display-filter-buttons")) {
0226         if (element.attributeNS(KoXmlNS::table, "display-filter-buttons", "false") == "true")
0227             d->displayFilterButtons = true;
0228         else
0229             d->displayFilterButtons = false;
0230     }
0231     if (element.hasAttributeNS(KoXmlNS::table, "target-range-address")) {
0232         const QString address = element.attributeNS(KoXmlNS::table, "target-range-address", QString());
0233         // only absolute addresses allowed; no fallback sheet needed
0234         d->targetRangeAddress = Region(Odf::loadRegion(address), map);
0235         if (!d->targetRangeAddress.isValid())
0236             return false;
0237     }
0238     if (element.hasAttributeNS(KoXmlNS::table, "refresh-delay")) {
0239         bool ok = false;
0240         d->refreshDelay = element.attributeNS(KoXmlNS::table, "refresh-delay", QString()).toInt(&ok);
0241         if (!ok || d->refreshDelay < 0)
0242             return false;
0243     }
0244     KoXmlElement child;
0245     forEachElement(child, element) {
0246         if (child.namespaceURI() != KoXmlNS::table)
0247             continue;
0248         if (child.localName() == "database-source-sql") {
0249             // TODO
0250         } else if (child.localName() == "database-source-table") {
0251             // TODO
0252         } else if (child.localName() == "database-source-query") {
0253             // TODO
0254         } else if (child.localName() == "sort") {
0255             // TODO
0256         } else if (child.localName() == "filter") {
0257             d->filter = new Filter();
0258             if (!d->filter->loadOdf(child, map)) {
0259                 delete d->filter;
0260                 d->filter = 0;
0261                 return false;
0262             }
0263         } else if (child.localName() == "subtotal-rules") {
0264             // TODO
0265         }
0266     }
0267     return true;
0268 }
0269 
0270 void Database::saveOdf(KoXmlWriter& xmlWriter) const
0271 {
0272     if (d->targetRangeAddress.isEmpty())
0273         return;
0274     xmlWriter.startElement("table:database-range");
0275     if (!d->name.isNull())
0276         xmlWriter.addAttribute("table:name", d->name);
0277     if (d->isSelection)
0278         xmlWriter.addAttribute("table:is-selection", "true");
0279     if (d->onUpdateKeepStyles)
0280         xmlWriter.addAttribute("table:on-update-keep-styles", "true");
0281     if (!d->onUpdateKeepSize)
0282         xmlWriter.addAttribute("table:on-update-keep-size", "false");
0283     if (!d->hasPersistentData)
0284         xmlWriter.addAttribute("table:has-persistent-data", "false");
0285     if (d->orientation == Private::Column)
0286         xmlWriter.addAttribute("table:orientation", "column");
0287     if (!d->containsHeader)
0288         xmlWriter.addAttribute("table:contains-header", "false");
0289     if (d->displayFilterButtons)
0290         xmlWriter.addAttribute("table:display-filter-buttons", "true");
0291     xmlWriter.addAttribute("table:target-range-address", Odf::saveRegion(d->targetRangeAddress.name()));
0292     if (d->refreshDelay)
0293         xmlWriter.addAttribute("table:refresh-delay", d->refreshDelay);
0294     // TODO
0295 //     if (d->source)
0296 //         d->source->saveOdf(xmlWriter);
0297 //     if (d->sort)
0298 //         d->sort->saveOdf(xmlWriter);
0299     if (d->filter)
0300         d->filter->saveOdf(xmlWriter);
0301 //     if (d->subtotalRules)
0302 //         d->subtotalRules->saveOdf(xmlWriter);
0303     xmlWriter.endElement();
0304 }
0305 
0306 void Database::operator=(const Database & other)
0307 {
0308     d = other.d;
0309 }
0310 
0311 bool Database::operator==(const Database& other) const
0312 {
0313     // NOTE Stefan: Don't compare targetRangeAddress.
0314     if (d->name != other.d->name)
0315         return false;
0316     if (d->isSelection != other.d->isSelection)
0317         return false;
0318     if (d->onUpdateKeepStyles != other.d->onUpdateKeepStyles)
0319         return false;
0320     if (d->onUpdateKeepSize != other.d->onUpdateKeepSize)
0321         return false;
0322     if (d->hasPersistentData != other.d->hasPersistentData)
0323         return false;
0324     if (d->orientation != other.d->orientation)
0325         return false;
0326     if (d->containsHeader != other.d->containsHeader)
0327         return false;
0328     if (d->displayFilterButtons != other.d->displayFilterButtons)
0329         return false;
0330     if (d->refreshDelay != other.d->refreshDelay)
0331         return false;
0332 //     if (*d->source != *other.d->source)
0333 //         return false;
0334 //     if (*d->sort != *other.d->sort)
0335 //         return false;
0336     if (*d->filter != *other.d->filter)
0337         return false;
0338 //     if (*d->subtotalRules != *other.d->subtotalRules)
0339 //         return false;
0340     return true;
0341 }
0342 
0343 bool Database::operator<(const Database& other) const
0344 {
0345     return (d < other.d);
0346 }
0347 
0348 void Database::dump() const
0349 {
0350     if (d->filter) d->filter->dump();
0351 }