summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sc/source/core/data/column3.cxx100
-rw-r--r--sc/source/ui/docshell/docsh8.cxx112
2 files changed, 162 insertions, 50 deletions
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 4bd6a5a99d43..3630be3e2239 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -1906,15 +1906,19 @@ xub_StrLen ScColumn::GetMaxNumberStringLen(
sal_uInt16& nPrecision, SCROW nRowStart, SCROW nRowEnd ) const
{
xub_StrLen nStringLen = 0;
- nPrecision = pDocument->GetDocOptions().GetStdPrecision();
- if ( nPrecision == SvNumberFormatter::UNLIMITED_PRECISION )
- // In case of unlimited precision, use 2 instead.
- nPrecision = 2;
+ nPrecision = 0;
if ( !maItems.empty() )
{
OUString aString;
+ String aSep;
SvNumberFormatter* pNumFmt = pDocument->GetFormatTable();
+ sal_uInt16 nMaxGeneralPrecision = pDocument->GetDocOptions().GetStdPrecision();
+ // Limit the decimals passed to doubleToUString().
+ // Also, the dBaseIII maximum precision is 15.
+ if (nMaxGeneralPrecision > 15)
+ nMaxGeneralPrecision = 15;
+ bool bHaveSigned = false;
SCSIZE nIndex;
SCROW nRow;
Search( nRowStart, nIndex );
@@ -1926,16 +1930,33 @@ xub_StrLen ScColumn::GetMaxNumberStringLen(
if ( eType == CELLTYPE_VALUE || (eType == CELLTYPE_FORMULA
&& aCell.mpFormula->IsValue()) )
{
- sal_uLong nFormat = (sal_uLong) ((SfxUInt32Item*) GetAttr(
- nRow, ATTR_VALUE_FORMAT ))->GetValue();
- ScCellFormat::GetInputString(aCell, nFormat, aString, *pNumFmt, pDocument);
- xub_StrLen nLen = aString.getLength();
- if ( nLen )
+ do
{
- if ( nFormat )
+ sal_uInt16 nCellPrecision = nMaxGeneralPrecision;
+ if (eType == CELLTYPE_FORMULA)
{
+ // 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 (nPrecision)
+ nCellPrecision = nPrecision;
+ else
+ nCellPrecision = (nMaxGeneralPrecision >= 15) ? 4 : nMaxGeneralPrecision;
+ }
+
+ double fVal = aCell.getValue();
+ if (!bHaveSigned && fVal < 0.0)
+ bHaveSigned = true;
+
+ sal_uInt16 nPrec;
+ sal_uLong nFormat = (sal_uLong) ((SfxUInt32Item*) GetAttr(
+ nRow, ATTR_VALUE_FORMAT ))->GetValue();
+ if (nFormat % SV_COUNTRY_LANGUAGE_OFFSET)
+ {
+ aSep = pNumFmt->GetFormatDecimalSep(nFormat);
+ ScCellFormat::GetInputString(aCell, nFormat, aString, *pNumFmt, pDocument);
const SvNumberformat* pEntry = pNumFmt->GetEntry( nFormat );
- sal_uInt16 nPrec;
if (pEntry)
{
bool bThousand, bNegRed;
@@ -1944,14 +1965,54 @@ xub_StrLen ScColumn::GetMaxNumberStringLen(
}
else
nPrec = pNumFmt->GetFormatPrecision( nFormat );
+ }
+ else
+ {
+ if (nPrecision >= nMaxGeneralPrecision)
+ break; // early bail out for nothing changes here
+
+ if (!fVal)
+ {
+ // 0 doesn't change precision, but set a maximum length if none yet.
+ if (!nStringLen)
+ nStringLen = 1;
+ break;
+ }
+
+ // 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;
+ }
+
+ sal_Int32 nLen = aString.getLength();
+ if (nLen <= 0)
+ // Ignore empty string.
+ break;
+
+ if (nPrec == SvNumberFormatter::UNLIMITED_PRECISION && nPrecision < nMaxGeneralPrecision)
+ {
+ 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 > nPrecision )
- nPrecision = nPrec;
}
+
+ if (nPrec != SvNumberFormatter::UNLIMITED_PRECISION && nPrec > nPrecision)
+ nPrecision = nPrec;
+
if ( nPrecision )
{ // less than nPrecision in string => widen it
// more => shorten it
- String aSep = pNumFmt->GetFormatDecimalSep( nFormat );
sal_Int32 nTmp = aString.indexOf( aSep );
if ( nTmp == -1 )
nLen += nPrecision + aSep.Len();
@@ -1964,9 +2025,18 @@ xub_StrLen ScColumn::GetMaxNumberStringLen(
// nPrecision < nTmp : nLen - Diff
}
}
+
+ // 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 (bHaveSigned && fVal >= 0.0)
+ ++nLen;
+
if ( nStringLen < nLen )
nStringLen = nLen;
- }
+
+ } while (0);
}
nIndex++;
}
diff --git a/sc/source/ui/docshell/docsh8.cxx b/sc/source/ui/docshell/docsh8.cxx
index 2eb7b253dd7d..5c8ee277dceb 100644
--- a/sc/source/ui/docshell/docsh8.cxx
+++ b/sc/source/ui/docshell/docsh8.cxx
@@ -519,8 +519,8 @@ void lcl_GetColumnTypes(
sal_Int32 nDbType = sdbc::DataType::SQLNULL;
String aFieldName, aString;
- // Feldname[,Type[,Width[,Prec]]]
- // Typ etc.: L; D; C[,W]; N[,W[,P]]
+ // Fieldname[,Type[,Width[,Prec]]]
+ // Type etc.: L; D; C[,W]; N[,W[,P]]
if ( bHasFieldNames )
{
aString = pDoc->GetString(nCol, nFirstRow, nTab);
@@ -535,29 +535,30 @@ void lcl_GetColumnTypes(
case 'L' :
nDbType = sdbc::DataType::BIT;
nFieldLen = 1;
- bTypeDefined = sal_True;
- bPrecDefined = sal_True;
+ bTypeDefined = true;
+ bPrecDefined = true;
break;
case 'D' :
nDbType = sdbc::DataType::DATE;
nFieldLen = 8;
- bTypeDefined = sal_True;
- bPrecDefined = sal_True;
+ bTypeDefined = true;
+ bPrecDefined = true;
break;
case 'M' :
nDbType = sdbc::DataType::LONGVARCHAR;
nFieldLen = 10;
- bTypeDefined = sal_True;
- bPrecDefined = sal_True;
- bHasMemo = sal_True;
+ bTypeDefined = true;
+ bPrecDefined = true;
+ bHasMemo = true;
break;
case 'C' :
nDbType = sdbc::DataType::VARCHAR;
- bTypeDefined = sal_True;
- bPrecDefined = sal_True;
+ bTypeDefined = true;
+ bPrecDefined = true;
break;
case 'N' :
nDbType = sdbc::DataType::DECIMAL;
+ bTypeDefined = true;
break;
}
if ( bTypeDefined && !nFieldLen && nToken > 2 )
@@ -569,6 +570,8 @@ void lcl_GetColumnTypes(
if ( CharClass::isAsciiNumeric(aTmp) )
{
nPrecision = aTmp.ToInt32();
+ if (nPrecision && nFieldLen < nPrecision+1)
+ nFieldLen = nPrecision + 1; // include decimal separator
bPrecDefined = sal_True;
}
}
@@ -577,11 +580,12 @@ void lcl_GetColumnTypes(
else
aFieldName = aString;
- // Feldnamen pruefen und ggbf. gueltigen Feldnamen erzeugen.
- // Erstes Zeichen muss Buchstabe sein,
- // weitere nur alphanumerisch und Unterstrich erlaubt,
- // "_DBASELOCK" ist reserviert (obsolet weil erstes Zeichen kein Buchstabe),
- // keine doppelten Namen.
+ // Check field name and generate valid field name if necessary.
+ // First character has to be alphabetical, subsequent characters
+ // have to be alphanumerical or underscore.
+ // "_DBASELOCK" is reserved (obsolete because first character is
+ // not alphabetical).
+ // No duplicated names.
if ( !IsAsciiAlpha( aFieldName.GetChar(0) ) )
aFieldName.Insert( 'N', 0 );
String aTmpStr;
@@ -598,7 +602,7 @@ void lcl_GetColumnTypes(
aFieldName.Erase( 10 );
if (!aFieldNames.insert(aFieldName).second)
- { // doppelter Feldname, numerisch erweitern
+ { // Duplicated field name, append numeric suffix.
sal_uInt16 nSub = 1;
String aFixPart( aFieldName );
do
@@ -619,7 +623,7 @@ void lcl_GetColumnTypes(
}
if ( !bTypeDefined )
- { // Feldtyp
+ { // Field type.
ScRefCellValue aCell;
aCell.assign(*pDoc, ScAddress(nCol, nFirstDataRow, nTab));
if (aCell.isEmpty() || aCell.hasString())
@@ -649,61 +653,99 @@ void lcl_GetColumnTypes(
}
bool bSdbLenAdjusted = false;
bool bSdbLenBad = false;
- // Feldlaenge
+ // Field length.
if ( nDbType == sdbc::DataType::VARCHAR && !nFieldLen )
- { // maximale Feldbreite bestimmen
+ { // Determine maximum field width.
nFieldLen = pDoc->GetMaxStringLen( nTab, nCol, nFirstDataRow,
nLastRow, eCharSet );
if ( nFieldLen == 0 )
nFieldLen = 1;
}
else if ( nDbType == sdbc::DataType::DECIMAL )
- { // maximale Feldbreite und Nachkommastellen bestimmen
+ { // Determine maximum field width and precision.
xub_StrLen nLen;
sal_uInt16 nPrec;
nLen = pDoc->GetMaxNumberStringLen( nPrec, nTab, nCol,
nFirstDataRow, nLastRow );
- // dBaseIII Limit Nachkommastellen: 15
+ // dBaseIII precision limit: 15
if ( nPrecision > 15 )
nPrecision = 15;
if ( nPrec > 15 )
nPrec = 15;
if ( bPrecDefined && nPrecision != nPrec )
- { // Laenge auf vorgegebene Nachkommastellen anpassen
- if ( nPrecision )
- nLen = sal::static_int_cast<xub_StrLen>( 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 = sal::static_int_cast<xub_StrLen>(nFieldLen - (nPrecision ? nPrecision+1 : 0) + 1 + nPrec);
+ // And override precision.
+ nPrecision = nPrec;
+ }
else
- nLen -= nPrec+1; // auch den . mit raus
+ {
+ // Adjust length to predefined precision.
+ if ( nPrecision )
+ nLen = sal::static_int_cast<xub_StrLen>(nLen + ( nPrecision - nPrec ));
+ else
+ nLen -= nPrec+1; // also remove the decimal separator
+ }
+ }
+ if (nFieldLen < nLen)
+ {
+ if (!bTypeDefined)
+ nFieldLen = nLen;
+ else
+ {
+ // 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 )
nFieldLen = 1;
else if ( nFieldLen > 19 )
- nFieldLen = 19; // dBaseIII Limit Feldlaenge numerisch: 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
//! of 1 and no decimals!
if ( nFieldLen == 1 && nPrecision == 0 )
- bSdbLenBad = sal_True;
+ bSdbLenBad = true;
nFieldLen = SvDbaseConverter::ConvertPrecisionToOdbc( nFieldLen, nPrecision );
- bSdbLenAdjusted = sal_True;
+ bSdbLenAdjusted = true;
}
if ( nFieldLen > 254 )
{
if ( nDbType == sdbc::DataType::VARCHAR )
- { // zu lang fuer normales Textfeld => Memofeld
+ { // Too long for a normal text field => memo field.
nDbType = sdbc::DataType::LONGVARCHAR;
nFieldLen = 10;
- bHasMemo = sal_True;
+ bHasMemo = true;
}
else
- nFieldLen = 254; // dumm gelaufen..
+ nFieldLen = 254; // bad luck..
}
pColNames[nField] = aFieldName;