summaryrefslogtreecommitdiffstats
path: root/sc/source
diff options
context:
space:
mode:
authorEike Rathke <erack@redhat.com>2014-03-31 19:28:31 +0200
committerEike Rathke <erack@redhat.com>2014-03-31 22:31:23 +0200
commite65141e93a540fc9fb4343ee65a5a7da7e3b1769 (patch)
treeaab49525e80c516f8e7261a873e4ad2d3ca13af2 /sc/source
parentone more comment (diff)
downloadcore-e65141e93a540fc9fb4343ee65a5a7da7e3b1769.tar.gz
core-e65141e93a540fc9fb4343ee65a5a7da7e3b1769.zip
re-enabled user-defined numeric fields for dBase export
Since commit f59e350d1733125055f1144f8b3b1b0a46f6d1ca it was impossible to define a numeric field with a precision of less than 2 decimals, even if all values were integers. It was also impossible to define a field width larger than needed for any values in that column. Furthermore, the integer part was shortened if the overall column's values resulted in more precision than defined, but the overall length did not reach the predefined length. This does not change the behavior of the original intention of f59e350d1733125055f1144f8b3b1b0a46f6d1ca to give the precision of number formats precedence over precision defined in the column header, which is debatable though because conflicts may silently change the field definition. Change-Id: I234c4bceaa1a6aadbd259cb8d9b6cb6f16bf91c2
Diffstat (limited to 'sc/source')
-rw-r--r--sc/source/core/data/column3.cxx108
-rw-r--r--sc/source/ui/docshell/docsh8.cxx53
2 files changed, 133 insertions, 28 deletions
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index bd1cebd63689..86ee9a269648 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -2683,25 +2683,42 @@ class MaxNumStringLenHandler
SvNumberFormatter* mpFormatter;
sal_Int32 mnMaxLen;
sal_uInt16 mnPrecision;
+ sal_uInt16 mnMaxGeneralPrecision;
+ bool mbHaveSigned;
void processCell(size_t nRow, ScRefCellValue& rCell)
{
- if (rCell.meType == CELLTYPE_FORMULA && !rCell.mpFormula->IsValue())
- return;
+ sal_uInt16 nCellPrecision = mnMaxGeneralPrecision;
+ if (rCell.meType == CELLTYPE_FORMULA)
+ {
+ if (!rCell.mpFormula->IsValue())
+ return;
+
+ // Limit unformatted formula cell precision to precision
+ // encountered so far, if any, otherwise we'd end up with 15 just
+ // because of =1/3 ... If no precision yet then arbitrarily limit
+ // to a maximum of 4 unless a maximum general precision is set.
+ if (mnPrecision)
+ nCellPrecision = mnPrecision;
+ else
+ nCellPrecision = (mnMaxGeneralPrecision >= 15) ? 4 : mnMaxGeneralPrecision;
+ }
+
+ double fVal = rCell.getValue();
+ if (!mbHaveSigned && fVal < 0.0)
+ mbHaveSigned = true;
OUString aString;
+ OUString aSep;
+ sal_Int32 nLen;
+ sal_uInt16 nPrec;
sal_uInt32 nFormat = static_cast<const SfxUInt32Item*>(
- mrColumn.GetAttr(nRow, ATTR_VALUE_FORMAT))->GetValue();
- ScCellFormat::GetInputString(rCell, nFormat, aString, *mpFormatter, &mrColumn.GetDoc());
- sal_Int32 nLen = aString.getLength();
- if (nLen <= 0)
- // Ignore empty string.
- return;
-
- if (nFormat)
+ mrColumn.GetAttr(nRow, ATTR_VALUE_FORMAT))->GetValue();
+ if (nFormat % SV_COUNTRY_LANGUAGE_OFFSET)
{
+ aSep = mpFormatter->GetFormatDecimalSep(nFormat);
+ ScCellFormat::GetInputString(rCell, nFormat, aString, *mpFormatter, &mrColumn.GetDoc());
const SvNumberformat* pEntry = mpFormatter->GetEntry(nFormat);
- sal_uInt16 nPrec;
if (pEntry)
{
bool bThousand, bNegRed;
@@ -2710,15 +2727,54 @@ class MaxNumStringLenHandler
}
else
nPrec = mpFormatter->GetFormatPrecision(nFormat);
+ }
+ else
+ {
+ if (mnPrecision >= mnMaxGeneralPrecision)
+ return; // early bail out for nothing changes here
- if (nPrec != SvNumberFormatter::UNLIMITED_PRECISION && nPrec > mnPrecision)
- mnPrecision = nPrec;
+ if (!fVal)
+ {
+ // 0 doesn't change precision, but set a maximum length if none yet.
+ if (!mnMaxLen)
+ mnMaxLen = 1;
+ return;
+ }
+
+ // Simple number string with at most 15 decimals and trailing
+ // decimal zeros eliminated.
+ aSep = ".";
+ aString = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_F, nCellPrecision, '.', true);
+ nPrec = SvNumberFormatter::UNLIMITED_PRECISION;
}
+ nLen = aString.getLength();
+ if (nLen <= 0)
+ // Ignore empty string.
+ return;
+
+ if (nPrec == SvNumberFormatter::UNLIMITED_PRECISION && mnPrecision < mnMaxGeneralPrecision)
+ {
+ if (nFormat % SV_COUNTRY_LANGUAGE_OFFSET)
+ {
+ // For some reason we couldn't obtain a precision from the
+ // format, retry with simple number string.
+ aSep = ".";
+ aString = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_F, nCellPrecision, '.', true);
+ nLen = aString.getLength();
+ }
+ sal_Int32 nSep = aString.indexOf( aSep);
+ if (nSep != -1)
+ nPrec = aString.getLength() - nSep - 1;
+
+ }
+
+ if (nPrec != SvNumberFormatter::UNLIMITED_PRECISION && nPrec > mnPrecision)
+ mnPrecision = nPrec;
+
if (mnPrecision)
{ // less than mnPrecision in string => widen it
// more => shorten it
- OUString aSep = mpFormatter->GetFormatDecimalSep(nFormat);
sal_Int32 nTmp = aString.indexOf(aSep);
if ( nTmp == -1 )
nLen += mnPrecision + aSep.getLength();
@@ -2732,15 +2788,27 @@ class MaxNumStringLenHandler
}
}
+ // Enlarge for sign if necessary. Bear in mind that
+ // GetMaxNumberStringLen() is for determining dBase decimal field width
+ // and precision where the overall field width must include the sign.
+ // Fitting -1 into "#.##" (width 4, 2 decimals) does not work.
+ if (mbHaveSigned && fVal >= 0.0)
+ ++nLen;
+
if (mnMaxLen < nLen)
mnMaxLen = nLen;
}
public:
- MaxNumStringLenHandler(const ScColumn& rColumn, sal_uInt16 nPrecision) :
+ MaxNumStringLenHandler(const ScColumn& rColumn, sal_uInt16 nMaxGeneralPrecision) :
mrColumn(rColumn), mpFormatter(rColumn.GetDoc().GetFormatTable()),
- mnMaxLen(0), mnPrecision(nPrecision)
+ mnMaxLen(0), mnPrecision(0), mnMaxGeneralPrecision(nMaxGeneralPrecision),
+ mbHaveSigned(false)
{
+ // Limit the decimals passed to doubleToUString().
+ // Also, the dBaseIII maximum precision is 15.
+ if (mnMaxGeneralPrecision > 15)
+ mnMaxGeneralPrecision = 15;
}
void operator() (size_t nRow, double fVal)
@@ -2765,12 +2833,8 @@ public:
sal_Int32 ScColumn::GetMaxNumberStringLen(
sal_uInt16& nPrecision, SCROW nRowStart, SCROW nRowEnd ) const
{
- nPrecision = pDocument->GetDocOptions().GetStdPrecision();
- if ( nPrecision == SvNumberFormatter::UNLIMITED_PRECISION )
- // In case of unlimited precision, use 2 instead.
- nPrecision = 2;
-
- MaxNumStringLenHandler aFunc(*this, nPrecision);
+ sal_uInt16 nMaxGeneralPrecision = pDocument->GetDocOptions().GetStdPrecision();
+ MaxNumStringLenHandler aFunc(*this, nMaxGeneralPrecision);
sc::ParseFormulaNumeric(maCells.begin(), maCells, nRowStart, nRowEnd, aFunc);
nPrecision = aFunc.getPrecision();
return aFunc.getMaxLen();
diff --git a/sc/source/ui/docshell/docsh8.cxx b/sc/source/ui/docshell/docsh8.cxx
index b7d049a9b52d..e35989354207 100644
--- a/sc/source/ui/docshell/docsh8.cxx
+++ b/sc/source/ui/docshell/docsh8.cxx
@@ -551,6 +551,7 @@ void lcl_GetColumnTypes(
break;
case 'N' :
nDbType = sdbc::DataType::DECIMAL;
+ bTypeDefined = true;
break;
}
if ( bTypeDefined && !nFieldLen && nToken > 2 )
@@ -562,6 +563,8 @@ void lcl_GetColumnTypes(
if ( CharClass::isAsciiNumeric(aTmp) )
{
nPrecision = aTmp.toInt32();
+ if (nPrecision && nFieldLen < nPrecision+1)
+ nFieldLen = nPrecision + 1; // include decimal separator
bPrecDefined = true;
}
}
@@ -662,14 +665,52 @@ void lcl_GetColumnTypes(
if ( nPrec > 15 )
nPrec = 15;
if ( bPrecDefined && nPrecision != nPrec )
- { // Adjust length to predefined precision.
- if ( nPrecision )
- nLen = nLen + ( nPrecision - nPrec );
+ {
+ if (nPrecision < nPrec)
+ {
+ // This is a hairy case. User defined nPrecision but a
+ // number format has more precision. Modifying a dBase
+ // field may as well render the resulting file useless for
+ // an application that relies on its defined structure,
+ // especially if we are resaving an already existing file.
+ // So who's right, the user who (or the loaded file that)
+ // defined the field, or the user who applied the format?
+ // Commit f59e350d1733125055f1144f8b3b1b0a46f6d1ca gave the
+ // format a higher priority, which is debatable.
+ SAL_WARN( "sc", "lcl_GetColumnTypes: conflicting dBase field precision for "
+ << aFieldName << " (" << nPrecision << "<" << nPrec << ")");
+
+ // Adjust length to larger predefined integer part. There
+ // may be a reason that the field was prepared for larger
+ // numbers.
+ if (nFieldLen - nPrecision > nLen - nPrec)
+ nLen = nFieldLen - (nPrecision ? nPrecision+1 : 0) + 1 + nPrec;
+ // And override precision.
+ nPrecision = nPrec;
+ }
+ else
+ {
+ // Adjust length to predefined precision.
+ if ( nPrecision )
+ nLen = nLen + ( nPrecision - nPrec );
+ else
+ nLen -= nPrec+1; // also remove the decimal separator
+ }
+ }
+ if (nFieldLen < nLen)
+ {
+ if (!bTypeDefined)
+ nFieldLen = nLen;
else
- nLen -= nPrec+1; // also remove the decimal separator
+ {
+ // Again a hairy case and conflict. Furthermore, the
+ // larger overall length may be a result of only a higher
+ // precision obtained from formats.
+ SAL_WARN( "sc", "lcl_GetColumnTypes: conflicting dBase field length for "
+ << aFieldName << " (" << nFieldLen << "<" << nLen << ")");
+ nFieldLen = nLen;
+ }
}
- if ( nLen > nFieldLen && !bTypeDefined )
- nFieldLen = nLen;
if ( !bPrecDefined )
nPrecision = nPrec;
if ( nFieldLen == 0 )