[Libreoffice-commits] core.git: Branch 'feature/unitver' - 10 commits - sc/inc sc/qa sc/source
Andrzej Hunt
andrzej at ahunt.org
Tue May 12 13:19:45 PDT 2015
sc/inc/units.hxx | 27 +-
sc/qa/unit/units.cxx | 337 ++++++++++++++++++++++-----
sc/source/core/units/unitsimpl.cxx | 362 +++++++++++++++++++-----------
sc/source/core/units/unitsimpl.hxx | 46 ++-
sc/source/core/units/utunit.cxx | 3
sc/source/core/units/utunit.hxx | 39 ++-
sc/source/ui/condformat/condformatdlg.cxx | 33 ++
sc/source/ui/inc/condformatdlg.hxx | 4
8 files changed, 629 insertions(+), 222 deletions(-)
New commits:
commit f732a2444a6cc18c016cc79b0c364a87cc7d87e8
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Tue May 12 21:16:13 2015 +0100
Convert convertCellUnits to handle ranges
Change-Id: Ibe95cbd9ea9efd08a48e0651f469434802bfa40e
diff --git a/sc/inc/units.hxx b/sc/inc/units.hxx
index b5e12da..8e15af4 100644
--- a/sc/inc/units.hxx
+++ b/sc/inc/units.hxx
@@ -107,7 +107,7 @@ public:
* rsInputUnit overrides the automatic determination of input units, i.e. disables
* input unit detection.
*/
- virtual bool convertCellUnits(const ScRange& rRange,
+ virtual bool convertCellUnits(const ScRangeList& rRanges,
ScDocument* pDoc,
const OUString& rsOutputUnit) = 0;
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index 29dfc2b..b35cd40 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -820,6 +820,7 @@ void UnitsTest::testRangeConversion() {
// TODO: we need to test:
// 1. mixture of units that can't be converted
// 2. mixtures of local and header annotations
+ // 3. actual sensible ranges
}
CPPUNIT_TEST_SUITE_REGISTRATION(UnitsTest);
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index f2aa9e0..0f79edb 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -799,7 +799,7 @@ bool UnitsImpl::convertCellUnitsForColumnRange(const ScRange& rRange,
return bAllConverted;
}
-bool UnitsImpl::convertCellUnits(const ScRange& rRange,
+bool UnitsImpl::convertCellUnits(const ScRangeList& rRangeList,
ScDocument* pDoc,
const OUString& rsOutputUnit) {
UtUnit aOutputUnit;
@@ -807,26 +807,28 @@ bool UnitsImpl::convertCellUnits(const ScRange& rRange,
return false;
}
- ScRange aRange(rRange);
- aRange.PutInOrder();
-
- SCCOL nStartCol, nEndCol;
- SCROW nStartRow, nEndRow;
- SCTAB nStartTab, nEndTab;
- aRange.GetVars(nStartCol, nStartRow, nStartTab,
- nEndCol, nEndRow, nEndTab);
-
- // Can only handle ranges in a single sheet for now
- assert(nStartTab == nEndTab);
-
- // Each column is independent hence we are able to handle each separately.
bool bAllConverted = true;
- for (SCCOL nCol = nStartCol; nCol <= nEndCol; nCol++) {
- ScRange aSubRange(ScAddress(nCol, nStartRow, nStartTab), ScAddress(nCol, nEndRow, nStartTab));
- bAllConverted = bAllConverted &&
- convertCellUnitsForColumnRange(aSubRange, pDoc, aOutputUnit);
- }
+ for (size_t i = 0; i < rRangeList.size(); i++) {
+ ScRange aRange(*rRangeList[i]);
+
+ aRange.PutInOrder();
+
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ SCTAB nStartTab, nEndTab;
+ aRange.GetVars(nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab);
+
+ // Each column is independent hence we are able to handle each separately.
+ for (SCTAB nTab = nStartTab; nTab <= nEndTab; nTab++) {
+ for (SCCOL nCol = nStartCol; nCol <= nEndCol; nCol++) {
+ ScRange aSubRange(ScAddress(nCol, nStartRow, nTab), ScAddress(nCol, nEndRow, nTab));
+ bAllConverted = bAllConverted &&
+ convertCellUnitsForColumnRange(aSubRange, pDoc, aOutputUnit);
+ }
+ }
+ }
return bAllConverted;
}
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
index e7b4597..58e0971 100644
--- a/sc/source/core/units/unitsimpl.hxx
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -103,7 +103,7 @@ public:
const OUString& rsNewUnit,
const OUString& rsOldUnit) SAL_OVERRIDE;
- virtual bool convertCellUnits(const ScRange& rRange,
+ virtual bool convertCellUnits(const ScRangeList& rRanges,
ScDocument* pDoc,
const OUString& rsOutputUnit) SAL_OVERRIDE;
commit d60765f6ebba774787d3e553dbd680bf1de1fb0a
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Tue May 12 20:57:20 2015 +0100
Split convertCellUnits and change contract
This is in preparation for rewriting convertCellUnits to handle
ScRangeList's.
Change-Id: I17fecdb64674af79a33f2b1a62b4b46150177af5
diff --git a/sc/inc/units.hxx b/sc/inc/units.hxx
index 4d83316..b5e12da 100644
--- a/sc/inc/units.hxx
+++ b/sc/inc/units.hxx
@@ -96,9 +96,8 @@ public:
* If possible the input unit will be determined automatically (using local
* and header units).
*
- * Returns false if input units are not compatible with the desired output units,
- * (including the case where some of the input units are compatibles but others
- * aren't).
+ * Returns false if not input units are not compatible with the desired output units,
+ * however this method still converts all cells containing compatible units.
*
* Local and header unit annotations are modified as appropriate such that the output
* remains unambiguous. Hence, if the header cell is included in rRange, its unit
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index 497331a..f2aa9e0 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -715,78 +715,72 @@ bool UnitsImpl::convertCellToHeaderUnit(const ScAddress& rCellAddress,
return false;
}
-bool UnitsImpl::convertCellUnits(const ScRange& rRange,
- ScDocument* pDoc,
- const OUString& rsOutputUnit) {
- UtUnit aOutputUnit;
- if (!UtUnit::createUnit(rsOutputUnit, aOutputUnit, mpUnitSystem)) {
- return false;
- }
-
- ScRange aRange(rRange);
- aRange.PutInOrder();
-
- SCCOL nStartCol, nEndCol;
- SCROW nStartRow, nEndRow;
- SCTAB nStartTab, nEndTab;
- aRange.GetVars(nStartCol, nStartRow, nStartTab,
- nEndCol, nEndRow, nEndTab);
-
- // Can only handle ranges in a single sheet for now
- assert(nStartTab == nEndTab);
-
- // Each column is independent hence we are able to handle each separately.
- for (SCCOL nCol = nStartCol; nCol <= nEndCol; nCol++) {
- HeaderUnitDescriptor aHeader = { false, UtUnit(), boost::optional< ScAddress >(), "", -1 };
-
- for (SCROW nRow = nEndRow; nRow >= nStartRow; nRow--) {
- ScAddress aCurrent(nCol, nRow, nStartTab);
-
- if (aCurrent == aHeader.address) {
- OUString sHeader = pDoc->GetString(aCurrent);
- sHeader = sHeader.replaceAt(aHeader.unitStringPosition, aHeader.unitString.getLength(), rsOutputUnit);
- pDoc->SetString(aCurrent, sHeader);
-
- aHeader.valid = false;
- } else if (pDoc->GetCellType(aCurrent) != CELLTYPE_STRING) {
+bool UnitsImpl::convertCellUnitsForColumnRange(const ScRange& rRange,
+ ScDocument* pDoc,
+ const UtUnit& rOutputUnit) {
+ assert(rRange.aStart.Row() <= rRange.aEnd.Row());
+ assert(rRange.aStart.Col() == rRange.aEnd.Col());
+ assert(rRange.aStart.Tab() == rRange.aEnd.Tab());
+ assert(rOutputUnit.getInputString());
+
+ HeaderUnitDescriptor aHeader = { false, UtUnit(), boost::optional< ScAddress >(), "", -1 };
+
+ SCCOL nCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nTab = rRange.aStart.Tab();
+
+ bool bAllConverted = true;
+
+ for (SCROW nRow = nEndRow; nRow >= nStartRow; nRow--) {
+ ScAddress aCurrent(nCol, nRow, nTab);
+
+ // It's possible that the header refers to an incompatible unit, hence
+ // shouldn't be modified when we're converting.
+ if (aCurrent == aHeader.address &&
+ aHeader.unit.areConvertibleTo(rOutputUnit)) {
+ OUString sHeader = pDoc->GetString(aCurrent);
+ sHeader = sHeader.replaceAt(aHeader.unitStringPosition, aHeader.unitString.getLength(), *rOutputUnit.getInputString());
+ pDoc->SetString(aCurrent, sHeader);
+
+ aHeader.valid = false;
+ } else if (pDoc->GetCellType(aCurrent) != CELLTYPE_STRING) {
+ if (!aHeader.valid) {
+ aHeader = findHeaderUnitForCell(aCurrent, pDoc);
+
+ // If there is no header we get an invalid unit returned from findHeaderUnitForCell,
+ // and therfore assume the dimensionless unit 1.
if (!aHeader.valid) {
- aHeader = findHeaderUnitForCell(aCurrent, pDoc);
-
- // If there is no header we get an invalid unit returned from findHeaderUnitForCell,
- // and therfore assume the dimensionless unit 1.
- if (!aHeader.valid) {
- UtUnit::createUnit("", aHeader.unit, mpUnitSystem);
- aHeader.valid = true;
- }
+ UtUnit::createUnit("", aHeader.unit, mpUnitSystem);
+ aHeader.valid = true;
}
+ }
- OUString sLocalUnit(extractUnitStringForCell(aCurrent, pDoc));
- UtUnit aLocalUnit;
- if (sLocalUnit.isEmpty()) {
- aLocalUnit = aHeader.unit;
- } else { // override header unit with annotation unit
- if (!UtUnit::createUnit(sLocalUnit, aLocalUnit, mpUnitSystem)) {
- // but assume dimensionless if invalid
- UtUnit::createUnit("", aLocalUnit, mpUnitSystem);
- }
+ OUString sLocalUnit(extractUnitStringForCell(aCurrent, pDoc));
+ UtUnit aLocalUnit;
+ if (sLocalUnit.isEmpty()) {
+ aLocalUnit = aHeader.unit;
+ } else { // override header unit with annotation unit
+ if (!UtUnit::createUnit(sLocalUnit, aLocalUnit, mpUnitSystem)) {
+ // but assume dimensionless if invalid
+ UtUnit::createUnit("", aLocalUnit, mpUnitSystem);
}
+ }
- bool bLocalAnnotationRequired = (!aRange.In(*aHeader.address)) &&
- (aOutputUnit != aHeader.unit);
- double nValue = pDoc->GetValue(aCurrent);
+ bool bLocalAnnotationRequired = (!rRange.In(*aHeader.address)) &&
+ (rOutputUnit != aHeader.unit);
+ double nValue = pDoc->GetValue(aCurrent);
- if (!aLocalUnit.areConvertibleTo(aOutputUnit)) {
- // TODO: in future we should undo all our changes here.
- return false;
- }
-
- double nNewValue = aLocalUnit.convertValueTo(nValue, aOutputUnit);
+ if (!aLocalUnit.areConvertibleTo(rOutputUnit)) {
+ bAllConverted = false;
+ } else {
+ double nNewValue = aLocalUnit.convertValueTo(nValue, rOutputUnit);
pDoc->SetValue(aCurrent, nNewValue);
if (bLocalAnnotationRequired) {
// All a local dirty hack too - needs to be refactored and improved.
// And ideally we should reuse the existing format.
- OUString sNewFormat = "General\"" + rsOutputUnit + "\"";
+ OUString sNewFormat = "General\"" + *rOutputUnit.getInputString() + "\"";
sal_uInt32 nFormatKey;
short nType = css::util::NumberFormat::DEFINED;
sal_Int32 nErrorPosition; // Unused, because we should be creating working number formats.
@@ -799,11 +793,41 @@ bool UnitsImpl::convertCellUnits(const ScRange& rRange,
pDoc->SetNumberFormat(aCurrent, 0);
}
}
-
}
+
}
+ return bAllConverted;
+}
- return true;
+bool UnitsImpl::convertCellUnits(const ScRange& rRange,
+ ScDocument* pDoc,
+ const OUString& rsOutputUnit) {
+ UtUnit aOutputUnit;
+ if (!UtUnit::createUnit(rsOutputUnit, aOutputUnit, mpUnitSystem)) {
+ return false;
+ }
+
+ ScRange aRange(rRange);
+ aRange.PutInOrder();
+
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ SCTAB nStartTab, nEndTab;
+ aRange.GetVars(nStartCol, nStartRow, nStartTab,
+ nEndCol, nEndRow, nEndTab);
+
+ // Can only handle ranges in a single sheet for now
+ assert(nStartTab == nEndTab);
+
+ // Each column is independent hence we are able to handle each separately.
+ bool bAllConverted = true;
+ for (SCCOL nCol = nStartCol; nCol <= nEndCol; nCol++) {
+ ScRange aSubRange(ScAddress(nCol, nStartRow, nStartTab), ScAddress(nCol, nEndRow, nStartTab));
+ bAllConverted = bAllConverted &&
+ convertCellUnitsForColumnRange(aSubRange, pDoc, aOutputUnit);
+ }
+
+ return bAllConverted;
}
bool UnitsImpl::areUnitsCompatible(const OUString& rsUnit1, const OUString& rsUnit2) {
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
index ebd20b0..e7b4597 100644
--- a/sc/source/core/units/unitsimpl.hxx
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -156,6 +156,15 @@ private:
ScDocument* pDoc);
/**
+ * Convert cells within a given range. The range MUST be restricted
+ * to being a group of cells within one column, in one sheet/tab.
+ * rOutputUnit MUST possess an input unit string.
+ */
+ bool convertCellUnitsForColumnRange(const ScRange& rRange,
+ ScDocument* pDoc,
+ const UtUnit& rOutputUnit);
+
+ /**
* Return both the UtUnit and the String as we usually want the UtUnit
* (which is created from the String, and has to be created to ensure
* that there is a valid unit), but we might also need the original
commit b0c23b9dc7992b15dfac065b58ae3aeedfb054a4
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Tue May 12 20:39:00 2015 +0100
Make more methods of UtUnit const
Change-Id: I652a77ad3bf547788bf6d566fdeaac525effb541
diff --git a/sc/source/core/units/utunit.hxx b/sc/source/core/units/utunit.hxx
index 6f82b05..5d1442a 100644
--- a/sc/source/core/units/utunit.hxx
+++ b/sc/source/core/units/utunit.hxx
@@ -101,11 +101,11 @@ public:
return ut_is_dimensionless(this->get());
}
- bool operator==(const UtUnit& rUnit) {
+ bool operator==(const UtUnit& rUnit) const {
return ut_compare(this->get(), rUnit.get()) == 0;
}
- bool operator!=(const UtUnit& rUnit) {
+ bool operator!=(const UtUnit& rUnit) const {
return !operator==(rUnit);
}
@@ -137,11 +137,11 @@ public:
return UtUnit(ut_divide(this->get(), rUnit.get()));
}
- bool areConvertibleTo(const UtUnit& rUnit) {
+ bool areConvertibleTo(const UtUnit& rUnit) const {
return ut_are_convertible(this->get(), rUnit.get());
}
- double convertValueTo(double nOriginalValue, const UtUnit& rUnit) {
+ double convertValueTo(double nOriginalValue, const UtUnit& rUnit) const {
assert(isValid());
assert(rUnit.isValid());
commit 2b773301309a2aad18a6e48f1148d46e485d2a3f
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Tue May 12 20:16:57 2015 +0100
Implement getUnitsForRange
This will be useful for e.g. the units conversion dialog.
Change-Id: I36391e9aeab5689bfde1d1865549cc2e136a4812
diff --git a/sc/inc/units.hxx b/sc/inc/units.hxx
index 1ba9121..4d83316 100644
--- a/sc/inc/units.hxx
+++ b/sc/inc/units.hxx
@@ -14,6 +14,8 @@
#include <boost/shared_ptr.hpp>
+#include "rangelst.hxx"
+
class ScAddress;
class ScDocument;
class ScRange;
@@ -24,6 +26,18 @@ namespace units {
class UnitsImpl;
+/**
+ * The units used for a range of data cells.
+ */
+struct RangeUnits {
+ std::vector< OUString > units;
+ /**
+ * Whether all the units in the list are compatible (i.e. data
+ * can be converted to any of the listed units).
+ */
+ bool compatible;
+};
+
class Units {
public:
static ::boost::shared_ptr< Units > GetUnits();
@@ -101,6 +115,9 @@ public:
virtual bool areUnitsCompatible(const OUString& rsUnit1,
const OUString& rsUnit2) = 0;
+ virtual RangeUnits getUnitsForRange(const ScRangeList& rRangeList,
+ ScDocument* pDoc) = 0;
+
virtual ~Units() {}
};
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index 7e9e320..29dfc2b 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -50,6 +50,7 @@ public:
void testUnitsCompatible();
void testCellConversion();
+ void testUnitsForRange();
void testRangeConversion();
CPPUNIT_TEST_SUITE(UnitsTest);
@@ -63,6 +64,7 @@ public:
CPPUNIT_TEST(testUnitsCompatible);
CPPUNIT_TEST(testCellConversion);
+ CPPUNIT_TEST(testUnitsForRange);
CPPUNIT_TEST(testRangeConversion);
CPPUNIT_TEST_SUITE_END();
@@ -551,6 +553,193 @@ void UnitsTest::testCellConversion() {
// to pass in the output of isCellConversionRecommended).
}
+void UnitsTest::testUnitsForRange() {
+ const SCTAB nTab = 2;
+ mpDoc->EnsureTable(nTab);
+
+ // Column 1: just cm, with header annotation
+ ScAddress headerAddress1(0, 0, nTab);
+ mpDoc->SetString(headerAddress1, "length [cm]");
+
+ ScAddress address1(headerAddress1);
+
+ vector<double> values1({10, 20, 30, 40, 1, 0.5, 0.25});
+ address1.IncRow();
+ mpDoc->SetValues(address1, values1);
+
+ ScAddress endAddress1( address1.Col(), address1.Row() + values1.size() - 1, nTab);
+
+ // Column2: header of [m], with some random units mixed in (ft, km, furlongs)
+ ScAddress headerAddress2(1, 0, nTab);
+ mpDoc->SetString(headerAddress2, "distance [m]");
+
+ ScAddress address2(headerAddress2);
+
+ vector<double> values2({1, 2, 3, 4, 0.1, 0.05, 0.025});
+ address2.IncRow();
+ mpDoc->SetValues(address2, values2);
+
+ address2.IncRow();
+ setNumberFormatUnit(address2, "furlongs");
+ address2.IncRow();
+ setNumberFormatUnit(address2, "ft");
+ address2.IncRow();
+ setNumberFormatUnit(address2, "m");
+ address2.IncRow();
+ setNumberFormatUnit(address2, "km");
+ address2.IncRow();
+ setNumberFormatUnit(address2, "cm");
+ address2 = headerAddress2; // reset to start of data range
+ address2.IncRow();
+
+ ScAddress endAddress2( address2.Col(), address2.Row() + values2.size() - 1, nTab);
+
+ // Column3: no units in header, local weight annotations (kg, lb, g, tons)
+ ScAddress headerAddress3(2, 0, nTab);
+ mpDoc->SetString(headerAddress3, "weight");
+
+ ScAddress address3(headerAddress3);
+
+ vector<double> values3({100, 200, 300, 400, 10, 5, 2.5 });
+ address3.IncRow();
+ mpDoc->SetValues(address3, values3);
+
+ setNumberFormatUnit(address3, "kg");
+ address3.IncRow();
+ setNumberFormatUnit(address3, "kg");
+ address3.IncRow();
+ setNumberFormatUnit(address3, "kg");
+ address3.IncRow();
+ setNumberFormatUnit(address3, "lb");
+ address3.IncRow();
+ setNumberFormatUnit(address3, "tons");
+ address3.IncRow();
+ setNumberFormatUnit(address3, "g");
+ address3.IncRow();
+ setNumberFormatUnit(address3, "atomic_mass_unit");
+ address3.IncRow();
+ setNumberFormatUnit(address3, "kg");
+ address3 = headerAddress3; // reset to start of data range
+ address3.IncRow();
+
+ ScAddress endAddress3( address3.Col(), address3.Row() + values3.size() - 1, nTab);
+
+ // COLUMN 1
+ // Test with just the data (not including header).
+ ScRange aRange1(address1, endAddress1);
+
+ RangeUnits aUnits(mpUnitsImpl->getUnitsForRange( ScRangeList(aRange1), mpDoc));
+ CPPUNIT_ASSERT(aUnits.compatible);
+ CPPUNIT_ASSERT_EQUAL(aUnits.units.size(), static_cast<size_t>(1));
+ CPPUNIT_ASSERT(aUnits.units[0] == "cm");
+
+ // Test including header
+ aRange1 = ScRange(headerAddress1, endAddress1);
+
+ aUnits = mpUnitsImpl->getUnitsForRange( ScRangeList(aRange1), mpDoc);
+ CPPUNIT_ASSERT(aUnits.compatible);
+ CPPUNIT_ASSERT_EQUAL(aUnits.units.size(), static_cast<size_t>(1));
+ CPPUNIT_ASSERT(aUnits.units[0] == "cm");
+
+ // COLUMN 2
+ // Test with just the data (not including header).
+ ScRange aRange2(address2, endAddress2);
+
+ aUnits = mpUnitsImpl->getUnitsForRange( ScRangeList(aRange2), mpDoc);
+ CPPUNIT_ASSERT(aUnits.compatible);
+ CPPUNIT_ASSERT_EQUAL(aUnits.units.size(), static_cast<size_t>(5));
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "cm") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "ft") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "m") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "furlongs") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "km") != aUnits.units.end());
+
+ // Test including header
+ aRange2 = ScRange(headerAddress2, endAddress2);
+
+ aUnits = mpUnitsImpl->getUnitsForRange( ScRangeList(aRange2), mpDoc);
+ CPPUNIT_ASSERT(aUnits.compatible);
+ CPPUNIT_ASSERT_EQUAL(aUnits.units.size(), static_cast<size_t>(5));
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "cm") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "ft") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "m") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "furlongs") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "km") != aUnits.units.end());
+
+ // COLUMN 3
+ // Test without header
+ ScRange aRange3(address3, endAddress3);
+
+ aUnits = mpUnitsImpl->getUnitsForRange( ScRangeList(aRange3), mpDoc);
+ CPPUNIT_ASSERT(aUnits.compatible);
+ CPPUNIT_ASSERT_EQUAL(aUnits.units.size(), static_cast<size_t>(5));
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "kg") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "lb") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "tons") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "g") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "atomic_mass_unit") != aUnits.units.end());
+
+ // Test including header
+ aRange3 = ScRange(headerAddress3, endAddress3);
+
+ aUnits = mpUnitsImpl->getUnitsForRange( ScRangeList(aRange3), mpDoc);
+ CPPUNIT_ASSERT(aUnits.compatible);
+ CPPUNIT_ASSERT_EQUAL(aUnits.units.size(), static_cast<size_t>(5));
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "kg") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "lb") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "tons") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "g") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "atomic_mass_unit") != aUnits.units.end());
+
+ // ROW 2:
+ ScRange aRow2(ScAddress( 0, 1, nTab), ScAddress(2, 1, nTab));
+ aUnits = mpUnitsImpl->getUnitsForRange(ScRangeList(aRow2), mpDoc);
+ CPPUNIT_ASSERT(!aUnits.compatible);
+ CPPUNIT_ASSERT_EQUAL(aUnits.units.size(), static_cast<size_t>(3));
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "cm") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "m") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "kg") != aUnits.units.end());
+
+ // ROW 2 including a blank cell
+ aRow2 = ScRange(ScAddress( 0, 1, nTab), ScAddress(3, 1, nTab));
+ aUnits = mpUnitsImpl->getUnitsForRange(ScRangeList(aRow2), mpDoc);
+ CPPUNIT_ASSERT(!aUnits.compatible);
+ CPPUNIT_ASSERT_EQUAL(aUnits.units.size(), static_cast<size_t>(3));
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "cm") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "m") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "kg") != aUnits.units.end());
+
+ // Finally check that multiple ranges actually work
+ ScRangeList aRangeList;
+ aRangeList.Append(aRange1);
+ aRangeList.Append(aRange2);
+
+ aUnits = mpUnitsImpl->getUnitsForRange( aRangeList, mpDoc);
+ CPPUNIT_ASSERT(aUnits.compatible);
+ CPPUNIT_ASSERT_EQUAL(aUnits.units.size(), static_cast<size_t>(5));
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "cm") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "ft") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "m") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "furlongs") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "km") != aUnits.units.end());
+
+ // And add the weights range:
+ aRangeList.Append(aRange3);
+ aUnits = mpUnitsImpl->getUnitsForRange( aRangeList, mpDoc);
+ CPPUNIT_ASSERT(!aUnits.compatible);
+ CPPUNIT_ASSERT_EQUAL(aUnits.units.size(), static_cast<size_t>(10));
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "cm") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "ft") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "m") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "furlongs") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "km") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "kg") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "lb") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "tons") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "g") != aUnits.units.end());
+ CPPUNIT_ASSERT(std::find(aUnits.units.begin(), aUnits.units.end(), "atomic_mass_unit") != aUnits.units.end());
+}
+
void UnitsTest::testRangeConversion() {
const SCTAB nTab = 1;
mpDoc->EnsureTable(nTab);
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index a67871d..497331a 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -817,4 +817,44 @@ bool UnitsImpl::areUnitsCompatible(const OUString& rsUnit1, const OUString& rsUn
&& aUnit1.areConvertibleTo(aUnit2);
}
+RangeUnits UnitsImpl::getUnitsForRange(const ScRangeList& rRangeList, ScDocument* pDoc) {
+ std::set< OUString > aUnits;
+
+ for (size_t i = 0; i < rRangeList.size(); i++) {
+ ScCellIterator aIt(pDoc, *rRangeList[i]);
+
+ if (!aIt.first())
+ continue;
+
+ do {
+ const ScAddress& aPos = aIt.GetPos();
+ UtUnit aUnit = getUnitForCell(aPos, pDoc);
+
+ // We ignore header cells (and comments too)
+ if (aUnit.isValid()) {
+ // Units retrieved directly must always have an input string
+ assert(aUnit.getInputString());
+ aUnits.insert(*aUnit.getInputString());
+ }
+ } while (aIt.next());
+ }
+
+ bool bCompatible = true;
+
+ if (aUnits.size() > 1) {
+ OUString sFirstUnit = *aUnits.cbegin();
+
+ // start iterating from the second item (++aUnits.cbegin())
+ for (auto aIt = ++aUnits.cbegin(); aIt != aUnits.cend(); aIt++) {
+ if (!areUnitsCompatible(sFirstUnit, *aIt)) {
+ bCompatible = false;
+ break;
+ }
+ }
+ }
+
+ std::vector< OUString > aUnitsList(aUnits.begin(), aUnits.end());
+ return { aUnitsList, bCompatible };
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
index 88975b8..ebd20b0 100644
--- a/sc/source/core/units/unitsimpl.hxx
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -110,6 +110,9 @@ public:
virtual bool areUnitsCompatible(const OUString& rsUnit1,
const OUString& rsUnit2) SAL_OVERRIDE;
+ virtual RangeUnits getUnitsForRange(const ScRangeList& rRangeList,
+ ScDocument* pDoc) SAL_OVERRIDE;
+
private:
UnitsResult getOutputUnitsForOpCode(std::stack< RAUSItem >& rStack, const formula::FormulaToken* pToken, ScDocument* pDoc);
commit f16793253e5144452d8c7b44dae29d1e0a2a14ca
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Tue May 12 20:16:27 2015 +0100
Store original input string for UtUnit
String->UtUnit isn't necessarily reversible, hence we should
store the original input too in case it is needed by the user.
Change-Id: I8794a1544a9c996da574ee753d95b44f067e819f
diff --git a/sc/source/core/units/utunit.cxx b/sc/source/core/units/utunit.cxx
index f539502..63e7e73 100644
--- a/sc/source/core/units/utunit.cxx
+++ b/sc/source/core/units/utunit.cxx
@@ -18,7 +18,8 @@ bool UtUnit::createUnit(const OUString& rUnitString, UtUnit& rUnitOut, const boo
// simplest just to do this during conversion:
OString sUnitStringUTF8 = OUStringToOString(rUnitString.trim(), RTL_TEXTENCODING_UTF8);
- UtUnit pParsedUnit(ut_parse(pUTSystem.get(), sUnitStringUTF8.getStr(), UT_UTF8));
+ UtUnit pParsedUnit(ut_parse(pUTSystem.get(), sUnitStringUTF8.getStr(), UT_UTF8),
+ rUnitString);
if (pParsedUnit.isValid()) {
rUnitOut = pParsedUnit;
diff --git a/sc/source/core/units/utunit.hxx b/sc/source/core/units/utunit.hxx
index b039c74..6f82b05 100644
--- a/sc/source/core/units/utunit.hxx
+++ b/sc/source/core/units/utunit.hxx
@@ -12,6 +12,7 @@
#include <rtl/ustring.hxx>
+#include <boost/optional.hpp>
#include <boost/shared_ptr.hpp>
#include <udunits2.h>
@@ -38,12 +39,26 @@ class UtUnit {
private:
::boost::shared_ptr< ut_unit > mpUnit;
+ /**
+ * The original input string used in createUnit.
+ * We can't necessarily convert a ut_unit back into the
+ * original representation (e.g. cm gets formatted as 0.01m
+ * by default), hence we should store the original string
+ * as may need to display it to the user again.
+ *
+ * There is no input string for units that are created when manipulating
+ * other units (i.e. multiplication/division of other UtUnits).
+ */
+ boost::optional< OUString > msInputString;
+
static void freeUt(ut_unit* pUnit) {
ut_free(pUnit);
}
- UtUnit(ut_unit* pUnit):
- mpUnit(pUnit, &freeUt)
+ UtUnit(ut_unit* pUnit,
+ const boost::optional< OUString > rInputString = boost::optional< OUString >())
+ : mpUnit(pUnit, &freeUt)
+ , msInputString(rInputString)
{}
void reset(ut_unit* pUnit) {
@@ -55,6 +70,9 @@ private:
}
public:
+ /**
+ * return false if we try to create in invalid unit.
+ */
static bool createUnit(const OUString& rUnitString, UtUnit& rUnitOut, const boost::shared_ptr< ut_system >& pUTSystem);
/*
@@ -63,10 +81,15 @@ public:
*/
UtUnit() {};
- UtUnit(const UtUnit& rUnit):
- mpUnit(rUnit.mpUnit)
+ UtUnit(const UtUnit& rUnit)
+ : mpUnit(rUnit.mpUnit)
+ , msInputString(rUnit.msInputString)
{}
+ boost::optional< OUString > getInputString() const {
+ return msInputString;
+ }
+
OUString getString() const;
bool isValid() const {
commit 5151ecb6a44f1570d8e09c5b892ed069880f6d1f
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Tue May 12 20:15:15 2015 +0100
Return invalid unit for empty and string cells.
Change-Id: I6dbec9be643040f9fc567e6065f860a3985f138a
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index a7b103f..a67871d 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -441,6 +441,11 @@ HeaderUnitDescriptor UnitsImpl::extractUnitFromHeaderString(const OUString& rsHe
}
UtUnit UnitsImpl::getUnitForCell(const ScAddress& rCellAddress, ScDocument* pDoc) {
+ CellType aType(pDoc->GetCellType(rCellAddress));
+ if (aType == CELLTYPE_STRING || aType == CELLTYPE_NONE) {
+ return UtUnit();
+ }
+
OUString sUnitString = extractUnitStringForCell(rCellAddress, pDoc);
UtUnit aUnit;
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
index ff30232..88975b8 100644
--- a/sc/source/core/units/unitsimpl.hxx
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -144,6 +144,8 @@ private:
* Retrieve the units for a given cell. This probes based on the usual rules
* for cell annotation/column header.
* Retrieving units for a formula cell is not yet supported.
+ *
+ * Units are undefined for any text cell (including header cells).
*/
UtUnit getUnitForCell(const ScAddress& rCellAddress, ScDocument* pDoc);
UtUnit getUnitForRef(formula::FormulaToken* pToken,
commit 7c3cc8ff1457061a596b83d47e4947481b20d089
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Tue May 12 16:28:30 2015 +0100
Implement areUnitsCompatible API method
We need this for the conversion dialog, where it's probably
better if we can avoid directly fiddling with UtUnits.
Change-Id: I090e59c49f3b77ffcc0571838023165c2da931a0
diff --git a/sc/inc/units.hxx b/sc/inc/units.hxx
index 381ec7d..1ba9121 100644
--- a/sc/inc/units.hxx
+++ b/sc/inc/units.hxx
@@ -98,6 +98,9 @@ public:
ScDocument* pDoc,
const OUString& rsOutputUnit) = 0;
+ virtual bool areUnitsCompatible(const OUString& rsUnit1,
+ const OUString& rsUnit2) = 0;
+
virtual ~Units() {}
};
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index 1e202aa..7e9e320 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -48,6 +48,7 @@ public:
void testUnitFromHeaderExtraction();
+ void testUnitsCompatible();
void testCellConversion();
void testRangeConversion();
@@ -60,6 +61,7 @@ public:
CPPUNIT_TEST(testUnitValueStringSplitting);
CPPUNIT_TEST(testUnitFromHeaderExtraction);
+ CPPUNIT_TEST(testUnitsCompatible);
CPPUNIT_TEST(testCellConversion);
CPPUNIT_TEST(testRangeConversion);
@@ -471,6 +473,17 @@ void UnitsTest::testUnitFromHeaderExtraction() {
CPPUNIT_ASSERT_EQUAL(aHeader.unitStringPosition, 8);
}
+void UnitsTest::testUnitsCompatible() {
+ // This test is primarily to ensure that our glue works correctly, it's
+ // assumed that UdUnits is able to correctly parse the units / determine
+ // their compatibility.
+ CPPUNIT_ASSERT(mpUnitsImpl->areUnitsCompatible("cm", "m"));
+ CPPUNIT_ASSERT(mpUnitsImpl->areUnitsCompatible("ft", "m")); // Sorry!
+
+ CPPUNIT_ASSERT(!mpUnitsImpl->areUnitsCompatible("m", "kg"));
+ CPPUNIT_ASSERT(!mpUnitsImpl->areUnitsCompatible("s", "J"));
+}
+
void UnitsTest::testCellConversion() {
// We test both isCellConversionRecommended, and convertCellToHeaderUnit
// since their arguments are essentially shared / dependent.
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index cca532d..a7b103f 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -801,4 +801,15 @@ bool UnitsImpl::convertCellUnits(const ScRange& rRange,
return true;
}
+bool UnitsImpl::areUnitsCompatible(const OUString& rsUnit1, const OUString& rsUnit2) {
+ // TODO: in future we should have some sort of map< OUString, shared_ptr<set< OUString > >
+ // or similar to cache compatible units, as we may have a large number of such queries.
+
+ UtUnit aUnit1, aUnit2;
+
+ return UtUnit::createUnit(rsUnit1, aUnit1, mpUnitSystem)
+ && UtUnit::createUnit(rsUnit2, aUnit2, mpUnitSystem)
+ && aUnit1.areConvertibleTo(aUnit2);
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
index 7320baa..ff30232 100644
--- a/sc/source/core/units/unitsimpl.hxx
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -107,6 +107,9 @@ public:
ScDocument* pDoc,
const OUString& rsOutputUnit) SAL_OVERRIDE;
+ virtual bool areUnitsCompatible(const OUString& rsUnit1,
+ const OUString& rsUnit2) SAL_OVERRIDE;
+
private:
UnitsResult getOutputUnitsForOpCode(std::stack< RAUSItem >& rStack, const formula::FormulaToken* pToken, ScDocument* pDoc);
commit 04b85d76e6559b0361e8cf4397c68dd933a40203
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Tue May 12 16:25:27 2015 +0100
Add setNumberFormatUnit utility method to units test
Should simplify writing further tests.
Change-Id: Idf991d12d0731d531f7e7c84e1ebe10b76d82b7a
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index e2be853..1e202aa 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -36,8 +36,9 @@ public:
virtual void setUp() SAL_OVERRIDE;
virtual void tearDown() SAL_OVERRIDE;
- ::boost::shared_ptr< UnitsImpl > mpUnitsImpl;
+ void setNumberFormatUnit(const ScAddress& rAddress, const OUString& sUnit);
+ ::boost::shared_ptr< UnitsImpl > mpUnitsImpl;
void testUTUnit();
void testUnitVerification();
@@ -127,85 +128,77 @@ void UnitsTest::testUTUnit() {
CPPUNIT_ASSERT(aCM.convertValueTo(100.0, aM) == 1.0);
}
-void UnitsTest::testUnitVerification() {
- // Make sure we have at least one tab to work with
- mpDoc->EnsureTable(0);
-
+void UnitsTest::setNumberFormatUnit(const ScAddress& rAddress, const OUString& sUnit) {
SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
- sal_uInt32 nKeyCM, nKeyM, nKeyKG, nKeyS, nKeyCM_S;
-
- // Used to return position of error in input string for PutEntry
- // -- not needed here.
- sal_Int32 nCheckPos;
+ OUString sFormat = "#\"" + sUnit + "\"";
+ sal_uInt32 nKey;
+ sal_Int32 nCheckPos; // unused, returns the error position (shouldn't ever happen in our tests)
short nType = css::util::NumberFormat::DEFINED;
- OUString sM = "#\"m\"";
- pFormatter->PutEntry(sM, nCheckPos, nType, nKeyM);
- OUString sCM = "#\"cm\"";
- pFormatter->PutEntry(sCM, nCheckPos, nType, nKeyCM);
- OUString sKG = "#\"kg\"";
- pFormatter->PutEntry(sKG, nCheckPos, nType, nKeyKG);
- OUString sS = "#\"s\"";
- pFormatter->PutEntry(sS, nCheckPos, nType, nKeyS);
- OUString sCM_S = "#\"cm/s\"";
- pFormatter->PutEntry(sCM_S, nCheckPos, nType, nKeyCM_S);
+ pFormatter->PutEntry(sFormat, nCheckPos, nType, nKey);
+ mpDoc->SetNumberFormat(rAddress, nKey);
+}
+
+void UnitsTest::testUnitVerification() {
+ // Make sure we have at least one tab to work with
+ mpDoc->EnsureTable(0);
// 1st column: 10cm, 20cm, 30cm
ScAddress address(0, 0, 0);
- mpDoc->SetNumberFormat(address, nKeyCM);
+ setNumberFormatUnit(address, "cm");
mpDoc->SetValue(address, 10);
address.IncRow();
- mpDoc->SetNumberFormat(address, nKeyCM);
+ setNumberFormatUnit(address, "cm");
mpDoc->SetValue(address, 20);
address.IncRow();
- mpDoc->SetNumberFormat(address, nKeyCM);
+ setNumberFormatUnit(address, "cm");
mpDoc->SetValue(address, 30);
// 2nd column: 1kg, 2kg, 3kg
address = ScAddress(1, 0, 0);
- mpDoc->SetNumberFormat(address, nKeyKG);
+ setNumberFormatUnit(address, "kg");
mpDoc->SetValue(address, 1);
address.IncRow();
- mpDoc->SetNumberFormat(address, nKeyKG);
+ setNumberFormatUnit(address, "kg");
mpDoc->SetValue(address, 2);
address.IncRow();
- mpDoc->SetNumberFormat(address, nKeyKG);
+ setNumberFormatUnit(address, "kg");
mpDoc->SetValue(address, 3);
// 3rd column: 1s, 2s, 3s
address = ScAddress(2, 0, 0);
- mpDoc->SetNumberFormat(address, nKeyS);
+ setNumberFormatUnit(address, "s");
mpDoc->SetValue(address, 1);
address.IncRow();
- mpDoc->SetNumberFormat(address, nKeyS);
+ setNumberFormatUnit(address, "s");
mpDoc->SetValue(address, 2);
address.IncRow();
- mpDoc->SetNumberFormat(address, nKeyS);
+ setNumberFormatUnit(address, "s");
mpDoc->SetValue(address, 3);
// 4th column: 5cm/s, 10cm/s, 15cm/s
address = ScAddress(3, 0, 0);
- mpDoc->SetNumberFormat(address, nKeyCM_S);
+ setNumberFormatUnit(address, "cm/s");
mpDoc->SetValue(address, 5);
address.IncRow();
- mpDoc->SetNumberFormat(address, nKeyCM_S);
+ setNumberFormatUnit(address, "cm/s");
mpDoc->SetValue(address, 10);
address.IncRow();
- mpDoc->SetNumberFormat(address, nKeyCM_S);
+ setNumberFormatUnit(address, "cm/s");
mpDoc->SetValue(address, 15);
// 5th column: 1m
address = ScAddress(4, 0, 0);
- mpDoc->SetNumberFormat(address, nKeyM);
+ setNumberFormatUnit(address, "m");
mpDoc->SetValue(address, 1);
ScFormulaCell* pCell;
commit adfa405c5d94c176fb7c04b4945a713567a41c3c
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Mon May 11 21:26:56 2015 +0100
Update title of Conditional Format dialog when range modified
Previously the title was set during construction as
"Conditional Format: $SOME$RANGE:$SELECTED$INITIALLY"
However the selected range can be modified while the dialog is open,
hence we update it whenever the selected range is modified.
Change-Id: I63790108553102cedb51ca32d672a62477493660
diff --git a/sc/source/ui/condformat/condformatdlg.cxx b/sc/source/ui/condformat/condformatdlg.cxx
index fe91602..cfd9e2e 100644
--- a/sc/source/ui/condformat/condformatdlg.cxx
+++ b/sc/source/ui/condformat/condformatdlg.cxx
@@ -437,23 +437,31 @@ ScCondFormatDlg::ScCondFormatDlg(vcl::Window* pParent, ScDocument* pDoc,
get(mpCondFormList, "list");
mpCondFormList->init(pDoc, this, pFormat, rRange, rPos, eType);
- OUStringBuffer aTitle( GetText() );
- aTitle.append(" ");
- OUString aRangeString;
- rRange.Format(aRangeString, SCA_VALID, pDoc, pDoc->GetAddressConvention());
- aTitle.append(aRangeString);
- SetText(aTitle.makeStringAndClear());
mpBtnAdd->SetClickHdl( LINK( mpCondFormList, ScCondFormatList, AddBtnHdl ) );
mpBtnRemove->SetClickHdl( LINK( mpCondFormList, ScCondFormatList, RemoveBtnHdl ) );
mpEdRange->SetModifyHdl( LINK( this, ScCondFormatDlg, EdRangeModifyHdl ) );
mpEdRange->SetGetFocusHdl( LINK( this, ScCondFormatDlg, RangeGetFocusHdl ) );
mpEdRange->SetLoseFocusHdl( LINK( this, ScCondFormatDlg, RangeLoseFocusHdl ) );
+ OUString aRangeString;
+ rRange.Format(aRangeString, SCA_VALID, pDoc, pDoc->GetAddressConvention());
mpEdRange->SetText(aRangeString);
+ msBaseTitle = GetText();
+ updateTitle();
+
SC_MOD()->PushNewAnyRefDlg(this);
}
+void ScCondFormatDlg::updateTitle()
+{
+ OUStringBuffer aTitle( msBaseTitle );
+ aTitle.append(" ");
+ aTitle.append(mpEdRange->GetText());
+
+ SetText(aTitle.makeStringAndClear());
+}
+
ScCondFormatDlg::~ScCondFormatDlg()
{
disposeOnce();
@@ -486,6 +494,16 @@ void ScCondFormatDlg::SetActive()
void ScCondFormatDlg::RefInputDone( bool bForced )
{
ScAnyRefModalDlg::RefInputDone(bForced);
+ // ScAnyRefModalDlg::RefInputDone resets the title back
+ // to it's original state.
+ // I.e. if we open the dialog normally, and then click into the sheet
+ // to modify the selection, the title is updated such that the range
+ // is only a single cell (e.g. $A$1), after which the dialog switches
+ // into the RefInput mode. During the RefInput mode the title is updated
+ // as expected, however at the end RefInputDone overwrites the title
+ // with the initial (now incorrect) single cell range. Hence we correct
+ // it here.
+ updateTitle();
}
bool ScCondFormatDlg::IsTableLocked() const
@@ -526,6 +544,7 @@ void ScCondFormatDlg::SetReference(const ScRange& rRef, ScDocument*)
OUString aRefStr(rRef.Format(n, mpDoc, ScAddress::Details(mpDoc->GetAddressConvention(), 0, 0)));
pEdit->SetRefString( aRefStr );
+ updateTitle();
}
}
@@ -564,6 +583,8 @@ IMPL_LINK( ScCondFormatDlg, EdRangeModifyHdl, Edit*, pEdit )
pEdit->SetControlBackground(GetSettings().GetStyleSettings().GetWindowColor());
else
pEdit->SetControlBackground(COL_LIGHTRED);
+
+ updateTitle();
return 0;
}
diff --git a/sc/source/ui/inc/condformatdlg.hxx b/sc/source/ui/inc/condformatdlg.hxx
index 6036861..999c3db 100644
--- a/sc/source/ui/inc/condformatdlg.hxx
+++ b/sc/source/ui/inc/condformatdlg.hxx
@@ -108,6 +108,10 @@ private:
VclPtr<formula::RefEdit> mpLastEdit;
+ OUString msBaseTitle;
+
+ void updateTitle();
+
DECL_LINK( EdRangeModifyHdl, Edit* );
protected:
commit 7bdf0604e48d4c9e99930d094f118df405a4b8da
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Mon May 11 17:37:27 2015 +0100
Use HeaderUnitDescriptor to pass around header specifics
Change-Id: I7c74211236b00c570941fda39cb0d69c1ce4e02c
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index 8e83483..e2be853 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -409,54 +409,73 @@ void UnitsTest::testUnitValueStringSplitting() {
}
void UnitsTest::testUnitFromHeaderExtraction() {
- UtUnit aUnit;
- OUString sUnitString;
+ HeaderUnitDescriptor aHeader;
OUString sEmpty = "";
- CPPUNIT_ASSERT(!mpUnitsImpl->extractUnitFromHeaderString(sEmpty, aUnit, sUnitString));
- CPPUNIT_ASSERT(aUnit == UtUnit());
- CPPUNIT_ASSERT(sUnitString.isEmpty());
+ aHeader = mpUnitsImpl->extractUnitFromHeaderString(sEmpty);
+ CPPUNIT_ASSERT(!aHeader.valid);
+ CPPUNIT_ASSERT(aHeader.unit == UtUnit());
+ CPPUNIT_ASSERT(aHeader.unitString.isEmpty());
OUString sSimple = "bla bla [cm/s]";
- CPPUNIT_ASSERT(mpUnitsImpl->extractUnitFromHeaderString(sSimple, aUnit, sUnitString));
+ aHeader = mpUnitsImpl->extractUnitFromHeaderString(sSimple);
+ CPPUNIT_ASSERT(aHeader.valid);
// We need to test in Units (rather than testing Unit::getString()) as
// any given unit can have multiple string representations (and utunits defaults to
// representing e.g. cm as (I think) "0.01m").
UtUnit aTestUnit;
CPPUNIT_ASSERT(UtUnit::createUnit("cm/s", aTestUnit, mpUnitsImpl->mpUnitSystem));
- CPPUNIT_ASSERT(aUnit == aTestUnit);
- CPPUNIT_ASSERT(sUnitString == "cm/s");
+ CPPUNIT_ASSERT(aHeader.unit == aTestUnit);
+ CPPUNIT_ASSERT(aHeader.unitString == "cm/s");
+ CPPUNIT_ASSERT_EQUAL(aHeader.unitStringPosition, 9);
+
+ OUString sSimple2 = "bla bla [km/s] (more text)";
+ aHeader = mpUnitsImpl->extractUnitFromHeaderString(sSimple2);
+ CPPUNIT_ASSERT(aHeader.valid);
+ CPPUNIT_ASSERT(UtUnit::createUnit("km/s", aTestUnit, mpUnitsImpl->mpUnitSystem));
+ CPPUNIT_ASSERT(aHeader.unit == aTestUnit);
+ CPPUNIT_ASSERT(aHeader.unitString == "km/s");
+ CPPUNIT_ASSERT_EQUAL(aHeader.unitStringPosition, 9);
OUString sFreeStanding = "bla bla kg/h";
- CPPUNIT_ASSERT(mpUnitsImpl->extractUnitFromHeaderString(sFreeStanding, aUnit, sUnitString));
+ aHeader = mpUnitsImpl->extractUnitFromHeaderString(sFreeStanding);
+ CPPUNIT_ASSERT(aHeader.valid);
CPPUNIT_ASSERT(UtUnit::createUnit("kg/h", aTestUnit, mpUnitsImpl->mpUnitSystem));
- CPPUNIT_ASSERT(aUnit == aTestUnit);
- CPPUNIT_ASSERT(sUnitString == "kg/h");
+ CPPUNIT_ASSERT(aHeader.unit == aTestUnit);
+ CPPUNIT_ASSERT(aHeader.unitString == "kg/h");
+ CPPUNIT_ASSERT_EQUAL(aHeader.unitStringPosition, 8);
+
+ OUString sFreeStanding2 = "bla bla J/m and more text here";
+ aHeader = mpUnitsImpl->extractUnitFromHeaderString(sFreeStanding2);
+ CPPUNIT_ASSERT(aHeader.valid);
+ CPPUNIT_ASSERT(UtUnit::createUnit("J/m", aTestUnit, mpUnitsImpl->mpUnitSystem));
+ CPPUNIT_ASSERT(aHeader.unit == aTestUnit);
+ CPPUNIT_ASSERT(aHeader.unitString == "J/m");
+ CPPUNIT_ASSERT_EQUAL(aHeader.unitStringPosition, 8);
OUString sFreeStandingWithSpaces = "bla bla m / s";
- CPPUNIT_ASSERT(mpUnitsImpl->extractUnitFromHeaderString(sFreeStandingWithSpaces, aUnit, sUnitString));
+ aHeader = mpUnitsImpl->extractUnitFromHeaderString(sFreeStandingWithSpaces);
+ CPPUNIT_ASSERT(aHeader.valid);
CPPUNIT_ASSERT(UtUnit::createUnit("m/s", aTestUnit, mpUnitsImpl->mpUnitSystem));
- CPPUNIT_ASSERT(aUnit == aTestUnit);
- CPPUNIT_ASSERT(sUnitString == "m/s");
+ CPPUNIT_ASSERT(aHeader.unit == aTestUnit);
+ CPPUNIT_ASSERT(aHeader.unitString == "m / s");
+ CPPUNIT_ASSERT_EQUAL(aHeader.unitStringPosition, 8);
- OUString sOperatorSeparated = "bla bla / t/s";
- CPPUNIT_ASSERT(mpUnitsImpl->extractUnitFromHeaderString(sOperatorSeparated, aUnit, sUnitString));
+ OUString sOperatorSeparated = "foobar / t/s";
+ aHeader = mpUnitsImpl->extractUnitFromHeaderString(sOperatorSeparated);
+ CPPUNIT_ASSERT(aHeader.valid);
CPPUNIT_ASSERT(UtUnit::createUnit("t/s", aTestUnit, mpUnitsImpl->mpUnitSystem));
- CPPUNIT_ASSERT(aUnit == aTestUnit);
- CPPUNIT_ASSERT(sUnitString == "t/s");
-
+ CPPUNIT_ASSERT(aHeader.unit == aTestUnit);
+ CPPUNIT_ASSERT(aHeader.unitString == "t/s");
+ CPPUNIT_ASSERT_EQUAL(aHeader.unitStringPosition, 9);
OUString sRoundBrackets = "bla bla (t/h)";
- CPPUNIT_ASSERT(mpUnitsImpl->extractUnitFromHeaderString(sRoundBrackets, aUnit, sUnitString));
+ aHeader = mpUnitsImpl->extractUnitFromHeaderString(sRoundBrackets);
+ CPPUNIT_ASSERT(aHeader.valid);
CPPUNIT_ASSERT(UtUnit::createUnit("t/h", aTestUnit, mpUnitsImpl->mpUnitSystem));
- CPPUNIT_ASSERT(aUnit == aTestUnit);
- CPPUNIT_ASSERT(sUnitString == "(t/h)");
-
- // This becomes more of a nightmare to support, so let's not bother for now.
- // OUString sFreeStandingMixedSpaces = "bla bla m /s* kg";
- // CPPUNIT_ASSERT(mpUnitsImpl->extractUnitFromHeaderString(sFreeStanding, aUnit, sUnitString));
- // CPPUNIT_ASSERT(UtUnit::createUnit("m/s", aTestUnit, mpUnitsImpl->mpUnitSystem));
- // CPPUNIT_ASSERT(aUnit == aTestUnit);
+ CPPUNIT_ASSERT(aHeader.unit == aTestUnit);
+ CPPUNIT_ASSERT(aHeader.unitString == "(t/h)");
+ CPPUNIT_ASSERT_EQUAL(aHeader.unitStringPosition, 8);
}
void UnitsTest::testCellConversion() {
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index aa3fc1e..cca532d 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -313,7 +313,7 @@ OUString UnitsImpl::extractUnitStringForCell(const ScAddress& rAddress, ScDocume
return extractUnitStringFromFormat(rFormatString);
}
-bool UnitsImpl::findUnitInStandardHeader(const OUString& rsHeader, UtUnit& aUnit, OUString& sUnitString) {
+HeaderUnitDescriptor UnitsImpl::findUnitInStandardHeader(const OUString& rsHeader) {
// TODO: we should do a sanity check that there's only one such unit though (and fail if there are multiple).
// Since otherwise there's no way for us to know which unit is the intended one, hence we need to get
// the user to deconfuse us by correcting their header to only contain the intended unit.
@@ -347,21 +347,23 @@ bool UnitsImpl::findUnitInStandardHeader(const OUString& rsHeader, UtUnit& aUnit
// i.e. startOffset is the last character of the intended substring, endOffset the first character.
// We specifically grab the offsets for the first actual regex group, which are stored in [1], the indexes
// at [0] represent the whole matched string (i.e. including square brackets).
- sUnitString = rsHeader.copy(aResult.endOffset[1], aResult.startOffset[1] - aResult.endOffset[1]);
+ UtUnit aUnit;
+ sal_Int32 nBegin = aResult.endOffset[1];
+ sal_Int32 nEnd = aResult.startOffset[1] - aResult.endOffset[1];
+ OUString sUnitString = rsHeader.copy( nBegin, nEnd);
if (UtUnit::createUnit(sUnitString, aUnit, mpUnitSystem)) {
- return true;
+ return { true, aUnit, boost::optional< ScAddress >(), sUnitString, nBegin };
}
nStartPosition = aResult.endOffset[0];
}
}
- sUnitString.clear();
- return false;
-}
+ return { false, UtUnit(), boost::optional< ScAddress >(), "", -1 };
+}
-bool UnitsImpl::findFreestandingUnitInHeader(const OUString& rsHeader, UtUnit& aUnit, OUString& sUnitString) {
+HeaderUnitDescriptor UnitsImpl::findFreestandingUnitInHeader(const OUString& rsHeader) {
// We just split the string and test whether each token is either a valid unit in its own right,
// or is an operator that could glue together multiple units (i.e. multiplication/division).
// This is sufficient for when there are spaces between elements composing the unit, and none
@@ -373,50 +375,69 @@ bool UnitsImpl::findFreestandingUnitInHeader(const OUString& rsHeader, UtUnit& a
const sal_Int32 nTokenCount = comphelper::string::getTokenCount(rsHeader, ' ');
const OUString sOperators = "/*"; // valid
- sUnitString.clear();
+
+ OUStringBuffer sUnitStringBuf;
+
+ sal_Int32 nStartPos = -1;
+ sal_Int32 nTokenPos = 0;
for (sal_Int32 nToken = 0; nToken < nTokenCount; nToken++) {
- OUString sToken = rsHeader.getToken(nToken, ' ');
+ OUString sToken = rsHeader.getToken( 0,' ', nTokenPos);
UtUnit aTestUnit;
+
+ // Only test for a separator character if we have already got something in our string, as
+ // some of the operators could be used as separators from description to unit
+ // (e.g. "a description / kg").
if (UtUnit::createUnit(sToken, aTestUnit, mpUnitSystem) ||
- // Only test for a separator character if we have already got something in our string, as
- // some of the operators could be used as separators from description to unit
- // (e.g. "a description / kg").
- ((sUnitString.getLength() > 0) && (sToken.getLength() == 1) && (sOperators.indexOf(sToken[0]) != -1))) {
- // we're repeatedly testing the string hence using an OUStringBuffer isn't of much use since there's
- // no simple/efficient way of repeatedly getting a testable OUString from the buffer.
- sUnitString += sToken;
- } else if (sUnitString.getLength() > 0) {
+ ((sUnitStringBuf.getLength() > 0) && (sToken.getLength() == 1) && (sOperators.indexOf(sToken[0]) != -1))) {
+
+ if (nStartPos == -1) {
+ // getToken sets nTokenPos to the first position after
+ // the current token (or -1 if the token is at the end
+ // the string).
+ if (nTokenPos == -1) {
+ nStartPos = rsHeader.getLength() - sToken.getLength();
+ } else {
+ nStartPos = nTokenPos - sToken.getLength() - 1;
+ }
+ }
+
+ sUnitStringBuf.append(" ").append(sToken);
+ } else if (sUnitStringBuf.getLength() > 0) {
// If we have units, followed by text, followed by units, we should still flag an error since
// that's ambiguous (unless the desired units are enclose in [] in which case we've
// already extracted these desired units in step 1 above.
break;
}
}
+ // Remove the leading space, it doesn't count as part of the unit string.
+ // (We reinsert spaces above as the HeaderUnitDescriptor must have the
+ // the original string as found in the header, i.e. we can't remove the
+ // spaces.)
+ sUnitStringBuf.remove(0, 1);
// We test the length to make sure we don't return the dimensionless unit 1 if we haven't found any units
// in the header.
+ UtUnit aUnit;
+ OUString sUnitString = sUnitStringBuf.makeStringAndClear();
if (sUnitString.getLength() && UtUnit::createUnit(sUnitString, aUnit, mpUnitSystem)) {
- return true;
+ return { true, aUnit, boost::optional< ScAddress >(), sUnitString, nStartPos };
}
- sUnitString.clear();
- return false;
+
+ return { false, UtUnit(), boost::optional< ScAddress >(), "", -1 };
}
-bool UnitsImpl::extractUnitFromHeaderString(const OUString& rsHeader, UtUnit& aUnit, OUString& sUnitString) {
+HeaderUnitDescriptor UnitsImpl::extractUnitFromHeaderString(const OUString& rsHeader) {
// 1. Ideally we have units in a 'standard' format, i.e. enclose in square brackets:
- if (findUnitInStandardHeader(rsHeader, aUnit, sUnitString)) {
- return true;
+ HeaderUnitDescriptor aHeader = findUnitInStandardHeader(rsHeader);
+ if (aHeader.valid) {
+ return aHeader;
}
// 2. But if not we check for free-standing units
- if (findFreestandingUnitInHeader(rsHeader, aUnit, sUnitString)) {
- return true;
- }
-
- // 3. Give up
- aUnit = UtUnit(); // assign invalid
- sUnitString.clear();
- return false;
+ aHeader = findFreestandingUnitInHeader(rsHeader);
+ // We return the result either way (it's either a valid unit,
+ // or invalid).
+ return aHeader;
}
UtUnit UnitsImpl::getUnitForCell(const ScAddress& rCellAddress, ScDocument* pDoc) {
@@ -428,11 +449,11 @@ UtUnit UnitsImpl::getUnitForCell(const ScAddress& rCellAddress, ScDocument* pDoc
return aUnit;
}
- OUString aHeaderUnitString; // Unused -- passed by reference below
- ScAddress aHeaderAddress; // Unused too
- UtUnit aHeaderUnit = findHeaderUnitForCell(rCellAddress, pDoc, aHeaderUnitString, aHeaderAddress);
- if (aHeaderUnit.isValid())
- return aHeaderUnit;
+ HeaderUnitDescriptor aHeader = findHeaderUnitForCell(rCellAddress, pDoc);
+
+ if (aHeader.valid) {
+ return aHeader.unit;
+ }
SAL_INFO("sc.units", "no unit obtained for token at cell " << rCellAddress.GetColRowString());
@@ -457,23 +478,27 @@ UtUnit UnitsImpl::getUnitForRef(FormulaToken* pToken, const ScAddress& rFormulaA
return getUnitForCell(aCellAddress, pDoc);
}
-UtUnit UnitsImpl::findHeaderUnitForCell(const ScAddress& rCellAddress,
- ScDocument* pDoc,
- OUString& rsHeaderUnitString,
- ScAddress& rHeaderAddress) {
+HeaderUnitDescriptor UnitsImpl::findHeaderUnitForCell(const ScAddress& rCellAddress,
+ ScDocument* pDoc) {
// Scan UPwards from the current cell to find a header. This is since we could potentially
// have two different sets of data sharing a column, hence finding the closest header is necessary.
- rHeaderAddress = rCellAddress;
- while (rHeaderAddress.Row() > 0) {
- rHeaderAddress.IncRow(-1);
+ ScAddress address = rCellAddress;
+
+ while (address.Row() > 0) {
+ address.IncRow(-1);
// We specifically test for string cells as intervening data cells could have
// differently defined units of their own. (However as these intervening cells
// will have the unit stored in the number format it would be ignored when
// checking the cell's string anyway.)
UtUnit aUnit;
- if (pDoc->GetCellType(rHeaderAddress) == CELLTYPE_STRING &&
- extractUnitFromHeaderString(pDoc->GetString(rHeaderAddress), aUnit, rsHeaderUnitString)) {
+ if (pDoc->GetCellType(address) == CELLTYPE_STRING) {
+ HeaderUnitDescriptor aHeader = extractUnitFromHeaderString(pDoc->GetString(address));
+
+ if (aHeader.valid) {
+ aHeader.address = address;
+ return aHeader;
+ }
// TODO: one potential problem is that we could have a text only "united" data cell
// (where the unit wasn't automatically extracted due to being entered via
// a different spreadsheet program).
@@ -482,12 +507,10 @@ UtUnit UnitsImpl::findHeaderUnitForCell(const ScAddress& rCellAddress,
//
// TODO: and what if there are multiple units in the header (for whatever reason?)?
// We can probably just warn the user that we'll be giving them garbage in that case?
- return aUnit;
}
}
- rHeaderAddress.SetInvalid();
- rsHeaderUnitString.clear();
- return UtUnit();
+
+ return { false, UtUnit(), boost::optional< ScAddress >(), "", -1 };
}
// getUnitForRef: check format -> if not in format, use more complicated method? (Format overrides header definition)
@@ -575,13 +598,10 @@ bool UnitsImpl::verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAdd
return false;
}
- OUString sUnitString;
- ScAddress aAddress;
-
- UtUnit aHeaderUnit = findHeaderUnitForCell(rFormulaAddress, pDoc, sUnitString, aAddress);
+ HeaderUnitDescriptor aHeader = findHeaderUnitForCell(rFormulaAddress, pDoc);
UtUnit aResultUnit = boost::get< UtUnit>(aStack.top().item);
- if (aHeaderUnit.isValid() && aHeaderUnit != aResultUnit) {
+ if (aHeader.valid && aHeader.unit != aResultUnit) {
return false;
}
@@ -635,11 +655,12 @@ bool UnitsImpl::isCellConversionRecommended(const ScAddress& rCellAddress,
rsCellUnit = extractUnitStringForCell(rCellAddress, pDoc);
if (!rsCellUnit.isEmpty() && UtUnit::createUnit(rsCellUnit, aCellUnit, mpUnitSystem)) {
- UtUnit aHeaderUnit = findHeaderUnitForCell(rCellAddress, pDoc, rsHeaderUnit, rHeaderCellAddress);
- if (rHeaderCellAddress.IsValid()) {
- if (aHeaderUnit.areConvertibleTo(aCellUnit)) {
- return true;
- }
+ HeaderUnitDescriptor aHeader = findHeaderUnitForCell(rCellAddress, pDoc);
+ if (aHeader.valid && aHeader.unit.areConvertibleTo(aCellUnit)) {
+ rsHeaderUnit = aHeader.unitString;
+ assert(aHeader.address);
+ rHeaderCellAddress = *aHeader.address;
+ return true;
}
}
@@ -659,9 +680,8 @@ bool UnitsImpl::convertCellToHeaderUnit(const ScAddress& rCellAddress,
UtUnit aOldUnit;
UtUnit::createUnit(sCellUnit, aOldUnit, mpUnitSystem);
- OUString sHeaderUnitFound;
- ScAddress aHeaderAddress; // Unused, but passed by reference
- UtUnit aNewUnit = findHeaderUnitForCell(rCellAddress, pDoc, sHeaderUnitFound, aHeaderAddress);
+ HeaderUnitDescriptor aHeader = findHeaderUnitForCell(rCellAddress, pDoc);
+ assert(aHeader.valid);
// We test that we still have all data in the same format as expected.
// This is maybe a tad defensive, but this call is most likely to be delayed
@@ -671,11 +691,11 @@ bool UnitsImpl::convertCellToHeaderUnit(const ScAddress& rCellAddress,
// called afterwards (especially for non-modal interactions, e.g.
// with an infobar which can remain open whilst the document is edited).
if ((sCellUnit == rsOldUnit) &&
- (sHeaderUnitFound == rsNewUnit) &&
+ (aHeader.unitString == rsNewUnit) &&
(pDoc->GetCellType(rCellAddress) == CELLTYPE_VALUE)) {
- assert(aOldUnit.areConvertibleTo(aNewUnit));
+ assert(aOldUnit.areConvertibleTo(aHeader.unit));
double nOldValue = pDoc->GetValue(rCellAddress);
- double nNewValue = aOldUnit.convertValueTo(nOldValue, aNewUnit);
+ double nNewValue = aOldUnit.convertValueTo(nOldValue, aHeader.unit);
pDoc->SetValue(rCellAddress, nNewValue);
pDoc->SetNumberFormat(rCellAddress, 0); // 0 == no number format?
@@ -712,37 +732,33 @@ bool UnitsImpl::convertCellUnits(const ScRange& rRange,
// Each column is independent hence we are able to handle each separately.
for (SCCOL nCol = nStartCol; nCol <= nEndCol; nCol++) {
- ScAddress aCurrentHeaderAddress(ScAddress::INITIALIZE_INVALID);
- UtUnit aCurrentHeaderUnit;
- OUString sHeaderUnitString;
+ HeaderUnitDescriptor aHeader = { false, UtUnit(), boost::optional< ScAddress >(), "", -1 };
for (SCROW nRow = nEndRow; nRow >= nStartRow; nRow--) {
ScAddress aCurrent(nCol, nRow, nStartTab);
- if (aCurrent == aCurrentHeaderAddress) {
- // TODO: rewrite this to use HeaderUnitDescriptor once implemented.
- // We can't do a dumb replace since that might overwrite other characters
- // (many units are just single characters).
+ if (aCurrent == aHeader.address) {
OUString sHeader = pDoc->GetString(aCurrent);
- sHeader = sHeader.replaceAll(sHeaderUnitString, rsOutputUnit);
+ sHeader = sHeader.replaceAt(aHeader.unitStringPosition, aHeader.unitString.getLength(), rsOutputUnit);
pDoc->SetString(aCurrent, sHeader);
- aCurrentHeaderAddress.SetInvalid();
+ aHeader.valid = false;
} else if (pDoc->GetCellType(aCurrent) != CELLTYPE_STRING) {
- if (!aCurrentHeaderUnit.isValid()) {
- aCurrentHeaderUnit = findHeaderUnitForCell(aCurrent, pDoc, sHeaderUnitString, aCurrentHeaderAddress);
+ if (!aHeader.valid) {
+ aHeader = findHeaderUnitForCell(aCurrent, pDoc);
// If there is no header we get an invalid unit returned from findHeaderUnitForCell,
// and therfore assume the dimensionless unit 1.
- if (!aCurrentHeaderUnit.isValid()) {
- UtUnit::createUnit("", aCurrentHeaderUnit, mpUnitSystem);
+ if (!aHeader.valid) {
+ UtUnit::createUnit("", aHeader.unit, mpUnitSystem);
+ aHeader.valid = true;
}
}
OUString sLocalUnit(extractUnitStringForCell(aCurrent, pDoc));
UtUnit aLocalUnit;
if (sLocalUnit.isEmpty()) {
- aLocalUnit = aCurrentHeaderUnit;
+ aLocalUnit = aHeader.unit;
} else { // override header unit with annotation unit
if (!UtUnit::createUnit(sLocalUnit, aLocalUnit, mpUnitSystem)) {
// but assume dimensionless if invalid
@@ -750,8 +766,8 @@ bool UnitsImpl::convertCellUnits(const ScRange& rRange,
}
}
- bool bLocalAnnotationRequired = (!aRange.In(aCurrentHeaderAddress)) &&
- (aOutputUnit != aCurrentHeaderUnit);
+ bool bLocalAnnotationRequired = (!aRange.In(*aHeader.address)) &&
+ (aOutputUnit != aHeader.unit);
double nValue = pDoc->GetValue(aCurrent);
if (!aLocalUnit.areConvertibleTo(aOutputUnit)) {
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
index ff927f2..7320baa 100644
--- a/sc/source/core/units/unitsimpl.hxx
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -58,6 +58,17 @@ struct UnitsResult {
boost::optional<UtUnit> units;
};
+struct HeaderUnitDescriptor {
+ bool valid;
+ UtUnit unit;
+ boost::optional< ScAddress > address;
+ // This must be the unit string copied verbatim from the header
+ // (i.e. including spaces)
+ OUString unitString;
+ // Position of unitString within the cell contents
+ sal_Int32 unitStringPosition;
+};
+
class UnitsImpl: public Units {
friend class test::UnitsTest;
@@ -103,9 +114,9 @@ private:
* Find and extract a Unit in the standard header notation,
* i.e. a unit enclose within square brackets (e.g. "length [cm]".
*
- * @return true if such a unit is found.
+ * @return The HeaderUnitDescriptor, with valid set to true if a unit was found.
*/
- bool findUnitInStandardHeader(const OUString& rHeader, UtUnit& aUnit, OUString& sUnitString);
+ HeaderUnitDescriptor findUnitInStandardHeader(const OUString& rHeader);
/**
* Find and extract a freestanding Unit from a header string.
* This includes strings such as "speed m/s", "speed m / s",
@@ -117,11 +128,11 @@ private:
* more permutations of the same unit, but this should at least cover the most
* obvious cases.
*
- * @ return true if a unit is found.
+ * @ return The HeaderUnitDescriptor, with valid set to true if a unit was found.
*/
- bool findFreestandingUnitInHeader(const OUString& rHeader, UtUnit& aUnit, OUString& sUnitString);
+ HeaderUnitDescriptor findFreestandingUnitInHeader(const OUString& rHeader);
- bool extractUnitFromHeaderString(const OUString& rHeader, UtUnit& aUnit, OUString& sUnitString);
+ HeaderUnitDescriptor extractUnitFromHeaderString(const OUString& rHeader);
static OUString extractUnitStringFromFormat(const OUString& rFormatString);
static OUString extractUnitStringForCell(const ScAddress& rAddress, ScDocument* pDoc);
@@ -142,10 +153,8 @@ private:
* that there is a valid unit), but we might also need the original
* String (which can't necessarily be regenerated from the UtUnit).
*/
- UtUnit findHeaderUnitForCell(const ScAddress& rCellAddress,
- ScDocument* pDoc,
- OUString& rsHeaderUnitString,
- ScAddress& rHeaderAddress);
+ HeaderUnitDescriptor findHeaderUnitForCell(const ScAddress& rCellAddress,
+ ScDocument* pDoc);
};
}} // namespace sc::units
More information about the Libreoffice-commits
mailing list