File indexing completed on 2024-04-28 16:21:34
0001 /* This file is part of the KDE project 0002 Copyright 2006,2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net> 0003 Copyright 1998,1999 Torben Weis <weis@kde.org> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Library General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Library General Public License for more details. 0014 0015 You should have received a copy of the GNU Library General Public License 0016 along with this library; see the file COPYING.LIB. If not, write to 0017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 Boston, MA 02110-1301, USA. 0019 */ 0020 0021 // Local 0022 #include "Util.h" 0023 0024 #include <ctype.h> 0025 0026 #include <QPen> 0027 0028 #include <KCharsets> 0029 0030 #include <KoUnit.h> 0031 0032 #include "SheetsDebug.h" 0033 #include "Formula.h" 0034 #include "calligra_sheets_limits.h" 0035 #include "Localization.h" 0036 #include "Map.h" 0037 #include "NamedAreaManager.h" 0038 #include "Region.h" 0039 #include "Sheet.h" 0040 #include "Style.h" 0041 #include "odf/SheetsOdf.h" 0042 0043 using namespace Calligra::Sheets; 0044 0045 0046 //used in Cell::encodeFormula and 0047 // dialogs/kspread_dlg_paperlayout.cc 0048 int Calligra::Sheets::Util::decodeColumnLabelText(const QString &labelText) 0049 { 0050 int col = 0; 0051 const int offset = 'a' - 'A'; 0052 int counterColumn = 0; 0053 const uint totalLength = labelText.length(); 0054 uint labelTextLength = 0; 0055 for ( ; labelTextLength < totalLength; labelTextLength++) { 0056 const char c = labelText[labelTextLength].toLatin1(); 0057 if (labelTextLength == 0 && c == '$') 0058 continue; // eat an absolute reference char that could be at the beginning only 0059 if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) 0060 break; 0061 } 0062 if (labelTextLength == 0) { 0063 warnSheets << "No column label text found for col:" << labelText; 0064 return 0; 0065 } 0066 for (uint i = 0; i < labelTextLength; i++) { 0067 const char c = labelText[i].toLatin1(); 0068 counterColumn = (int) ::pow(26.0 , static_cast<int>(labelTextLength - i - 1)); 0069 if (c >= 'A' && c <= 'Z') 0070 col += counterColumn * (c - 'A' + 1); // okay here (Werner) 0071 else if (c >= 'a' && c <= 'z') 0072 col += counterColumn * (c - 'A' - offset + 1); 0073 } 0074 return col; 0075 } 0076 0077 int Calligra::Sheets::Util::decodeRowLabelText(const QString &labelText) 0078 { 0079 QRegExp rx("(|\\$)([A-Za-z]+)(|\\$)([0-9]+)"); 0080 if(rx.exactMatch(labelText)) 0081 return rx.cap(4).toInt(); 0082 return 0; 0083 } 0084 0085 QString Calligra::Sheets::Util::encodeColumnLabelText(int column) 0086 { 0087 return Cell::columnName(column); 0088 } 0089 0090 bool Calligra::Sheets::Util::isCellReference(const QString &text, int startPos) 0091 { 0092 int length = text.length(); 0093 if (length < 1 || startPos >= length) 0094 return false; 0095 0096 const QChar *data = text.constData(); 0097 0098 if (startPos > 0) { 0099 data += startPos; 0100 } 0101 0102 if (*data == QChar('$', 0)) { 0103 ++data; 0104 } 0105 0106 bool letterFound = false; 0107 while (1) { 0108 if (data->isNull()) { 0109 return false; 0110 } 0111 0112 ushort c = data->unicode(); 0113 if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) 0114 break; 0115 0116 letterFound = true; 0117 ++data; 0118 } 0119 0120 if (!letterFound) { 0121 return false; 0122 } 0123 0124 if (*data == QChar('$', 0)) { 0125 ++data; 0126 } 0127 0128 bool numberFound = false; 0129 while (!data->isNull()) { 0130 ushort c = data->unicode(); 0131 if (c < '0' || c > '9') 0132 break; 0133 numberFound = true; 0134 ++data; 0135 } 0136 0137 return numberFound && data->isNull(); // we found the number and reached end 0138 } 0139 0140 QDomElement Calligra::Sheets::NativeFormat::createElement(const QString & tagName, const QFont & font, QDomDocument & doc) 0141 { 0142 QDomElement e(doc.createElement(tagName)); 0143 0144 e.setAttribute("family", font.family()); 0145 e.setAttribute("size", QString::number(font.pointSize())); 0146 e.setAttribute("weight", QString::number(font.weight())); 0147 if (font.bold()) 0148 e.setAttribute("bold", "yes"); 0149 if (font.italic()) 0150 e.setAttribute("italic", "yes"); 0151 if (font.underline()) 0152 e.setAttribute("underline", "yes"); 0153 if (font.strikeOut()) 0154 e.setAttribute("strikeout", "yes"); 0155 //e.setAttribute( "charset", KCharsets::charsets()->name( font ) ); 0156 0157 return e; 0158 } 0159 0160 QDomElement Calligra::Sheets::NativeFormat::createElement(const QString & tagname, const QPen & pen, QDomDocument & doc) 0161 { 0162 QDomElement e(doc.createElement(tagname)); 0163 e.setAttribute("color", pen.color().name()); 0164 e.setAttribute("style", QString::number((int)pen.style())); 0165 e.setAttribute("width", QString::number((int)pen.width())); // not qreal, would need spec change 0166 return e; 0167 } 0168 0169 QFont Calligra::Sheets::NativeFormat::toFont(KoXmlElement & element) 0170 { 0171 QFont f; 0172 f.setFamily(element.attribute("family")); 0173 0174 bool ok; 0175 const int size = element.attribute("size").toInt(&ok); 0176 if (ok) 0177 f.setPointSize(size); 0178 0179 const int weight = element.attribute("weight").toInt(&ok); 0180 if (!ok) 0181 f.setWeight(weight); 0182 0183 if (element.hasAttribute("italic") && element.attribute("italic") == "yes") 0184 f.setItalic(true); 0185 0186 if (element.hasAttribute("bold") && element.attribute("bold") == "yes") 0187 f.setBold(true); 0188 0189 if (element.hasAttribute("underline") && element.attribute("underline") == "yes") 0190 f.setUnderline(true); 0191 0192 if (element.hasAttribute("strikeout") && element.attribute("strikeout") == "yes") 0193 f.setStrikeOut(true); 0194 0195 /* Uncomment when charset is added to kspread_dlg_layout 0196 + save a document-global charset 0197 if ( element.hasAttribute( "charset" ) ) 0198 KCharsets::charsets()->setQFont( f, element.attribute("charset") ); 0199 else 0200 */ 0201 // ######## Not needed anymore in 3.0? 0202 //KCharsets::charsets()->setQFont( f, KLocale::global()->charset() ); 0203 0204 return f; 0205 } 0206 0207 QPen Calligra::Sheets::NativeFormat::toPen(KoXmlElement & element) 0208 { 0209 bool ok; 0210 QPen p; 0211 0212 p.setStyle((Qt::PenStyle)element.attribute("style").toInt(&ok)); 0213 if (!ok) 0214 return QPen(); 0215 0216 p.setWidth(element.attribute("width").toInt(&ok)); 0217 if (!ok) 0218 return QPen(); 0219 0220 p.setColor(QColor(element.attribute("color"))); 0221 0222 return p; 0223 } 0224 0225 bool util_isPointValid(const QPoint& point) 0226 { 0227 if (point.x() >= 0 0228 && point.y() >= 0 0229 && point.x() <= KS_colMax 0230 && point.y() <= KS_rowMax 0231 ) 0232 return true; 0233 else 0234 return false; 0235 } 0236 0237 bool util_isRectValid(const QRect& rect) 0238 { 0239 if (util_isPointValid(rect.topLeft()) 0240 && util_isPointValid(rect.bottomRight()) 0241 ) 0242 return true; 0243 else 0244 return false; 0245 } 0246 0247 0248 //not used anywhere 0249 int Calligra::Sheets::Util::penCompare(QPen const & pen1, QPen const & pen2) 0250 { 0251 if (pen1.style() == Qt::NoPen && pen2.style() == Qt::NoPen) 0252 return 0; 0253 0254 if (pen1.style() == Qt::NoPen) 0255 return -1; 0256 0257 if (pen2.style() == Qt::NoPen) 0258 return 1; 0259 0260 if (pen1.width() < pen2.width()) 0261 return -1; 0262 0263 if (pen1.width() > pen2.width()) 0264 return 1; 0265 0266 if (pen1.style() < pen2.style()) 0267 return -1; 0268 0269 if (pen1.style() > pen2.style()) 0270 return 1; 0271 0272 if (pen1.color().name() < pen2.color().name()) 0273 return -1; 0274 0275 if (pen1.color().name() > pen2.color().name()) 0276 return 1; 0277 0278 return 0; 0279 } 0280 0281 0282 QString Calligra::Sheets::Odf::convertRefToBase(const QString & sheet, const QRect & rect) 0283 { 0284 QPoint bottomRight(rect.bottomRight()); 0285 0286 QString s = '$' + 0287 sheet + 0288 ".$" + 0289 Cell::columnName(bottomRight.x()) + 0290 '$' + 0291 QString::number(bottomRight.y()); 0292 0293 return s; 0294 } 0295 0296 QString Calligra::Sheets::Odf::convertRefToRange(const QString & sheet, const QRect & rect) 0297 { 0298 QPoint topLeft(rect.topLeft()); 0299 QPoint bottomRight(rect.bottomRight()); 0300 0301 if (topLeft == bottomRight) 0302 return Odf::convertRefToBase(sheet, rect); 0303 0304 QString s = '$' + 0305 sheet + 0306 ".$" + 0307 /*Util::encodeColumnLabelText*/Cell::columnName(topLeft.x()) + 0308 '$' + 0309 QString::number(topLeft.y()) + 0310 ":.$" + 0311 /*Util::encodeColumnLabelText*/Cell::columnName(bottomRight.x()) + 0312 '$' + 0313 QString::number(bottomRight.y()); 0314 0315 return s; 0316 } 0317 0318 // e.g.: Sheet4.A1:Sheet4.E28 0319 //used in Sheet::saveOdf 0320 QString Calligra::Sheets::Odf::convertRangeToRef(const QString & sheetName, const QRect & _area) 0321 { 0322 return sheetName + '.' + Cell::name(_area.left(), _area.top()) + ':' + sheetName + '.' + Cell::name(_area.right(), _area.bottom()); 0323 } 0324 0325 //Return true when it's a reference to cell from sheet. 0326 bool Calligra::Sheets::Util::localReferenceAnchor(const QString &_ref) 0327 { 0328 bool isLocalRef = (_ref.indexOf("http://") != 0 && 0329 _ref.indexOf("https://") != 0 && 0330 _ref.indexOf("mailto:") != 0 && 0331 _ref.indexOf("ftp://") != 0 && 0332 _ref.indexOf("file:") != 0); 0333 return isLocalRef; 0334 } 0335 0336 0337 QString Calligra::Sheets::Odf::decodeFormula(const QString& expression_, const KLocale *locale, const QString &namespacePrefix) 0338 { 0339 // parsing state 0340 enum { Start, InNumber, InString, InIdentifier, InReference, InSheetName } state = Start; 0341 0342 QString expression = expression_; 0343 if (namespacePrefix == "msoxl:") { 0344 expression = MSOOXML::convertFormula(expression); 0345 } 0346 0347 // use locale settings 0348 QString decimal = locale ? locale->decimalSymbol() : "."; 0349 0350 const QChar *data = expression.constData(); 0351 const QChar *start = data; 0352 0353 if (data->isNull()) { 0354 return QString(); 0355 } 0356 0357 int length = expression.length() * 2; 0358 QString result(length, QChar()); 0359 result.reserve(length); 0360 QChar * out = result.data(); 0361 QChar * outStart = result.data(); 0362 0363 if (*data == QChar('=', 0)) { 0364 *out = *data; 0365 ++data; 0366 ++out; 0367 } 0368 0369 const QChar *pos = data; 0370 while (!data->isNull()) { 0371 switch (state) { 0372 case Start: { 0373 if (data->isDigit()) { // check for number 0374 state = InNumber; 0375 *out++ = *data++; 0376 } 0377 else if (*data == QChar('.', 0)) { 0378 state = InNumber; 0379 *out = decimal[0]; 0380 ++out; 0381 ++data; 0382 } 0383 else if (isIdentifier(*data)) { 0384 // beginning with alphanumeric ? 0385 // could be identifier, cell, range, or function... 0386 state = InIdentifier; 0387 int i = data - start; 0388 const static QString errorTypeReplacement("ERRORTYPE"); 0389 const static QString legacyNormsdistReplacement("LEGACYNORMSDIST"); 0390 const static QString legacyNormsinvReplacement("LEGACYNORMSINV"); 0391 const static QString multipleOperations("MULTIPLE.OPERATIONS"); 0392 if (expression.midRef(i,10).compare(QLatin1String("ERROR.TYPE")) == 0) { 0393 // replace it 0394 int outPos = out - outStart; 0395 result.replace(outPos, 9, errorTypeReplacement); 0396 data += 10; // number of characters in "ERROR.TYPE" 0397 out += 9; 0398 } 0399 else if (expression.midRef(i, 12).compare(QLatin1String("LEGACY.NORMS")) == 0) { 0400 if (expression.midRef(i + 12, 4).compare(QLatin1String("DIST")) == 0) { 0401 // replace it 0402 int outPos = out - outStart; 0403 result.replace(outPos, 15, legacyNormsdistReplacement); 0404 data += 16; // number of characters in "LEGACY.NORMSDIST" 0405 out += 15; 0406 } 0407 else if (expression.midRef(i + 12, 3).compare(QLatin1String("INV")) == 0) { 0408 // replace it 0409 int outPos = out - outStart; 0410 result.replace(outPos, 14, legacyNormsinvReplacement); 0411 data += 15; // number of characters in "LEGACY.NORMSINV" 0412 out += 14; 0413 } 0414 } 0415 else if (namespacePrefix == "oooc:" && expression.midRef(i, 5).compare(QLatin1String("TABLE")) == 0 && !isIdentifier(expression[i+5])) { 0416 int outPos = out - outStart; 0417 result.replace(outPos, 19, multipleOperations); 0418 data += 5; 0419 out += 19; 0420 } 0421 else if (expression.midRef(i, 3).compare(QLatin1String("NEG")) == 0) { 0422 *out = QChar('-', 0); 0423 data += 3; 0424 ++out; 0425 } 0426 } 0427 else { 0428 switch (data->unicode()) { 0429 case '"': // a string ? 0430 state = InString; 0431 *out++ = *data++; 0432 break; 0433 case '[': // [ marks sheet name for 3-d cell, e.g ['Sales Q3'.A4] 0434 state = InReference; 0435 ++data; 0436 // NOTE: As long as Calligra::Sheets does not support fixed sheets eat the dollar sign. 0437 if (*data == QChar('$', 0)) { 0438 ++data; 0439 } 0440 pos = data; 0441 break; 0442 default: 0443 const QChar *operatorStart = data; 0444 if (!parseOperator(data, out)) { 0445 *out++ = *data++; 0446 } 0447 else if (*operatorStart == QChar('=', 0) && data - operatorStart == 1) { // only one = 0448 *out++ = QChar('=', 0); 0449 } 0450 break; 0451 } 0452 } 0453 } break; 0454 case InNumber: 0455 if (data->isDigit()) { 0456 *out++ = *data++; 0457 } 0458 else if (*data == QChar('.', 0)) { 0459 const QChar *decimalChar = decimal.constData(); 0460 while (!decimalChar->isNull()) { 0461 *out++ = *decimalChar++; 0462 } 0463 ++data; 0464 } 0465 else if (*data == QChar('E', 0) || *data == QChar('e', 0)) { 0466 *out++ = QChar('E', 0); 0467 ++data; 0468 } 0469 else { 0470 state = Start; 0471 } 0472 0473 break; 0474 case InString: 0475 if (*data == QChar('"', 0)) { 0476 state = Start; 0477 } 0478 *out++ = *data++; 0479 break; 0480 case InIdentifier: { 0481 if (isIdentifier(*data) || data->isDigit()) { 0482 *out++ = *data++; 0483 } 0484 else { 0485 state = Start; 0486 } 0487 } break; 0488 case InReference: 0489 switch (data->unicode()) { 0490 case ']': 0491 Odf::loadRegion(pos, data, out); 0492 pos = data; 0493 state = Start; 0494 break; 0495 case '\'': 0496 state = InSheetName; 0497 break; 0498 default: 0499 break; 0500 } 0501 ++data; 0502 break; 0503 case InSheetName: 0504 if (*data == QChar('\'', 0)) { 0505 ++data; 0506 if (!data->isNull() && *data == QChar('\'', 0)) { 0507 ++data; 0508 } 0509 else { 0510 state = InReference; 0511 } 0512 } 0513 else { 0514 ++data; 0515 } 0516 break; 0517 } 0518 } 0519 result.resize(out - outStart); 0520 return result; 0521 } 0522 0523 QString Calligra::Sheets::Odf::encodeFormula(const QString& expr, const KLocale* locale) 0524 { 0525 // use locale settings 0526 const QString decimal = locale ? locale->decimalSymbol() : "."; 0527 0528 QString result('='); 0529 0530 Formula formula; 0531 Tokens tokens = formula.scan(expr, locale); 0532 0533 if (!tokens.valid() || tokens.count() == 0) 0534 return expr; // no altering on error 0535 0536 for (int i = 0; i < tokens.count(); ++i) { 0537 const QString tokenText = tokens[i].text(); 0538 const Token::Type type = tokens[i].type(); 0539 0540 switch (type) { 0541 case Token::Cell: 0542 case Token::Range: { 0543 result.append('['); 0544 // FIXME Stefan: Hack to get the apostrophes right. Fix and remove! 0545 const int pos = tokenText.lastIndexOf('!'); 0546 if (pos != -1 && tokenText.left(pos).contains(' ')) 0547 result.append(Odf::saveRegion('\'' + tokenText.left(pos) + '\'' + tokenText.mid(pos))); 0548 else 0549 result.append(Odf::saveRegion(tokenText)); 0550 result.append(']'); 0551 break; 0552 } 0553 case Token::Float: { 0554 QString tmp(tokenText); 0555 result.append(tmp.replace(decimal, ".")); 0556 break; 0557 } 0558 case Token::Operator: { 0559 if (tokens[i].asOperator() == Token::Equal) 0560 result.append('='); 0561 else 0562 result.append(tokenText); 0563 break; 0564 } 0565 case Token::Identifier: { 0566 if (tokenText == "ERRORTYPE") { 0567 // need to replace this 0568 result.append("ERROR.TYPE"); 0569 } else if (tokenText == "LEGACYNORMSDIST") { 0570 result.append("LEGACY.NORMSDIST"); 0571 } else if (tokenText == "LEGACYNORMSINV") { 0572 result.append("LEGACY.NORMSINV"); 0573 } else { 0574 // dump it out unchanged 0575 result.append(tokenText); 0576 } 0577 break; 0578 0579 } 0580 case Token::Boolean: 0581 case Token::Integer: 0582 case Token::String: 0583 default: 0584 result.append(tokenText); 0585 break; 0586 } 0587 } 0588 return result; 0589 } 0590 0591 static bool isCellnameCharacter(const QChar &c) 0592 { 0593 return c.isDigit() || c.isLetter() || c == '$'; 0594 } 0595 0596 static void replaceFormulaReference(int referencedRow, int referencedColumn, int thisRow, int thisColumn, QString &result, int cellReferenceStart, int cellReferenceLength) 0597 { 0598 const QString ref = result.mid(cellReferenceStart, cellReferenceLength); 0599 QRegExp rx("(|\\$)[A-Za-z]+(|\\$)[0-9]+"); 0600 if (rx.exactMatch(ref)) { 0601 int c = Calligra::Sheets::Util::decodeColumnLabelText(ref); 0602 int r = Calligra::Sheets::Util::decodeRowLabelText(ref); 0603 if (rx.cap(1) != "$") // absolute or relative column? 0604 c += thisColumn - referencedColumn; 0605 if (rx.cap(2) != "$") // absolute or relative row? 0606 r += thisRow - referencedRow; 0607 result.replace(cellReferenceStart, 0608 cellReferenceLength, 0609 rx.cap(1) + Calligra::Sheets::Util::encodeColumnLabelText(c) + 0610 rx.cap(2) + QString::number(r) ); 0611 } 0612 } 0613 0614 QString Calligra::Sheets::Util::adjustFormulaReference(const QString& formula, int referencedRow, int referencedColumn, int thisRow, int thisColumn) 0615 { 0616 QString result = formula; 0617 if (result.isEmpty()) 0618 return QString(); 0619 enum { InStart, InCellReference, InString, InSheetOrAreaName } state; 0620 state = InStart; 0621 int cellReferenceStart = 0; 0622 for(int i = 1; i < result.length(); ++i) { 0623 QChar ch = result[i]; 0624 switch (state) { 0625 case InStart: 0626 if (ch == '"') 0627 state = InString; 0628 else if (ch.unicode() == '\'') 0629 state = InSheetOrAreaName; 0630 else if (isCellnameCharacter(ch)) { 0631 state = InCellReference; 0632 cellReferenceStart = i; 0633 } 0634 break; 0635 case InString: 0636 if (ch == '"') 0637 state = InStart; 0638 break; 0639 case InSheetOrAreaName: 0640 if (ch == '\'') 0641 state = InStart; 0642 break; 0643 case InCellReference: 0644 if (!isCellnameCharacter(ch)) { 0645 // We need to update cell-references according to the position of the referenced cell and this 0646 // cell. This means that if the referenced cell is for example at C5 and contains the formula 0647 // "=SUM(K22)" and if thisCell is at E6 then thisCell will get the formula "=SUM(L23)". 0648 if (ch != '(') /* skip formula-names */ { 0649 replaceFormulaReference(referencedRow, referencedColumn, thisRow, thisColumn, result, cellReferenceStart, i - cellReferenceStart); 0650 } 0651 state = InStart; 0652 --i; // decrement again to handle the current char in the InStart-switch. 0653 } 0654 break; 0655 }; 0656 } 0657 if(state == InCellReference) { 0658 replaceFormulaReference(referencedRow, referencedColumn, thisRow, thisColumn, result, cellReferenceStart, result.length() - cellReferenceStart); 0659 } 0660 return result; 0661 } 0662 0663 QString Calligra::Sheets::MSOOXML::convertFormula(const QString& formula) 0664 { 0665 if (formula.isEmpty()) 0666 return QString(); 0667 enum { InStart, InArguments, InParenthesizedArgument, InString, InSheetOrAreaName, InCellReference } state; 0668 state = InStart; 0669 int cellReferenceStart = 0; 0670 int sheetOrAreaNameDelimiterCount = 0; 0671 QString result = formula.startsWith('=') ? formula : '=' + formula; 0672 for(int i = 1; i < result.length(); ++i) { 0673 QChar ch = result[i]; 0674 switch (state) { 0675 case InStart: 0676 if(ch == '(') 0677 state = InArguments; 0678 break; 0679 case InArguments: 0680 if (ch == '"') 0681 state = InString; 0682 else if (ch.unicode() == '\'') { 0683 sheetOrAreaNameDelimiterCount = 1; 0684 for(int j = i + 1; j < result.length(); ++j) { 0685 if (result[j].unicode() != '\'') 0686 break; 0687 ++sheetOrAreaNameDelimiterCount; 0688 } 0689 if (sheetOrAreaNameDelimiterCount >= 2) 0690 result.remove(i + 1, sheetOrAreaNameDelimiterCount - 1); 0691 state = InSheetOrAreaName; 0692 } else if (isCellnameCharacter(ch)) { 0693 state = InCellReference; 0694 cellReferenceStart = i; 0695 } else if (ch == ',') 0696 result[i] = ';'; // replace argument delimiter 0697 else if (ch == '(' && !result[i-1].isLetterOrNumber()) 0698 state = InParenthesizedArgument; 0699 else if (ch == ' ') { 0700 // check if it might be an intersection operator 0701 // for it to be an intersection operator the next non-space char must be a cell-name-character or ' 0702 // and previous converted char cannot be ';' 0703 int firstNonSpace = i+1; 0704 while (firstNonSpace < result.length() && result[firstNonSpace] == ' ') { 0705 firstNonSpace++; 0706 } 0707 bool wasDelimeter = (i-1 > 0) && (result[i-1] == ';'); 0708 bool isIntersection = !wasDelimeter && firstNonSpace < result.length() && (result[firstNonSpace].isLetter() || result[firstNonSpace] == '$' || result[firstNonSpace] == '\''); 0709 if (isIntersection) { 0710 result[i] = '!'; 0711 i = firstNonSpace-1; 0712 } 0713 } 0714 break; 0715 case InParenthesizedArgument: 0716 if (ch == ',') 0717 result[i] = '~'; // union operator 0718 else if (ch == ' ') 0719 result[i] = '!'; // intersection operator 0720 else if (ch == ')') 0721 state = InArguments; 0722 break; 0723 case InString: 0724 if (ch == '"') 0725 state = InArguments; 0726 break; 0727 case InSheetOrAreaName: 0728 Q_ASSERT( i >= 1 ); 0729 if (ch == '\'' && result[i - 1].unicode() != '\\') { 0730 int count = 1; 0731 for(int j = i + 1; count < sheetOrAreaNameDelimiterCount && j < result.length(); ++j) { 0732 if (result[j].unicode() != '\'') 0733 break; 0734 ++count; 0735 } 0736 if (count == sheetOrAreaNameDelimiterCount) { 0737 if (sheetOrAreaNameDelimiterCount >= 2) 0738 result.remove(i + 1, sheetOrAreaNameDelimiterCount - 1); 0739 state = InArguments; 0740 } else { 0741 result.insert(i, '\''); 0742 ++i; 0743 } 0744 } 0745 break; 0746 case InCellReference: 0747 if (!isCellnameCharacter(ch)) { 0748 if (ch != '(') /* skip formula-names */ { 0749 // Excel is able to use only the column-name to define a column 0750 // where all rows are selected. Since that is not supported in 0751 // ODF we add to such definitions the minimum/maximum row-number. 0752 // So, something like "A:B" would become "A$1:B$65536". Note that 0753 // such whole column-definitions are only allowed for ranges like 0754 // "A:B" but not for single column definitions like "A" or "B". 0755 const QString ref = result.mid(qMax(0, cellReferenceStart - 1), i - cellReferenceStart + 2); 0756 QRegExp rxStart(".*(|\\$)[A-Za-z]+\\:"); 0757 QRegExp rxEnd("\\:(|\\$)[A-Za-z]+(|(|\\$)[0-9]+).*"); 0758 if (rxEnd.exactMatch(ref) && rxEnd.cap(2).isEmpty()) { 0759 result.insert(i, "$65536"); 0760 i += 6; 0761 } else if (rxStart.exactMatch(ref)) { 0762 result.insert(i, "$1"); 0763 i += 2; 0764 } 0765 } 0766 state = InArguments; 0767 --i; // decrement again to handle the current char in the InArguments-switch. 0768 } 0769 break; 0770 }; 0771 }; 0772 return result; 0773 }