File indexing completed on 2024-05-12 16:35:33
0001 /* This file is part of the KDE project 0002 Copyright (C) 1998-2002 The KSpread Team <calligra-devel@kde.org> 0003 Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com> 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; only 0008 version 2 of the License. 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 // built-in reference functions 0022 #include "ReferenceModule.h" 0023 0024 #include "Cell.h" 0025 #include "Region.h" 0026 #include "Sheet.h" 0027 #include "Util.h" 0028 #include "Value.h" 0029 #include "Map.h" 0030 #include "CalculationSettings.h" 0031 #include "CellStorage.h" 0032 #include "Formula.h" 0033 #include "Function.h" 0034 #include "FunctionModuleRegistry.h" 0035 #include "ValueCalc.h" 0036 #include "ValueConverter.h" 0037 0038 using namespace Calligra::Sheets; 0039 0040 // prototypes (sorted alphabetically) 0041 Value func_address(valVector args, ValueCalc *calc, FuncExtra *); 0042 Value func_areas(valVector args, ValueCalc *calc, FuncExtra *); 0043 Value func_cell(valVector args, ValueCalc *calc, FuncExtra *e); 0044 Value func_choose(valVector args, ValueCalc *calc, FuncExtra *); 0045 Value func_column(valVector args, ValueCalc *calc, FuncExtra *); 0046 Value func_columns(valVector args, ValueCalc *calc, FuncExtra *); 0047 Value func_hlookup(valVector args, ValueCalc *calc, FuncExtra *); 0048 Value func_index(valVector args, ValueCalc *calc, FuncExtra *); 0049 Value func_indirect(valVector args, ValueCalc *calc, FuncExtra *); 0050 Value func_lookup(valVector args, ValueCalc *calc, FuncExtra *); 0051 Value func_match(valVector args, ValueCalc *calc, FuncExtra *); 0052 Value func_multiple_operations(valVector args, ValueCalc *calc, FuncExtra *); 0053 Value func_offset(valVector args, ValueCalc *calc, FuncExtra *); 0054 Value func_row(valVector args, ValueCalc *calc, FuncExtra *); 0055 Value func_rows(valVector args, ValueCalc *calc, FuncExtra *); 0056 Value func_sheet(valVector args, ValueCalc *calc, FuncExtra *); 0057 Value func_sheets(valVector args, ValueCalc *calc, FuncExtra *); 0058 Value func_vlookup(valVector args, ValueCalc *calc, FuncExtra *); 0059 0060 0061 CALLIGRA_SHEETS_EXPORT_FUNCTION_MODULE("kspreadreferencemodule.json", ReferenceModule) 0062 0063 0064 ReferenceModule::ReferenceModule(QObject* parent, const QVariantList&) 0065 : FunctionModule(parent) 0066 { 0067 Function *f; 0068 0069 f = new Function("ADDRESS", func_address); 0070 f->setParamCount(2, 5); 0071 add(f); 0072 f = new Function("AREAS", func_areas); 0073 f->setParamCount(1); 0074 f->setNeedsExtra(true); 0075 f->setAcceptArray(); 0076 add(f); 0077 f = new Function("CELL", func_cell); 0078 f->setParamCount(1, 2); 0079 f->setAcceptArray(); 0080 f->setNeedsExtra(true); 0081 add(f); 0082 f = new Function("CHOOSE", func_choose); 0083 f->setParamCount(2, -1); 0084 f->setAcceptArray(); 0085 add(f); 0086 f = new Function("COLUMN", func_column); 0087 f->setParamCount(0, 1); 0088 add(f); 0089 f = new Function("COLUMNS", func_columns); 0090 f->setParamCount(1); 0091 f->setAcceptArray(); 0092 f->setNeedsExtra(true); 0093 add(f); 0094 f = new Function("HLOOKUP", func_hlookup); 0095 f->setParamCount(3, 4); 0096 f->setAcceptArray(); 0097 add(f); 0098 f = new Function("INDEX", func_index); 0099 f->setParamCount(3); 0100 f->setAcceptArray(); 0101 add(f); 0102 f = new Function("INDIRECT", func_indirect); 0103 f->setParamCount(1, 2); 0104 f->setNeedsExtra(true); 0105 add(f); 0106 f = new Function("LOOKUP", func_lookup); 0107 f->setParamCount(3); 0108 f->setAcceptArray(); 0109 add(f); 0110 f = new Function("MATCH", func_match); 0111 f->setParamCount(2, 3); 0112 f->setAcceptArray(); 0113 f->setNeedsExtra(true); 0114 add(f); 0115 f = new Function("MULTIPLE.OPERATIONS", func_multiple_operations); 0116 f->setParamCount(3, 5); 0117 f->setNeedsExtra(true); 0118 add(f); 0119 f = new Function("OFFSET", func_offset); 0120 f->setParamCount(3, 5); 0121 f->setNeedsExtra(true); 0122 add(f); 0123 f = new Function("ROW", func_row); 0124 f->setParamCount(0, 1); 0125 add(f); 0126 f = new Function("ROWS", func_rows); 0127 f->setParamCount(1); 0128 f->setAcceptArray(); 0129 f->setNeedsExtra(true); 0130 add(f); 0131 f = new Function("SHEET", func_sheet); 0132 f->setParamCount(0, 1); 0133 f->setNeedsExtra(true); 0134 add(f); 0135 f = new Function("SHEETS", func_sheets); 0136 f->setParamCount(0, 1); 0137 f->setAcceptArray(); 0138 f->setNeedsExtra(true); 0139 add(f); 0140 f = new Function("VLOOKUP", func_vlookup); 0141 f->setParamCount(3, 4); 0142 f->setAcceptArray(); 0143 add(f); 0144 } 0145 0146 QString ReferenceModule::descriptionFileName() const 0147 { 0148 return QString("reference.xml"); 0149 } 0150 0151 0152 // 0153 // Function: ADDRESS 0154 // 0155 Value func_address(valVector args, ValueCalc *calc, FuncExtra *) 0156 { 0157 bool r1c1 = false; 0158 QString sheetName; 0159 int absNum = 1; 0160 if (args.count() > 2) 0161 absNum = calc->conv()->asInteger(args[2]).asInteger(); 0162 if (args.count() > 3) 0163 r1c1 = !(calc->conv()->asBoolean(args[3]).asBoolean()); 0164 if (args.count() == 5) 0165 sheetName = calc->conv()->asString(args[4]).asString(); 0166 0167 QString result; 0168 int row = calc->conv()->asInteger(args[0]).asInteger(); 0169 int col = calc->conv()->asInteger(args[1]).asInteger(); 0170 0171 if (!sheetName.isEmpty()) { 0172 result += sheetName; 0173 result += '!'; 0174 } 0175 0176 if (r1c1) { 0177 // row first 0178 bool abs = false; 0179 if (absNum == 1 || absNum == 2) 0180 abs = true; 0181 0182 result += 'R'; 0183 if (!abs) 0184 result += '['; 0185 result += QString::number(row); 0186 0187 if (!abs) 0188 result += ']'; 0189 0190 // column 0191 abs = false; 0192 if (absNum == 1 || absNum == 3) 0193 abs = true; 0194 0195 result += 'C'; 0196 if (!abs) 0197 result += '['; 0198 result += QString::number(col); 0199 0200 if (!abs) 0201 result += ']'; 0202 } else { 0203 bool abs = false; 0204 if (absNum == 1 || absNum == 3) 0205 abs = true; 0206 0207 if (abs) 0208 result += '$'; 0209 0210 result += Cell::columnName(col); 0211 0212 abs = false; 0213 if (absNum == 1 || absNum == 2) 0214 abs = true; 0215 0216 if (abs) 0217 result += '$'; 0218 0219 result += QString::number(row); 0220 } 0221 0222 return Value(result); 0223 } 0224 0225 0226 // 0227 // Function: AREAS 0228 // 0229 Value func_areas(valVector args, ValueCalc *calc, FuncExtra *e) 0230 { 0231 if (e) { 0232 if (e->regions[0].isValid()) 0233 return Value(e->regions[0].rects().size()); 0234 0235 if ((e->ranges[0].col1 != -1) && (e->ranges[0].row1 != -1) && 0236 (e->ranges[0].col2 != -1) && (e->ranges[0].row2 != -1)) 0237 // we have a range reference - return 1 0238 return Value(1); 0239 } 0240 0241 QString s = calc->conv()->asString(args[0]).asString(); 0242 if (s[0] != '(' || s[s.length() - 1] != ')') 0243 return Value::errorVALUE(); 0244 0245 int l = s.length(); 0246 0247 int num = 0; 0248 QString ref; 0249 for (int i = 1; i < l; ++i) { 0250 if (s[i] == ',' || s[i] == ')') { 0251 if (!Calligra::Sheets::Region(ref).isValid()) 0252 return Value::errorVALUE(); 0253 else { 0254 ++num; 0255 ref.clear(); 0256 } 0257 } else 0258 ref += s[i]; 0259 } 0260 0261 return Value(num); 0262 } 0263 0264 // 0265 // Function: CELL 0266 // 0267 Value func_cell(valVector args, ValueCalc *calc, FuncExtra *e) 0268 { 0269 const QString type = calc->conv()->asString(args[0]).asString().toLower(); 0270 0271 args.pop_front(); 0272 FuncExtra extra(*e); 0273 extra.ranges.pop_front(); 0274 extra.regions.pop_front(); 0275 0276 if (type == "col") { 0277 return func_column(args, calc, &extra); 0278 } 0279 if (type == "row") { 0280 return func_row(args, calc, &extra); 0281 } 0282 if (type == "sheet") { 0283 return func_sheet(args, calc, &extra); 0284 } 0285 if (type == "address") { 0286 const Calligra::Sheets::Region ®ion = args.count() ? extra.regions[0] : Calligra::Sheets::Region(QPoint(e->mycol, e->myrow), e->sheet); 0287 QString s; 0288 if (region.firstSheet() && region.firstSheet() != e->sheet) 0289 s += '\'' + region.firstSheet()->sheetName() + "'!"; 0290 s += '$' + Cell::columnName(region.firstRange().x()) + '$' + QString::number(region.firstRange().y()); 0291 if (region.firstRange() != region.lastRange()) 0292 s += ":$" + Cell::columnName(region.lastRange().x()) + '$' + QString::number(region.lastRange().y()); 0293 return Value(s); 0294 } 0295 if (type == "filename") { 0296 return Value(calc->settings()->fileName()); 0297 } 0298 0299 /*TODO 0300 if (type == "contents") 0301 if (type == "color") 0302 if (type == "format") 0303 if (type == "width") 0304 if (type == "type") 0305 if (type == "protect") 0306 if (type == "parenthesis") 0307 if (type == "prefix") 0308 */ 0309 0310 return Value::errorVALUE(); 0311 } 0312 0313 // 0314 // Function: CHOOSE 0315 // 0316 Value func_choose(valVector args, ValueCalc *calc, FuncExtra *) 0317 { 0318 int cnt = args.count() - 1; 0319 int num = calc->conv()->asInteger(args[0]).asInteger(); 0320 if ((num <= 0) || (num > cnt)) 0321 return Value::errorVALUE(); 0322 return args[num]; 0323 } 0324 0325 0326 // 0327 // Function: COLUMN 0328 // 0329 Value func_column(valVector args, ValueCalc *, FuncExtra *e) 0330 { 0331 int col = e ? e->mycol : 0; 0332 if (e && args.count()) 0333 col = e->ranges[0].col1; 0334 if (col > 0) 0335 return Value(col); 0336 return Value::errorVALUE(); 0337 } 0338 0339 0340 // 0341 // Function: COLUMNS 0342 // 0343 Value func_columns(valVector, ValueCalc *, FuncExtra *e) 0344 { 0345 int col1 = e->ranges[0].col1; 0346 int col2 = e->ranges[0].col2; 0347 if ((col1 == -1) || (col2 == -1)) 0348 return Value::errorVALUE(); 0349 return Value(col2 - col1 + 1); 0350 } 0351 0352 0353 // 0354 // Function: HLOOKUP 0355 // 0356 Value func_hlookup(valVector args, ValueCalc *calc, FuncExtra *) 0357 { 0358 const Value key = args[0]; 0359 const Value data = args[1]; 0360 const int row = calc->conv()->asInteger(args[2]).asInteger(); 0361 const int cols = data.columns(); 0362 const int rows = data.rows(); 0363 if (row < 1 || row > rows) 0364 return Value::errorVALUE(); 0365 const bool rangeLookup = (args.count() > 3) ? calc->conv()->asBoolean(args[3]).asBoolean() : true; 0366 0367 // now traverse the array and perform comparison 0368 Value r; 0369 Value v = Value::errorNA(); 0370 for (int col = 0; col < cols; ++col) { 0371 // search in the first row 0372 const Value le = data.element(col, 0); 0373 if (calc->naturalEqual(key, le)) { 0374 return data.element(col, row - 1); 0375 } 0376 // optionally look for the next largest value that is less than key 0377 if (rangeLookup && calc->naturalLower(le, key) && calc->naturalLower(r, le)) { 0378 r = le; 0379 v = data.element(col, row - 1); 0380 } 0381 } 0382 return v; 0383 } 0384 0385 0386 // 0387 // Function: INDEX 0388 // 0389 Value func_index(valVector args, ValueCalc *calc, FuncExtra *) 0390 { 0391 // first argument can be either a range, then we return a given cell's 0392 // value, or a single cell containing an array - then we return the array 0393 // element. In any case, this function can assume that the given value 0394 // is the same. Because it is. 0395 0396 Value val = args[0]; 0397 unsigned row = calc->conv()->asInteger(args[1]).asInteger() - 1; 0398 unsigned col = calc->conv()->asInteger(args[2]).asInteger() - 1; 0399 if ((row >= val.rows()) || (col >= val.columns())) 0400 return Value::errorREF(); 0401 return val.element(col, row); 0402 } 0403 0404 0405 // 0406 // Function: INDIRECT 0407 // 0408 Value func_indirect(valVector args, ValueCalc *calc, FuncExtra *e) 0409 { 0410 bool r1c1 = false; 0411 QString ref = calc->conv()->asString(args[0]).asString(); 0412 if (args.count() == 2) 0413 r1c1 = !(calc->conv()->asBoolean(args[1]).asBoolean()); 0414 0415 if (ref.isEmpty()) 0416 return Value::errorVALUE(); 0417 0418 if (r1c1) { 0419 // TODO: translate the r1c1 style to a1 style 0420 ref = ref; 0421 } 0422 0423 const Calligra::Sheets::Region region(ref, e->sheet->map(), e->sheet); 0424 if (!region.isValid() || !region.isSingular()) 0425 return Value::errorVALUE(); 0426 0427 const Cell cell(region.firstSheet(), region.firstRange().topLeft()); 0428 if (!cell.isNull()) 0429 return cell.value(); 0430 return Value::errorVALUE(); 0431 } 0432 0433 0434 // 0435 // Function: LOOKUP 0436 // 0437 Value func_lookup(valVector args, ValueCalc *calc, FuncExtra *) 0438 { 0439 Value num = calc->conv()->asNumeric(args[0]); 0440 if (num.isArray()) 0441 return Value::errorVALUE(); 0442 Value lookup = args[1]; 0443 Value rr = args[2]; 0444 unsigned cols = lookup.columns(); 0445 unsigned rows = lookup.rows(); 0446 if ((cols != rr.columns()) || (rows != rr.rows())) 0447 return Value::errorVALUE(); 0448 Value res = Value::errorNA(); 0449 0450 // now traverse the array and perform comparison 0451 for (unsigned r = 0; r < rows; ++r) 0452 for (unsigned c = 0; c < cols; ++c) { 0453 // update the result, return if we cross the line 0454 Value le = lookup.element(c, r); 0455 if (calc->lower(le, num) || calc->equal(num, le)) 0456 res = rr.element(c, r); 0457 else 0458 return res; 0459 } 0460 return res; 0461 } 0462 0463 // 0464 // Function: MATCH 0465 // 0466 Value func_match(valVector args, ValueCalc *calc, FuncExtra* e) 0467 { 0468 int matchType = 1; 0469 if (args.count() == 3) { 0470 bool ok = true; 0471 matchType = calc->conv()->asInteger(args[2], &ok).asInteger(); 0472 if (!ok) 0473 return Value::errorVALUE(); // invalid matchtype 0474 } 0475 0476 const Value& searchValue = args[0]; 0477 const Value& searchArray = args[1]; 0478 0479 if (e->ranges[1].rows() != 1 && e->ranges[1].columns() != 1) 0480 return Value::errorNA(); 0481 int dr = 1, dc = 0; 0482 if (searchArray.columns() != 1) { 0483 dr = 0; dc = 1; 0484 } 0485 int n = qMax(searchArray.rows(), searchArray.columns()); 0486 0487 if (matchType == 0) { 0488 // linear search 0489 for (int r = 0, c = 0; r < n && c < n; r += dr, c += dc) { 0490 if (calc->naturalEqual(searchValue, searchArray.element(c, r), false)) { 0491 return Value(qMax(r, c) + 1); 0492 } 0493 } 0494 return Value::errorNA(); 0495 } else if (matchType > 0) { 0496 // binary search 0497 int l = -1; 0498 int h = n; 0499 while (l+1 < h) { 0500 int m = (l+h)/2; 0501 if (calc->naturalLequal(searchArray.element(m*dc, m*dr), searchValue, false)) { 0502 l = m; 0503 } else { 0504 h = m; 0505 } 0506 } 0507 if (l == -1) return Value::errorNA(); 0508 return Value(l+1); 0509 } else /* matchType < 0 */ { 0510 // binary search 0511 int l = -1; 0512 int h = n; 0513 while (l+1 < h) { 0514 int m = (l+h)/2; 0515 if (calc->naturalGequal(searchArray.element(m*dc, m*dr), searchValue, false)) { 0516 l = m; 0517 } else { 0518 h = m; 0519 } 0520 } 0521 if (l == -1) return Value::errorNA(); 0522 return Value(l+1); 0523 } 0524 } 0525 0526 // 0527 // Function: MULTIPLE.OPERATIONS 0528 // 0529 Value func_multiple_operations(valVector args, ValueCalc *, FuncExtra *e) 0530 { 0531 if (args.count() != 3 && args.count() != 5) 0532 return Value::errorVALUE(); // invalid number of parameters 0533 0534 for (int i = 0; i < args.count(); i++) { 0535 if (e->ranges[i].col1 == -1 || e->ranges[i].row1 == -1) 0536 return Value::errorVALUE(); 0537 } 0538 0539 CellStorage *s = e->sheet->cellStorage(); 0540 0541 // get formula to evaluate 0542 int formulaCol = e->ranges[0].col1; 0543 int formulaRow = e->ranges[0].row1; 0544 Formula formula = s->formula(formulaCol, formulaRow); 0545 if (!formula.isValid()) 0546 return Value::errorVALUE(); 0547 0548 CellIndirection cellIndirections; 0549 cellIndirections.insert(Cell(e->sheet, e->ranges[1].col1, e->ranges[1].row1), Cell(e->sheet, e->ranges[2].col1, e->ranges[2].row1)); 0550 if (args.count() > 3) { 0551 cellIndirections.insert(Cell(e->sheet, e->ranges[3].col1, e->ranges[3].row1), Cell(e->sheet, e->ranges[4].col1, e->ranges[4].row1)); 0552 } 0553 0554 return formula.eval(cellIndirections); 0555 } 0556 0557 // 0558 // Function: OFFSET 0559 // 0560 Value func_offset(valVector args, ValueCalc *calc, FuncExtra *e) 0561 { 0562 const int rowPlus = calc->conv()->asInteger(args[1]).asInteger(); 0563 const int colPlus = calc->conv()->asInteger(args[2]).asInteger(); 0564 0565 //const int rowNew = args.count() >= 4 ? calc->conv()->asInteger(args[3]).asInteger() : -1; 0566 //const int colNew = args.count() >= 5 ? calc->conv()->asInteger(args[4]).asInteger() : -1; 0567 //if (colNew == 0 || rowNew == 0) return Value::errorVALUE(); 0568 0569 // Doesn't take references to other sheets into account 0570 //const QRect rect(e->ranges[0].col1, e->ranges[0].row1, e->ranges[0].col2, e->ranges[0].row2); 0571 //const Calligra::Sheets::Region region(rect, e->sheet); 0572 0573 if (e->regions.isEmpty()) 0574 return Value::errorVALUE(); 0575 0576 const Calligra::Sheets::Region ®ion = e->regions[0]; 0577 0578 if (!region.isValid() /* || !region.isSingular() */) 0579 return Value::errorVALUE(); 0580 0581 QPoint p = region.firstRange().topLeft() + QPoint(colPlus, rowPlus); 0582 const Cell cell(region.firstSheet(), p); 0583 if (!cell.isNull()) 0584 return cell.value(); 0585 0586 return Value::errorVALUE(); 0587 } 0588 0589 // 0590 // Function: ROW 0591 // 0592 Value func_row(valVector args, ValueCalc *, FuncExtra *e) 0593 { 0594 int row = e ? e->myrow : 0; 0595 if (e && args.count()) 0596 row = e->ranges[0].row1; 0597 if (row > 0) 0598 return Value(row); 0599 return Value::errorVALUE(); 0600 } 0601 0602 0603 // 0604 // Function: ROWS 0605 // 0606 Value func_rows(valVector, ValueCalc *, FuncExtra *e) 0607 { 0608 int row1 = e->ranges[0].row1; 0609 int row2 = e->ranges[0].row2; 0610 if ((row1 == -1) || (row2 == -1)) 0611 return Value::errorVALUE(); 0612 return Value(row2 - row1 + 1); 0613 } 0614 0615 // 0616 // Function: SHEET 0617 // 0618 Value func_sheet(valVector /*args*/, ValueCalc *, FuncExtra *e) 0619 { 0620 Sheet *sheet = e->sheet; 0621 if (!e->regions.isEmpty()) { 0622 const Calligra::Sheets::Region ®ion = e->regions[0]; 0623 if (region.isValid()) 0624 sheet = region.firstSheet(); 0625 } 0626 return Value(sheet->map()->indexOf(sheet) + 1); 0627 } 0628 0629 // 0630 // Function: SHEETS 0631 // 0632 Value func_sheets(valVector /*args*/, ValueCalc *, FuncExtra *e) 0633 { 0634 if (!e->regions.isEmpty()) { 0635 const Calligra::Sheets::Region ®ion = e->regions[0]; 0636 if (region.isValid()) { 0637 QList<Calligra::Sheets::Sheet*> sheets; 0638 Calligra::Sheets::Region::ConstIterator it(region.constBegin()), end(region.constEnd()); 0639 for(; it != end; ++it) 0640 if (!sheets.contains((*it)->sheet())) 0641 sheets.append((*it)->sheet()); 0642 return Value(sheets.count()); 0643 } 0644 } 0645 return Value(e->sheet->map()->count()); 0646 } 0647 0648 // 0649 // Function: VLOOKUP 0650 // 0651 Value func_vlookup(valVector args, ValueCalc *calc, FuncExtra *) 0652 { 0653 const Value key = args[0]; 0654 const Value data = args[1]; 0655 const int col = calc->conv()->asInteger(args[2]).asInteger(); 0656 const int cols = data.columns(); 0657 const int rows = data.rows(); 0658 if (col < 1 || col > cols) 0659 return Value::errorVALUE(); 0660 const bool rangeLookup = (args.count() > 3) ? calc->conv()->asBoolean(args[3]).asBoolean() : true; 0661 0662 // now traverse the array and perform comparison 0663 Value r; 0664 Value v = Value::errorNA(); 0665 for (int row = 0; row < rows; ++row) { 0666 // search in the first column 0667 const Value le = data.element(0, row); 0668 if (calc->naturalEqual(key, le)) { 0669 return data.element(col - 1, row); 0670 } 0671 // optionally look for the next largest value that is less than key 0672 if (rangeLookup && calc->naturalLower(le, key) && calc->naturalLower(r, le)) { 0673 r = le; 0674 v = data.element(col - 1, row); 0675 } 0676 } 0677 return v; 0678 } 0679 0680 #include "reference.moc"