[Libreoffice-commits] core.git: Branch 'feature/unitver' - 940 commits - accessibility/inc accessibility/source android/Bootstrap android/CustomTarget_android_desktop.mk android/CustomTarget_lo_android.mk android/experimental android/Makefile android/README android/source avmedia/inc avmedia/Library_avmediagst_0_10.mk avmedia/Library_avmediagst.mk avmedia/source basctl/source basegfx/source basic/inc basic/qa basic/source bin/gbuild-to-ide bin/get-bugzilla-attachments-by-mimetype bin/lo-commit-stat bin/lo-generate-source-tarball bin/rename-sw-abbreviations.sh bridges/source canvas/source chart2/inc chart2/qa chart2/source cli_ure/source comphelper/source compilerplugins/clang config_host/config_java.h.in config_host.mk.in configure.ac connectivity/inc connectivity/source connectivity/workben cppcanvas/inc cppcanvas/source cppuhelper/source cppu/source cui/source cui/uiconfig dbaccess/source desktop/inc desktop/scripts desktop/source dictionaries download.lst drawinglayer/source editeng/source emb eddedobj/source embeddedobj/test extensions/source external/harfbuzz external/libetonyek external/libexttextcat external/libmwaw external/libodfgen external/libwps external/Module_external.mk external/nss external/python3 external/udunits2 extras/source filter/Configuration_filter.mk filter/qa filter/source forms/source formula/source fpicker/source framework/inc framework/source .gitignore helpcompiler/Executable_helpindexer.mk helpcompiler/Executable_HelpIndexer.mk helpcompiler/Executable_helplinker.mk helpcompiler/Executable_HelpLinker.mk helpcompiler/Module_helpcompiler.mk helpcontent2 hwpfilter/source i18nlangtag/source i18npool/qa i18npool/source icon-themes/breeze icon-themes/classic icon-themes/galaxy icon-themes/hicontrast icon-themes/human icon-themes/oxygen icon-themes/sifr icon-themes/tango icon-themes/tango_testing idlc/inc idlc/source idl/source include/basic include/comphelper include/cppuhelper include/drawinglayer include/editeng include/filter include/formula inclu de/framework include/LibreOfficeKit include/o3tl include/oox include/sal include/sfx2 include/svl include/svtools include/svx include/test include/tools include/unotools include/vbahelper include/vcl include/writerperfect include/xmloff ios/CustomTarget_LibreOffice_app.mk ios/CustomTarget_MobileLibreOffice_app.mk ios/MobileLibreOffice ios/shared jvmfwk/inc jvmfwk/source l10ntools/source librelogo/source libreofficekit/qa libreofficekit/source lingucomponent/source Makefile.fetch Makefile.in odk/CustomTarget_doxygen.mk offapi/com offapi/org officecfg/registry oox/inc oox/source pyuno/source readlicense_oo/license README.cross registry/source reportdesign/source RepositoryExternal.mk rsc/source sal/cppunittester sal/Module_sal.mk sal/osl sal/qa sal/textenc sax/source sc/AllLangResTarget_sc.mk sc/CppunitTest_sc_copypaste.mk sc/CppunitTest_sc_subsequent_filters_test.mk sc/CppunitTest_sc_ucalc.mk sc/CppunitTest_sc_units.mk sc/inc sc/Library_sc.mk sc/Module_sc.mk scp2/source sc/qa scripti ng/source sc/sdi sc/source sc/workben sd/qa sd/source sd/uiconfig sfx2/sdi sfx2/source shell/source slideshow/source slideshow/test solenv/bin solenv/gbuild solenv/README sot/source starmath/inc starmath/Library_sm.mk starmath/sdi starmath/source starmath/uiconfig starmath/UIConfig_smath.mk stoc/source store/source svl/qa svl/source svtools/inc svtools/source svtools/uiconfig svx/sdi svx/source svx/workben sw/CppunitTest_sw_docbookexport.mk swext/mediawiki sw/inc sw/Module_sw.mk sw/ooxmlexport_setup.mk sw/qa sw/README sw/sdi sw/source sw/uiconfig test/source toolkit/source tools/source translations ucb/source unotools/source unoxml/source unusedcode.easy uui/source uui/uiconfig vbahelper/source vcl/android vcl/generic vcl/inc vcl/Library_vcl.mk vcl/opengl vcl/osx vcl/qa vcl/quartz vcl/README.GDIMetaFile vcl/source vcl/unx vcl/win vcl/workben writerfilter/inc writerfilter/source writerperfect/CppunitTest_writerperfect_stream.mk writerperfect/inc writerperfect/Library_wpftcalc.mk writ erperfect/Library_wpftdraw.mk writerperfect/Library_wpftimpress.mk writerperfect/Library_wpftwriter.mk writerperfect/Library_writerperfect.mk writerperfect/Module_writerperfect.mk writerperfect/qa writerperfect/source writerperfect/uiconfig writerperfect/UIConfig_writerperfect.mk xmloff/inc xmloff/source xmlscript/source xmlsecurity/source
Andrzej Hunt
andrzej at ahunt.org
Sun May 24 02:51:10 PDT 2015
Rebased ref, commits from common ancestor:
commit c7cb454e1137346ea3b8ad1bc6090db4e213e9fe
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Sun May 24 10:35:39 2015 +0100
Update units error messsage for clarity.
Change-Id: Ib59b2317f962d01bcc65a981040c3452cee7206e
diff --git a/sc/source/ui/src/units.src b/sc/source/ui/src/units.src
index be4f970..7a818e9 100644
--- a/sc/source/ui/src/units.src
+++ b/sc/source/ui/src/units.src
@@ -21,7 +21,7 @@
String STR_UNITS_ERRORINCELL
{
- Text [ en-US ] = "Error in formula in Cell $1" ;
+ Text [ en-US ] = "Units error in formula in Cell $1" ;
};
PushButton BT_UNITS_EDIT_CELL
commit a8b7811610f52480511841d55496dc869ef05439
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 551979dc1ebe1d712ad1d1a78cbdb3f71e752e62
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 1a376321ea4d6e09029e70479fd3e9c6a8d20023
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 b2fb7bf0424915ae20f3cca98eecffa70ee5b34c
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 f88b099479db3ea9a36db30a955adc5430761570
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 57d31b0b35de19f7148da17f6a9301d2add3ff58
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 9aa11b052658c59cd1c45264b90af948ad84804b
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 b43c5f8c7a3e1d4fa12c5ab80d9d7634cdc3071e
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 c03508ed5db8296f4765bcd01c170f273d0c57c8
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
commit ada6b639faea8fd707d3d3957f1608b308ef9354
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 b46695eed650d0dcb307f7a1b3bf9a8fbb1c45df
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 33cd8ccb4bfa742f1ac839c6c063a1434f3c9cb5
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 64eb8bedd7c0d457774425c04429dd99b62e8509
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 c096d36ac2556921946e43bcd71cc2a28c26d516
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)) \
)
commit a301e18b51228dab71cedae4d20161cf4340d23f
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Sat May 9 20:35:04 2015 +0100
Store pre-conversion value in cell annotation.
Change-Id: I67d8d1a7b0190b91107987a1ae4f03f2e91b06ca
diff --git a/sc/source/ui/view/viewfunc.cxx b/sc/source/ui/view/viewfunc.cxx
index 605b79a..b156587 100644
--- a/sc/source/ui/view/viewfunc.cxx
+++ b/sc/source/ui/view/viewfunc.cxx
@@ -2971,11 +2971,27 @@ IMPL_LINK( ScViewFunc, UnitConversionRecommendedHandler, UnitConversionPushButto
ScDocShellModificator aModificator( *pButton->mpDocSh );
+ OUString sOriginalValue = pButton->mpDoc->GetString( pButton->aCellAddress );
+
pUnits->convertCellToHeaderUnit( pButton->aCellAddress,
pButton->mpDoc,
pButton->sHeaderUnit,
pButton->sCellUnit );
+ ScPostIt* pNote = pButton->mpDoc->GetOrCreateNote( pButton->aCellAddress );
+ OUString sCurrentNote = pNote->GetText();
+
+ OUString sConversionNote("Original input: " + sOriginalValue);
+
+ if (sCurrentNote.isEmpty())
+ {
+ pNote->SetText( pButton->aCellAddress, sConversionNote );
+ }
+ else
+ {
+ pNote->SetText( pButton->aCellAddress, sCurrentNote + "\n\n" + sConversionNote );
+ }
+
aModificator.SetDocumentModified();
#endif
commit 85aa40659ed2e332d8fa9a08b7a3f9b2c1c19f9e
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Sat May 9 20:34:10 2015 +0100
Set document modified on local unit conversion.
Change-Id: I6666668817a7987d14728fb1de1abe3711e34d9a
diff --git a/sc/source/ui/inc/viewfunc.hxx b/sc/source/ui/inc/viewfunc.hxx
index 20d6872..a6b36b8 100644
--- a/sc/source/ui/inc/viewfunc.hxx
+++ b/sc/source/ui/inc/viewfunc.hxx
@@ -380,7 +380,8 @@ private:
ScDocument* pDoc,
const OUString& sHeaderUnit,
const ScAddress& rHeaderAddress,
- const OUString& sCellUnit );
+ const OUString& sCellUnit,
+ ScDocShell* pDocSh );
DECL_LINK( UnitConversionRecommendedHandler, UnitConversionPushButton* );
};
diff --git a/sc/source/ui/view/viewfunc.cxx b/sc/source/ui/view/viewfunc.cxx
index 595c5a9..605b79a 100644
--- a/sc/source/ui/view/viewfunc.cxx
+++ b/sc/source/ui/view/viewfunc.cxx
@@ -586,7 +586,7 @@ void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
ScAddress aHeaderAddress;
if ( pUnits->isCellConversionRecommended( aAddress, pDoc, sHeaderUnit, aHeaderAddress, sCellUnit ) ) {
- NotifyUnitConversionRecommended( aAddress, pDoc, sHeaderUnit, aHeaderAddress, sCellUnit );
+ NotifyUnitConversionRecommended( aAddress, pDoc, sHeaderUnit, aHeaderAddress, sCellUnit, pDocSh );
} else {
SfxViewFrame* pViewFrame = GetViewData().GetViewShell()->GetFrame();
OUString sAddress = aAddress.Format( SCA_BITS, pDoc );
@@ -2904,26 +2904,30 @@ struct UnitConversionPushButton: public PushButton
ScDocument* mpDoc;
const OUString sHeaderUnit;
const OUString sCellUnit;
+ ScDocShell* mpDocSh;
UnitConversionPushButton( vcl::Window* pParent,
const ResId& rResId,
const ScAddress& rCellAddress,
ScDocument* pDoc,
const OUString& rsHeaderUnit,
- const OUString& rsCellUnit ):
+ const OUString& rsCellUnit,
+ ScDocShell* pDocSh ):
PushButton( pParent, rResId ),
aCellAddress( rCellAddress ),
mpDoc( pDoc ),
sHeaderUnit( rsHeaderUnit ),
- sCellUnit( rsCellUnit )
+ sCellUnit( rsCellUnit ),
+ mpDocSh( pDocSh )
{}
};
void ScViewFunc::NotifyUnitConversionRecommended( const ScAddress& rCellAddress,
- ScDocument* pDoc,
- const OUString& rsHeaderUnit,
- const ScAddress& rHeaderAddress,
- const OUString& rsCellUnit ) {
+ ScDocument* pDoc,
+ const OUString& rsHeaderUnit,
+ const ScAddress& rHeaderAddress,
+ const OUString& rsCellUnit,
+ ScDocShell* pDocSh ) {
SfxViewFrame* pViewFrame = GetViewData().GetViewShell()->GetFrame();
// As with NotifyUnitErrorInFormula we use the cell address as the infobar id.
@@ -2947,7 +2951,8 @@ void ScViewFunc::NotifyUnitConversionRecommended( const ScAddress& rCellAddress,
rCellAddress,
pDoc,
rsHeaderUnit,
- rsCellUnit );
+ rsCellUnit,
+ pDocSh );
pButtonConvertCell->SetClickHdl( LINK( this, ScViewFunc, UnitConversionRecommendedHandler ) );
OUString sConvertText = pButtonConvertCell->GetText();
@@ -2964,10 +2969,14 @@ IMPL_LINK( ScViewFunc, UnitConversionRecommendedHandler, UnitConversionPushButto
#ifdef ENABLE_CALC_UNITVERIFICATION
boost::shared_ptr< sc::units::Units > pUnits = sc::units::Units::GetUnits();
+ ScDocShellModificator aModificator( *pButton->mpDocSh );
+
pUnits->convertCellToHeaderUnit( pButton->aCellAddress,
pButton->mpDoc,
pButton->sHeaderUnit,
pButton->sCellUnit );
+
+ aModificator.SetDocumentModified();
#endif
OUString sAddress;
commit 5186857793b01f196a4d4f4d9f7ba7f295e79e7e
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Sat May 9 11:09:24 2015 +0100
Upgrade units test to use SfxModelFlags (rebase fixup).
Necessary due to afc728fe76fbf1afea725afd6ff5e9af92e10b08
Change-Id: Iea6dfb08a36f56485ed43a9c4cd2dcf652ff0b97
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index 3fbcaa9..e6ed779 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -72,9 +72,9 @@ void UnitsTest::setUp() {
ScDLL::Init();
m_xDocShRef = new ScDocShell(
- SFXMODEL_STANDARD |
- SFXMODEL_DISABLE_EMBEDDED_SCRIPTS |
- SFXMODEL_DISABLE_DOCUMENT_RECOVERY);
+ SfxModelFlags::EMBEDDED_OBJECT |
+ SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS |
+ SfxModelFlags::DISABLE_DOCUMENT_RECOVERY);
mpDoc = &m_xDocShRef->GetDocument();
commit 86c7b05d526d98e67c3b0b83980e20bf15503286
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Sat May 9 11:07:10 2015 +0100
loplugin:staticmethods
Change-Id: I31969836cc9e9147aaa370779fa281792efa9de2
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index 7b5fd25..3fbcaa9 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -363,8 +363,8 @@ void UnitsTest::testUnitVerification() {
}
void UnitsTest::testUnitFromFormatStringExtraction() {
- CPPUNIT_ASSERT(mpUnitsImpl->extractUnitStringFromFormat("\"weight: \"0.0\"kg\"") == "kg");
- CPPUNIT_ASSERT(mpUnitsImpl->extractUnitStringFromFormat("#\"cm\"") == "cm");
+ CPPUNIT_ASSERT(UnitsImpl::extractUnitStringFromFormat("\"weight: \"0.0\"kg\"") == "kg");
+ CPPUNIT_ASSERT(UnitsImpl::extractUnitStringFromFormat("#\"cm\"") == "cm");
}
void UnitsTest::testUnitValueStringSplitting() {
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
index 379a6a7..0eea129 100644
--- a/sc/source/core/units/unitsimpl.hxx
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -119,8 +119,8 @@ private:
bool extractUnitFromHeaderString(const OUString& rHeader, UtUnit& aUnit, OUString& sUnitString);
- OUString extractUnitStringFromFormat(const OUString& rFormatString);
- OUString extractUnitStringForCell(const ScAddress& rAddress, ScDocument* pDoc);
+ static OUString extractUnitStringFromFormat(const OUString& rFormatString);
+ static OUString extractUnitStringForCell(const ScAddress& rAddress, ScDocument* pDoc);
/**
* Retrieve the units for a given cell. This probes based on the usual rules
commit 6428703e4cf1b3aab1982c86b5ce876b17f0308c
Author: Andrzej Hunt <andrzej.hunt at collabora.com>
Date: Fri Apr 10 11:03:33 2015 +0100
Move and rename Range/Unit Stack.
This in preparation for implementing a combined Unit
and Range iterator.
Change-Id: I08d28e175453f65c3696e9d1c6c20c7076d9b164
diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk
index 5d8f086..04052c6 100644
--- a/sc/Library_sc.mk
+++ b/sc/Library_sc.mk
@@ -689,6 +689,7 @@ $(call gb_Library_add_exception_objects,sc,\
ifeq ($(ENABLE_CALC_UNITVERIFICATION),TRUE)
$(eval $(call gb_Library_add_exception_objects,sc,\
+ sc/source/core/units/raustack \
sc/source/core/units/units \
sc/source/core/units/unitsimpl \
sc/source/core/units/util \
diff --git a/sc/source/core/units/raustack.cxx b/sc/source/core/units/raustack.cxx
new file mode 100644
index 0000000..f838acf
--- /dev/null
+++ b/sc/source/core/units/raustack.cxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+#include "raustack.hxx"
+
+using namespace sc::units;
+
+RangeListIterator::RangeListIterator(ScDocument* pDoc, const ScRangeList& rRangeList)
+ :
+ mRangeList(rRangeList),
+ mpDoc(pDoc),
+ mIt(pDoc, ScRange()),
+ nCurrentIndex(0)
+{
+}
+
+bool RangeListIterator::first() {
+ if (mRangeList.size() > 0) {
+ mIt = ScCellIterator(mpDoc, *mRangeList[0]);
+ return mIt.first();
+ } else {
+ return false;
+ }
+}
+
+const ScAddress& RangeListIterator::GetPos() const {
+ return mIt.GetPos();
+}
+
+bool RangeListIterator::next() {
+ if (!(mRangeList.size() > 0) || nCurrentIndex >= mRangeList.size()) {
+ return false;
+ }
+
+ if (mIt.next()) {
+ return true;
+ } else if (++nCurrentIndex < mRangeList.size()) {
+ mIt = ScCellIterator(mpDoc, *mRangeList[nCurrentIndex]);
+ mIt.first();
+ // TODO: if emtpy - skip to next...?
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
diff --git a/sc/source/core/units/raustack.hxx b/sc/source/core/units/raustack.hxx
new file mode 100644
index 0000000..b5cf48b
--- /dev/null
+++ b/sc/source/core/units/raustack.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+#ifndef INCLUDED_SC_SOURCE_CORE_UNITS_RAUSTACK_HXX
+#define INCLUDED_SC_SOURCE_CORE_UNITS_RAUSTACK_HXX
+
+#include <boost/variant.hpp>
+
+#include <address.hxx>
+#include <dociter.hxx>
+#include <rangelst.hxx>
+
+
+#include "utunit.hxx"
+
+namespace sc {
+namespace units {
+
+enum class RAUSItemType {
+ UNITS,
+ RANGE
+};
+
+struct RAUSItem {
+ RAUSItemType type;
+ boost::variant< ScRange, UtUnit > item;
+};
+
+class RangeListIterator {
+private:
+ const ScRangeList mRangeList;
+ ScDocument* mpDoc;
+
+ ScCellIterator mIt;
+ size_t nCurrentIndex;
+
+public:
+ RangeListIterator(ScDocument* pDoc, const ScRangeList& rRangeList);
+
+ bool first();
+
+ const ScAddress& GetPos() const;
+
+ bool next();
+};
+
+}} // sc::units
+
+// class RangeAndUnitStack {
+
+
+
+// };
+
+// class RATSIterator {
+// public:
+// // TODO: need to be able to return non-initialisation
+// static RATSIterator getIterator(RangeAndTokenStack& rStack, ScDoc* pDoc, int nItems);
+
+// }
+
+#endif // INCLUDED_SC_SOURCE_CORE_UNITS_RAUSTACK_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index 99e03af..e8fb261 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -11,7 +11,6 @@
#include "util.hxx"
-#include "dociter.hxx"
#include "document.hxx"
#include "refdata.hxx"
#include "stringutil.hxx"
@@ -85,55 +84,7 @@ UnitsImpl::~UnitsImpl() {
// (i.e. if udunits can't handle being used across threads)?
}
-class RangeListIterator {
-private:
- const ScRangeList mRangeList;
- ScDocument* mpDoc;
-
- ScCellIterator mIt;
- size_t nCurrentIndex;
-
-public:
- RangeListIterator(ScDocument* pDoc, const ScRangeList& rRangeList)
- :
- mRangeList(rRangeList),
- mpDoc(pDoc),
- mIt(pDoc, ScRange()),
- nCurrentIndex(0)
- {
- }
-
- bool first() {
- if (mRangeList.size() > 0) {
- mIt = ScCellIterator(mpDoc, *mRangeList[0]);
- return mIt.first();
- } else {
- return false;
- }
- }
-
- const ScAddress& GetPos() const {
- return mIt.GetPos();
- }
-
- bool next() {
- if (!(mRangeList.size() > 0) || nCurrentIndex >= mRangeList.size()) {
- return false;
- }
-
- if (mIt.next()) {
- return true;
- } else if (++nCurrentIndex < mRangeList.size()) {
- mIt = ScCellIterator(mpDoc, *mRangeList[nCurrentIndex]);
- mIt.first();
- return true;
- } else {
- return false;
- }
- }
-};
-
-UnitsResult UnitsImpl::getOutputUnitsForOpCode(stack< StackItem >& rStack, const formula::FormulaToken* pToken, ScDocument* pDoc) {
+UnitsResult UnitsImpl::getOutputUnitsForOpCode(stack< RAUSItem >& rStack, const formula::FormulaToken* pToken, ScDocument* pDoc) {
const OpCode aOpCode = pToken->GetOpCode();
auto nOpCode = static_cast<std::underlying_type<const OpCode>::type>(aOpCode);
@@ -147,7 +98,7 @@ UnitsResult UnitsImpl::getOutputUnitsForOpCode(stack< StackItem >& rStack, const
if (rStack.size() == 0) {
SAL_WARN("sc.units", "Single item opcode failed (no stack items, or range used)");
return { UnitsStatus::UNITS_INVALID, boost::none };
- } else if (rStack.top().type != StackItemType::UNITS) {
+ } else if (rStack.top().type != RAUSItemType::UNITS) {
return { UnitsStatus::UNITS_UNKNOWN, boost::none };
}
@@ -183,13 +134,13 @@ UnitsResult UnitsImpl::getOutputUnitsForOpCode(stack< StackItem >& rStack, const
return { UnitsStatus::UNITS_INVALID, boost::none };
}
- if (rStack.top().type != StackItemType::UNITS) {
+ if (rStack.top().type != RAUSItemType::UNITS) {
return { UnitsStatus::UNITS_UNKNOWN, boost::none };
}
UtUnit pSecondUnit = boost::get<UtUnit>(rStack.top().item);
rStack.pop();
- if (rStack.top().type != StackItemType::UNITS) {
+ if (rStack.top().type != RAUSItemType::UNITS) {
return { UnitsStatus::UNITS_UNKNOWN, boost::none };
}
UtUnit pFirstUnit = boost::get<UtUnit>(rStack.top().item);
@@ -240,12 +191,12 @@ UnitsResult UnitsImpl::getOutputUnitsForOpCode(stack< StackItem >& rStack, const
for ( ; nParams > 0; nParams--) {
switch (rStack.top().type) {
- case StackItemType::UNITS:
+ case RAUSItemType::UNITS:
{
aUnitsStack.push(boost::get< UtUnit >(rStack.top().item));
break;
}
- case StackItemType::RANGE:
+ case RAUSItemType::RANGE:
{
aRangeList.Append(boost::get< ScRange >(rStack.top().item));
break;
@@ -545,7 +496,7 @@ bool UnitsImpl::verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAdd
pArray->Dump();
#endif
- stack< StackItem > aStack;
+ stack< RAUSItem > aStack;
for (FormulaToken* pToken = pArray->FirstRPN(); pToken != 0; pToken = pArray->NextRPN()) {
switch (pToken->GetType()) {
@@ -564,14 +515,14 @@ bool UnitsImpl::verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAdd
return false;
}
- aStack.push( { StackItemType::UNITS, aUnit } );
+ aStack.push( { RAUSItemType::UNITS, aUnit } );
break;
}
case formula::svDoubleRef:
{
ScComplexRefData* pDoubleRef = pToken->GetDoubleRef();
ScRange aRange = pDoubleRef->toAbs(rFormulaAddress);
- aStack.push( { StackItemType::RANGE, aRange } );
+ aStack.push( { RAUSItemType::RANGE, aRange } );
break;
}
@@ -588,7 +539,7 @@ bool UnitsImpl::verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAdd
case UnitsStatus::UNITS_VALID:
... etc. - the rest is truncated
More information about the Libreoffice-commits
mailing list