File indexing completed on 2024-04-28 16:21:20
0001 /* This file is part of the KDE project 0002 Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org> 0003 Copyright 1998, 1999 Torben Weis <weis@kde.org> 0004 Copyright 1999- 2006 The KSpread Team <calligra-devel@kde.org> 0005 0006 This library is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU Library General Public 0008 License as published by the Free Software Foundation; either 0009 version 2 of the License, or (at your option) any later version. 0010 0011 This library is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 Library General Public License for more details. 0015 0016 You should have received a copy of the GNU Library General Public License 0017 along with this library; see the file COPYING.LIB. If not, write to 0018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 Boston, MA 02110-1301, USA. 0020 */ 0021 0022 // Local 0023 #include "Condition.h" 0024 0025 #include <float.h> 0026 0027 #include "SheetsDebug.h" 0028 #include "CalculationSettings.h" 0029 #include "Cell.h" 0030 #include "Formula.h" 0031 #include "Map.h" 0032 #include "NamedAreaManager.h" 0033 #include "Region.h" 0034 #include "Sheet.h" 0035 #include "Style.h" 0036 #include "StyleManager.h" 0037 #include "ValueCalc.h" 0038 #include "ValueConverter.h" 0039 #include "ValueParser.h" 0040 0041 0042 #include <QDomDocument> 0043 0044 using namespace Calligra::Sheets; 0045 0046 ///////////////////////////////////////////////////////////////////////////// 0047 // 0048 // Conditional 0049 // 0050 ///////////////////////////////////////////////////////////////////////////// 0051 0052 Conditional::Conditional() 0053 : cond(None) 0054 { 0055 } 0056 0057 bool Conditional::operator==(const Conditional &other) const 0058 { 0059 if (cond != other.cond) { 0060 return false; 0061 } 0062 if (!value1.equal(other.value1)) { 0063 return false; 0064 } 0065 if (!value2.equal(other.value2)) { 0066 return false; 0067 } 0068 return styleName == other.styleName; 0069 } 0070 ///////////////////////////////////////////////////////////////////////////// 0071 // 0072 // Conditions 0073 // 0074 ///////////////////////////////////////////////////////////////////////////// 0075 0076 class Q_DECL_HIDDEN Conditions::Private : public QSharedData 0077 { 0078 public: 0079 QLinkedList<Conditional> conditionList; 0080 Style defaultStyle; 0081 }; 0082 0083 Conditions::Conditions() 0084 : d(new Private) 0085 { 0086 } 0087 0088 Conditions::Conditions(const Conditions& other) 0089 : d(other.d) 0090 { 0091 } 0092 0093 Conditions::~Conditions() 0094 { 0095 } 0096 0097 bool Conditions::isEmpty() const 0098 { 0099 return d->conditionList.isEmpty(); 0100 } 0101 0102 Style Conditions::testConditions( const Cell& cell ) const 0103 { 0104 Conditional condition; 0105 if (currentCondition(cell, condition)) { 0106 StyleManager *const styleManager = cell.sheet()->map()->styleManager(); 0107 Style *const style = styleManager->style(condition.styleName); 0108 if (style) 0109 return *style; 0110 } 0111 return d->defaultStyle; 0112 } 0113 0114 bool Conditions::currentCondition(const Cell& cell, Conditional & condition) const 0115 { 0116 /* for now, the first condition that is true is the one that will be used */ 0117 0118 const Value value = cell.value(); 0119 ValueCalc *const calc = cell.sheet()->map()->calc(); 0120 0121 QLinkedList<Conditional>::const_iterator it; 0122 for (it = d->conditionList.begin(); it != d->conditionList.end(); ++it) { 0123 condition = *it; 0124 // debugSheets << "Checking condition resulting in applying" << it->styleName; 0125 0126 // The first value of the condition is always used and has to be 0127 // comparable to the cell's value. 0128 if (!value.allowComparison(condition.value1)) { 0129 continue; 0130 } 0131 0132 switch (condition.cond) { 0133 case Conditional::Equal: 0134 if (value.equal(condition.value1, calc->settings()->caseSensitiveComparisons())) { 0135 return true; 0136 } 0137 break; 0138 case Conditional::Superior: 0139 if (value.greater(condition.value1, calc->settings()->caseSensitiveComparisons())) { 0140 return true; 0141 } 0142 break; 0143 case Conditional::Inferior: 0144 if (value.less(condition.value1, calc->settings()->caseSensitiveComparisons())) { 0145 return true; 0146 } 0147 break; 0148 case Conditional::SuperiorEqual: 0149 if (value.compare(condition.value1, calc->settings()->caseSensitiveComparisons()) >= 0) { 0150 return true; 0151 } 0152 break; 0153 case Conditional::InferiorEqual: 0154 if (value.compare(condition.value1, calc->settings()->caseSensitiveComparisons()) <= 0) { 0155 return true; 0156 } 0157 break; 0158 case Conditional::Between: { 0159 const QVector<Value> values(QVector<Value>() << condition.value1 << condition.value2); 0160 const Value min = calc->min(values); 0161 const Value max = calc->max(values); 0162 if (value.compare(min, calc->settings()->caseSensitiveComparisons()) >= 0 0163 && value.compare(max, calc->settings()->caseSensitiveComparisons()) <= 0) { 0164 return true; 0165 } 0166 break; 0167 } 0168 case Conditional::Different: { 0169 const QVector<Value> values(QVector<Value>() << condition.value1 << condition.value2); 0170 const Value min = calc->min(values); 0171 const Value max = calc->max(values); 0172 if (value.greater(max, calc->settings()->caseSensitiveComparisons()) 0173 || value.less(min, calc->settings()->caseSensitiveComparisons())) { 0174 return true; 0175 } 0176 break; 0177 } 0178 case Conditional::DifferentTo: 0179 if (!value.equal(condition.value1, calc->settings()->caseSensitiveComparisons())) { 0180 return true; 0181 } 0182 break; 0183 case Conditional::IsTrueFormula: 0184 // TODO: do some caching 0185 if (isTrueFormula(cell, condition.value1.asString(), condition.baseCellAddress)) { 0186 return true; 0187 } 0188 break; 0189 default: 0190 break; 0191 } 0192 } 0193 return false; 0194 } 0195 0196 bool Conditions::isTrueFormula(const Cell &cell, const QString &formula, const QString &baseCellAddress) const 0197 { 0198 Map* const map = cell.sheet()->map(); 0199 ValueCalc *const calc = map->calc(); 0200 Formula f(cell.sheet(), cell); 0201 f.setExpression('=' + formula); 0202 Region r(baseCellAddress, map, cell.sheet()); 0203 if (r.isValid() && r.isSingular()) { 0204 QPoint basePoint = static_cast<Region::Point*>(*r.constBegin())->pos(); 0205 QString newFormula('='); 0206 const Tokens tokens = f.tokens(); 0207 for (int t = 0; t < tokens.count(); ++t) { 0208 const Token token = tokens[t]; 0209 if (token.type() == Token::Cell || token.type() == Token::Range) { 0210 if (map->namedAreaManager()->contains(token.text())) { 0211 newFormula.append(token.text()); 0212 continue; 0213 } 0214 const Region region(token.text(), map, cell.sheet()); 0215 if (!region.isValid() || !region.isContiguous()) { 0216 newFormula.append(token.text()); 0217 continue; 0218 } 0219 if (region.firstSheet() != r.firstSheet()) { 0220 newFormula.append(token.text()); 0221 continue; 0222 } 0223 Region::Element* element = *region.constBegin(); 0224 if (element->type() == Region::Element::Point) { 0225 Region::Point* point = static_cast<Region::Point*>(element); 0226 QPoint pos = point->pos(); 0227 if (!point->isRowFixed()) { 0228 int delta = pos.y() - basePoint.y(); 0229 pos.setY(cell.row() + delta); 0230 } 0231 if (!point->isColumnFixed()) { 0232 int delta = pos.x() - basePoint.x(); 0233 pos.setX(cell.column() + delta); 0234 } 0235 newFormula.append(Region(pos, cell.sheet()).name()); 0236 } else { 0237 Region::Range* range = static_cast<Region::Range*>(element); 0238 QRect r = range->rect(); 0239 if (!range->isTopFixed()) { 0240 int delta = r.top() - basePoint.y(); 0241 r.setTop(cell.row() + delta); 0242 } 0243 if (!range->isBottomFixed()) { 0244 int delta = r.bottom() - basePoint.y(); 0245 r.setBottom(cell.row() + delta); 0246 } 0247 if (!range->isLeftFixed()) { 0248 int delta = r.left() - basePoint.x(); 0249 r.setLeft(cell.column() + delta); 0250 } 0251 if (!range->isRightFixed()) { 0252 int delta = r.right() - basePoint.x(); 0253 r.setRight(cell.column() + delta); 0254 } 0255 newFormula.append(Region(r, cell.sheet()).name()); 0256 } 0257 } else { 0258 newFormula.append(token.text()); 0259 } 0260 } 0261 f.setExpression(newFormula); 0262 } 0263 Value val = f.eval(); 0264 return calc->conv()->asBoolean(val).asBoolean(); 0265 } 0266 0267 QLinkedList<Conditional> Conditions::conditionList() const 0268 { 0269 return d->conditionList; 0270 } 0271 0272 void Conditions::setConditionList(const QLinkedList<Conditional> & list) 0273 { 0274 d->conditionList = list; 0275 } 0276 0277 Style Conditions::defaultStyle() const 0278 { 0279 return d->defaultStyle; 0280 } 0281 0282 void Conditions::setDefaultStyle(const Style &style) 0283 { 0284 d->defaultStyle = style; 0285 } 0286 0287 void Conditions::addCondition(Conditional cond) 0288 { 0289 d->conditionList.append(cond); 0290 } 0291 0292 0293 QDomElement Conditions::saveConditions(QDomDocument &doc, ValueConverter *converter) const 0294 { 0295 QDomElement conditions = doc.createElement("condition"); 0296 QLinkedList<Conditional>::const_iterator it; 0297 QDomElement child; 0298 int num = 0; 0299 QString name; 0300 0301 for (it = d->conditionList.begin(); it != d->conditionList.end(); ++it) { 0302 Conditional condition = *it; 0303 0304 /* the name of the element will be "condition<n>" 0305 * This is unimportant now but in older versions three conditions were 0306 * hardcoded with names "first" "second" and "third" 0307 */ 0308 name.setNum(num); 0309 name.prepend("condition"); 0310 0311 child = doc.createElement(name); 0312 child.setAttribute("cond", QString::number((int) condition.cond)); 0313 0314 // TODO: saving in KSpread 1.1 | KSpread 1.2 format 0315 if (condition.value1.isString()) { 0316 child.setAttribute("strval1", condition.value1.asString()); 0317 if (!condition.value2.asString().isEmpty()) { 0318 child.setAttribute("strval2", condition.value2.asString()); 0319 } 0320 } else { 0321 child.setAttribute("val1", converter->asString(condition.value1).asString()); 0322 child.setAttribute("val2", converter->asString(condition.value2).asString()); 0323 } 0324 if (!condition.styleName.isEmpty()) { 0325 child.setAttribute("style", condition.styleName); 0326 } 0327 0328 conditions.appendChild(child); 0329 0330 ++num; 0331 } 0332 0333 if (num == 0) { 0334 /* there weren't any real conditions -- return a null dom element */ 0335 return QDomElement(); 0336 } else { 0337 return conditions; 0338 } 0339 } 0340 0341 void Conditions::loadConditions(const KoXmlElement &element, const ValueParser *parser) 0342 { 0343 Conditional newCondition; 0344 0345 KoXmlElement conditionElement; 0346 forEachElement(conditionElement, element) { 0347 if (!conditionElement.hasAttribute("cond")) 0348 continue; 0349 0350 bool ok = true; 0351 newCondition.cond = (Conditional::Type) conditionElement.attribute("cond").toInt(&ok); 0352 if(!ok) 0353 continue; 0354 0355 if (conditionElement.hasAttribute("val1")) { 0356 newCondition.value1 = parser->parse(conditionElement.attribute("val1")); 0357 0358 if (conditionElement.hasAttribute("val2")) 0359 newCondition.value2 = parser->parse(conditionElement.attribute("val2")); 0360 } 0361 0362 if (conditionElement.hasAttribute("strval1")) { 0363 newCondition.value1 = Value(conditionElement.attribute("strval1")); 0364 0365 if (conditionElement.hasAttribute("strval2")) 0366 newCondition.value2 = Value(conditionElement.attribute("strval2")); 0367 } 0368 0369 if (conditionElement.hasAttribute("style")) { 0370 newCondition.styleName = conditionElement.attribute("style"); 0371 } 0372 0373 d->conditionList.append(newCondition); 0374 } 0375 } 0376 0377 void Conditions::operator=(const Conditions & other) 0378 { 0379 d = other.d; 0380 } 0381 0382 bool Conditions::operator==(const Conditions& other) const 0383 { 0384 if (d->defaultStyle != other.d->defaultStyle) 0385 return false; 0386 if (d->conditionList.count() != other.d->conditionList.count()) 0387 return false; 0388 QLinkedList<Conditional>::ConstIterator end(d->conditionList.end()); 0389 for (QLinkedList<Conditional>::ConstIterator it(d->conditionList.begin()); it != end; ++it) { 0390 bool found = false; 0391 QLinkedList<Conditional>::ConstIterator otherEnd(other.d->conditionList.end()); 0392 for (QLinkedList<Conditional>::ConstIterator otherIt(other.d->conditionList.begin()); otherIt != otherEnd; ++otherIt) { 0393 if ((*it) == (*otherIt)) 0394 found = true; 0395 } 0396 if (!found) 0397 return false; 0398 } 0399 return true; 0400 } 0401 0402 uint Calligra::Sheets::qHash(const Conditions &c) 0403 { 0404 uint res = qHash(c.defaultStyle()); 0405 foreach (const Conditional& co, c.conditionList()) { 0406 res ^= qHash(co); 0407 } 0408 return res; 0409 } 0410 0411 uint Calligra::Sheets::qHash(const Conditional& c) 0412 { 0413 return qHash(c.value1); 0414 }