1
/***************************************************************************
2
* Copyright (c) 2015 Eivind Kvedalen <eivind@kvedalen.name> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
25
#include <boost/regex.hpp>
34
#include <Base/Exception.h>
40
const int App::CellAddress::MAX_ROWS = 16384;
41
const int App::CellAddress::MAX_COLUMNS = 26 * 26 + 26;
44
// From a given cell address the '$' must be at within the first
46
bool maybeAbsolute(std::string_view address)
48
const int MAX_COLUMNS_LETTERS = 2;
50
address = address.substr(0, MAX_COLUMNS_LETTERS + 1);
51
return address.find("$") != std::string_view::npos;
55
Range::Range(const char * range, bool normalize)
62
if (!strchr(range, ':')) {
67
std::string s = range;
68
from = s.substr(0, s.find(':'));
69
to = s.substr(s.find(':') + 1);
72
CellAddress begin(from);
75
row_begin = begin.row();
76
col_begin = begin.col();
87
Range::Range(int _row_begin, int _col_begin, int _row_end, int _col_end, bool normalize)
88
: row_begin(_row_begin)
89
, col_begin(_col_begin)
100
Range::Range(const CellAddress &from, const CellAddress &to, bool normalize)
101
: row_begin(from.row())
102
, col_begin(from.col())
109
row_curr = row_begin;
110
col_curr = col_begin;
113
void Range::normalize()
115
if (row_begin > row_end) {
116
std::swap(row_begin, row_end);
118
if (col_begin > col_end) {
119
std::swap(col_begin, col_end);
125
if (row_curr < row_end) {
130
if (col_curr < col_end) {
131
if (row_curr == row_end + 1)
133
row_curr = row_begin;
141
* @brief Decode a row specification into a 0-based integer.
143
* @param rowstr Row specified as a string, with "1" being the first row.
148
int App::decodeRow(const std::string &rowstr, bool silent)
150
int row = validRow(rowstr);
152
if (silent || row >= 0) {
156
throw Base::IndexError("Invalid row specification.");
160
* Assumes well-formed input. A through ZZZ. 0-based output
162
int columnStringToNum(const std::string &colstr) {
165
for (auto chr = colstr.crbegin(); chr != colstr.crend(); chr++){
166
out += (*chr - 'A' + 1) * std::pow(26, pos++);
169
return static_cast<int>(out - 1);
173
* @brief Decode a column name string into a 0-based integer.
175
* @param colstr input string.
177
* @returns The column.
181
int App::decodeColumn( const std::string &colstr, bool silent )
183
if (validColumn(colstr)) {
184
return columnStringToNum(colstr);
191
throw Base::IndexError("Invalid column specification");
195
* @brief Determine whether a row specification is valid or not.
197
* @param rowstr Row specified as a string, with "1" being the first row.
199
* @returns 0 or positive on success, -1 on error.
202
int App::validRow(const std::string &rowstr)
205
int i = strtol(rowstr.c_str(), &end, 10);
207
if (i <=0 || i > CellAddress::MAX_ROWS || *end) {
215
* @brief Determine if a string is a valid column specification.
217
* @param colstr input string.
219
* @returns true if valid, false if not.
223
bool App::validColumn( const std::string &colstr )
225
return boost::regex_match(colstr, boost::regex("[A-Z]{1,3}"));
229
* @brief Convert a string address into integer \a row and \a column.
230
* row and col are 0-based.
232
* This function will throw an exception if the specified \a address is invalid.
234
* @param strAddress Address to parse.
238
App::CellAddress App::stringToAddress(const char * strAddress, bool silent)
242
static boost::regex e("(\\$?[A-Z]{1,2})(\\$?[0-9]{1,5})");
245
if (boost::regex_match(strAddress, cm, e)) {
246
bool absCol = (cm[1].first[0]=='$');
249
c = std::string(cm[1].first+1,cm[1].second);
252
c = std::string(cm[1].first,cm[1].second);
255
bool absRow = (cm[2].first[0]=='$');
257
r = std::string(cm[2].first+1,cm[2].second);
260
r = std::string(cm[2].first,cm[2].second);
263
return CellAddress(decodeRow(r,silent), decodeColumn(c,silent), absRow, absCol);
266
return CellAddress();
269
throw Base::RuntimeError("Invalid cell specifier.");
273
* @brief Convert given \a cell address into its string representation.
275
* @returns Address given as a string.
278
std::string App::CellAddress::toString(Cell cell) const
282
Base::Flags<Cell> flags(cell);
283
if (flags.testFlag(Cell::ShowColumn)) {
284
if (_absCol && flags.testFlag(Cell::Absolute)) {
288
s << static_cast<char>('A' + col());
291
int colnum = col() - 26;
293
s << static_cast<char>('A' + (colnum / 26));
294
s << static_cast<char>('A' + (colnum % 26));
298
if (flags.testFlag(Cell::ShowRow)) {
299
if (_absRow && flags.testFlag(Cell::Absolute)) {
309
* \brief App::CellAddress::parseAbsoluteAddress
311
* If the passed string is a valid and absolute cell address it will be assigned to this instance.
312
* \return True if it's an absolute cell address and false otherwise
314
bool App::CellAddress::parseAbsoluteAddress(const char *address) {
315
if (maybeAbsolute(address)) {
316
CellAddress addr = stringToAddress(address, true);
317
if (addr.isValid()) {