[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