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 }