tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / docshell / docsh8.cxx
blob6df8ec07d8e896c06665c2561127253f54e507a8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #include <vcl/errinf.hxx>
23 #include <tools/urlobj.hxx>
24 #include <svl/converter.hxx>
25 #include <svl/numformat.hxx>
26 #include <comphelper/processfactory.hxx>
27 #include <comphelper/propertysequence.hxx>
28 #include <comphelper/types.hxx>
29 #include <ucbhelper/content.hxx>
30 #include <svx/txenctab.hxx>
31 #include <unotools/sharedunocomponent.hxx>
32 #include <unotools/charclass.hxx>
33 #include <rtl/character.hxx>
34 #include <rtl/tencinfo.h>
35 #include <sal/log.hxx>
36 #include <osl/diagnose.h>
37 #include <comphelper/diagnose_ex.hxx>
38 #include <o3tl/string_view.hxx>
40 #include <com/sun/star/sdb/CommandType.hpp>
41 #include <com/sun/star/sdbc/DataType.hpp>
42 #include <com/sun/star/sdbc/SQLException.hpp>
43 #include <com/sun/star/sdbc/XConnection.hpp>
44 #include <com/sun/star/sdbc/DriverManager.hpp>
45 #include <com/sun/star/sdbc/XResultSetUpdate.hpp>
46 #include <com/sun/star/sdbc/XRow.hpp>
47 #include <com/sun/star/sdbc/XRowSet.hpp>
48 #include <com/sun/star/sdbc/XRowUpdate.hpp>
49 #include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
50 #include <com/sun/star/sdbcx/XAppend.hpp>
51 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
52 #include <com/sun/star/sdbcx/XDataDefinitionSupplier.hpp>
53 #include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
54 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
55 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
56 #include <com/sun/star/beans/XPropertySet.hpp>
57 #include <com/sun/star/ucb/NameClash.hpp>
58 #include <com/sun/star/ucb/TransferInfo.hpp>
59 #include <com/sun/star/ucb/XCommandInfo.hpp>
61 #include <scerrors.hxx>
62 #include <docsh.hxx>
63 #include <progress.hxx>
64 #include <editutil.hxx>
65 #include <cellform.hxx>
66 #include <dbdocutl.hxx>
67 #include <dociter.hxx>
68 #include <globstr.hrc>
69 #include <scresid.hxx>
70 #include <svl/zformat.hxx>
71 #include <svl/intitem.hxx>
72 #include <patattr.hxx>
73 #include <scitems.hxx>
74 #include <docpool.hxx>
75 #include <segmenttree.hxx>
76 #include <docparam.hxx>
77 #include <cellvalue.hxx>
79 #include <unordered_set>
80 #include <vector>
82 using namespace com::sun::star;
83 using ::std::vector;
85 #if HAVE_FEATURE_DBCONNECTIVITY
87 constexpr OUString SC_SERVICE_ROWSET = u"com.sun.star.sdb.RowSet"_ustr;
89 //! move to a header file?
90 constexpr OUString SC_DBPROP_ACTIVECONNECTION = u"ActiveConnection"_ustr;
91 constexpr OUString SC_DBPROP_COMMAND = u"Command"_ustr;
92 constexpr OUString SC_DBPROP_COMMANDTYPE = u"CommandType"_ustr;
93 constexpr OUStringLiteral SC_DBPROP_PROPCHANGE_NOTIFY = u"PropertyChangeNotificationEnabled";
95 constexpr OUString SC_DBPROP_NAME = u"Name"_ustr;
96 constexpr OUStringLiteral SC_DBPROP_TYPE = u"Type";
97 constexpr OUStringLiteral SC_DBPROP_PRECISION = u"Precision";
98 constexpr OUStringLiteral SC_DBPROP_SCALE = u"Scale";
100 constexpr OUString SC_DBPROP_EXTENSION = u"Extension"_ustr;
101 constexpr OUString SC_DBPROP_CHARSET = u"CharSet"_ustr;
103 namespace
105 ErrCode lcl_getDBaseConnection(uno::Reference<sdbc::XDriverManager2>& _rDrvMgr, uno::Reference<sdbc::XConnection>& _rConnection, OUString& _rTabName, std::u16string_view rFullFileName, rtl_TextEncoding eCharSet)
107 INetURLObject aURL;
108 aURL.SetSmartProtocol( INetProtocol::File );
109 aURL.SetSmartURL( rFullFileName );
110 _rTabName = aURL.getBase( INetURLObject::LAST_SEGMENT, true,
111 INetURLObject::DecodeMechanism::Unambiguous );
112 OUString aExtension = aURL.getExtension();
113 aURL.removeSegment();
114 aURL.removeFinalSlash();
115 OUString aPath = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
116 const uno::Reference<uno::XComponentContext>& xContext = comphelper::getProcessComponentContext();
118 _rDrvMgr.set( sdbc::DriverManager::create( xContext ) );
120 // get connection
122 const OUString aConnUrl{"sdbc:dbase:" + aPath};
124 // sdbc:dbase is based on the css.sdbc.FILEConnectionProperties UNOIDL service, so we can
125 // transport the raw rtl_TextEncoding value instead of having to translate it into an IANA
126 // character set name string (which might not exist for certain eCharSet values, like
127 // RTL_TEXTENCODING_MS_950):
128 uno::Sequence<beans::PropertyValue> aProps( comphelper::InitPropertySequence({
129 { SC_DBPROP_EXTENSION, uno::Any(aExtension) },
130 { SC_DBPROP_CHARSET, uno::Any(eCharSet) }
131 }));
133 _rConnection = _rDrvMgr->getConnectionWithInfo( aConnUrl, aProps );
134 return ERRCODE_NONE;
138 #endif // HAVE_FEATURE_DBCONNECTIVITY
140 // MoveFile/KillFile/IsDocument: similar to SfxContentHelper
142 bool ScDocShell::MoveFile( const INetURLObject& rSourceObj, const INetURLObject& rDestObj )
144 bool bMoveData = true;
145 bool bRet = true, bKillSource = false;
146 if ( rSourceObj.GetProtocol() != rDestObj.GetProtocol() )
148 bMoveData = false;
149 bKillSource = true;
151 OUString aName = rDestObj.getName();
152 INetURLObject aDestPathObj = rDestObj;
153 aDestPathObj.removeSegment();
154 aDestPathObj.setFinalSlash();
158 ::ucbhelper::Content aDestPath( aDestPathObj.GetMainURL(INetURLObject::DecodeMechanism::NONE),
159 uno::Reference< css::ucb::XCommandEnvironment >(),
160 comphelper::getProcessComponentContext() );
161 uno::Reference< css::ucb::XCommandInfo > xInfo = aDestPath.getCommands();
162 OUString aTransferName = u"transfer"_ustr;
163 if ( xInfo->hasCommandByName( aTransferName ) )
165 aDestPath.executeCommand( aTransferName, uno::Any(
166 css::ucb::TransferInfo( bMoveData, rSourceObj.GetMainURL(INetURLObject::DecodeMechanism::NONE), aName,
167 css::ucb::NameClash::ERROR ) ) );
169 else
171 OSL_FAIL( "transfer command not available" );
174 catch( uno::Exception& )
176 // ucb may throw different exceptions on failure now
177 bRet = false;
180 if ( bKillSource )
181 KillFile( rSourceObj );
183 return bRet;
186 bool ScDocShell::KillFile( const INetURLObject& rURL )
188 bool bRet = true;
191 ::ucbhelper::Content aCnt( rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),
192 uno::Reference< css::ucb::XCommandEnvironment >(),
193 comphelper::getProcessComponentContext() );
194 aCnt.executeCommand( u"delete"_ustr, css::uno::Any( true ) );
196 catch( uno::Exception& )
198 // ucb may throw different exceptions on failure now
199 bRet = false;
202 return bRet;
205 bool ScDocShell::IsDocument( const INetURLObject& rURL )
207 bool bRet = false;
210 ::ucbhelper::Content aCnt( rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),
211 uno::Reference< css::ucb::XCommandEnvironment >(),
212 comphelper::getProcessComponentContext() );
213 bRet = aCnt.isDocument();
215 catch( uno::Exception& )
217 // ucb may throw different exceptions on failure now - warning only
218 TOOLS_WARN_EXCEPTION( "sc", "Any other exception" );
221 return bRet;
224 #if HAVE_FEATURE_DBCONNECTIVITY
226 static void lcl_setScalesToColumns(ScDocument& rDoc, const vector<tools::Long>& rScales)
228 SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
229 if (!pFormatter)
230 return;
232 SCCOL nColCount = static_cast<SCCOL>(rScales.size());
233 for (SCCOL i = 0; i < nColCount; ++i)
235 if (rScales[i] < 0)
236 continue;
238 sal_uInt32 nOldFormat = rDoc.GetNumberFormat(i, 0, 0);
239 const SvNumberformat* pOldEntry = pFormatter->GetEntry(nOldFormat);
240 if (!pOldEntry)
241 continue;
243 LanguageType eLang = pOldEntry->GetLanguage();
244 bool bThousand, bNegRed;
245 sal_uInt16 nPrecision, nLeading;
246 pOldEntry->GetFormatSpecialInfo(bThousand, bNegRed, nPrecision, nLeading);
248 nPrecision = static_cast<sal_uInt16>(rScales[i]);
249 OUString aNewPicture = pFormatter->GenerateFormat(nOldFormat, eLang,
250 bThousand, bNegRed, nPrecision, nLeading);
252 sal_uInt32 nNewFormat = pFormatter->GetEntryKey(aNewPicture, eLang);
253 if (nNewFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
255 sal_Int32 nErrPos = 0;
256 SvNumFormatType nNewType = SvNumFormatType::ALL;
257 bool bOk = pFormatter->PutEntry(
258 aNewPicture, nErrPos, nNewType, nNewFormat, eLang);
260 if (!bOk)
261 continue;
264 ScPatternAttr aNewAttrs(rDoc.getCellAttributeHelper());
265 SfxItemSet& rSet = aNewAttrs.GetItemSet();
266 rSet.Put( SfxUInt32Item(ATTR_VALUE_FORMAT, nNewFormat) );
267 rDoc.ApplyPatternAreaTab(i, 0, i, rDoc.MaxRow(), 0, aNewAttrs);
271 #endif // HAVE_FEATURE_DBCONNECTIVITY
273 ErrCode ScDocShell::DBaseImport( const OUString& rFullFileName, rtl_TextEncoding eCharSet,
274 std::map<SCCOL, ScColWidthParam>& aColWidthParam, ScFlatBoolRowSegments& rRowHeightsRecalc )
276 #if !HAVE_FEATURE_DBCONNECTIVITY
277 (void) rFullFileName;
278 (void) eCharSet;
279 (void) aColWidthParam;
280 (void) rRowHeightsRecalc;
282 return ERRCODE_IO_GENERAL;
283 #else
285 ErrCode nErr = ERRCODE_NONE;
289 sal_Int32 nColCount = 0;
290 OUString aTabName;
291 uno::Reference<sdbc::XDriverManager2> xDrvMan;
292 uno::Reference<sdbc::XConnection> xConnection;
293 ErrCode nRet = lcl_getDBaseConnection(xDrvMan,xConnection,aTabName,rFullFileName,eCharSet);
294 if ( !xConnection.is() || !xDrvMan.is() )
295 return nRet;
296 ::utl::DisposableComponent aConnectionHelper(xConnection);
298 ScProgress aProgress( this, ScResId( STR_LOAD_DOC ), 0, true );
299 uno::Reference<lang::XMultiServiceFactory> xFactory = comphelper::getProcessServiceFactory();
300 uno::Reference<sdbc::XRowSet> xRowSet( xFactory->createInstance(SC_SERVICE_ROWSET),
301 uno::UNO_QUERY);
302 ::utl::DisposableComponent aRowSetHelper(xRowSet);
303 uno::Reference<beans::XPropertySet> xRowProp( xRowSet, uno::UNO_QUERY );
304 OSL_ENSURE( xRowProp.is(), "can't get RowSet" );
305 if (!xRowProp.is()) return SCERR_IMPORT_CONNECT;
307 xRowProp->setPropertyValue( SC_DBPROP_ACTIVECONNECTION, uno::Any(xConnection) );
309 xRowProp->setPropertyValue( SC_DBPROP_COMMANDTYPE, uno::Any(sdb::CommandType::TABLE) );
311 xRowProp->setPropertyValue( SC_DBPROP_COMMAND, uno::Any(aTabName) );
313 xRowProp->setPropertyValue( SC_DBPROP_PROPCHANGE_NOTIFY, uno::Any(false) );
315 xRowSet->execute();
317 uno::Reference<sdbc::XResultSetMetaData> xMeta;
318 uno::Reference<sdbc::XResultSetMetaDataSupplier> xMetaSupp( xRowSet, uno::UNO_QUERY );
319 if ( xMetaSupp.is() )
320 xMeta = xMetaSupp->getMetaData();
321 if ( xMeta.is() )
322 nColCount = xMeta->getColumnCount(); // this is the number of real columns
324 if ( nColCount > m_pDocument->MaxCol()+1 )
326 nColCount = m_pDocument->MaxCol()+1;
327 nErr = SCWARN_IMPORT_COLUMN_OVERFLOW; // warning
330 uno::Reference<sdbc::XRow> xRow( xRowSet, uno::UNO_QUERY );
331 OSL_ENSURE( xRow.is(), "can't get Row" );
332 if (!xRow.is()) return SCERR_IMPORT_CONNECT;
334 // currency flag is not needed for dBase
335 uno::Sequence<sal_Int32> aColTypes( nColCount ); // column types
336 sal_Int32* pTypeArr = aColTypes.getArray();
337 for (sal_Int32 i=0; i<nColCount; i++)
338 pTypeArr[i] = xMeta->getColumnType( i+1 );
340 // read column names
341 //! add type descriptions
343 aProgress.SetState( 0 );
345 vector<tools::Long> aScales(nColCount, -1);
346 for (sal_Int32 i=0; i<nColCount; i++)
348 OUString aHeader = xMeta->getColumnLabel( i+1 );
350 switch ( pTypeArr[i] )
352 case sdbc::DataType::BIT:
353 aHeader += ",L";
354 break;
355 case sdbc::DataType::DATE:
356 aHeader += ",D";
357 break;
358 case sdbc::DataType::LONGVARCHAR:
359 aHeader += ",M";
360 break;
361 case sdbc::DataType::VARCHAR:
362 aHeader += ",C," + OUString::number( xMeta->getColumnDisplaySize( i+1 ) );
363 break;
364 case sdbc::DataType::DECIMAL:
366 tools::Long nPrec = xMeta->getPrecision( i+1 );
367 tools::Long nScale = xMeta->getScale( i+1 );
368 aHeader += ",N," +
369 OUString::number(
370 SvDbaseConverter::ConvertPrecisionToDbase(
371 nPrec, nScale ) ) +
372 "," +
373 OUString::number( nScale );
374 aScales[i] = nScale;
376 break;
379 m_pDocument->SetString( static_cast<SCCOL>(i), 0, 0, aHeader );
382 lcl_setScalesToColumns(*m_pDocument, aScales);
384 SCROW nRow = 1; // 0 is column titles
385 bool bEnd = false;
386 while ( !bEnd && xRowSet->next() )
388 if (nRow <= m_pDocument->MaxRow())
390 bool bSimpleRow = true;
391 SCCOL nCol = 0;
392 for (sal_Int32 i=0; i<nColCount; i++)
394 ScDatabaseDocUtil::StrData aStrData;
395 ScDatabaseDocUtil::PutData( *m_pDocument, nCol, nRow, 0,
396 xRow, i+1, pTypeArr[i], false,
397 &aStrData );
399 if (aStrData.mnStrLength > aColWidthParam[nCol].mnMaxTextLen)
401 aColWidthParam[nCol].mnMaxTextLen = aStrData.mnStrLength;
402 aColWidthParam[nCol].mnMaxTextRow = nRow;
405 if (!aStrData.mbSimpleText)
407 bSimpleRow = false;
408 aColWidthParam[nCol].mbSimpleText = false;
411 ++nCol;
413 if (!bSimpleRow)
414 rRowHeightsRecalc.setTrue(nRow, nRow);
415 ++nRow;
417 else // past the end of the spreadsheet
419 bEnd = true; // don't continue
420 nErr = SCWARN_IMPORT_RANGE_OVERFLOW; // warning message
424 catch ( sdbc::SQLException& )
426 nErr = SCERR_IMPORT_CONNECT;
428 catch ( uno::Exception& )
430 TOOLS_WARN_EXCEPTION( "sc", "Unexpected exception in database");
431 nErr = ERRCODE_IO_GENERAL;
434 return nErr;
435 #endif // HAVE_FEATURE_DBCONNECTIVITY
438 #if HAVE_FEATURE_DBCONNECTIVITY
440 namespace {
442 void lcl_GetColumnTypes(
443 ScDocShell& rDocShell, const ScRange& rDataRange, bool bHasFieldNames,
444 OUString* pColNames, sal_Int32* pColTypes, sal_Int32* pColLengths,
445 sal_Int32* pColScales, bool& bHasMemo, rtl_TextEncoding eCharSet )
447 ScDocument& rDoc = rDocShell.GetDocument();
448 ScInterpreterContext& rContext = rDoc.GetNonThreadedContext();
450 SCTAB nTab = rDataRange.aStart.Tab();
451 SCCOL nFirstCol = rDataRange.aStart.Col();
452 SCROW nFirstRow = rDataRange.aStart.Row();
453 SCCOL nLastCol = rDataRange.aEnd.Col();
454 SCROW nLastRow = rDataRange.aEnd.Row();
456 typedef std::unordered_set<OUString> StrSetType;
457 StrSetType aFieldNames;
459 tools::Long nField = 0;
460 SCROW nFirstDataRow = ( bHasFieldNames ? nFirstRow + 1 : nFirstRow );
461 for ( SCCOL nCol = nFirstCol; nCol <= nLastCol; nCol++ )
463 bool bTypeDefined = false;
464 bool bPrecDefined = false;
465 sal_Int32 nFieldLen = 0;
466 sal_Int32 nPrecision = 0;
467 sal_Int32 nDbType = sdbc::DataType::SQLNULL;
468 OUString aFieldName;
470 // Fieldname[,Type[,Width[,Prec]]]
471 // Type etc.: L; D; C[,W]; N[,W[,P]]
472 if ( bHasFieldNames )
474 OUString aString {rDoc.GetString(nCol, nFirstRow, nTab).toAsciiUpperCase()};
475 sal_Int32 nIdx {0};
476 aFieldName = aString.getToken( 0, ',', nIdx);
477 if ( nIdx>0 )
479 aString = aString.replaceAll(" ", "");
480 switch ( o3tl::getToken(aString, 0, ',', nIdx )[0] )
482 case 'L' :
483 nDbType = sdbc::DataType::BIT;
484 nFieldLen = 1;
485 bTypeDefined = true;
486 bPrecDefined = true;
487 break;
488 case 'D' :
489 nDbType = sdbc::DataType::DATE;
490 nFieldLen = 8;
491 bTypeDefined = true;
492 bPrecDefined = true;
493 break;
494 case 'M' :
495 nDbType = sdbc::DataType::LONGVARCHAR;
496 nFieldLen = 10;
497 bTypeDefined = true;
498 bPrecDefined = true;
499 bHasMemo = true;
500 break;
501 case 'C' :
502 nDbType = sdbc::DataType::VARCHAR;
503 bTypeDefined = true;
504 bPrecDefined = true;
505 break;
506 case 'N' :
507 nDbType = sdbc::DataType::DECIMAL;
508 bTypeDefined = true;
509 break;
511 if ( bTypeDefined && !nFieldLen && nIdx>0 )
513 nFieldLen = o3tl::toInt32(o3tl::getToken(aString, 0, ',', nIdx ));
514 if ( !bPrecDefined && nIdx>0 )
516 OUString aTmp( aString.getToken( 0, ',', nIdx ) );
517 if ( CharClass::isAsciiNumeric(aTmp) )
519 nPrecision = aTmp.toInt32();
520 if (nPrecision && nFieldLen < nPrecision+1)
521 nFieldLen = nPrecision + 1; // include decimal separator
522 bPrecDefined = true;
528 // Check field name and generate valid field name if necessary.
529 // First character has to be alphabetical, subsequent characters
530 // have to be alphanumerical or underscore.
531 // "_DBASELOCK" is reserved (obsolete because first character is
532 // not alphabetical).
533 // No duplicated names.
534 if ( !rtl::isAsciiAlpha(aFieldName[0]) )
535 aFieldName = "N" + aFieldName;
536 OUStringBuffer aTmpStr;
537 sal_Unicode c;
538 for ( const sal_Unicode* p = aFieldName.getStr(); ( c = *p ) != 0; p++ )
540 if ( rtl::isAsciiAlpha(c) || rtl::isAsciiDigit(c) || c == '_' )
541 aTmpStr.append(c);
542 else
543 aTmpStr.append("_");
545 aFieldName = aTmpStr.makeStringAndClear();
546 if ( aFieldName.getLength() > 10 )
547 aFieldName = aFieldName.copy(0, 10);
549 if (!aFieldNames.insert(aFieldName).second)
550 { // Duplicated field name, append numeric suffix.
551 sal_uInt16 nSub = 1;
552 OUString aFixPart( aFieldName );
555 ++nSub;
556 OUString aVarPart = OUString::number( nSub );
557 if ( aFixPart.getLength() + aVarPart.getLength() > 10 )
558 aFixPart = aFixPart.copy( 0, 10 - aVarPart.getLength() );
559 aFieldName = aFixPart + aVarPart;
560 } while (!aFieldNames.insert(aFieldName).second);
563 else
565 aFieldName = "N" + OUString::number(nCol+1);
568 if ( !bTypeDefined )
569 { // Field type.
570 ScRefCellValue aCell(rDoc, ScAddress(nCol, nFirstDataRow, nTab));
571 if (aCell.isEmpty() || aCell.hasString())
572 nDbType = sdbc::DataType::VARCHAR;
573 else
575 sal_uInt32 nFormat = rDoc.GetNumberFormat( nCol, nFirstDataRow, nTab );
576 switch (rContext.NFGetType(nFormat))
578 case SvNumFormatType::LOGICAL :
579 nDbType = sdbc::DataType::BIT;
580 nFieldLen = 1;
581 break;
582 case SvNumFormatType::DATE :
583 nDbType = sdbc::DataType::DATE;
584 nFieldLen = 8;
585 break;
586 case SvNumFormatType::TIME :
587 case SvNumFormatType::DATETIME :
588 nDbType = sdbc::DataType::VARCHAR;
589 break;
590 default:
591 nDbType = sdbc::DataType::DECIMAL;
595 // Field length.
596 if ( nDbType == sdbc::DataType::VARCHAR && !nFieldLen )
597 { // Determine maximum field width.
598 nFieldLen = rDoc.GetMaxStringLen( nTab, nCol, nFirstDataRow,
599 nLastRow, eCharSet );
600 if ( nFieldLen == 0 )
601 nFieldLen = 1;
603 else if ( nDbType == sdbc::DataType::DECIMAL )
604 { // Determine maximum field width and precision.
605 sal_Int32 nLen;
606 sal_uInt16 nPrec;
607 nLen = rDoc.GetMaxNumberStringLen( nPrec, nTab, nCol,
608 nFirstDataRow, nLastRow );
609 // dBaseIII precision limit: 15
610 if ( nPrecision > 15 )
611 nPrecision = 15;
612 if ( nPrec > 15 )
613 nPrec = 15;
614 if ( bPrecDefined && nPrecision != nPrec )
616 if (nPrecision < nPrec)
618 // This is a hairy case. User defined nPrecision but a
619 // number format has more precision. Modifying a dBase
620 // field may as well render the resulting file useless for
621 // an application that relies on its defined structure,
622 // especially if we are resaving an already existing file.
623 // So who's right, the user who (or the loaded file that)
624 // defined the field, or the user who applied the format?
625 // Commit f59e350d1733125055f1144f8b3b1b0a46f6d1ca gave the
626 // format a higher priority, which is debatable.
627 SAL_WARN( "sc", "lcl_GetColumnTypes: conflicting dBase field precision for "
628 << aFieldName << " (" << nPrecision << "<" << nPrec << ")");
630 // Adjust length to larger predefined integer part. There
631 // may be a reason that the field was prepared for larger
632 // numbers.
633 if (nFieldLen - nPrecision > nLen - nPrec)
634 nLen = nFieldLen - (nPrecision ? nPrecision+1 : 0) + 1 + nPrec;
635 // And override precision.
636 nPrecision = nPrec;
638 else
640 #if 1
641 // Adjust length to predefined precision.
642 nLen = nLen + ( nPrecision - nPrec );
643 #else
644 /* If the above override for (nPrecision < nPrec) was not in place then
645 * nPrecision could be 0 and this would be the code path to correctly
646 * calculate nLen. But as is, nPrecision is never 0 here, see CID#982304 */
648 // Adjust length to predefined precision.
649 if ( nPrecision )
650 nLen = nLen + ( nPrecision - nPrec );
651 else
652 nLen -= nPrec+1; // also remove the decimal separator
653 #endif
656 if (nFieldLen < nLen)
658 if (!bTypeDefined)
659 nFieldLen = nLen;
660 else
662 // Again a hairy case and conflict. Furthermore, the
663 // larger overall length may be a result of only a higher
664 // precision obtained from formats.
665 SAL_WARN( "sc", "lcl_GetColumnTypes: conflicting dBase field length for "
666 << aFieldName << " (" << nFieldLen << "<" << nLen << ")");
667 nFieldLen = nLen;
670 if ( !bPrecDefined )
671 nPrecision = nPrec;
672 if ( nFieldLen == 0 )
673 nFieldLen = 1;
674 else if ( nFieldLen > 19 )
675 nFieldLen = 19; // dBaseIII numeric field length limit: 19
676 if ( nPrecision && nFieldLen < nPrecision + 2 )
677 nFieldLen = nPrecision + 2; // 0. must fit into
678 // 538 MUST: Sdb internal representation adds 2 to the field length!
679 // To give the user what he wants we must subtract it here.
680 //! CAVEAT! There is no way to define a numeric field with a length
681 //! of 1 and no decimals!
682 nFieldLen = SvDbaseConverter::ConvertPrecisionToOdbc( nFieldLen, nPrecision );
684 if ( nFieldLen > 254 )
686 if ( nDbType == sdbc::DataType::VARCHAR )
687 { // Too long for a normal text field => memo field.
688 nDbType = sdbc::DataType::LONGVARCHAR;
689 nFieldLen = 10;
690 bHasMemo = true;
692 else
693 nFieldLen = 254; // bad luck...
696 pColNames[nField] = aFieldName;
697 pColTypes[nField] = nDbType;
698 pColLengths[nField] = nFieldLen;
699 pColScales[nField] = nPrecision;
701 ++nField;
705 void lcl_getLongVarCharEditString( OUString& rString,
706 const ScRefCellValue& rCell, ScFieldEditEngine& rEditEngine )
708 if (!rCell.getEditText())
709 return;
711 rEditEngine.SetTextCurrentDefaults(*rCell.getEditText());
712 rString = rEditEngine.GetText( LINEEND_CRLF );
715 void lcl_getLongVarCharString(
716 OUString& rString, ScDocument& rDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, ScInterpreterContext& rContext )
718 const Color* pColor;
719 ScAddress aPos(nCol, nRow, nTab);
720 sal_uInt32 nFormat = rDoc.GetNumberFormat(ScRange(aPos));
721 rString = ScCellFormat::GetString(rDoc, aPos, nFormat, &pColor, &rContext);
726 #endif // HAVE_FEATURE_DBCONNECTIVITY
728 ErrCodeMsg ScDocShell::DBaseExport( const OUString& rFullFileName, rtl_TextEncoding eCharSet, bool& bHasMemo )
730 #if !HAVE_FEATURE_DBCONNECTIVITY
731 (void) rFullFileName;
732 (void) eCharSet;
733 (void) bHasMemo;
735 return ERRCODE_IO_GENERAL;
736 #else
737 // remove the file so the dBase driver doesn't find an invalid file
738 INetURLObject aDeleteObj( rFullFileName, INetProtocol::File );
739 KillFile( aDeleteObj );
741 ErrCodeMsg nErr = ERRCODE_NONE;
743 SCCOL nFirstCol, nLastCol;
744 SCROW nFirstRow, nLastRow;
745 SCTAB nTab = GetSaveTab();
746 m_pDocument->GetDataStart( nTab, nFirstCol, nFirstRow );
747 m_pDocument->GetCellArea( nTab, nLastCol, nLastRow );
748 if ( nFirstCol > nLastCol )
749 nFirstCol = nLastCol;
750 if ( nFirstRow > nLastRow )
751 nFirstRow = nLastRow;
752 ScProgress aProgress( this, ScResId( STR_SAVE_DOC ),
753 nLastRow - nFirstRow, true );
754 ScInterpreterContext& rContext = m_pDocument->GetNonThreadedContext();
756 bool bHasFieldNames = true;
757 for ( SCCOL nDocCol = nFirstCol; nDocCol <= nLastCol && bHasFieldNames; nDocCol++ )
758 { // only Strings in first row => are field names
759 if ( !m_pDocument->HasStringData( nDocCol, nFirstRow, nTab ) )
760 bHasFieldNames = false;
763 sal_Int32 nColCount = nLastCol - nFirstCol + 1;
764 uno::Sequence<OUString> aColNames( nColCount );
765 uno::Sequence<sal_Int32> aColTypes( nColCount );
766 uno::Sequence<sal_Int32> aColLengths( nColCount );
767 uno::Sequence<sal_Int32> aColScales( nColCount );
769 ScRange aDataRange( nFirstCol, nFirstRow, nTab, nLastCol, nLastRow, nTab );
770 lcl_GetColumnTypes( *this, aDataRange, bHasFieldNames,
771 aColNames.getArray(), aColTypes.getArray(),
772 aColLengths.getArray(), aColScales.getArray(),
773 bHasMemo, eCharSet );
774 // also needed for exception catch
775 SCROW nDocRow = 0;
776 ScFieldEditEngine aEditEngine(m_pDocument.get(), m_pDocument->GetEditPool());
777 OUString aString;
781 uno::Reference<sdbc::XDriverManager2> xDrvMan;
782 uno::Reference<sdbc::XConnection> xConnection;
783 OUString aTabName;
784 ErrCode nRet = lcl_getDBaseConnection(xDrvMan,xConnection,aTabName,rFullFileName,eCharSet);
785 if ( !xConnection.is() || !xDrvMan.is() )
786 return nRet;
787 ::utl::DisposableComponent aConnectionHelper(xConnection);
789 // get dBase driver
790 uno::Reference< sdbcx::XDataDefinitionSupplier > xDDSup( xDrvMan->getDriverByURL( xConnection->getMetaData()->getURL() ), uno::UNO_QUERY );
791 if ( !xDDSup.is() )
792 return SCERR_EXPORT_CONNECT;
794 // create table
795 uno::Reference<sdbcx::XTablesSupplier> xTablesSupp =xDDSup->getDataDefinitionByConnection( xConnection );
796 OSL_ENSURE( xTablesSupp.is(), "can't get Data Definition" );
797 if (!xTablesSupp.is()) return SCERR_EXPORT_CONNECT;
799 uno::Reference<container::XNameAccess> xTables = xTablesSupp->getTables();
800 OSL_ENSURE( xTables.is(), "can't get Tables" );
801 if (!xTables.is()) return SCERR_EXPORT_CONNECT;
803 uno::Reference<sdbcx::XDataDescriptorFactory> xTablesFact( xTables, uno::UNO_QUERY );
804 OSL_ENSURE( xTablesFact.is(), "can't get tables factory" );
805 if (!xTablesFact.is()) return SCERR_EXPORT_CONNECT;
807 uno::Reference<sdbcx::XAppend> xTablesAppend( xTables, uno::UNO_QUERY );
808 OSL_ENSURE( xTablesAppend.is(), "can't get tables XAppend" );
809 if (!xTablesAppend.is()) return SCERR_EXPORT_CONNECT;
811 uno::Reference<beans::XPropertySet> xTableDesc = xTablesFact->createDataDescriptor();
812 OSL_ENSURE( xTableDesc.is(), "can't get table descriptor" );
813 if (!xTableDesc.is()) return SCERR_EXPORT_CONNECT;
815 xTableDesc->setPropertyValue( SC_DBPROP_NAME, uno::Any(aTabName) );
817 // create columns
819 uno::Reference<sdbcx::XColumnsSupplier> xColumnsSupp( xTableDesc, uno::UNO_QUERY );
820 OSL_ENSURE( xColumnsSupp.is(), "can't get columns supplier" );
821 if (!xColumnsSupp.is()) return SCERR_EXPORT_CONNECT;
823 uno::Reference<container::XNameAccess> xColumns = xColumnsSupp->getColumns();
824 OSL_ENSURE( xColumns.is(), "can't get columns" );
825 if (!xColumns.is()) return SCERR_EXPORT_CONNECT;
827 uno::Reference<sdbcx::XDataDescriptorFactory> xColumnsFact( xColumns, uno::UNO_QUERY );
828 OSL_ENSURE( xColumnsFact.is(), "can't get columns factory" );
829 if (!xColumnsFact.is()) return SCERR_EXPORT_CONNECT;
831 uno::Reference<sdbcx::XAppend> xColumnsAppend( xColumns, uno::UNO_QUERY );
832 OSL_ENSURE( xColumnsAppend.is(), "can't get columns XAppend" );
833 if (!xColumnsAppend.is()) return SCERR_EXPORT_CONNECT;
835 const OUString* pColNames = aColNames.getConstArray();
836 const sal_Int32* pColTypes = aColTypes.getConstArray();
837 const sal_Int32* pColLengths = aColLengths.getConstArray();
838 const sal_Int32* pColScales = aColScales.getConstArray();
839 sal_Int32 nCol;
841 for (nCol=0; nCol<nColCount; nCol++)
843 uno::Reference<beans::XPropertySet> xColumnDesc = xColumnsFact->createDataDescriptor();
844 OSL_ENSURE( xColumnDesc.is(), "can't get column descriptor" );
845 if (!xColumnDesc.is()) return SCERR_EXPORT_CONNECT;
847 xColumnDesc->setPropertyValue( SC_DBPROP_NAME, uno::Any(pColNames[nCol]) );
849 xColumnDesc->setPropertyValue( SC_DBPROP_TYPE, uno::Any(pColTypes[nCol]) );
851 xColumnDesc->setPropertyValue( SC_DBPROP_PRECISION, uno::Any(pColLengths[nCol]) );
853 xColumnDesc->setPropertyValue( SC_DBPROP_SCALE, uno::Any(pColScales[nCol]) );
855 xColumnsAppend->appendByDescriptor( xColumnDesc );
858 xTablesAppend->appendByDescriptor( xTableDesc );
860 // get row set for writing
861 uno::Reference<lang::XMultiServiceFactory> xFactory = comphelper::getProcessServiceFactory();
862 uno::Reference<sdbc::XRowSet> xRowSet( xFactory->createInstance(SC_SERVICE_ROWSET),
863 uno::UNO_QUERY);
864 ::utl::DisposableComponent aRowSetHelper(xRowSet);
865 uno::Reference<beans::XPropertySet> xRowProp( xRowSet, uno::UNO_QUERY );
866 OSL_ENSURE( xRowProp.is(), "can't get RowSet" );
867 if (!xRowProp.is()) return SCERR_EXPORT_CONNECT;
869 xRowProp->setPropertyValue( SC_DBPROP_ACTIVECONNECTION, uno::Any(xConnection) );
871 xRowProp->setPropertyValue( SC_DBPROP_COMMANDTYPE, uno::Any(sal_Int32(sdb::CommandType::TABLE)) );
873 xRowProp->setPropertyValue( SC_DBPROP_COMMAND, uno::Any(aTabName) );
875 xRowSet->execute();
877 // write data rows
879 uno::Reference<sdbc::XResultSetUpdate> xResultUpdate( xRowSet, uno::UNO_QUERY );
880 OSL_ENSURE( xResultUpdate.is(), "can't get XResultSetUpdate" );
881 if (!xResultUpdate.is()) return SCERR_EXPORT_CONNECT;
883 uno::Reference<sdbc::XRowUpdate> xRowUpdate( xRowSet, uno::UNO_QUERY );
884 OSL_ENSURE( xRowUpdate.is(), "can't get XRowUpdate" );
885 if (!xRowUpdate.is()) return SCERR_EXPORT_CONNECT;
887 SCROW nFirstDataRow = ( bHasFieldNames ? nFirstRow + 1 : nFirstRow );
888 double fVal;
890 for ( nDocRow = nFirstDataRow; nDocRow <= nLastRow; nDocRow++ )
892 xResultUpdate->moveToInsertRow();
894 for (nCol=0; nCol<nColCount; nCol++)
896 SCCOL nDocCol = sal::static_int_cast<SCCOL>( nFirstCol + nCol );
898 switch (pColTypes[nCol])
900 case sdbc::DataType::LONGVARCHAR:
902 ScRefCellValue aCell(*m_pDocument, ScAddress(nDocCol, nDocRow, nTab));
903 if (!aCell.isEmpty())
905 if (aCell.getType() == CELLTYPE_EDIT)
906 { // preserve paragraphs
907 lcl_getLongVarCharEditString(aString, aCell, aEditEngine);
909 else
911 lcl_getLongVarCharString(
912 aString, *m_pDocument, nDocCol, nDocRow, nTab, rContext);
914 xRowUpdate->updateString( nCol+1, aString );
916 else
917 xRowUpdate->updateNull( nCol+1 );
919 break;
921 case sdbc::DataType::VARCHAR:
922 aString = m_pDocument->GetString(nDocCol, nDocRow, nTab);
923 xRowUpdate->updateString( nCol+1, aString );
924 if ( nErr == ERRCODE_NONE && pColLengths[nCol] < aString.getLength() )
925 nErr = SCWARN_EXPORT_DATALOST;
926 break;
928 case sdbc::DataType::DATE:
930 fVal = m_pDocument->GetValue( nDocCol, nDocRow, nTab );
931 // differentiate between 0 with value and 0 no-value
932 bool bIsNull = (fVal == 0.0);
933 if ( bIsNull )
934 bIsNull = !m_pDocument->HasValueData( nDocCol, nDocRow, nTab );
935 if ( bIsNull )
937 xRowUpdate->updateNull( nCol+1 );
938 if ( nErr == ERRCODE_NONE &&
939 m_pDocument->HasStringData( nDocCol, nDocRow, nTab ) )
940 nErr = SCWARN_EXPORT_DATALOST;
942 else
944 Date aDate = rContext.NFGetNullDate(); // tools date
945 aDate.AddDays(fVal); //! approxfloor?
946 xRowUpdate->updateDate( nCol+1, aDate.GetUNODate() );
949 break;
951 case sdbc::DataType::DECIMAL:
952 case sdbc::DataType::BIT:
953 fVal = m_pDocument->GetValue( nDocCol, nDocRow, nTab );
954 if ( fVal == 0.0 && nErr == ERRCODE_NONE &&
955 m_pDocument->HasStringData( nDocCol, nDocRow, nTab ) )
956 nErr = SCWARN_EXPORT_DATALOST;
957 if ( pColTypes[nCol] == sdbc::DataType::BIT )
958 xRowUpdate->updateBoolean( nCol+1, ( fVal != 0.0 ) );
959 else
960 xRowUpdate->updateDouble( nCol+1, fVal );
961 break;
963 default:
964 OSL_FAIL( "ScDocShell::DBaseExport: unknown FieldType" );
965 if ( nErr == ERRCODE_NONE )
966 nErr = SCWARN_EXPORT_DATALOST;
967 fVal = m_pDocument->GetValue( nDocCol, nDocRow, nTab );
968 xRowUpdate->updateDouble( nCol+1, fVal );
972 xResultUpdate->insertRow();
974 //! error handling and recovery of old
975 //! ScDocShell::SbaSdbExport is still missing!
977 aProgress.SetStateOnPercent( nDocRow - nFirstRow );
980 comphelper::disposeComponent( xRowSet );
981 comphelper::disposeComponent( xConnection );
983 catch ( const sdbc::SQLException& aException )
985 sal_Int32 nError = aException.ErrorCode;
986 TOOLS_WARN_EXCEPTION("sc", "ScDocShell::DBaseExport");
988 if (nError == 22018 || nError == 22001)
990 // SQL error 22018: Character not in target encoding.
991 // SQL error 22001: String length exceeds field width (after encoding).
992 bool bEncErr = (nError == 22018);
993 bool bIsOctetTextEncoding = rtl_isOctetTextEncoding( eCharSet);
994 OSL_ENSURE( !bEncErr || bIsOctetTextEncoding, "ScDocShell::DBaseExport: encoding error and not an octet textencoding");
995 SCCOL nDocCol = nFirstCol;
996 const sal_Int32* pColTypes = aColTypes.getConstArray();
997 const sal_Int32* pColLengths = aColLengths.getConstArray();
998 ScHorizontalCellIterator aIter( *m_pDocument, nTab, nFirstCol,
999 nDocRow, nLastCol, nDocRow);
1000 bool bTest = true;
1001 while (bTest)
1003 ScRefCellValue* pCell = aIter.GetNext( nDocCol, nDocRow);
1004 if (!pCell)
1005 break;
1006 SCCOL nCol = nDocCol - nFirstCol;
1007 switch (pColTypes[nCol])
1009 case sdbc::DataType::LONGVARCHAR:
1011 if (pCell->getType() == CELLTYPE_EDIT)
1012 lcl_getLongVarCharEditString(aString, *pCell, aEditEngine);
1013 else
1014 lcl_getLongVarCharString(
1015 aString, *m_pDocument, nDocCol, nDocRow, nTab, rContext);
1017 break;
1019 case sdbc::DataType::VARCHAR:
1020 aString = m_pDocument->GetString(nDocCol, nDocRow, nTab);
1021 break;
1023 // NOTE: length of DECIMAL fields doesn't need to be
1024 // checked here, the database driver adjusts the field
1025 // width accordingly.
1027 default:
1028 bTest = false;
1030 if (bTest)
1032 sal_Int32 nLen;
1033 if (bIsOctetTextEncoding)
1035 OString aOString;
1036 if (!aString.convertToString( &aOString, eCharSet,
1037 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
1038 RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))
1040 bTest = false;
1041 bEncErr = true;
1043 nLen = aOString.getLength();
1044 if (!bTest)
1045 SAL_WARN("sc", "ScDocShell::DBaseExport encoding error, string with default replacements: ``" << aString << "''");
1047 else
1048 nLen = aString.getLength() * sizeof(sal_Unicode);
1049 if (!bEncErr &&
1050 pColTypes[nCol] != sdbc::DataType::LONGVARCHAR &&
1051 pColLengths[nCol] < nLen)
1053 bTest = false;
1054 SAL_INFO("sc", "ScDocShell::DBaseExport: field width: " << pColLengths[nCol] << ", encoded length: " << nLen);
1057 else
1058 bTest = true;
1060 OUString sPosition(ScAddress(nDocCol, nDocRow, nTab).GetColRowString());
1061 OUString sEncoding(SvxTextEncodingTable::GetTextString(eCharSet));
1062 nErr = ErrCodeMsg( (bEncErr ? SCERR_EXPORT_ENCODING :
1063 SCERR_EXPORT_FIELDWIDTH), sPosition, sEncoding,
1064 DialogMask::ButtonsOk | DialogMask::MessageError);
1066 else if ( !aException.Message.isEmpty() )
1067 nErr = ErrCodeMsg( SCERR_EXPORT_SQLEXCEPTION, aException.Message, DialogMask::ButtonsOk | DialogMask::MessageError);
1068 else
1069 nErr = SCERR_EXPORT_DATA;
1071 catch ( uno::Exception& )
1073 TOOLS_WARN_EXCEPTION( "sc", "Unexpected exception in database");
1074 nErr = ERRCODE_IO_GENERAL;
1077 return nErr;
1078 #endif // HAVE_FEATURE_DBCONNECTIVITY
1081 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */