You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
412 lines
11 KiB
412 lines
11 KiB
/* This file is part of the KDE project
|
|
Copyright (C) 1998-2002 The KSpread Team
|
|
www.koffice.org/kspread
|
|
Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
// built-in reference functions
|
|
|
|
#include "kspread_cell.h"
|
|
#include "kspread_sheet.h"
|
|
#include "kspread_util.h"
|
|
#include "kspread_value.h"
|
|
|
|
#include "functions.h"
|
|
#include "valuecalc.h"
|
|
#include "valueconverter.h"
|
|
|
|
using namespace KSpread;
|
|
|
|
// prototypes (sorted alphabetically)
|
|
Value func_address (valVector args, ValueCalc *calc, FuncExtra *);
|
|
Value func_areas (valVector args, ValueCalc *calc, FuncExtra *);
|
|
Value func_choose (valVector args, ValueCalc *calc, FuncExtra *);
|
|
Value func_column (valVector args, ValueCalc *calc, FuncExtra *);
|
|
Value func_columns (valVector args, ValueCalc *calc, FuncExtra *);
|
|
Value func_hlookup (valVector args, ValueCalc *calc, FuncExtra *);
|
|
Value func_index (valVector args, ValueCalc *calc, FuncExtra *);
|
|
Value func_indirect (valVector args, ValueCalc *calc, FuncExtra *);
|
|
Value func_lookup (valVector args, ValueCalc *calc, FuncExtra *);
|
|
Value func_row (valVector args, ValueCalc *calc, FuncExtra *);
|
|
Value func_rows (valVector args, ValueCalc *calc, FuncExtra *);
|
|
Value func_vlookup (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
// registers all reference functions
|
|
void RegisterReferenceFunctions()
|
|
{
|
|
FunctionRepository* repo = FunctionRepository::self();
|
|
Function *f;
|
|
|
|
f = new Function ("ADDRESS", func_address);
|
|
f->setParamCount (2, 5);
|
|
repo->add (f);
|
|
f = new Function ("AREAS", func_areas);
|
|
f->setParamCount (1);
|
|
f->setNeedsExtra (true);
|
|
repo->add (f);
|
|
f = new Function ("CHOOSE", func_choose);
|
|
f->setParamCount (2, -1);
|
|
f->setAcceptArray ();
|
|
repo->add (f);
|
|
f = new Function ("COLUMN", func_column);
|
|
f->setParamCount (0, 1);
|
|
repo->add (f);
|
|
f = new Function ("COLUMNS", func_columns);
|
|
f->setParamCount (1);
|
|
f->setAcceptArray ();
|
|
f->setNeedsExtra (true);
|
|
repo->add (f);
|
|
f = new Function ("HLOOKUP", func_hlookup);
|
|
f->setParamCount (3, 4);
|
|
f->setAcceptArray ();
|
|
repo->add (f);
|
|
f = new Function ("INDEX", func_index);
|
|
f->setParamCount (3);
|
|
f->setAcceptArray ();
|
|
repo->add (f);
|
|
f = new Function ("INDIRECT", func_indirect);
|
|
f->setParamCount (1, 2);
|
|
f->setNeedsExtra (true);
|
|
repo->add (f);
|
|
f = new Function ("LOOKUP", func_lookup);
|
|
f->setParamCount (3);
|
|
f->setAcceptArray ();
|
|
repo->add (f);
|
|
f = new Function ("ROW", func_row);
|
|
f->setParamCount (0, 1);
|
|
repo->add (f);
|
|
f = new Function ("ROWS", func_rows);
|
|
f->setParamCount (1);
|
|
f->setAcceptArray ();
|
|
f->setNeedsExtra (true);
|
|
repo->add (f);
|
|
f = new Function ("VLOOKUP", func_vlookup);
|
|
f->setParamCount (3, 4);
|
|
f->setAcceptArray ();
|
|
repo->add (f);
|
|
}
|
|
|
|
// Function: ADDRESS
|
|
Value func_address (valVector args, ValueCalc *calc, FuncExtra *)
|
|
{
|
|
bool r1c1 = false;
|
|
TQString sheetName;
|
|
int absNum = 1;
|
|
if (args.count() > 2)
|
|
absNum = calc->conv()->asInteger (args[2]).asInteger();
|
|
if (args.count() > 3)
|
|
r1c1 = !(calc->conv()->asBoolean (args[3]).asBoolean());
|
|
if (args.count() == 5)
|
|
sheetName = calc->conv()->asString (args[4]).asString();
|
|
|
|
TQString result;
|
|
int row = calc->conv()->asInteger (args[0]).asInteger();
|
|
int col = calc->conv()->asInteger (args[1]).asInteger();
|
|
|
|
if ( !sheetName.isEmpty() )
|
|
{
|
|
result += sheetName;
|
|
result += "!";
|
|
}
|
|
|
|
if ( r1c1 )
|
|
{
|
|
// row first
|
|
bool abs = false;
|
|
if ( absNum == 1 || absNum == 2 )
|
|
abs = true;
|
|
|
|
result += 'R';
|
|
if ( !abs )
|
|
result += '[';
|
|
result += TQString::number( row );
|
|
|
|
if ( !abs )
|
|
result += ']';
|
|
|
|
// column
|
|
abs = false;
|
|
if ( absNum == 1 || absNum == 3 )
|
|
abs = true;
|
|
|
|
result += 'C';
|
|
if ( !abs )
|
|
result += '[';
|
|
result += TQString::number( col );
|
|
|
|
if ( !abs )
|
|
result += ']';
|
|
}
|
|
else
|
|
{
|
|
bool abs = false;
|
|
if ( absNum == 1 || absNum == 3 )
|
|
abs = true;
|
|
|
|
if ( abs )
|
|
result += '$';
|
|
|
|
result += Cell::columnName( col );
|
|
|
|
abs = false;
|
|
if ( absNum == 1 || absNum == 2 )
|
|
abs = true;
|
|
|
|
if ( abs )
|
|
result += '$';
|
|
|
|
result += TQString::number( row );
|
|
}
|
|
|
|
return Value (result);
|
|
}
|
|
|
|
bool checkRef( TQString const & ref )
|
|
{
|
|
Range r( ref );
|
|
if ( !r.isValid() )
|
|
{
|
|
Point p( ref );
|
|
if ( !p.isValid() )
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Function: AREAS
|
|
Value func_areas (valVector args, ValueCalc *calc, FuncExtra *e)
|
|
{
|
|
if (e) {
|
|
if ((e->ranges[0].col1 != -1) && (e->ranges[0].row1 != -1) &&
|
|
(e->ranges[0].col2 != -1) && (e->ranges[0].row2 != -1))
|
|
// we have a range reference - return 1
|
|
return 1;
|
|
}
|
|
|
|
TQString s = calc->conv()->asString (args[0]).asString();
|
|
if ( s[0] != '(' || s[s.length() - 1] != ')' )
|
|
return Value::errorVALUE();
|
|
|
|
int l = s.length();
|
|
|
|
int num = 0;
|
|
TQString ref;
|
|
for ( int i = 1; i < l; ++i )
|
|
{
|
|
if ( s[i] == ',' || s[i] == ')' )
|
|
{
|
|
if ( !checkRef( ref ) )
|
|
return Value::errorVALUE();
|
|
else
|
|
{
|
|
++num;
|
|
ref = "";
|
|
}
|
|
}
|
|
else
|
|
ref += s[i];
|
|
}
|
|
|
|
return Value (num);
|
|
}
|
|
|
|
// Function: CHOOSE
|
|
Value func_choose (valVector args, ValueCalc *calc, FuncExtra *)
|
|
{
|
|
int cnt = args.count () - 1;
|
|
int num = calc->conv()->asInteger (args[0]).asInteger();
|
|
if ((num <= 0) || (num > cnt))
|
|
return Value::errorVALUE();
|
|
return args[num];
|
|
}
|
|
|
|
// Function: HLOOKUP
|
|
Value func_hlookup (valVector args, ValueCalc *calc, FuncExtra *)
|
|
{
|
|
const Value key = args[0];
|
|
const Value data = args[1];
|
|
const int row = calc->conv()->asInteger( args[2] ).asInteger();
|
|
const int cols = data.columns();
|
|
const int rows = data.rows();
|
|
if ( row < 1 || row > rows )
|
|
return Value::errorVALUE();
|
|
const bool rangeLookup = ( args.count() > 3 ) ? calc->conv()->asBoolean( args[3] ).asBoolean() : true;
|
|
|
|
// now traverse the array and perform comparison
|
|
Value r;
|
|
Value v = Value::errorNA();
|
|
for (int col = 0; col < cols; ++col) {
|
|
// search in the first row
|
|
const Value le = data.element(col, 0);
|
|
if (calc->naturalEqual(key, le)) {
|
|
return data.element(col, row - 1);
|
|
}
|
|
// optionally look for the next largest value that is less than key
|
|
if (rangeLookup && calc->naturalLower(le, key) && calc->naturalLower(r, le)) {
|
|
r = le;
|
|
v = data.element(col, row - 1);
|
|
}
|
|
}
|
|
return v;
|
|
}
|
|
|
|
// Function: INDEX
|
|
Value func_index (valVector args, ValueCalc *calc, FuncExtra *)
|
|
{
|
|
// first argument can be either a range, then we return a given cell's
|
|
// value, or a single cell containing an array - then we return the array
|
|
// element. In any case, this function can assume that the given value
|
|
// is the same. Because it is.
|
|
|
|
Value val = args[0];
|
|
unsigned row = calc->conv()->asInteger (args[1]).asInteger() - 1;
|
|
unsigned col = calc->conv()->asInteger (args[2]).asInteger() - 1;
|
|
if ((row >= val.rows()) || (col >= val.columns()))
|
|
return Value::errorREF();
|
|
return val.element (col, row);
|
|
}
|
|
|
|
// Function: LOOKUP
|
|
Value func_lookup (valVector args, ValueCalc *calc, FuncExtra *)
|
|
{
|
|
Value num = calc->conv()->asNumeric (args[0]);
|
|
if (num.isArray())
|
|
return Value::errorVALUE();
|
|
Value lookup = args[1];
|
|
Value rr = args[2];
|
|
unsigned cols = lookup.columns();
|
|
unsigned rows = lookup.rows();
|
|
if ((cols != rr.columns()) || (rows != rr.rows()))
|
|
return Value::errorVALUE();
|
|
Value res = Value::errorNA();
|
|
|
|
// now traverse the array and perform comparison
|
|
for (unsigned r = 0; r < rows; ++r)
|
|
for (unsigned c = 0; c < cols; ++c)
|
|
{
|
|
// update the result, return if we cross the line
|
|
Value le = lookup.element (c, r);
|
|
if (calc->lower (le, num) || calc->equal (num, le))
|
|
res = rr.element (c, r);
|
|
else
|
|
return res;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// Function: COLUMN
|
|
Value func_column (valVector args, ValueCalc *, FuncExtra *e)
|
|
{
|
|
int col = e ? e->mycol : 0;
|
|
if (e && args.count())
|
|
col = e->ranges[0].col1;
|
|
if (col > 0)
|
|
return Value (col);
|
|
return Value::errorVALUE();
|
|
}
|
|
|
|
// Function: ROW
|
|
Value func_row (valVector args, ValueCalc *, FuncExtra *e)
|
|
{
|
|
int row = e ? e->myrow : 0;
|
|
if (e && args.count())
|
|
row = e->ranges[0].row1;
|
|
if (row > 0)
|
|
return Value (row);
|
|
return Value::errorVALUE();
|
|
}
|
|
|
|
// Function: COLUMNS
|
|
Value func_columns (valVector, ValueCalc *, FuncExtra *e)
|
|
{
|
|
int col1 = e->ranges[0].col1;
|
|
int col2 = e->ranges[0].col2;
|
|
if ((col1 == -1) || (col2 == -1))
|
|
return Value::errorVALUE();
|
|
return Value (col2 - col1 + 1);
|
|
}
|
|
|
|
// Function: ROWS
|
|
Value func_rows (valVector, ValueCalc *, FuncExtra *e)
|
|
{
|
|
int row1 = e->ranges[0].row1;
|
|
int row2 = e->ranges[0].row2;
|
|
if ((row1 == -1) || (row2 == -1))
|
|
return Value::errorVALUE();
|
|
return Value (row2 - row1 + 1);
|
|
}
|
|
|
|
|
|
// Function: INDIRECT
|
|
Value func_indirect (valVector args, ValueCalc *calc, FuncExtra *e)
|
|
{
|
|
bool r1c1 = false;
|
|
TQString ref = calc->conv()->asString (args[0]).asString();
|
|
if (args.count() == 2)
|
|
r1c1 = !(calc->conv()->asBoolean (args[1]).asBoolean());
|
|
|
|
if (ref.isEmpty())
|
|
return Value::errorVALUE();
|
|
|
|
if ( r1c1 )
|
|
{
|
|
// TODO: translate the r1c1 style to a1 style
|
|
ref = ref;
|
|
}
|
|
|
|
Point p (ref, e->sheet->workbook(), e->sheet);
|
|
|
|
if ( !p.isValid() )
|
|
return Value::errorVALUE();
|
|
|
|
Cell * cell = p.cell();
|
|
if (cell)
|
|
return cell->value();
|
|
return Value::errorVALUE();
|
|
}
|
|
|
|
// Function: VLOOKUP
|
|
Value func_vlookup (valVector args, ValueCalc *calc, FuncExtra *)
|
|
{
|
|
const Value key = args[0];
|
|
const Value data = args[1];
|
|
const int col = calc->conv()->asInteger(args[2]).asInteger();
|
|
const int cols = data.columns();
|
|
const int rows = data.rows();
|
|
if (col < 1 || col > cols)
|
|
return Value::errorVALUE();
|
|
const bool rangeLookup = (args.count() > 3) ? calc->conv()->asBoolean(args[3]).asBoolean() : true;
|
|
|
|
// now traverse the array and perform comparison
|
|
Value r;
|
|
Value v = Value::errorNA();
|
|
for (int row = 0; row < rows; ++row) {
|
|
// search in the first column
|
|
const Value le = data.element(0, row);
|
|
if (calc->naturalEqual(key, le)) {
|
|
return data.element(col - 1, row);
|
|
}
|
|
// optionally look for the next largest value that is less than key
|
|
if (rangeLookup && calc->naturalLower(le, key) && calc->naturalLower(r, le)) {
|
|
r = le;
|
|
v = data.element(col - 1, row);
|
|
}
|
|
}
|
|
return v;
|
|
}
|