diff --git a/src/App/Application.cpp b/src/App/Application.cpp index a3c76a0153..084388fbe9 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -1981,6 +1981,8 @@ void Application::initTypes() App::FeatureTest ::init(); App::FeatureTestException ::init(); App::FeatureTestColumn ::init(); + App::FeatureTestRow ::init(); + App::FeatureTestAbsAddress ::init(); App::FeatureTestPlacement ::init(); App::FeatureTestAttribute ::init(); diff --git a/src/App/FeatureTest.cpp b/src/App/FeatureTest.cpp index e5ef2875ff..9b804d0d63 100644 --- a/src/App/FeatureTest.cpp +++ b/src/App/FeatureTest.cpp @@ -219,6 +219,42 @@ DocumentObjectExecReturn *FeatureTestColumn::execute() // ---------------------------------------------------------------------------- +PROPERTY_SOURCE(App::FeatureTestRow, App::DocumentObject) + + +FeatureTestRow::FeatureTestRow() +{ + ADD_PROPERTY_TYPE(Row, ("1"), "Test", App::Prop_None, ""); + ADD_PROPERTY_TYPE(Silent, (false), "Test", App::Prop_None, ""); + ADD_PROPERTY_TYPE(Value, (0L), "Test", App::Prop_Output, ""); +} + +DocumentObjectExecReturn *FeatureTestRow::execute() +{ + Value.setValue(decodeRow(Row.getStrValue(), Silent.getValue())); + return nullptr; +} + +// ---------------------------------------------------------------------------- + +PROPERTY_SOURCE(App::FeatureTestAbsAddress, App::DocumentObject) + + +FeatureTestAbsAddress::FeatureTestAbsAddress() +{ + ADD_PROPERTY_TYPE(Address, (""), "Test", Prop_None, ""); + ADD_PROPERTY_TYPE(Valid, (false), "Test", PropertyType(Prop_Output | Prop_ReadOnly), ""); +} + +DocumentObjectExecReturn *FeatureTestAbsAddress::execute() +{ + CellAddress address; + Valid.setValue(address.parseAbsoluteAddress(Address.getValue())); + return StdReturn; +} + +// ---------------------------------------------------------------------------- + PROPERTY_SOURCE(App::FeatureTestPlacement, App::DocumentObject) diff --git a/src/App/FeatureTest.h b/src/App/FeatureTest.h index 032e9473cd..2a52a6ed83 100644 --- a/src/App/FeatureTest.h +++ b/src/App/FeatureTest.h @@ -153,6 +153,36 @@ public: //@} }; +class FeatureTestRow : public DocumentObject +{ + PROPERTY_HEADER_WITH_OVERRIDE(App::FeatureTestRow); + +public: + FeatureTestRow(); + + // Standard Properties (PropertyStandard.h) + App::PropertyString Row; + App::PropertyBool Silent; + App::PropertyInteger Value; + + /** @name methods override Feature */ + //@{ + DocumentObjectExecReturn *execute() override; + //@} +}; + +class FeatureTestAbsAddress : public DocumentObject +{ + PROPERTY_HEADER_WITH_OVERRIDE(App::FeatureTestAbsAddress); + +public: + FeatureTestAbsAddress(); + DocumentObjectExecReturn *execute() override; + + App::PropertyString Address; + App::PropertyBool Valid; +}; + class FeatureTestPlacement : public DocumentObject { PROPERTY_HEADER_WITH_OVERRIDE(App::FeatureTestPlacement); diff --git a/src/App/Range.cpp b/src/App/Range.cpp index 25d80f2ef9..d33ab6228c 100644 --- a/src/App/Range.cpp +++ b/src/App/Range.cpp @@ -30,6 +30,7 @@ #include #endif +#include #include #include "Range.h" @@ -39,6 +40,18 @@ using namespace App; const int App::CellAddress::MAX_ROWS = 16384; const int App::CellAddress::MAX_COLUMNS = 26 * 26 + 26; +namespace App { +// From a given cell address the '$' must be at within the first +// few characters +bool maybeAbsolute(std::string_view address) +{ + const int MAX_COLUMNS_LETTERS = 2; + + address = address.substr(0, MAX_COLUMNS_LETTERS + 1); + return address.find("$") != std::string_view::npos; +} +} + Range::Range(const char * range, bool normalize) { std::string from; @@ -271,10 +284,16 @@ std::string App::CellAddress::toString(Cell cell) const return s.str(); } -bool App::CellAddress::parseAbsoluteAddress(const char *txt) { - if(txt[0]=='$' || (txt[0] && txt[1] && (txt[1]=='$' || txt[2]=='$'))) { - CellAddress addr = stringToAddress(txt,true); - if(addr.isValid()) { +/*! + * \brief App::CellAddress::parseAbsoluteAddress + * \param address + * If the passed string is a valid and absolute cell address it will be assigned to this instance. + * \return True if it's an absolute cell address and false otherwise + */ +bool App::CellAddress::parseAbsoluteAddress(const char *address) { + if (maybeAbsolute(address)) { + CellAddress addr = stringToAddress(address, true); + if (addr.isValid()) { *this = addr; return true; } diff --git a/src/Mod/Test/Document.py b/src/Mod/Test/Document.py index 9cdd2ed827..6bb2c9a3f7 100644 --- a/src/Mod/Test/Document.py +++ b/src/Mod/Test/Document.py @@ -2308,6 +2308,113 @@ class FeatureTestColumn(unittest.TestCase): FreeCAD.closeDocument("TestColumn") + +class FeatureTestRow(unittest.TestCase): + def setUp(self): + doc = FreeCAD.newDocument("TestRow") + self.obj = doc.addObject("App::FeatureTestRow", "Row") + + def testEmpty(self): + self.obj.Silent = True + self.obj.Row = "" + self.obj.recompute() + self.assertEqual(self.obj.Value, -1) + + def testA(self): + self.obj.Silent = True + self.obj.Row = "A" + self.obj.recompute() + self.assertEqual(self.obj.Value, -1) + + def testException(self): + value = self.obj.Value + self.obj.Row = "A" + self.assertFalse(self.obj.recompute()) + self.assertEqual(self.obj.Value, value) + + def test0(self): + self.obj.Silent = True + self.obj.Row = "0" + self.obj.recompute() + self.assertEqual(self.obj.Value, -1) + + def test1(self): + self.obj.Silent = True + self.obj.Row = "1" + self.obj.recompute() + self.assertEqual(self.obj.Value, 0) + + def test16384(self): + self.obj.Silent = True + self.obj.Row = "16384" + self.obj.recompute() + self.assertEqual(self.obj.Value, 16383) + + def test16385(self): + self.obj.Silent = True + self.obj.Row = "16385" + self.obj.recompute() + self.assertEqual(self.obj.Value, -1) + + def tearDown(self): + FreeCAD.closeDocument("TestRow") + + + +class FeatureTestAbsAddress(unittest.TestCase): + def setUp(self): + doc = FreeCAD.newDocument("TestAbsAddress") + self.obj = doc.addObject("App::FeatureTestAbsAddress", "Cell") + + def testAbsoluteA12(self): + self.obj.Address = "$A$12" + self.obj.recompute() + self.assertEqual(self.obj.Valid, True) + + def testAbsoluteA13(self): + self.obj.Address = "A$13" + self.obj.recompute() + self.assertEqual(self.obj.Valid, True) + + def testAbsoluteAA13(self): + self.obj.Address = "AA$13" + self.obj.recompute() + self.assertEqual(self.obj.Valid, True) + + def testAbsoluteZZ12(self): + self.obj.Address = "$ZZ$12" + self.obj.recompute() + self.assertEqual(self.obj.Valid, True) + + def testAbsoluteABC1(self): + self.obj.Address = "$ABC1" + self.obj.recompute() + self.assertEqual(self.obj.Valid, False) + + def testAbsoluteABC2(self): + self.obj.Address = "ABC$2" + self.obj.recompute() + self.assertEqual(self.obj.Valid, False) + + def testRelative(self): + self.obj.Address = "A1" + self.obj.recompute() + self.assertEqual(self.obj.Valid, False) + + def testInvalid(self): + self.obj.Address = "A" + self.obj.recompute() + self.assertEqual(self.obj.Valid, False) + + def testEmpty(self): + self.obj.Address = "" + self.obj.recompute() + self.assertEqual(self.obj.Valid, False) + + def tearDown(self): + FreeCAD.closeDocument("TestAbsAddress") + + class FeatureTestAttribute(unittest.TestCase): def setUp(self): self.doc = FreeCAD.newDocument("TestAttribute")