45
53
/////////////////////////////////////////////////////////////////////////////
47
55
Conditional::Conditional()
59
Conditional::~Conditional()
68
Conditional::Conditional(const Conditional& o)
70
strVal1 = o.strVal1 ? new QString(*o.strVal1) : 0;
71
strVal2 = o.strVal2 ? new QString(*o.strVal2) : 0;
72
styleName = o.styleName ? new QString(*o.styleName) : 0;
73
fontcond = o.fontcond ? new QFont(*o.fontcond) : 0;
74
colorcond = o.colorcond ? new QColor(*o.colorcond) : 0;
80
Conditional& Conditional::operator=(const Conditional & o)
87
strVal1 = o.strVal1 ? new QString(*o.strVal1) : 0;
88
strVal2 = o.strVal2 ? new QString(*o.strVal2) : 0;
89
styleName = o.styleName ? new QString(*o.styleName) : 0;
90
fontcond = o.fontcond ? new QFont(*o.fontcond) : 0;
91
colorcond = o.colorcond ? new QColor(*o.colorcond) : 0;
99
bool Conditional::operator==(const Conditional& other) const
101
return (cond == other.cond &&
102
val1 == other.val1 &&
103
val2 == other.val2 &&
104
(strVal1 && other.strVal1) ? (*strVal1 == *other.strVal1) : (strVal1 == other.strVal1) &&
105
(strVal2 && other.strVal2) ? (*strVal2 == *other.strVal2) : (strVal2 == other.strVal2) &&
106
(colorcond && other.strVal2) ? (*colorcond == *other.colorcond) : (colorcond == other.colorcond) &&
107
(fontcond && other.fontcond) ? (*fontcond == *other.fontcond) : (fontcond == other.fontcond) &&
108
(styleName && other.styleName) ? (*styleName == *other.styleName) : (styleName == other.styleName) );
60
bool Conditional::operator==(const Conditional &other) const
62
if (cond != other.cond) {
65
if (!value1.equal(other.value1)) {
68
if (!value2.equal(other.value2)) {
71
return styleName == other.styleName;
112
73
/////////////////////////////////////////////////////////////////////////////
140
102
return d->conditionList.isEmpty();
143
Style* Conditions::testConditions(const Cell& cell, const StyleManager* styleManager) const
105
Style Conditions::testConditions( const Cell& cell ) const
145
107
Conditional condition;
146
if (currentCondition(cell, condition) && condition.styleName)
147
return styleManager->style(*condition.styleName);
108
if (currentCondition(cell, condition)) {
109
StyleManager *const styleManager = cell.sheet()->map()->styleManager();
110
Style *const style = styleManager->style(condition.styleName);
114
return d->defaultStyle;
152
117
bool Conditions::currentCondition(const Cell& cell, Conditional & condition) const
154
119
/* for now, the first condition that is true is the one that will be used */
121
const Value value = cell.value();
122
ValueCalc *const calc = cell.sheet()->map()->calc();
156
124
QLinkedList<Conditional>::const_iterator it;
157
double value = numToDouble(cell.value().asFloat());
158
QString strVal = cell.value().asString();
161
125
for (it = d->conditionList.begin(); it != d->conditionList.end(); ++it) {
127
// kDebug() << "Checking condition resulting in applying" << it->styleName;
164
if (condition.strVal1 && cell.value().isNumber())
129
// The first value of the condition is always used and has to be
130
// comparable to the cell's value.
131
if (!value.allowComparison(condition.value1)) {
167
135
switch (condition.cond) {
168
136
case Conditional::Equal:
169
if (condition.strVal1) {
170
if (strVal == *condition.strVal1)
173
if (value - condition.val1 < DBL_EPSILON &&
174
value - condition.val1 > (0.0 - DBL_EPSILON)) {
137
if (value.equal(condition.value1)) {
179
141
case Conditional::Superior:
180
if (condition.strVal1) {
181
if (strVal > *condition.strVal1)
184
if (value > condition.val1) {
142
if (value.greater(condition.value1)) {
189
146
case Conditional::Inferior:
190
if (condition.strVal1) {
191
if (strVal < *condition.strVal1)
194
if (value < condition.val1) {
199
case Conditional::SuperiorEqual :
200
if (condition.strVal1) {
201
if (strVal >= *condition.strVal1)
204
if (value >= condition.val1) {
209
case Conditional::InferiorEqual :
210
if (condition.strVal1) {
211
if (strVal <= *condition.strVal1)
214
if (value <= condition.val1) {
219
case Conditional::Between :
220
if (condition.strVal1 && condition.strVal2) {
221
if (strVal > *condition.strVal1 && strVal < *condition.strVal2)
224
if ((value > qMin(condition.val1, condition.val2))
225
&& (value < qMax(condition.val1, condition.val2))) {
230
case Conditional::Different :
231
if (condition.strVal1 && condition.strVal2) {
232
if (strVal < *condition.strVal1 || strVal > *condition.strVal2)
235
if ((value < qMin(condition.val1, condition.val2))
236
|| (value > qMax(condition.val1, condition.val2))) {
240
case Conditional::DifferentTo :
241
if (condition.strVal1) {
242
if (strVal != *condition.strVal1)
245
if (value != condition.val1) {
147
if (value.less(condition.value1)) {
151
case Conditional::SuperiorEqual:
152
if (value.compare(condition.value1) >= 0) {
156
case Conditional::InferiorEqual:
157
if (value.compare(condition.value1) <= 0) {
161
case Conditional::Between: {
162
const QVector<Value> values(QVector<Value>() << condition.value1 << condition.value2);
163
const Value min = calc->min(values);
164
const Value max = calc->max(values);
165
if (value.compare(min) >= 0 && value.compare(max) <= 0) {
170
case Conditional::Different: {
171
const QVector<Value> values(QVector<Value>() << condition.value1 << condition.value2);
172
const Value min = calc->min(values);
173
const Value max = calc->max(values);
174
if (value.greater(max) || value.less(min)) {
179
case Conditional::DifferentTo:
180
if (!value.equal(condition.value1)) {
184
case Conditional::IsTrueFormula:
185
// TODO: do some caching
186
if (isTrueFormula(cell, condition.value1.asString(), condition.baseCellAddress)) {
197
bool Conditions::isTrueFormula(const Cell &cell, const QString &formula, const QString &baseCellAddress) const
199
Map* const map = cell.sheet()->map();
200
ValueCalc *const calc = map->calc();
201
Formula f(cell.sheet(), cell);
202
f.setExpression('=' + formula);
203
Region r(baseCellAddress, map, cell.sheet());
204
if (r.isValid() && r.isSingular()) {
205
QPoint basePoint = static_cast<Region::Point*>(*r.constBegin())->pos();
206
QString newFormula('=');
207
const Tokens tokens = f.tokens();
208
for (int t = 0; t < tokens.count(); ++t) {
209
const Token token = tokens[t];
210
if (token.type() == Token::Cell || token.type() == Token::Range) {
211
if (map->namedAreaManager()->contains(token.text())) {
212
newFormula.append(token.text());
215
const Region region(token.text(), map, cell.sheet());
216
if (!region.isValid() || !region.isContiguous()) {
217
newFormula.append(token.text());
220
if (region.firstSheet() != r.firstSheet()) {
221
newFormula.append(token.text());
224
Region::Element* element = *region.constBegin();
225
if (element->type() == Region::Element::Point) {
226
Region::Point* point = static_cast<Region::Point*>(element);
227
QPoint pos = point->pos();
228
if (!point->isRowFixed()) {
229
int delta = pos.y() - basePoint.y();
230
pos.setY(cell.row() + delta);
232
if (!point->isColumnFixed()) {
233
int delta = pos.x() - basePoint.x();
234
pos.setX(cell.column() + delta);
236
newFormula.append(Region(pos, cell.sheet()).name());
238
Region::Range* range = static_cast<Region::Range*>(element);
239
QRect r = range->rect();
240
if (!range->isTopFixed()) {
241
int delta = r.top() - basePoint.y();
242
r.setTop(cell.row() + delta);
244
if (!range->isBottomFixed()) {
245
int delta = r.bottom() - basePoint.y();
246
r.setBottom(cell.row() + delta);
248
if (!range->isLeftFixed()) {
249
int delta = r.left() - basePoint.x();
250
r.setLeft(cell.column() + delta);
252
if (!range->isRightFixed()) {
253
int delta = r.right() - basePoint.x();
254
r.setRight(cell.column() + delta);
256
newFormula.append(Region(r, cell.sheet()).name());
259
newFormula.append(token.text());
262
f.setExpression(newFormula);
264
Value val = f.eval();
265
return calc->conv()->asBoolean(val).asBoolean();
257
268
QLinkedList<Conditional> Conditions::conditionList() const
259
270
return d->conditionList;
275
296
Conditional condition = *it;
276
297
//<style:map style:condition="cell-content()=45" style:apply-style-name="Default" style:base-cell-address="Sheet1.E10"/>
277
298
QMap<QString, QString> map;
278
map.insert("style:condition", saveOdfConditionValue(condition));
279
map.insert("style:apply-style-name", *(condition.styleName));
280
//map.insert( ""style:base-cell-address", "..." );//todo
299
map.insert("style:condition", saveOdfConditionValue(condition, converter));
300
map.insert("style:apply-style-name", condition.styleName);
301
if (!condition.baseCellAddress.isEmpty())
302
map.insert("style:base-cell-address", condition.baseCellAddress);
281
303
currentCellStyle.addStyleMap(map);
285
QString Conditions::saveOdfConditionValue(Conditional &condition) const
307
QString Conditions::saveOdfConditionValue(const Conditional &condition, ValueConverter* converter) const
287
309
//we can also compare text value.
291
313
case Conditional::None:
293
315
case Conditional::Equal:
294
value = "cell-content()=";
295
if (condition.strVal1)
296
value += *condition.strVal1;
298
value += QString::number(condition.val1);
316
value = "cell-content()=" + converter->asString(condition.value1).asString();
300
318
case Conditional::Superior:
301
value = "cell-content()>";
302
if (condition.strVal1)
303
value += *condition.strVal1;
305
value += QString::number(condition.val1);
319
value = "cell-content()>" + converter->asString(condition.value1).asString();
307
321
case Conditional::Inferior:
308
value = "cell-content()<";
309
if (condition.strVal1)
310
value += *condition.strVal1;
312
value += QString::number(condition.val1);
322
value = "cell-content()<" + converter->asString(condition.value1).asString();
314
324
case Conditional::SuperiorEqual:
315
value = "cell-content()>=";
316
if (condition.strVal1)
317
value += *condition.strVal1;
319
value += QString::number(condition.val1);
325
value = "cell-content()>=" + converter->asString(condition.value1).asString();
321
327
case Conditional::InferiorEqual:
322
value = "cell-content()<=";
323
if (condition.strVal1)
324
value += *condition.strVal1;
326
value += QString::number(condition.val1);
328
value = "cell-content()<=" + converter->asString(condition.value1).asString();
328
330
case Conditional::Between:
329
331
value = "cell-content-is-between(";
330
if (condition.strVal1) {
331
value += *condition.strVal1;
333
if (condition.strVal2)
334
value += *condition.strVal2;
336
value += QString::number(condition.val1);
338
value += QString::number(condition.val2);
332
value += converter->asString(condition.value1).asString();
334
value += converter->asString(condition.value2).asString();
342
337
case Conditional::DifferentTo:
343
value = "cell-content()!="; //FIXME not good here !
344
if (condition.strVal1)
345
value += *condition.strVal1;
347
value += QString::number(condition.val1);
338
value = "cell-content()!=" + converter->asString(condition.value1).asString();
349
340
case Conditional::Different:
350
341
value = "cell-content-is-not-between(";
351
if (condition.strVal1) {
352
value += *condition.strVal1;
354
if (condition.strVal2)
355
value += *condition.strVal2;
357
value += QString::number(condition.val1);
359
value += QString::number(condition.val2);
342
value += converter->asString(condition.value1).asString();
344
value += converter->asString(condition.value2).asString();
347
case Conditional::IsTrueFormula:
348
value = "is-true-formula(";
349
value += Odf::encodeFormula(condition.value1.asString());
368
QDomElement Conditions::saveConditions(QDomDocument & doc) const
356
QDomElement Conditions::saveConditions(QDomDocument &doc, ValueConverter *converter) const
370
358
QDomElement conditions = doc.createElement("condition");
371
359
QLinkedList<Conditional>::const_iterator it;
387
375
child.setAttribute("cond", (int) condition.cond);
389
377
// TODO: saving in KSpread 1.1 | KSpread 1.2 format
390
if (condition.strVal1) {
391
child.setAttribute("strval1", *condition.strVal1);
392
if (condition.strVal2)
393
child.setAttribute("strval2", *condition.strVal2);
378
if (condition.value1.isString()) {
379
child.setAttribute("strval1", condition.value1.asString());
380
if (!condition.value2.asString().isEmpty()) {
381
child.setAttribute("strval2", condition.value2.asString());
395
child.setAttribute("val1", condition.val1);
396
child.setAttribute("val2", condition.val2);
384
child.setAttribute("val1", converter->asString(condition.value1).asString());
385
child.setAttribute("val2", converter->asString(condition.value2).asString());
398
if (condition.styleName) {
399
child.setAttribute("style", *condition.styleName);
401
child.setAttribute("color", condition.colorcond->name());
402
child.appendChild(NativeFormat::createElement("font", *condition.fontcond, doc));
387
if (!condition.styleName.isEmpty()) {
388
child.setAttribute("style", condition.styleName);
405
391
conditions.appendChild(child);
418
Conditional Conditions::loadOdfCondition(const StyleManager* styleManager, const QString &conditionValue, const QString &applyStyleName)
404
Conditional Conditions::loadOdfCondition(const QString &conditionValue, const QString &applyStyleName,
405
const QString& baseCellAddress, const ValueParser *parser)
420
Q_UNUSED(styleManager);
421
kDebug(36003) << "\tcondition:" << conditionValue;
407
//kDebug(36003) << "\tcondition:" << conditionValue;
422
408
Conditional newCondition;
423
loadOdfConditionValue(conditionValue, newCondition);
409
loadOdfConditionValue(conditionValue, newCondition, parser);
424
410
if (!applyStyleName.isNull()) {
425
kDebug(36003) << "\tstyle:" << applyStyleName;
426
newCondition.styleName = new QString(applyStyleName);
411
//kDebug(36003) << "\tstyle:" << applyStyleName;
412
newCondition.styleName = applyStyleName;
414
newCondition.baseCellAddress = baseCellAddress;
428
415
d->conditionList.append(newCondition);
429
416
return newCondition;
432
void Conditions::loadOdfConditions(const StyleManager* styleManager, const KoXmlElement & element)
419
void Conditions::loadOdfConditions(const KoXmlElement &element, const ValueParser *parser, const StyleManager *styleManager)
434
421
kDebug(36003) << "Loading conditional styles";
435
422
KoXmlNode node(element);
436
424
while (!node.isNull()) {
437
425
KoXmlElement elementItem = node.toElement();
438
426
if (elementItem.tagName() == "map" && elementItem.namespaceURI() == KoXmlNS::style) {
440
428
QString applyStyleName;
441
429
if (elementItem.hasAttributeNS(KoXmlNS::style, "apply-style-name"))
442
430
applyStyleName = elementItem.attributeNS(KoXmlNS::style, "apply-style-name", QString());
443
loadOdfCondition(styleManager, conditionValue, applyStyleName);
431
if (!applyStyleName.isEmpty() && styleManager) {
432
QString odfStyle = styleManager->openDocumentName(applyStyleName);
433
if (!odfStyle.isEmpty()) applyStyleName = odfStyle;
435
QString baseCellAddress = elementItem.attributeNS(KoXmlNS::style, "base-cell-address");
436
loadOdfCondition(conditionValue, applyStyleName, baseCellAddress, parser);
445
438
node = node.nextSibling();
449
void Conditions::loadOdfConditionValue(const QString &styleCondition, Conditional &newCondition)
442
void Conditions::loadOdfConditionValue(const QString &styleCondition, Conditional &newCondition, const ValueParser *parser)
451
444
QString val(styleCondition);
452
445
if (val.contains("cell-content()")) {
453
446
val = val.remove("cell-content()");
454
loadOdfCondition(val, newCondition);
456
else if (val.contains("value()")) {
447
loadOdfCondition(val, newCondition, parser);
448
} else if (val.contains("value()")) {
457
449
val = val.remove("value()");
458
loadOdfCondition(val, newCondition);
450
loadOdfCondition(val, newCondition, parser);
461
453
//GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
464
456
val = val.remove("cell-content-is-between(");
465
457
val = val.remove(')');
466
458
QStringList listVal = val.split(',', QString::SkipEmptyParts);
467
loadOdfValidationValue(listVal, newCondition);
459
loadOdfValidationValue(listVal, newCondition, parser);
468
460
newCondition.cond = Conditional::Between;
470
else if (val.contains("cell-content-is-not-between(")) {
461
} else if (val.contains("cell-content-is-not-between(")) {
471
462
val = val.remove("cell-content-is-not-between(");
472
463
val = val.remove(')');
473
464
QStringList listVal = val.split(',', QString::SkipEmptyParts);
474
loadOdfValidationValue(listVal, newCondition);
465
loadOdfValidationValue(listVal, newCondition, parser);
475
466
newCondition.cond = Conditional::Different;
467
} else if (val.startsWith("is-true-formula(")) {
469
if (val.endsWith(")")) val = val.left(val.length() - 1);
470
newCondition.cond = Conditional::IsTrueFormula;
471
newCondition.value1 = Value(Odf::decodeFormula(val));
479
void Conditions::loadOdfCondition(QString &valExpression, Conditional &newCondition)
475
void Conditions::loadOdfCondition(QString &valExpression, Conditional &newCondition, const ValueParser *parser)
482
478
if (valExpression.indexOf("<=") == 0) {
500
496
newCondition.cond = Conditional::Equal;
502
498
kDebug(36003) << " I don't know how to parse it :" << valExpression;
503
kDebug(36003) << "\tvalue:" << value;
505
newCondition.val1 = value.toDouble(&ok);
507
newCondition.val1 = value.toInt(&ok);
509
newCondition.strVal1 = new QString(value);
510
kDebug(36003) << " Try to parse this value :" << value;
499
//kDebug(36003) << "\tvalue:" << value;
501
if (value.length() > 1 && value[0] == '"' && value[value.length()-1] == '"') {
502
newCondition.value1 = Value(value.mid(1, value.length()-2));
504
newCondition.value1 = parser->parse(value);
515
void Conditions::loadOdfValidationValue(const QStringList &listVal, Conditional &newCondition)
508
void Conditions::loadOdfValidationValue(const QStringList &listVal, Conditional &newCondition, const ValueParser *parser)
518
510
kDebug(36003) << " listVal[0] :" << listVal[0] << " listVal[1] :" << listVal[1];
520
newCondition.val1 = listVal[0].toDouble(&ok);
522
newCondition.val1 = listVal[0].toInt(&ok);
524
newCondition.strVal1 = new QString(listVal[0]);
525
kDebug(36003) << " Try to parse this value :" << listVal[0];
529
newCondition.val2 = listVal[1].toDouble(&ok);
531
newCondition.val2 = listVal[1].toInt(&ok);
533
newCondition.strVal2 = new QString(listVal[1]);
534
kDebug(36003) << " Try to parse this value :" << listVal[1];
511
newCondition.value1 = parser->parse(listVal[0]);
512
newCondition.value2 = parser->parse(listVal[1]);
539
void Conditions::loadConditions(const StyleManager* styleManager, const KoXmlElement & element)
515
void Conditions::loadConditions(const KoXmlElement &element, const ValueParser *parser)
541
Q_UNUSED(styleManager);
542
517
Conditional newCondition;
544
519
KoXmlElement conditionElement;
545
520
forEachElement(conditionElement, element) {
546
newCondition.strVal1 = 0;
547
newCondition.strVal2 = 0;
548
newCondition.styleName = 0;
549
newCondition.fontcond = 0;
550
newCondition.colorcond = 0;
552
521
if (!conditionElement.hasAttribute("cond"))
560
529
if (conditionElement.hasAttribute("val1")) {
561
newCondition.val1 = conditionElement.attribute("val1").toDouble(&ok);
530
newCondition.value1 = parser->parse(conditionElement.attribute("val1"));
563
532
if (conditionElement.hasAttribute("val2"))
564
newCondition.val2 = conditionElement.attribute("val2").toDouble(&ok);
533
newCondition.value2 = parser->parse(conditionElement.attribute("val2"));
567
536
if (conditionElement.hasAttribute("strval1")) {
568
newCondition.strVal1 = new QString(conditionElement.attribute("strval1"));
537
newCondition.value1 = Value(conditionElement.attribute("strval1"));
570
539
if (conditionElement.hasAttribute("strval2"))
571
newCondition.strVal2 = new QString(conditionElement.attribute("strval2"));
574
if (conditionElement.hasAttribute("color")) {
575
QColor color(conditionElement.attribute("color"));
577
newCondition.colorcond = new QColor(color);
580
KoXmlElement font = conditionElement.namedItem("font").toElement();
582
newCondition.fontcond = new QFont(NativeFormat::toFont(font));
540
newCondition.value2 = Value(conditionElement.attribute("strval2"));
584
543
if (conditionElement.hasAttribute("style")) {
585
newCondition.styleName = new QString(conditionElement.attribute("style"));
544
newCondition.styleName = conditionElement.attribute("style");
588
547
d->conditionList.append(newCondition);