diff options
-rw-r--r-- | sc/source/core/data/column3.cxx | 100 | ||||
-rw-r--r-- | sc/source/ui/docshell/docsh8.cxx | 112 |
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; |