1 | /* This file is part of the KDE project |
2 | Copyright 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net> |
3 | |
4 | This library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Library General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2 of the License, or (at your option) any later version. |
8 | |
9 | This library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Library General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Library General Public License |
15 | along with this library; see the file COPYING.LIB. If not, write to |
16 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 | Boston, MA 02110-1301, USA. |
18 | */ |
19 | |
20 | // Local |
21 | #include "Validity.h" |
22 | |
23 | // KDE |
24 | #include <kmessagebox.h> |
25 | |
26 | // Calligra |
27 | #include <KoXmlReader.h> |
28 | #include <KoXmlNS.h> |
29 | |
30 | // KSpread |
31 | #include "CalculationSettings.h" |
32 | #include "Cell.h" |
33 | #include "Map.h" |
34 | #include "OdfLoadingContext.h" |
35 | #include "Sheet.h" |
36 | #include "Value.h" |
37 | #include "ValueCalc.h" |
38 | #include "ValueConverter.h" |
39 | #include "ValueParser.h" |
40 | |
41 | using namespace Calligra::Sheets; |
42 | |
43 | class Validity::Private : public QSharedData |
44 | { |
45 | public: |
46 | QString message; |
47 | QString title; |
48 | QString titleInfo; |
49 | QString messageInfo; |
50 | Value minValue; |
51 | Value maxValue; |
52 | Conditional::Type cond; |
53 | Action action; |
54 | Restriction restriction; |
55 | bool displayMessage; |
56 | bool allowEmptyCell; |
57 | bool displayValidationInformation; |
58 | QStringList listValidity; |
59 | }; |
60 | |
61 | Validity::Validity() |
62 | : d(new Private) |
63 | { |
64 | d->cond = Conditional::None; |
65 | d->action = Stop; |
66 | d->restriction = None; |
67 | d->displayMessage = true; |
68 | d->allowEmptyCell = false; |
69 | d->displayValidationInformation = false; |
70 | } |
71 | |
72 | Validity::Validity(const Validity& other) |
73 | : d(other.d) |
74 | { |
75 | } |
76 | |
77 | Validity::~Validity() |
78 | { |
79 | } |
80 | |
81 | bool Validity::isEmpty() const |
82 | { |
83 | return d->restriction == None; |
84 | } |
85 | |
86 | bool Validity::loadXML(Cell* const cell, const KoXmlElement& validityElement) |
87 | { |
88 | ValueParser *const parser = cell->sheet()->map()->parser(); |
89 | bool ok = false; |
90 | KoXmlElement param = validityElement.namedItem("param" ).toElement(); |
91 | if (!param.isNull()) { |
92 | if (param.hasAttribute("cond" )) { |
93 | d->cond = (Conditional::Type) param.attribute("cond" ).toInt(&ok); |
94 | if (!ok) |
95 | return false; |
96 | } |
97 | if (param.hasAttribute("action" )) { |
98 | d->action = (Action) param.attribute("action" ).toInt(&ok); |
99 | if (!ok) |
100 | return false; |
101 | } |
102 | if (param.hasAttribute("allow" )) { |
103 | d->restriction = (Restriction) param.attribute("allow" ).toInt(&ok); |
104 | if (!ok) |
105 | return false; |
106 | } |
107 | if (param.hasAttribute("valmin" )) { |
108 | d->minValue = parser->tryParseNumber(param.attribute("valmin" ), &ok); |
109 | if (!ok) |
110 | return false; |
111 | } |
112 | if (param.hasAttribute("valmax" )) { |
113 | d->maxValue = parser->tryParseNumber(param.attribute("valmax" ), &ok); |
114 | if (!ok) |
115 | return false; |
116 | } |
117 | if (param.hasAttribute("displaymessage" )) { |
118 | d->displayMessage = (bool)param.attribute("displaymessage" ).toInt(); |
119 | } |
120 | if (param.hasAttribute("displayvalidationinformation" )) { |
121 | d->displayValidationInformation = (bool)param.attribute("displayvalidationinformation" ).toInt(); |
122 | } |
123 | if (param.hasAttribute("allowemptycell" )) { |
124 | d->allowEmptyCell = (bool)param.attribute("allowemptycell" ).toInt(); |
125 | } |
126 | if (param.hasAttribute("listvalidity" )) { |
127 | d->listValidity = param.attribute("listvalidity" ).split(';', QString::SkipEmptyParts); |
128 | } |
129 | } |
130 | KoXmlElement inputTitle = validityElement.namedItem("inputtitle" ).toElement(); |
131 | if (!inputTitle.isNull()) { |
132 | d->titleInfo = inputTitle.text(); |
133 | } |
134 | KoXmlElement inputMessage = validityElement.namedItem("inputmessage" ).toElement(); |
135 | if (!inputMessage.isNull()) { |
136 | d->messageInfo = inputMessage.text(); |
137 | } |
138 | |
139 | KoXmlElement titleElement = validityElement.namedItem("title" ).toElement(); |
140 | if (!titleElement.isNull()) { |
141 | d->title = titleElement.text(); |
142 | } |
143 | KoXmlElement messageElement = validityElement.namedItem("message" ).toElement(); |
144 | if (!messageElement.isNull()) { |
145 | d->message = messageElement.text(); |
146 | } |
147 | KoXmlElement timeMinElement = validityElement.namedItem("timemin" ).toElement(); |
148 | if (!timeMinElement.isNull()) { |
149 | d->minValue = parser->tryParseTime(timeMinElement.text()); |
150 | } |
151 | KoXmlElement timeMaxElement = validityElement.namedItem("timemax" ).toElement(); |
152 | if (!timeMaxElement.isNull()) { |
153 | d->maxValue = parser->tryParseTime(timeMaxElement.text()); |
154 | } |
155 | KoXmlElement dateMinElement = validityElement.namedItem("datemin" ).toElement(); |
156 | if (!dateMinElement.isNull()) { |
157 | d->minValue = parser->tryParseTime(dateMinElement.text()); |
158 | } |
159 | KoXmlElement dateMaxElement = validityElement.namedItem("datemax" ).toElement(); |
160 | if (!dateMaxElement.isNull()) { |
161 | d->maxValue = parser->tryParseTime(dateMaxElement.text()); |
162 | } |
163 | return true; |
164 | } |
165 | |
166 | QDomElement Validity::saveXML(QDomDocument& doc, const ValueConverter *converter) const |
167 | { |
168 | QDomElement validityElement = doc.createElement("validity" ); |
169 | |
170 | QDomElement param = doc.createElement("param" ); |
171 | param.setAttribute("cond" , (int)d->cond); |
172 | param.setAttribute("action" , (int)d->action); |
173 | param.setAttribute("allow" , (int)d->restriction); |
174 | param.setAttribute("valmin" , converter->asString(d->minValue).asString()); |
175 | param.setAttribute("valmax" , converter->asString(d->maxValue).asString()); |
176 | param.setAttribute("displaymessage" , d->displayMessage); |
177 | param.setAttribute("displayvalidationinformation" , d->displayValidationInformation); |
178 | param.setAttribute("allowemptycell" , d->allowEmptyCell); |
179 | if (!d->listValidity.isEmpty()) |
180 | param.setAttribute("listvalidity" , d->listValidity.join(";" )); |
181 | validityElement.appendChild(param); |
182 | QDomElement titleElement = doc.createElement("title" ); |
183 | titleElement.appendChild(doc.createTextNode(d->title)); |
184 | validityElement.appendChild(titleElement); |
185 | QDomElement messageElement = doc.createElement("message" ); |
186 | messageElement.appendChild(doc.createCDATASection(d->message)); |
187 | validityElement.appendChild(messageElement); |
188 | |
189 | QDomElement inputTitle = doc.createElement("inputtitle" ); |
190 | inputTitle.appendChild(doc.createTextNode(d->titleInfo)); |
191 | validityElement.appendChild(inputTitle); |
192 | |
193 | QDomElement inputMessage = doc.createElement("inputmessage" ); |
194 | inputMessage.appendChild(doc.createTextNode(d->messageInfo)); |
195 | validityElement.appendChild(inputMessage); |
196 | |
197 | |
198 | |
199 | QString tmp; |
200 | if (d->restriction == Time) { |
201 | QDomElement timeMinElement = doc.createElement("timemin" ); |
202 | tmp = converter->asString(d->minValue).asString(); |
203 | timeMinElement.appendChild(doc.createTextNode(tmp)); |
204 | validityElement.appendChild(timeMinElement); |
205 | |
206 | if (d->cond == Conditional::Between || d->cond == Conditional::Different) { |
207 | QDomElement timeMaxElement = doc.createElement("timemax" ); |
208 | tmp = converter->asString(d->maxValue).asString(); |
209 | timeMaxElement.appendChild(doc.createTextNode(tmp)); |
210 | validityElement.appendChild(timeMaxElement); |
211 | } |
212 | } |
213 | |
214 | if (d->restriction == Date) { |
215 | QDomElement dateMinElement = doc.createElement("datemin" ); |
216 | const QDate minDate = d->minValue.asDate(converter->settings()); |
217 | QString tmp("%1/%2/%3" ); |
218 | tmp = tmp.arg(minDate.year()).arg(minDate.month()).arg(minDate.day()); |
219 | dateMinElement.appendChild(doc.createTextNode(tmp)); |
220 | validityElement.appendChild(dateMinElement); |
221 | |
222 | if (d->cond == Conditional::Between || d->cond == Conditional::Different) { |
223 | QDomElement dateMaxElement = doc.createElement("datemax" ); |
224 | const QDate maxDate = d->maxValue.asDate(converter->settings()); |
225 | QString tmp("%1/%2/%3" ); |
226 | tmp = tmp.arg(maxDate.year()).arg(maxDate.month()).arg(maxDate.day()); |
227 | dateMaxElement.appendChild(doc.createTextNode(tmp)); |
228 | validityElement.appendChild(dateMaxElement); |
229 | } |
230 | } |
231 | return validityElement; |
232 | } |
233 | |
234 | |
235 | void Validity::loadOdfValidation(Cell* const cell, const QString& validationName, |
236 | OdfLoadingContext& tableContext) |
237 | { |
238 | KoXmlElement element = tableContext.validities.value(validationName); |
239 | Validity validity; |
240 | if (element.hasAttributeNS(KoXmlNS::table, "condition" )) { |
241 | QString valExpression = element.attributeNS(KoXmlNS::table, "condition" , QString()); |
242 | kDebug(36003) << " element.attribute( table:condition )" << valExpression; |
243 | //Condition ::= ExtendedTrueCondition | TrueFunction 'and' TrueCondition |
244 | //TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time() |
245 | //ExtendedTrueCondition ::= ExtendedGetFunction | cell-content-text-length() Operator Value |
246 | //TrueCondition ::= GetFunction | cell-content() Operator Value |
247 | //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value) |
248 | //ExtendedGetFunction ::= cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value) |
249 | //Operator ::= '<' | '>' | '<=' | '>=' | '=' | '!=' |
250 | //Value ::= NumberValue | String | Formula |
251 | //A Formula is a formula without an equals (=) sign at the beginning. See section 8.1.3 for more information. |
252 | //A String comprises one or more characters surrounded by quotation marks. |
253 | //A NumberValue is a whole or decimal number. It must not contain comma separators for numbers of 1000 or greater. |
254 | |
255 | //ExtendedTrueCondition |
256 | if (valExpression.contains("cell-content-text-length()" )) { |
257 | //"cell-content-text-length()>45" |
258 | valExpression = valExpression.remove("oooc:cell-content-text-length()" ); |
259 | kDebug(36003) << " valExpression = :" << valExpression; |
260 | setRestriction(Validity::TextLength); |
261 | |
262 | loadOdfValidationCondition(valExpression, cell->sheet()->map()->parser()); |
263 | } else if (valExpression.contains("cell-content-is-text()" )) { |
264 | setRestriction(Validity::Text); |
265 | } |
266 | //cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value) | cell-content-is-in-list( StringList ) |
267 | else if (valExpression.contains("cell-content-text-length-is-between" )) { |
268 | setRestriction(Validity::TextLength); |
269 | setCondition(Conditional::Between); |
270 | valExpression.remove("oooc:cell-content-text-length-is-between(" ); |
271 | kDebug(36003) << " valExpression :" << valExpression; |
272 | valExpression.remove(')'); |
273 | QStringList listVal = valExpression.split(',', QString::SkipEmptyParts); |
274 | loadOdfValidationValue(listVal, cell->sheet()->map()->parser()); |
275 | } else if (valExpression.contains("cell-content-text-length-is-not-between" )) { |
276 | setRestriction(Validity::TextLength); |
277 | setCondition(Conditional::Different); |
278 | valExpression.remove("oooc:cell-content-text-length-is-not-between(" ); |
279 | kDebug(36003) << " valExpression :" << valExpression; |
280 | valExpression.remove(')'); |
281 | kDebug(36003) << " valExpression :" << valExpression; |
282 | QStringList listVal = valExpression.split(',', QString::SkipEmptyParts); |
283 | loadOdfValidationValue(listVal, cell->sheet()->map()->parser()); |
284 | } else if (valExpression.contains("cell-content-is-in-list(" )) { |
285 | setRestriction(Validity::List); |
286 | valExpression.remove("oooc:cell-content-is-in-list(" ); |
287 | kDebug(36003) << " valExpression :" << valExpression; |
288 | valExpression.remove(')'); |
289 | setValidityList(valExpression.split(';', QString::SkipEmptyParts)); |
290 | |
291 | } |
292 | //TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time() |
293 | else { |
294 | if (valExpression.contains("cell-content-is-whole-number()" )) { |
295 | setRestriction(Validity::Number); |
296 | valExpression.remove("oooc:cell-content-is-whole-number() and " ); |
297 | } else if (valExpression.contains("cell-content-is-decimal-number()" )) { |
298 | setRestriction(Validity::Integer); |
299 | valExpression.remove("oooc:cell-content-is-decimal-number() and " ); |
300 | } else if (valExpression.contains("cell-content-is-date()" )) { |
301 | setRestriction(Validity::Date); |
302 | valExpression.remove("oooc:cell-content-is-date() and " ); |
303 | } else if (valExpression.contains("cell-content-is-time()" )) { |
304 | setRestriction(Validity::Time); |
305 | valExpression.remove("oooc:cell-content-is-time() and " ); |
306 | } |
307 | kDebug(36003) << "valExpression :" << valExpression; |
308 | |
309 | if (valExpression.contains("cell-content()" )) { |
310 | valExpression.remove("cell-content()" ); |
311 | loadOdfValidationCondition(valExpression, cell->sheet()->map()->parser()); |
312 | } |
313 | //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value) |
314 | //for the moment we support just int/double value, not text/date/time :( |
315 | if (valExpression.contains("cell-content-is-between(" )) { |
316 | valExpression.remove("cell-content-is-between(" ); |
317 | valExpression.remove(')'); |
318 | QStringList listVal = valExpression.split(',', QString::SkipEmptyParts); |
319 | loadOdfValidationValue(listVal, cell->sheet()->map()->parser()); |
320 | setCondition(Conditional::Between); |
321 | } |
322 | if (valExpression.contains("cell-content-is-not-between(" )) { |
323 | valExpression.remove("cell-content-is-not-between(" ); |
324 | valExpression.remove(')'); |
325 | QStringList listVal = valExpression.split(',', QString::SkipEmptyParts); |
326 | loadOdfValidationValue(listVal, cell->sheet()->map()->parser()); |
327 | setCondition(Conditional::Different); |
328 | } |
329 | } |
330 | } |
331 | if (element.hasAttributeNS(KoXmlNS::table, "allow-empty-cell" )) { |
332 | kDebug(36003) << " element.hasAttribute( table:allow-empty-cell ) :" << element.hasAttributeNS(KoXmlNS::table, "allow-empty-cell" ); |
333 | setAllowEmptyCell(((element.attributeNS(KoXmlNS::table, "allow-empty-cell" , QString()) == "true" ) ? true : false)); |
334 | } |
335 | if (element.hasAttributeNS(KoXmlNS::table, "base-cell-address" )) { |
336 | //todo what is it ? |
337 | } |
338 | |
339 | KoXmlElement help = KoXml::namedItemNS(element, KoXmlNS::table, "help-message" ); |
340 | if (!help.isNull()) { |
341 | if (help.hasAttributeNS(KoXmlNS::table, "title" )) { |
342 | kDebug(36003) << "help.attribute( table:title ) :" << help.attributeNS(KoXmlNS::table, "title" , QString()); |
343 | setTitleInfo(help.attributeNS(KoXmlNS::table, "title" , QString())); |
344 | } |
345 | if (help.hasAttributeNS(KoXmlNS::table, "display" )) { |
346 | kDebug(36003) << "help.attribute( table:display ) :" << help.attributeNS(KoXmlNS::table, "display" , QString()); |
347 | setDisplayValidationInformation(((help.attributeNS(KoXmlNS::table, "display" , QString()) == "true" ) ? true : false)); |
348 | } |
349 | KoXmlElement attrText = KoXml::namedItemNS(help, KoXmlNS::text, "p" ); |
350 | if (!attrText.isNull()) { |
351 | kDebug(36003) << "help text :" << attrText.text(); |
352 | setMessageInfo(attrText.text()); |
353 | } |
354 | } |
355 | |
356 | KoXmlElement error = KoXml::namedItemNS(element, KoXmlNS::table, "error-message" ); |
357 | if (!error.isNull()) { |
358 | if (error.hasAttributeNS(KoXmlNS::table, "title" )) |
359 | setTitle(error.attributeNS(KoXmlNS::table, "title" , QString())); |
360 | if (error.hasAttributeNS(KoXmlNS::table, "message-type" )) { |
361 | QString str = error.attributeNS(KoXmlNS::table, "message-type" , QString()); |
362 | if (str == "warning" ) |
363 | setAction(Validity::Warning); |
364 | else if (str == "information" ) |
365 | setAction(Validity::Information); |
366 | else if (str == "stop" ) |
367 | setAction(Validity::Stop); |
368 | else |
369 | kDebug(36003) << "validation : message type unknown :" << str; |
370 | } |
371 | |
372 | if (error.hasAttributeNS(KoXmlNS::table, "display" )) { |
373 | kDebug(36003) << " display message :" << error.attributeNS(KoXmlNS::table, "display" , QString()); |
374 | setDisplayMessage((error.attributeNS(KoXmlNS::table, "display" , QString()) == "true" )); |
375 | } |
376 | KoXmlElement attrText = KoXml::namedItemNS(error, KoXmlNS::text, "p" ); |
377 | if (!attrText.isNull()) |
378 | setMessage(attrText.text()); |
379 | } |
380 | cell->setValidity(validity); |
381 | } |
382 | |
383 | void Validity::loadOdfValidationValue(const QStringList &listVal, const ValueParser *parser) |
384 | { |
385 | bool ok = false; |
386 | kDebug(36003) << " listVal[0] :" << listVal[0] << " listVal[1] :" << listVal[1]; |
387 | |
388 | if (restriction() == Validity::Date) { |
389 | setMinimumValue(parser->tryParseDate(listVal[0])); |
390 | setMaximumValue(parser->tryParseDate(listVal[1])); |
391 | } else if (restriction() == Validity::Time) { |
392 | setMinimumValue(parser->tryParseTime(listVal[0])); |
393 | setMaximumValue(parser->tryParseTime(listVal[1])); |
394 | } else { |
395 | setMinimumValue(Value(listVal[0].toDouble(&ok))); |
396 | if (!ok) { |
397 | setMinimumValue(Value(listVal[0].toInt(&ok))); |
398 | if (!ok) |
399 | kDebug(36003) << " Try to parse this value :" << listVal[0]; |
400 | |
401 | #if 0 |
402 | if (!ok) |
403 | setMinimumValue(listVal[0]); |
404 | #endif |
405 | } |
406 | ok = false; |
407 | setMaximumValue(Value(listVal[1].toDouble(&ok))); |
408 | if (!ok) { |
409 | setMaximumValue(Value(listVal[1].toInt(&ok))); |
410 | if (!ok) |
411 | kDebug(36003) << " Try to parse this value :" << listVal[1]; |
412 | |
413 | #if 0 |
414 | if (!ok) |
415 | setMaximumValue(listVal[1]); |
416 | #endif |
417 | } |
418 | } |
419 | } |
420 | |
421 | void Validity::loadOdfValidationCondition(QString &valExpression, const ValueParser *parser) |
422 | { |
423 | if (isEmpty()) return; |
424 | QString value; |
425 | if (valExpression.indexOf("<=" ) == 0) { |
426 | value = valExpression.remove(0, 2); |
427 | setCondition(Conditional::InferiorEqual); |
428 | } else if (valExpression.indexOf(">=" ) == 0) { |
429 | value = valExpression.remove(0, 2); |
430 | setCondition(Conditional::SuperiorEqual); |
431 | } else if (valExpression.indexOf("!=" ) == 0) { |
432 | //add Differentto attribute |
433 | value = valExpression.remove(0, 2); |
434 | setCondition(Conditional::DifferentTo); |
435 | } else if (valExpression.indexOf('<') == 0) { |
436 | value = valExpression.remove(0, 1); |
437 | setCondition(Conditional::Inferior); |
438 | } else if (valExpression.indexOf('>') == 0) { |
439 | value = valExpression.remove(0, 1); |
440 | setCondition(Conditional::Superior); |
441 | } else if (valExpression.indexOf('=') == 0) { |
442 | value = valExpression.remove(0, 1); |
443 | setCondition(Conditional::Equal); |
444 | } else |
445 | kDebug(36003) << " I don't know how to parse it :" << valExpression; |
446 | if (restriction() == Validity::Date) { |
447 | setMinimumValue(parser->tryParseDate(value)); |
448 | } else if (restriction() == Validity::Date) { |
449 | setMinimumValue(parser->tryParseTime(value)); |
450 | } else { |
451 | bool ok = false; |
452 | setMinimumValue(Value(value.toDouble(&ok))); |
453 | if (!ok) { |
454 | setMinimumValue(Value(value.toInt(&ok))); |
455 | if (!ok) |
456 | kDebug(36003) << " Try to parse this value :" << value; |
457 | |
458 | #if 0 |
459 | if (!ok) |
460 | setMinimumValue(value); |
461 | #endif |
462 | } |
463 | } |
464 | } |
465 | |
466 | Validity::Action Validity::action() const |
467 | { |
468 | return d->action; |
469 | } |
470 | |
471 | bool Validity::allowEmptyCell() const |
472 | { |
473 | return d->allowEmptyCell; |
474 | } |
475 | |
476 | Conditional::Type Validity::condition() const |
477 | { |
478 | return d->cond; |
479 | } |
480 | |
481 | bool Validity::displayMessage() const |
482 | { |
483 | return d->displayMessage; |
484 | } |
485 | |
486 | bool Validity::displayValidationInformation() const |
487 | { |
488 | return d->displayValidationInformation; |
489 | } |
490 | |
491 | const QString& Validity::messageInfo() const |
492 | { |
493 | return d->messageInfo; |
494 | } |
495 | |
496 | const Value &Validity::maximumValue() const |
497 | { |
498 | return d->maxValue; |
499 | } |
500 | |
501 | const QString& Validity::message() const |
502 | { |
503 | return d->message; |
504 | } |
505 | |
506 | const Value &Validity::minimumValue() const |
507 | { |
508 | return d->minValue; |
509 | } |
510 | |
511 | Validity::Restriction Validity::restriction() const |
512 | { |
513 | return d->restriction; |
514 | } |
515 | |
516 | const QString& Validity::title() const |
517 | { |
518 | return d->title; |
519 | } |
520 | |
521 | const QString& Validity::titleInfo() const |
522 | { |
523 | return d->titleInfo; |
524 | } |
525 | |
526 | const QStringList& Validity::validityList() const |
527 | { |
528 | return d->listValidity; |
529 | } |
530 | |
531 | void Validity::setAction(Action action) |
532 | { |
533 | d->action = action; |
534 | } |
535 | |
536 | void Validity::setAllowEmptyCell(bool allow) |
537 | { |
538 | d->allowEmptyCell = allow; |
539 | } |
540 | |
541 | void Validity::setCondition(Conditional::Type condition) |
542 | { |
543 | d->cond = condition; |
544 | } |
545 | |
546 | void Validity::setDisplayMessage(bool display) |
547 | { |
548 | d->displayMessage = display; |
549 | } |
550 | |
551 | void Validity::setDisplayValidationInformation(bool display) |
552 | { |
553 | d->displayValidationInformation = display; |
554 | } |
555 | |
556 | void Validity::setMaximumValue(const Value &value) |
557 | { |
558 | d->maxValue = value; |
559 | } |
560 | |
561 | void Validity::setMessage(const QString& msg) |
562 | { |
563 | d->message = msg; |
564 | } |
565 | |
566 | void Validity::setMessageInfo(const QString& info) |
567 | { |
568 | d->messageInfo = info; |
569 | } |
570 | |
571 | void Validity::setMinimumValue(const Value &value) |
572 | { |
573 | d->minValue = value; |
574 | } |
575 | |
576 | void Validity::setRestriction(Restriction restriction) |
577 | { |
578 | d->restriction = restriction; |
579 | } |
580 | |
581 | void Validity::setTitle(const QString& t) |
582 | { |
583 | d->title = t; |
584 | } |
585 | |
586 | void Validity::setTitleInfo(const QString& info) |
587 | { |
588 | d->titleInfo = info; |
589 | } |
590 | |
591 | void Validity::setValidityList(const QStringList& list) |
592 | { |
593 | d->listValidity = list; |
594 | } |
595 | |
596 | bool Validity::testValidity(const Cell* cell) const |
597 | { |
598 | bool valid = false; |
599 | if (d->restriction != None) { |
600 | //fixme |
601 | if (d->allowEmptyCell && cell->userInput().isEmpty()) |
602 | return true; |
603 | |
604 | ValueCalc *const calc = cell->sheet()->map()->calc(); |
605 | const Qt::CaseSensitivity cs = calc->settings()->caseSensitiveComparisons(); |
606 | |
607 | if ((cell->value().isNumber() && |
608 | (d->restriction == Number || |
609 | (d->restriction == Integer && |
610 | numToDouble(cell->value().asFloat()) == ceil(numToDouble(cell->value().asFloat()))))) |
611 | || (d->restriction == Time && cell->isTime()) |
612 | || (d->restriction == Date && cell->isDate())) { |
613 | switch (d->cond) { |
614 | case Conditional::Equal: |
615 | valid = cell->value().equal(d->minValue, cs); |
616 | break; |
617 | case Conditional::DifferentTo: |
618 | valid = !cell->value().equal(d->minValue, cs); |
619 | break; |
620 | case Conditional::Superior: |
621 | valid = cell->value().greater(d->minValue, cs); |
622 | break; |
623 | case Conditional::Inferior: |
624 | valid = cell->value().less(d->minValue, cs); |
625 | break; |
626 | case Conditional::SuperiorEqual: |
627 | valid = (cell->value().compare(d->minValue, cs)) >= 0; |
628 | break; |
629 | case Conditional::InferiorEqual: |
630 | valid = (cell->value().compare(d->minValue, cs)) <= 0; |
631 | break; |
632 | case Conditional::Between: |
633 | valid = (cell->value().compare(d->minValue, cs) >= 0 && |
634 | cell->value().compare(d->maxValue, cs) <= 0); |
635 | break; |
636 | case Conditional::Different: |
637 | valid = (cell->value().compare(d->minValue, cs) < 0 || |
638 | cell->value().compare(d->maxValue, cs) > 0); |
639 | break; |
640 | default : |
641 | break; |
642 | } |
643 | } else if (d->restriction == Text) { |
644 | valid = cell->value().isString(); |
645 | } else if (d->restriction == List) { |
646 | //test int value |
647 | if (cell->value().isString() && d->listValidity.contains(cell->value().asString())) |
648 | valid = true; |
649 | } else if (d->restriction == TextLength) { |
650 | if (cell->value().isString()) { |
651 | int len = cell->displayText().length(); |
652 | const int min = d->minValue.asInteger(); |
653 | const int max = d->maxValue.asInteger(); |
654 | switch (d->cond) { |
655 | case Conditional::Equal: |
656 | if (len == min) |
657 | valid = true; |
658 | break; |
659 | case Conditional::DifferentTo: |
660 | if (len != min) |
661 | valid = true; |
662 | break; |
663 | case Conditional::Superior: |
664 | if (len > min) |
665 | valid = true; |
666 | break; |
667 | case Conditional::Inferior: |
668 | if (len < min) |
669 | valid = true; |
670 | break; |
671 | case Conditional::SuperiorEqual: |
672 | if (len >= min) |
673 | valid = true; |
674 | break; |
675 | case Conditional::InferiorEqual: |
676 | if (len <= min) |
677 | valid = true; |
678 | break; |
679 | case Conditional::Between: |
680 | if (len >= min && len <= max) |
681 | valid = true; |
682 | break; |
683 | case Conditional::Different: |
684 | if (len < min || len > max) |
685 | valid = true; |
686 | break; |
687 | default : |
688 | break; |
689 | } |
690 | } |
691 | } |
692 | } else { |
693 | valid = true; |
694 | } |
695 | |
696 | if (!valid) { |
697 | if (d->displayMessage) { |
698 | switch (d->action) { |
699 | case Stop: |
700 | KMessageBox::error((QWidget*)0, d->message, d->title); |
701 | break; |
702 | case Warning: |
703 | if (KMessageBox::warningYesNo((QWidget*)0, d->message, d->title) == KMessageBox::Yes) { |
704 | valid = true; |
705 | } |
706 | break; |
707 | case Information: |
708 | KMessageBox::information((QWidget*)0, d->message, d->title); |
709 | valid = true; |
710 | break; |
711 | } |
712 | } |
713 | |
714 | cell->sheet()->showStatusMessage(i18n("Validation for cell %1 failed" , cell->fullName())); |
715 | } |
716 | return valid; |
717 | } |
718 | |
719 | void Validity::operator=(const Validity & other) |
720 | { |
721 | d = other.d; |
722 | } |
723 | |
724 | bool Validity::operator==(const Validity& other) const |
725 | { |
726 | if (d->message == other.d->message && |
727 | d->title == other.d->title && |
728 | d->titleInfo == other.d->titleInfo && |
729 | d->messageInfo == other.d->messageInfo && |
730 | d->minValue == other.d->minValue && |
731 | d->maxValue == other.d->maxValue && |
732 | d->cond == other.d->cond && |
733 | d->action == other.d->action && |
734 | d->restriction == other.d->restriction && |
735 | d->displayMessage == other.d->displayMessage && |
736 | d->allowEmptyCell == other.d->allowEmptyCell && |
737 | d->displayValidationInformation == other.d->displayValidationInformation && |
738 | d->listValidity == other.d->listValidity) { |
739 | return true; |
740 | } |
741 | return false; |
742 | } |
743 | |
744 | // static |
745 | QHash<QString, KoXmlElement> Validity::preloadValidities(const KoXmlElement& body) |
746 | { |
747 | QHash<QString, KoXmlElement> validities; |
748 | KoXmlNode validation = KoXml::namedItemNS(body, KoXmlNS::table, "content-validations" ); |
749 | kDebug() << "validation.isNull?" << validation.isNull(); |
750 | if (!validation.isNull()) { |
751 | KoXmlElement element; |
752 | forEachElement(element, validation) { |
753 | if (element.tagName() == "content-validation" && element.namespaceURI() == KoXmlNS::table) { |
754 | const QString name = element.attributeNS(KoXmlNS::table, "name" , QString()); |
755 | validities.insert(name, element); |
756 | kDebug() << " validation found:" << name; |
757 | } else { |
758 | kDebug() << " Tag not recognized:" << element.tagName(); |
759 | } |
760 | } |
761 | } |
762 | return validities; |
763 | } |
764 | |