[Libreoffice-commits] core.git: Branch 'feature/unitver' - 5 commits - external/udunits2 sc/inc sc/qa sc/source
Andrzej Hunt
andrzej at ahunt.org
Mon May 11 07:16:16 PDT 2015
external/udunits2/ExternalProject_udunits2.mk | 1
sc/inc/units.hxx | 22 ++++++
sc/qa/unit/units.cxx | 84 ++++++++++++++++++++++
sc/source/core/data/column3.cxx | 2
sc/source/core/units/unitsimpl.cxx | 95 ++++++++++++++++++++++++++
sc/source/core/units/unitsimpl.hxx | 4 +
sc/source/core/units/utunit.hxx | 5 +
7 files changed, 211 insertions(+), 2 deletions(-)
New commits:
commit 59618d77b6150b50775d7d67f826c15a44ee505e
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Mon May 11 15:13:16 2015 +0100
Implement unit conversion for ranges.
Not entirely finished yet, some further refactoring needed elsewhere
to allow sensible implementation of the header editing.
Change-Id: I81af74d698098f901b17fcda413e7aac04c94274
diff --git a/sc/inc/units.hxx b/sc/inc/units.hxx
index 9837d36..381ec7d 100644
--- a/sc/inc/units.hxx
+++ b/sc/inc/units.hxx
@@ -16,6 +16,7 @@
class ScAddress;
class ScDocument;
+class ScRange;
class ScTokenArray;
namespace sc {
@@ -75,6 +76,27 @@ public:
const OUString& rsNewUnit,
const OUString& rsOldUnit) = 0;
+ /**
+ * Convert cells from one unit to another.
+ *
+ * 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).
+ *
+ * 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
+ * annotation is also updated as appropriate. If instead the header is excluded,
+ * but all other cells are selected in a column, then local annotations are added.
+ *
+ * rsInputUnit overrides the automatic determination of input units, i.e. disables
+ * input unit detection.
+ */
+ virtual bool convertCellUnits(const ScRange& rRange,
+ ScDocument* pDoc,
+ const OUString& rsOutputUnit) = 0;
virtual ~Units() {}
};
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index e6ed779..8e83483 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -48,6 +48,7 @@ public:
void testUnitFromHeaderExtraction();
void testCellConversion();
+ void testRangeConversion();
CPPUNIT_TEST_SUITE(UnitsTest);
@@ -59,6 +60,7 @@ public:
CPPUNIT_TEST(testUnitFromHeaderExtraction);
CPPUNIT_TEST(testCellConversion);
+ CPPUNIT_TEST(testRangeConversion);
CPPUNIT_TEST_SUITE_END();
@@ -524,6 +526,88 @@ void UnitsTest::testCellConversion() {
// to pass in the output of isCellConversionRecommended).
}
+void UnitsTest::testRangeConversion() {
+ const SCTAB nTab = 1;
+ mpDoc->EnsureTable(nTab);
+
+ // Column 1: convert [cm] to [cm].
+ ScAddress headerAddress(0, 0, nTab);
+ mpDoc->SetString(headerAddress, "length [cm]");
+
+ ScAddress address(headerAddress);
+
+ vector<double> values({10, 20, 30, 40, 1, 0.5, 0.25});
+ address.IncRow();
+ mpDoc->SetValues(address, values);
+
+ // Test conversion of range _not_ including header
+ ScAddress endAddress( address.Col(), address.Row() + values.size() - 1, nTab);
+
+ ScRange aRange(address, endAddress);
+ CPPUNIT_ASSERT(mpUnitsImpl->convertCellUnits(aRange, mpDoc, "cm"));
+ CPPUNIT_ASSERT(!mpUnitsImpl->convertCellUnits(aRange, mpDoc, "kg"));
+
+ CPPUNIT_ASSERT(mpDoc->GetString(headerAddress) == "length [cm]");
+
+ for (double d: values) {
+ // Test that the value is unchanged
+ CPPUNIT_ASSERT(mpDoc->GetValue(address) == d);
+ // And NO annotation has been added
+ CPPUNIT_ASSERT(mpDoc->GetString(address) == OUString::number(d));
+ address.IncRow();
+ }
+
+ // Test conversion of range including header (from cm to cm)
+ aRange = ScRange(headerAddress, endAddress);
+ CPPUNIT_ASSERT(mpUnitsImpl->convertCellUnits(aRange, mpDoc, "cm"));
+ CPPUNIT_ASSERT(!mpUnitsImpl->convertCellUnits(aRange, mpDoc, "kg"));
+
+ CPPUNIT_ASSERT(mpDoc->GetString(headerAddress) == "length [cm]");
+
+ address = headerAddress;
+ address.IncRow();
+ for (double d: values) {
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(mpDoc->GetValue(address), d, 1e-7);
+ // And NO annotation has been added
+ CPPUNIT_ASSERT(mpDoc->GetString(address) == OUString::number(d));
+ address.IncRow();
+ }
+
+ // Convert just the values (but not header): [cm] to [m]
+ address.SetRow(1);
+ aRange = ScRange(address, endAddress);
+ CPPUNIT_ASSERT(mpUnitsImpl->convertCellUnits(aRange, mpDoc, "m"));
+
+ CPPUNIT_ASSERT(mpDoc->GetString(headerAddress) == "length [cm]");
+
+ for (double d: values) {
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(mpDoc->GetValue(address), d/100, 1e-7);
+ // AND test annotation
+ // Disabled for now until the precision problems are figured out
+ // CPPUNIT_ASSERT(mpDoc->GetString(address) == OUString::number(d/100) + "m");
+ address.IncRow();
+ }
+
+ // Convert everything (including header) to mm: [m] to [mm]
+ aRange = ScRange(headerAddress, endAddress);
+ CPPUNIT_ASSERT(mpUnitsImpl->convertCellUnits(aRange, mpDoc, "mm"));
+
+ CPPUNIT_ASSERT(mpDoc->GetString(headerAddress) == "length [mm]");
+
+ address.SetRow(1);
+
+ for (double d: values) {
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(mpDoc->GetValue(address), d*10, 1e-7);
+ // And the annotation has been REMOVED
+ CPPUNIT_ASSERT(mpDoc->GetString(address) == OUString::number(d*10));
+ address.IncRow();
+ }
+
+ // TODO: we need to test:
+ // 1. mixture of units that can't be converted
+ // 2. mixtures of local and header annotations
+}
+
CPPUNIT_TEST_SUITE_REGISTRATION(UnitsTest);
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index e8fb261..aa3fc1e 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -690,4 +690,99 @@ 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++) {
+ ScAddress aCurrentHeaderAddress(ScAddress::INITIALIZE_INVALID);
+ UtUnit aCurrentHeaderUnit;
+ OUString sHeaderUnitString;
+
+ 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).
+ OUString sHeader = pDoc->GetString(aCurrent);
+ sHeader = sHeader.replaceAll(sHeaderUnitString, rsOutputUnit);
+ pDoc->SetString(aCurrent, sHeader);
+
+ aCurrentHeaderAddress.SetInvalid();
+ } else if (pDoc->GetCellType(aCurrent) != CELLTYPE_STRING) {
+ if (!aCurrentHeaderUnit.isValid()) {
+ aCurrentHeaderUnit = findHeaderUnitForCell(aCurrent, pDoc, sHeaderUnitString, aCurrentHeaderAddress);
+
+ // 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);
+ }
+ }
+
+ OUString sLocalUnit(extractUnitStringForCell(aCurrent, pDoc));
+ UtUnit aLocalUnit;
+ if (sLocalUnit.isEmpty()) {
+ aLocalUnit = aCurrentHeaderUnit;
+ } 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(aCurrentHeaderAddress)) &&
+ (aOutputUnit != aCurrentHeaderUnit);
+ 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);
+ 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 + "\"";
+ sal_uInt32 nFormatKey;
+ short nType = css::util::NumberFormat::DEFINED;
+ sal_Int32 nErrorPosition; // Unused, because we should be creating working number formats.
+
+ SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
+ pFormatter->PutEntry(sNewFormat, nErrorPosition, nType, nFormatKey);
+ pDoc->SetNumberFormat(aCurrent, nFormatKey);
+ } else {
+ // The number formats will by definition be wrong once we've converted, so just reset completely.
+ pDoc->SetNumberFormat(aCurrent, 0);
+ }
+ }
+
+ }
+ }
+
+ return true;
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
index 0eea129..ff927f2 100644
--- a/sc/source/core/units/unitsimpl.hxx
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -92,6 +92,10 @@ public:
const OUString& rsNewUnit,
const OUString& rsOldUnit) SAL_OVERRIDE;
+ virtual bool convertCellUnits(const ScRange& rRange,
+ ScDocument* pDoc,
+ const OUString& rsOutputUnit) SAL_OVERRIDE;
+
private:
UnitsResult getOutputUnitsForOpCode(std::stack< RAUSItem >& rStack, const formula::FormulaToken* pToken, ScDocument* pDoc);
commit 684c88aea4cbdec3893a071c212d5e45815e3920
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Mon May 11 12:20:15 2015 +0100
Use the actual default format for value/unit splitting.
Change-Id: I1da34b43d273d1ae2f91591aafcfab8adf9a4f02
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index f0d7fa8..9666d60 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -1689,7 +1689,7 @@ bool ScColumn::ParseString(
// But in that case it would make sense to actually store units independently of number format.
// (This is all just a dirty hack for now...)
- OUString sNewFormat = "#\"" + sUnit + "\"";
+ OUString sNewFormat = "General\"" + sUnit + "\"";
sal_uInt32 nFormatKey;
short nType = css::util::NumberFormat::DEFINED;
sal_Int32 nErrorPosition; // Unused, because we should be creating working number formats.
commit ede806db075d4205bd4d0209f0cf1c4863228be7
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Mon May 11 12:19:25 2015 +0100
Add some assertions for UtUnit::convertValueTo
Change-Id: Ie606bbb3cab0b4445274b8bf92f8b1533b39c9fd
diff --git a/sc/source/core/units/utunit.hxx b/sc/source/core/units/utunit.hxx
index 6bfb120..b039c74 100644
--- a/sc/source/core/units/utunit.hxx
+++ b/sc/source/core/units/utunit.hxx
@@ -119,6 +119,9 @@ public:
}
double convertValueTo(double nOriginalValue, const UtUnit& rUnit) {
+ assert(isValid());
+ assert(rUnit.isValid());
+
// We could write our own cv_converter wrapper too, but that
// seems unnecessary given the limited selection of
// operations (convert float/double, either individually
commit 25c1b09a928b19cab29ad43d55b6b1e9b9e81d08
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Mon May 11 12:19:01 2015 +0100
UtUnit::isValid should be const.
Change-Id: I209dca17b0101ec8a36e590870e90dcaca510a42
diff --git a/sc/source/core/units/utunit.hxx b/sc/source/core/units/utunit.hxx
index c137f67..6bfb120 100644
--- a/sc/source/core/units/utunit.hxx
+++ b/sc/source/core/units/utunit.hxx
@@ -69,7 +69,7 @@ public:
OUString getString() const;
- bool isValid() {
+ bool isValid() const {
// We use a null pointer/empty unit to indicate an invalid unit.
return mpUnit.get() != 0;
}
commit 4405e44d4c6622e3e19fe401f99845abb0eece6d
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Mon May 11 12:18:04 2015 +0100
Add --enable-debug for udunits.
Change-Id: Ic737f7fee40ed564bfd1eec973e2b9843ed68405
diff --git a/external/udunits2/ExternalProject_udunits2.mk b/external/udunits2/ExternalProject_udunits2.mk
index 6fdbefa..070dc67 100644
--- a/external/udunits2/ExternalProject_udunits2.mk
+++ b/external/udunits2/ExternalProject_udunits2.mk
@@ -27,6 +27,7 @@ $(call gb_ExternalProject_get_state_target,udunits2,configure) :
$(call gb_ExternalProject_run,configure,\
autoreconf -i && \
MAKE=$(MAKE) ./configure \
+ $(if $(ENABLE_DEBUG),--enable-debug) \
--build=$(if $(filter WNT,$(OS)),i686-pc-cygwin,$(BUILD_PLATFORM)) \
)
More information about the Libreoffice-commits
mailing list