diff --git a/src/Mod/Spreadsheet/App/PropertySheet.cpp b/src/Mod/Spreadsheet/App/PropertySheet.cpp index 576c7ae1d..063ed06a0 100644 --- a/src/Mod/Spreadsheet/App/PropertySheet.cpp +++ b/src/Mod/Spreadsheet/App/PropertySheet.cpp @@ -176,6 +176,33 @@ const Cell * PropertySheet::getValueFromAlias(const std::string &alias) const return getValue(it->second); else return 0; + +} + +bool PropertySheet::isValidAlias(const std::string &candidate) +{ + static const boost::regex gen("^[A-Za-z][_A-Za-z0-9]*$"); + boost::cmatch cm; + + /* Check if it is used before */ + if (getValueFromAlias(candidate) != 0) + return false; + + if (boost::regex_match(candidate.c_str(), cm, gen)) { + static const boost::regex e("\\${0,1}([A-Z]{1,2})\\${0,1}([0-9]{1,5})"); + + if (boost::regex_match(candidate.c_str(), cm, e)) { + const boost::sub_match colstr = cm[1]; + const boost::sub_match rowstr = cm[2]; + + // A valid cell address? + if (Spreadsheet::validRow(rowstr.str()) >= 0 && Spreadsheet::validColumn(colstr.str()) >= 0) + return false; + } + return true; + } + else + return false; } std::set PropertySheet::getUsedCells() const @@ -452,7 +479,15 @@ void PropertySheet::setDisplayUnit(CellAddress address, const std::string &unit) void PropertySheet::setAlias(CellAddress address, const std::string &alias) { + if (alias.size() > 0 && !isValidAlias(alias)) + throw Base::Exception("Invalid alias"); + + const Cell * aliasedCell = getValueFromAlias(alias); Cell * cell = nonNullCellAt(address); + + if (aliasedCell != 0 && cell != aliasedCell) + throw Base::Exception("Alias already defined."); + assert(cell != 0); /* Mark cells depending on this cell dirty; they need to be resolved when an alias changes or disappears */ diff --git a/src/Mod/Spreadsheet/App/PropertySheet.h b/src/Mod/Spreadsheet/App/PropertySheet.h index 8ba5169f1..d032a84f4 100644 --- a/src/Mod/Spreadsheet/App/PropertySheet.h +++ b/src/Mod/Spreadsheet/App/PropertySheet.h @@ -85,6 +85,8 @@ public: const Cell * getValueFromAlias(const std::string &alias) const; + bool isValidAlias(const std::string &candidate); + std::set getUsedCells() const; Sheet * sheet() const { return owner; } diff --git a/src/Mod/Spreadsheet/App/Sheet.cpp b/src/Mod/Spreadsheet/App/Sheet.cpp index db9ac92fe..02e03e821 100644 --- a/src/Mod/Spreadsheet/App/Sheet.cpp +++ b/src/Mod/Spreadsheet/App/Sheet.cpp @@ -1123,19 +1123,28 @@ void Sheet::setComputedUnit(CellAddress address, const Base::Unit &unit) } /** - * @brief Set alias for cell at address \a address to \a alias. + * @brief Set alias for cell at address \a address to \a alias. If the alias + * is an empty string, the existing alias is removed. * @param address Address of cell * @param alias New alias. */ void Sheet::setAlias(CellAddress address, const std::string &alias) { - const Cell * cell = cells.getValueFromAlias(alias); + std::string existingAlias = getAddressFromAlias(alias); - if (cell != 0) - throw Base::Exception("Alias already defined."); - else + if (existingAlias.size() > 0) { + if (existingAlias == address.toString()) // Same as old? + return; + else + throw Base::Exception("Alias already defined"); + } + else if (alias.size() == 0) // Empty? + cells.setAlias(address, ""); + else if (isValidAlias(alias)) // Valid? cells.setAlias(address, alias); + else + throw Base::Exception("Invalid alias"); } /** @@ -1155,6 +1164,31 @@ std::string Sheet::getAddressFromAlias(const std::string &alias) const return std::string(); } +/** + * @brief Determine whether a given alias candiate is valid or not. + * + * A candidate is valid is the string is syntactically correct, + * and the alias does not conflict with an existing property. + * + */ + +bool Sheet::isValidAlias(const std::string & candidate) +{ + // Valid syntactically? + if (!cells.isValidAlias(candidate)) + return false; + + // Existing alias? Then it's ok + if (getAddressFromAlias(candidate).size() > 0 ) + return true; + + // Check to see that is does not crash with any other property in the Sheet object. + if (getPropertyByName(candidate.c_str())) + return false; + else + return true; +} + /** * @brief Set row and column span for the cell at address \a address to \a rows and \a columns. * @param address Address to upper right corner of cell diff --git a/src/Mod/Spreadsheet/App/Sheet.h b/src/Mod/Spreadsheet/App/Sheet.h index 65f7b9e96..0ccf02333 100644 --- a/src/Mod/Spreadsheet/App/Sheet.h +++ b/src/Mod/Spreadsheet/App/Sheet.h @@ -142,6 +142,8 @@ public: std::string getAddressFromAlias(const std::string & alias) const; + bool isValidAlias(const std::string &candidate); + void setSpans(CellAddress address, int rows, int columns); std::set dependsOn(CellAddress address) const; diff --git a/src/Mod/Spreadsheet/App/Utils.cpp b/src/Mod/Spreadsheet/App/Utils.cpp index 5b50a9fe6..e687de429 100644 --- a/src/Mod/Spreadsheet/App/Utils.cpp +++ b/src/Mod/Spreadsheet/App/Utils.cpp @@ -84,13 +84,12 @@ std::string Spreadsheet::rowName(int row) int Spreadsheet::decodeRow(const std::string &rowstr) { - char * end; - int i = strtol(rowstr.c_str(), &end, 10); + int row = validRow(rowstr); - if (i <0 || i >= CellAddress::MAX_ROWS || *end) + if (row >= 0) + return row; + else throw Base::Exception("Invalid row specification."); - - return i - 1; } /** @@ -103,6 +102,44 @@ int Spreadsheet::decodeRow(const std::string &rowstr) */ int Spreadsheet::decodeColumn(const std::string &colstr) +{ + int col = validColumn(colstr); + + if (col >= 0) + return col; + else + throw Base::Exception("Invalid column specification"); +} + +/** + * Determine wheter a row specification is valid or not. + * + * @param rowstr Row specified as a string, with "1" being the first row. + * + * @returns 0 or positive on success, -1 on error. + */ + +int Spreadsheet::validRow(const std::string &rowstr) +{ + char * end; + int i = strtol(rowstr.c_str(), &end, 10); + + if (i <0 || i >= CellAddress::MAX_ROWS || *end) + return -1; + + return i - 1; +} + +/** + * Determine whether a column specification is valid or not. + * + * @param colstr Column specified as a string, with "A" begin the first column. + * + * @returns 0 or positive on success, -1 on error. + * + */ + +int Spreadsheet::validColumn(const std::string &colstr) { int col = 0; @@ -110,7 +147,7 @@ int Spreadsheet::decodeColumn(const std::string &colstr) if ((colstr[0] >= 'A' && colstr[0] <= 'Z')) col = colstr[0] - 'A'; else - throw Base::Exception("Invalid column specification"); + return -1; } else { col = 0; @@ -120,7 +157,7 @@ int Spreadsheet::decodeColumn(const std::string &colstr) if ((*i >= 'A' && *i <= 'Z')) v = *i - 'A'; else - throw Base::Exception("Invalid column specification"); + return -1; col = col * 26 + v; } diff --git a/src/Mod/Spreadsheet/App/Utils.h b/src/Mod/Spreadsheet/App/Utils.h index 0080397c8..7be4b60be 100644 --- a/src/Mod/Spreadsheet/App/Utils.h +++ b/src/Mod/Spreadsheet/App/Utils.h @@ -37,6 +37,9 @@ SpreadsheetExport std::string columnName(int col); SpreadsheetExport std::string rowName(int row); int decodeColumn(const std::string &colstr); int decodeRow(const std::string &rowstr); +int validColumn(const std::string &colstr); +int validRow(const std::string &rowstr); + SpreadsheetExport CellAddress stringToAddress(const char *strAddress); SpreadsheetExport void createRectangles(std::set > & cells, std::map, std::pair > & rectangles); SpreadsheetExport std::string quote(const std::string &input); diff --git a/src/Mod/Spreadsheet/Gui/PropertiesDialog.cpp b/src/Mod/Spreadsheet/Gui/PropertiesDialog.cpp index b963bfb20..a10ef3e66 100644 --- a/src/Mod/Spreadsheet/Gui/PropertiesDialog.cpp +++ b/src/Mod/Spreadsheet/Gui/PropertiesDialog.cpp @@ -204,22 +204,7 @@ void PropertiesDialog::aliasChanged(const QString & text) { QPalette palette = ui->alias->palette(); - aliasOk = true; - - if (sheet->getAddressFromAlias(Base::Tools::toStdString(text)).size() > 0) - aliasOk = false; - - if (text.indexOf(QRegExp(QString::fromLatin1("^[A-Za-z][_A-Za-z0-9]*$"))) >= 0) { - try { - CellAddress address(text.toUtf8().constData()); - aliasOk = false; - } - catch (...) { } - } - else { - if (!text.isEmpty()) - aliasOk = false; - } + aliasOk = text.isEmpty() || sheet->isValidAlias(Base::Tools::toStdString(text)); alias = aliasOk ? Base::Tools::toStdString(text) : ""; palette.setColor(QPalette::Text, aliasOk ? Qt::black : Qt::red);