[Libreoffice-commits] core.git: 2 commits - sc/source

Eike Rathke erack at redhat.com
Mon Mar 31 13:31:53 PDT 2014


 sc/source/core/data/column3.cxx  |  108 +++++++++++++++++++++++++++++++--------
 sc/source/ui/docshell/docsh8.cxx |   55 +++++++++++++++++--
 2 files changed, 134 insertions(+), 29 deletions(-)

New commits:
commit e65141e93a540fc9fb4343ee65a5a7da7e3b1769
Author: Eike Rathke <erack at redhat.com>
Date:   Mon Mar 31 19:28:31 2014 +0200

    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

diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index bd1cebd..86ee9a2 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 b7d049a9..e359893 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 )
commit 94a10aa1d6af3520f4c4f31337a040eb42e8c39e
Author: Eike Rathke <erack at redhat.com>
Date:   Fri Mar 28 20:18:15 2014 +0100

    one more comment
    
    Change-Id: I473efb4c8233eed1a3a00ce9ab8c1fa84583e2a4

diff --git a/sc/source/ui/docshell/docsh8.cxx b/sc/source/ui/docshell/docsh8.cxx
index 989b7f4..b7d049a9 100644
--- a/sc/source/ui/docshell/docsh8.cxx
+++ b/sc/source/ui/docshell/docsh8.cxx
@@ -677,7 +677,7 @@ void lcl_GetColumnTypes(
             else if ( nFieldLen > 19 )
                 nFieldLen = 19;     // dBaseIII numeric field length limit: 19
             if ( nPrecision && nFieldLen < nPrecision + 2 )
-                nFieldLen = nPrecision + 2;     // 0. muss mit reinpassen
+                nFieldLen = nPrecision + 2;     // 0. must fit into
             // 538 MUST: Sdb internal representation adds 2 to the field length!
             // To give the user what he wants we must substract it here.
              //! CAVEAT! There is no way to define a numeric field with a length


More information about the Libreoffice-commits mailing list