File indexing completed on 2024-04-28 16:21:35

0001 /* This file is part of the KDE project
0002    Copyright 2006 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 // Local
0021 #include "Validity.h"
0022 
0023 // KF5
0024 #include <kmessagebox.h>
0025 
0026 // Calligra
0027 #include <KoXmlNS.h>
0028 
0029 // Sheets
0030 #include "CalculationSettings.h"
0031 #include "Cell.h"
0032 #include "Map.h"
0033 #include "Sheet.h"
0034 #include "Value.h"
0035 #include "ValueCalc.h"
0036 #include "ValueConverter.h"
0037 #include "ValueParser.h"
0038 
0039 using namespace Calligra::Sheets;
0040 
0041 class Q_DECL_HIDDEN Validity::Private : public QSharedData
0042 {
0043 public:
0044     QString message;
0045     QString title;
0046     QString titleInfo;
0047     QString messageInfo;
0048     Value minValue;
0049     Value maxValue;
0050     Conditional::Type cond;
0051     Action action;
0052     Restriction restriction;
0053     bool displayMessage;
0054     bool allowEmptyCell;
0055     bool displayValidationInformation;
0056     QStringList listValidity;
0057 };
0058 
0059 Validity::Validity()
0060         : d(new Private)
0061 {
0062     d->cond = Conditional::None;
0063     d->action = Stop;
0064     d->restriction = None;
0065     d->displayMessage = true;
0066     d->allowEmptyCell = false;
0067     d->displayValidationInformation = false;
0068 }
0069 
0070 Validity::Validity(const Validity& other)
0071         : d(other.d)
0072 {
0073 }
0074 
0075 Validity::~Validity()
0076 {
0077 }
0078 
0079 bool Validity::isEmpty() const
0080 {
0081     return d->restriction == None;
0082 }
0083 
0084 bool Validity::loadXML(Cell* const cell, const KoXmlElement& validityElement)
0085 {
0086     ValueParser *const parser = cell->sheet()->map()->parser();
0087     bool ok = false;
0088     KoXmlElement param = validityElement.namedItem("param").toElement();
0089     if (!param.isNull()) {
0090         if (param.hasAttribute("cond")) {
0091             d->cond = (Conditional::Type) param.attribute("cond").toInt(&ok);
0092             if (!ok)
0093                 return false;
0094         }
0095         if (param.hasAttribute("action")) {
0096             d->action = (Action) param.attribute("action").toInt(&ok);
0097             if (!ok)
0098                 return false;
0099         }
0100         if (param.hasAttribute("allow")) {
0101             d->restriction = (Restriction) param.attribute("allow").toInt(&ok);
0102             if (!ok)
0103                 return false;
0104         }
0105         if (param.hasAttribute("valmin")) {
0106             d->minValue = parser->tryParseNumber(param.attribute("valmin"), &ok);
0107             if (!ok)
0108                 return false;
0109         }
0110         if (param.hasAttribute("valmax")) {
0111             d->maxValue = parser->tryParseNumber(param.attribute("valmax"), &ok);
0112             if (!ok)
0113                 return false;
0114         }
0115         if (param.hasAttribute("displaymessage")) {
0116             d->displayMessage = (bool)param.attribute("displaymessage").toInt();
0117         }
0118         if (param.hasAttribute("displayvalidationinformation")) {
0119             d->displayValidationInformation = (bool)param.attribute("displayvalidationinformation").toInt();
0120         }
0121         if (param.hasAttribute("allowemptycell")) {
0122             d->allowEmptyCell = (bool)param.attribute("allowemptycell").toInt();
0123         }
0124         if (param.hasAttribute("listvalidity")) {
0125             d->listValidity = param.attribute("listvalidity").split(';', QString::SkipEmptyParts);
0126         }
0127     }
0128     KoXmlElement inputTitle = validityElement.namedItem("inputtitle").toElement();
0129     if (!inputTitle.isNull()) {
0130         d->titleInfo = inputTitle.text();
0131     }
0132     KoXmlElement inputMessage = validityElement.namedItem("inputmessage").toElement();
0133     if (!inputMessage.isNull()) {
0134         d->messageInfo = inputMessage.text();
0135     }
0136 
0137     KoXmlElement titleElement = validityElement.namedItem("title").toElement();
0138     if (!titleElement.isNull()) {
0139         d->title = titleElement.text();
0140     }
0141     KoXmlElement messageElement = validityElement.namedItem("message").toElement();
0142     if (!messageElement.isNull()) {
0143         d->message = messageElement.text();
0144     }
0145     KoXmlElement timeMinElement = validityElement.namedItem("timemin").toElement();
0146     if (!timeMinElement.isNull()) {
0147         d->minValue = parser->tryParseTime(timeMinElement.text());
0148     }
0149     KoXmlElement timeMaxElement = validityElement.namedItem("timemax").toElement();
0150     if (!timeMaxElement.isNull()) {
0151         d->maxValue = parser->tryParseTime(timeMaxElement.text());
0152     }
0153     KoXmlElement dateMinElement = validityElement.namedItem("datemin").toElement();
0154     if (!dateMinElement.isNull()) {
0155         d->minValue = parser->tryParseTime(dateMinElement.text());
0156     }
0157     KoXmlElement dateMaxElement = validityElement.namedItem("datemax").toElement();
0158     if (!dateMaxElement.isNull()) {
0159         d->maxValue = parser->tryParseTime(dateMaxElement.text());
0160     }
0161     return true;
0162 }
0163 
0164 QDomElement Validity::saveXML(QDomDocument& doc, const ValueConverter *converter) const
0165 {
0166     QDomElement validityElement = doc.createElement("validity");
0167 
0168     QDomElement param = doc.createElement("param");
0169     param.setAttribute("cond", QString::number((int)d->cond));
0170     param.setAttribute("action", QString::number((int)d->action));
0171     param.setAttribute("allow", QString::number((int)d->restriction));
0172     param.setAttribute("valmin", converter->asString(d->minValue).asString());
0173     param.setAttribute("valmax", converter->asString(d->maxValue).asString());
0174     param.setAttribute("displaymessage", QString::number(d->displayMessage));
0175     param.setAttribute("displayvalidationinformation", QString::number(d->displayValidationInformation));
0176     param.setAttribute("allowemptycell", QString::number(d->allowEmptyCell));
0177     if (!d->listValidity.isEmpty())
0178         param.setAttribute("listvalidity", d->listValidity.join(";"));
0179     validityElement.appendChild(param);
0180     QDomElement titleElement = doc.createElement("title");
0181     titleElement.appendChild(doc.createTextNode(d->title));
0182     validityElement.appendChild(titleElement);
0183     QDomElement messageElement = doc.createElement("message");
0184     messageElement.appendChild(doc.createCDATASection(d->message));
0185     validityElement.appendChild(messageElement);
0186 
0187     QDomElement inputTitle = doc.createElement("inputtitle");
0188     inputTitle.appendChild(doc.createTextNode(d->titleInfo));
0189     validityElement.appendChild(inputTitle);
0190 
0191     QDomElement inputMessage = doc.createElement("inputmessage");
0192     inputMessage.appendChild(doc.createTextNode(d->messageInfo));
0193     validityElement.appendChild(inputMessage);
0194 
0195 
0196 
0197     QString tmp;
0198     if (d->restriction == Time) {
0199         QDomElement timeMinElement = doc.createElement("timemin");
0200         tmp = converter->asString(d->minValue).asString();
0201         timeMinElement.appendChild(doc.createTextNode(tmp));
0202         validityElement.appendChild(timeMinElement);
0203 
0204         if (d->cond == Conditional::Between || d->cond == Conditional::Different) {
0205             QDomElement timeMaxElement = doc.createElement("timemax");
0206             tmp = converter->asString(d->maxValue).asString();
0207             timeMaxElement.appendChild(doc.createTextNode(tmp));
0208             validityElement.appendChild(timeMaxElement);
0209         }
0210     }
0211 
0212     if (d->restriction == Date) {
0213         QDomElement dateMinElement = doc.createElement("datemin");
0214         const QDate minDate = d->minValue.asDate(converter->settings());
0215         QString tmp("%1/%2/%3");
0216         tmp = tmp.arg(minDate.year()).arg(minDate.month()).arg(minDate.day());
0217         dateMinElement.appendChild(doc.createTextNode(tmp));
0218         validityElement.appendChild(dateMinElement);
0219 
0220         if (d->cond == Conditional::Between || d->cond == Conditional::Different) {
0221             QDomElement dateMaxElement = doc.createElement("datemax");
0222             const QDate maxDate = d->maxValue.asDate(converter->settings());
0223             QString tmp("%1/%2/%3");
0224             tmp = tmp.arg(maxDate.year()).arg(maxDate.month()).arg(maxDate.day());
0225             dateMaxElement.appendChild(doc.createTextNode(tmp));
0226             validityElement.appendChild(dateMaxElement);
0227         }
0228     }
0229     return validityElement;
0230 }
0231 
0232 
0233 Validity::Action Validity::action() const
0234 {
0235     return d->action;
0236 }
0237 
0238 bool Validity::allowEmptyCell() const
0239 {
0240     return d->allowEmptyCell;
0241 }
0242 
0243 Conditional::Type Validity::condition() const
0244 {
0245     return d->cond;
0246 }
0247 
0248 bool Validity::displayMessage() const
0249 {
0250     return d->displayMessage;
0251 }
0252 
0253 bool Validity::displayValidationInformation() const
0254 {
0255     return d->displayValidationInformation;
0256 }
0257 
0258 const QString& Validity::messageInfo() const
0259 {
0260     return d->messageInfo;
0261 }
0262 
0263 const Value &Validity::maximumValue() const
0264 {
0265     return d->maxValue;
0266 }
0267 
0268 const QString& Validity::message() const
0269 {
0270     return d->message;
0271 }
0272 
0273 const Value &Validity::minimumValue() const
0274 {
0275     return d->minValue;
0276 }
0277 
0278 Validity::Restriction Validity::restriction() const
0279 {
0280     return d->restriction;
0281 }
0282 
0283 const QString& Validity::title() const
0284 {
0285     return d->title;
0286 }
0287 
0288 const QString& Validity::titleInfo() const
0289 {
0290     return d->titleInfo;
0291 }
0292 
0293 const QStringList& Validity::validityList() const
0294 {
0295     return d->listValidity;
0296 }
0297 
0298 void Validity::setAction(Action action)
0299 {
0300     d->action = action;
0301 }
0302 
0303 void Validity::setAllowEmptyCell(bool allow)
0304 {
0305     d->allowEmptyCell = allow;
0306 }
0307 
0308 void Validity::setCondition(Conditional::Type condition)
0309 {
0310     d->cond = condition;
0311 }
0312 
0313 void Validity::setDisplayMessage(bool display)
0314 {
0315     d->displayMessage = display;
0316 }
0317 
0318 void Validity::setDisplayValidationInformation(bool display)
0319 {
0320     d->displayValidationInformation = display;
0321 }
0322 
0323 void Validity::setMaximumValue(const Value &value)
0324 {
0325     d->maxValue = value;
0326 }
0327 
0328 void Validity::setMessage(const QString& msg)
0329 {
0330     d->message = msg;
0331 }
0332 
0333 void Validity::setMessageInfo(const QString& info)
0334 {
0335     d->messageInfo = info;
0336 }
0337 
0338 void Validity::setMinimumValue(const Value &value)
0339 {
0340     d->minValue = value;
0341 }
0342 
0343 void Validity::setRestriction(Restriction restriction)
0344 {
0345     d->restriction = restriction;
0346 }
0347 
0348 void Validity::setTitle(const QString& t)
0349 {
0350     d->title = t;
0351 }
0352 
0353 void Validity::setTitleInfo(const QString& info)
0354 {
0355     d->titleInfo = info;
0356 }
0357 
0358 void Validity::setValidityList(const QStringList& list)
0359 {
0360     d->listValidity = list;
0361 }
0362 
0363 bool Validity::testValidity(const Cell* cell) const
0364 {
0365     bool valid = false;
0366     if (d->restriction != None) {
0367         //fixme
0368         if (d->allowEmptyCell && cell->userInput().isEmpty())
0369             return true;
0370 
0371         ValueCalc *const calc = cell->sheet()->map()->calc();
0372         const Qt::CaseSensitivity cs = calc->settings()->caseSensitiveComparisons();
0373 
0374         if ((cell->value().isNumber() &&
0375                 (d->restriction == Number ||
0376                  (d->restriction == Integer &&
0377                   numToDouble(cell->value().asFloat()) == ceil(numToDouble(cell->value().asFloat())))))
0378             || (d->restriction == Time && cell->isTime())
0379             || (d->restriction == Date && cell->isDate())) {
0380             switch (d->cond) {
0381             case Conditional::Equal:
0382                 valid = cell->value().equal(d->minValue, cs);
0383                 break;
0384             case Conditional::DifferentTo:
0385                 valid = !cell->value().equal(d->minValue, cs);
0386                 break;
0387             case Conditional::Superior:
0388                 valid = cell->value().greater(d->minValue, cs);
0389                 break;
0390             case Conditional::Inferior:
0391                 valid = cell->value().less(d->minValue, cs);
0392                 break;
0393             case Conditional::SuperiorEqual:
0394                 valid = (cell->value().compare(d->minValue, cs)) >= 0;
0395                 break;
0396             case Conditional::InferiorEqual:
0397                 valid = (cell->value().compare(d->minValue, cs)) <= 0;
0398                 break;
0399             case Conditional::Between:
0400                 valid = (cell->value().compare(d->minValue, cs) >= 0 &&
0401                          cell->value().compare(d->maxValue, cs) <= 0);
0402                 break;
0403             case Conditional::Different:
0404                 valid = (cell->value().compare(d->minValue, cs) < 0 ||
0405                          cell->value().compare(d->maxValue, cs) > 0);
0406                 break;
0407             default :
0408                 break;
0409             }
0410         } else if (d->restriction == Text) {
0411             valid = cell->value().isString();
0412         } else if (d->restriction == List) {
0413             //test int value
0414             if (cell->value().isString() && d->listValidity.contains(cell->value().asString()))
0415                 valid = true;
0416         } else if (d->restriction == TextLength) {
0417             if (cell->value().isString()) {
0418                 int len = cell->displayText().length();
0419                 const int min = d->minValue.asInteger();
0420                 const int max = d->maxValue.asInteger();
0421                 switch (d->cond) {
0422                 case Conditional::Equal:
0423                     if (len == min)
0424                         valid = true;
0425                     break;
0426                 case Conditional::DifferentTo:
0427                     if (len != min)
0428                         valid = true;
0429                     break;
0430                 case Conditional::Superior:
0431                     if (len > min)
0432                         valid = true;
0433                     break;
0434                 case Conditional::Inferior:
0435                     if (len < min)
0436                         valid = true;
0437                     break;
0438                 case Conditional::SuperiorEqual:
0439                     if (len >= min)
0440                         valid = true;
0441                     break;
0442                 case Conditional::InferiorEqual:
0443                     if (len <= min)
0444                         valid = true;
0445                     break;
0446                 case Conditional::Between:
0447                     if (len >= min && len <= max)
0448                         valid = true;
0449                     break;
0450                 case Conditional::Different:
0451                     if (len < min || len > max)
0452                         valid = true;
0453                     break;
0454                 default :
0455                     break;
0456                 }
0457             }
0458         }
0459     } else {
0460         valid = true;
0461     }
0462 
0463     if (!valid) {
0464         if (d->displayMessage) {
0465             switch (d->action) {
0466             case Stop:
0467                 KMessageBox::error((QWidget*)0, d->message, d->title);
0468                 break;
0469             case Warning:
0470                 if (KMessageBox::warningYesNo((QWidget*)0, d->message, d->title) == KMessageBox::Yes) {
0471                     valid = true;
0472                 }
0473                 break;
0474             case Information:
0475                 KMessageBox::information((QWidget*)0, d->message, d->title);
0476                 valid = true;
0477                 break;
0478             }
0479         }
0480 
0481         cell->sheet()->showStatusMessage(i18n("Validation for cell %1 failed", cell->fullName()));
0482     }
0483     return valid;
0484 }
0485 
0486 void Validity::operator=(const Validity & other)
0487 {
0488     d = other.d;
0489 }
0490 
0491 bool Validity::operator==(const Validity& other) const
0492 {
0493     if (d->message == other.d->message &&
0494             d->title == other.d->title &&
0495             d->titleInfo == other.d->titleInfo &&
0496             d->messageInfo == other.d->messageInfo &&
0497             d->minValue == other.d->minValue &&
0498             d->maxValue == other.d->maxValue &&
0499             d->cond == other.d->cond &&
0500             d->action == other.d->action &&
0501             d->restriction == other.d->restriction &&
0502             d->displayMessage == other.d->displayMessage &&
0503             d->allowEmptyCell == other.d->allowEmptyCell &&
0504             d->displayValidationInformation == other.d->displayValidationInformation &&
0505             d->listValidity == other.d->listValidity) {
0506         return true;
0507     }
0508     return false;
0509 }
0510 
0511 // static
0512 QHash<QString, KoXmlElement> Validity::preloadValidities(const KoXmlElement& body)
0513 {
0514     QHash<QString, KoXmlElement> validities;
0515     KoXmlNode validation = KoXml::namedItemNS(body, KoXmlNS::table, "content-validations");
0516     debugSheets << "validation.isNull?" << validation.isNull();
0517     if (!validation.isNull()) {
0518         KoXmlElement element;
0519         forEachElement(element, validation) {
0520             if (element.tagName() ==  "content-validation" && element.namespaceURI() == KoXmlNS::table) {
0521                 const QString name = element.attributeNS(KoXmlNS::table, "name", QString());
0522                 validities.insert(name, element);
0523                 debugSheets << " validation found:" << name;
0524             } else {
0525                 debugSheets << " Tag not recognized:" << element.tagName();
0526             }
0527         }
0528     }
0529     return validities;
0530 }