1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <sal/config.h>
24 #include <unotxdoc.hxx>
25 #include <sfx2/app.hxx>
26 #include <com/sun/star/sdb/CommandType.hpp>
27 #include <com/sun/star/sdb/XDocumentDataSource.hpp>
28 #include <com/sun/star/lang/DisposedException.hpp>
29 #include <com/sun/star/lang/XEventListener.hpp>
30 #include <com/sun/star/uri/UriReferenceFactory.hpp>
31 #include <com/sun/star/uri/VndSunStarPkgUrlReferenceFactory.hpp>
32 #include <com/sun/star/util/NumberFormatter.hpp>
33 #include <com/sun/star/sdb/DatabaseContext.hpp>
34 #include <com/sun/star/sdb/TextConnectionSettings.hpp>
35 #include <com/sun/star/sdb/XCompletedConnection.hpp>
36 #include <com/sun/star/sdb/XCompletedExecution.hpp>
37 #include <com/sun/star/container/XChild.hpp>
38 #include <com/sun/star/text/MailMergeEvent.hpp>
39 #include <com/sun/star/frame/XStorable.hpp>
40 #include <com/sun/star/task/InteractionHandler.hpp>
41 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
42 #include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
43 #include <com/sun/star/beans/XPropertySet.hpp>
44 #include <vcl/errinf.hxx>
45 #include <vcl/print.hxx>
46 #include <vcl/scheduler.hxx>
47 #include <sfx2/fcontnr.hxx>
48 #include <sfx2/filedlghelper.hxx>
49 #include <sfx2/viewfrm.hxx>
50 #include <dbconfig.hxx>
51 #include <unotools/tempfile.hxx>
52 #include <unotools/pathoptions.hxx>
53 #include <svl/numformat.hxx>
54 #include <svl/zforlist.hxx>
55 #include <svl/stritem.hxx>
56 #include <sfx2/docfile.hxx>
57 #include <sfx2/docfilt.hxx>
58 #include <sfx2/progress.hxx>
59 #include <sfx2/dispatch.hxx>
61 #include <swmodule.hxx>
70 #include <IDocumentLinksAdministration.hxx>
71 #include <IDocumentFieldsAccess.hxx>
72 #include <IDocumentUndoRedo.hxx>
74 #include <swunohelper.hxx>
75 #include <strings.hrc>
76 #include <mmconfigitem.hxx>
77 #include <com/sun/star/sdbc/XRowSet.hpp>
78 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
79 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
80 #include <com/sun/star/sdb/XQueriesSupplier.hpp>
81 #include <com/sun/star/sdb/XColumn.hpp>
82 #include <com/sun/star/sdbc/DataType.hpp>
83 #include <com/sun/star/sdbc/ResultSetType.hpp>
84 #include <com/sun/star/sdbc/SQLException.hpp>
85 #include <com/sun/star/mail/MailAttachment.hpp>
86 #include <comphelper/processfactory.hxx>
87 #include <comphelper/property.hxx>
88 #include <comphelper/propertyvalue.hxx>
89 #include <comphelper/storagehelper.hxx>
90 #include <comphelper/string.hxx>
91 #include <comphelper/types.hxx>
92 #include <mailmergehelper.hxx>
93 #include <maildispatcher.hxx>
94 #include <svtools/htmlcfg.hxx>
95 #include <i18nlangtag/languagetag.hxx>
96 #include <com/sun/star/util/XNumberFormatTypes.hpp>
97 #include <svl/numuno.hxx>
98 #include <connectivity/dbtools.hxx>
99 #include <connectivity/dbconversion.hxx>
100 #include <unotools/charclass.hxx>
101 #include <comphelper/diagnose_ex.hxx>
103 #include <unomailmerge.hxx>
104 #include <sfx2/event.hxx>
105 #include <svx/dataaccessdescriptor.hxx>
106 #include <rtl/textenc.h>
107 #include <rtl/tencinfo.h>
108 #include <cppuhelper/implbase.hxx>
109 #include <ndindex.hxx>
110 #include <swevent.hxx>
111 #include <sal/log.hxx>
112 #include <swabstdlg.hxx>
114 #include <section.hxx>
115 #include <rootfrm.hxx>
118 #include <IDocumentState.hxx>
119 #include <imaildsplistener.hxx>
120 #include <iodetect.hxx>
121 #include <IDocumentDeviceAccess.hxx>
125 #include <comphelper/propertysequence.hxx>
127 using namespace ::com::sun::star
;
132 void lcl_emitEvent(SfxEventHintId nEventId
, sal_Int32 nStrId
, SfxObjectShell
* pDocShell
)
134 SfxGetpApp()->NotifyEvent(SfxEventHint(nEventId
,
135 SwDocShell::GetEventName(nStrId
),
139 // Construct vnd.sun.star.pkg:// URL
140 OUString
ConstructVndSunStarPkgUrl(const OUString
& rMainURL
, std::u16string_view rStreamRelPath
)
142 const auto& xContext(comphelper::getProcessComponentContext());
143 auto xUri
= css::uri::UriReferenceFactory::create(xContext
)->parse(rMainURL
);
145 xUri
= css::uri::VndSunStarPkgUrlReferenceFactory::create(xContext
)
146 ->createVndSunStarPkgUrlReference(xUri
);
148 return xUri
->getUriReference() + "/"
149 + INetURLObject::encode(
150 rStreamRelPath
, INetURLObject::PART_FPATH
,
151 INetURLObject::EncodeMechanism::All
);
156 std::vector
<std::pair
<SwDocShell
*, OUString
>> SwDBManager::s_aUncommittedRegistrations
;
160 enum class SwDBNextRecord
{ NEXT
, FIRST
};
164 static bool lcl_ToNextRecord( SwDSParam
* pParam
, const SwDBNextRecord action
= SwDBNextRecord::NEXT
);
168 enum class WorkingDocType
{ SOURCE
, TARGET
, COPY
};
172 static SfxObjectShell
* lcl_CreateWorkingDocument(
173 const WorkingDocType aType
, const SwWrtShell
&rSourceWrtShell
,
174 const vcl::Window
*pSourceWindow
,
175 SwDBManager
** const ppDBManager
,
176 SwView
** const pView
, SwWrtShell
** const pWrtShell
, rtl::Reference
<SwDoc
>* const pDoc
);
178 static bool lcl_getCountFromResultSet( sal_Int32
& rCount
, const SwDSParam
* pParam
)
180 rCount
= pParam
->aSelection
.getLength();
184 uno::Reference
<beans::XPropertySet
> xPrSet(pParam
->xResultSet
, uno::UNO_QUERY
);
190 uno::Any aFinal
= xPrSet
->getPropertyValue(u
"IsRowCountFinal"_ustr
);
194 pParam
->xResultSet
->last();
195 pParam
->xResultSet
->first();
197 uno::Any aCount
= xPrSet
->getPropertyValue(u
"RowCount"_ustr
);
198 if( aCount
>>= rCount
)
201 catch(const uno::Exception
&)
208 class SwDBManager::ConnectionDisposedListener_Impl
209 : public cppu::WeakImplHelper
< lang::XEventListener
>
212 SwDBManager
* m_pDBManager
;
214 virtual void SAL_CALL
disposing( const lang::EventObject
& Source
) override
;
217 explicit ConnectionDisposedListener_Impl(SwDBManager
& rMgr
);
219 void Dispose() { m_pDBManager
= nullptr; }
225 /// Listens to removed data sources, and if it's one that's embedded into this document, triggers embedding removal.
226 class SwDataSourceRemovedListener
: public cppu::WeakImplHelper
<sdb::XDatabaseRegistrationsListener
>
228 uno::Reference
<sdb::XDatabaseContext
> m_xDatabaseContext
;
229 SwDBManager
* m_pDBManager
;
232 explicit SwDataSourceRemovedListener(SwDBManager
& rDBManager
);
233 virtual ~SwDataSourceRemovedListener() override
;
234 virtual void SAL_CALL
registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
) override
;
235 virtual void SAL_CALL
revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
) override
;
236 virtual void SAL_CALL
changedDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
) override
;
237 virtual void SAL_CALL
disposing(const lang::EventObject
& rObject
) override
;
243 SwDataSourceRemovedListener::SwDataSourceRemovedListener(SwDBManager
& rDBManager
)
244 : m_pDBManager(&rDBManager
)
246 const uno::Reference
<uno::XComponentContext
>& xComponentContext(comphelper::getProcessComponentContext());
247 m_xDatabaseContext
= sdb::DatabaseContext::create(xComponentContext
);
248 m_xDatabaseContext
->addDatabaseRegistrationsListener(this);
251 SwDataSourceRemovedListener::~SwDataSourceRemovedListener()
253 if (m_xDatabaseContext
.is())
254 m_xDatabaseContext
->removeDatabaseRegistrationsListener(this);
257 void SAL_CALL
SwDataSourceRemovedListener::registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent
& /*rEvent*/)
261 void SAL_CALL
SwDataSourceRemovedListener::revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
)
263 if (!m_pDBManager
|| m_pDBManager
->getEmbeddedName().isEmpty())
266 SwDoc
* pDoc
= m_pDBManager
->getDoc();
270 SwDocShell
* pDocShell
= pDoc
->GetDocShell();
274 const OUString sTmpName
= ConstructVndSunStarPkgUrl(
275 pDocShell
->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE
),
276 m_pDBManager
->getEmbeddedName());
278 if (sTmpName
!= rEvent
.OldLocation
)
281 // The revoked database location is inside this document, then remove the
282 // embedding, as otherwise it would be back on the next reload of the
284 pDocShell
->GetStorage()->removeElement(m_pDBManager
->getEmbeddedName());
285 m_pDBManager
->setEmbeddedName(OUString(), *pDocShell
);
288 void SAL_CALL
SwDataSourceRemovedListener::changedDatabaseLocation(const sdb::DatabaseRegistrationEvent
& rEvent
)
290 if (rEvent
.OldLocation
!= rEvent
.NewLocation
)
291 revokedDatabaseLocation(rEvent
);
294 void SwDataSourceRemovedListener::disposing(const lang::EventObject
& /*rObject*/)
296 m_xDatabaseContext
.clear();
299 void SwDataSourceRemovedListener::Dispose()
301 m_pDBManager
= nullptr;
304 struct SwDBManager::SwDBManager_Impl
306 std::unique_ptr
<SwDSParam
> pMergeData
;
307 VclPtr
<AbstractMailMergeDlg
> pMergeDialog
;
308 rtl::Reference
<SwDBManager::ConnectionDisposedListener_Impl
> m_xDisposeListener
;
309 rtl::Reference
<SwDataSourceRemovedListener
> m_xDataSourceRemovedListener
;
310 std::mutex m_aAllEmailSendMutex
;
311 uno::Reference
< mail::XMailMessage
> m_xLastMessage
;
313 explicit SwDBManager_Impl(SwDBManager
& rDBManager
)
314 : m_xDisposeListener(new ConnectionDisposedListener_Impl(rDBManager
))
319 m_xDisposeListener
->Dispose();
320 if (m_xDataSourceRemovedListener
.is())
321 m_xDataSourceRemovedListener
->Dispose();
325 static void lcl_InitNumberFormatter(SwDSParam
& rParam
, uno::Reference
<sdbc::XDataSource
> const & xSource
)
327 const uno::Reference
<uno::XComponentContext
>& xContext
= ::comphelper::getProcessComponentContext();
328 rParam
.xFormatter
= util::NumberFormatter::create(xContext
);
329 uno::Reference
<beans::XPropertySet
> xSourceProps(
332 : SwDBManager::getDataSourceAsParent(
333 rParam
.xConnection
, rParam
.sDataSource
)),
335 if(!xSourceProps
.is())
338 uno::Any aFormats
= xSourceProps
->getPropertyValue(u
"NumberFormatsSupplier"_ustr
);
339 if(!aFormats
.hasValue())
342 uno::Reference
<util::XNumberFormatsSupplier
> xSuppl
;
346 uno::Reference
< beans::XPropertySet
> xSettings
= xSuppl
->getNumberFormatSettings();
347 uno::Any aNull
= xSettings
->getPropertyValue(u
"NullDate"_ustr
);
348 aNull
>>= rParam
.aNullDate
;
349 if(rParam
.xFormatter
.is())
350 rParam
.xFormatter
->attachNumberFormatsSupplier(xSuppl
);
354 static bool lcl_MoveAbsolute(SwDSParam
* pParam
, tools::Long nAbsPos
)
359 if(pParam
->aSelection
.hasElements())
361 if(pParam
->aSelection
.getLength() <= nAbsPos
)
363 pParam
->bEndOfDB
= true;
368 pParam
->nSelectionIndex
= nAbsPos
;
370 pParam
->aSelection
.getConstArray()[ pParam
->nSelectionIndex
] >>= nPos
;
371 pParam
->bEndOfDB
= !pParam
->xResultSet
->absolute( nPos
);
372 bRet
= !pParam
->bEndOfDB
;
375 else if(pParam
->bScrollable
)
377 bRet
= pParam
->xResultSet
->absolute( nAbsPos
);
381 OSL_FAIL("no absolute positioning available");
384 catch(const uno::Exception
&)
390 static void lcl_GetColumnCnt(SwDSParam
*pParam
,
391 const uno::Reference
< beans::XPropertySet
> &rColumnProps
,
392 LanguageType nLanguage
, OUString
&rResult
, double* pNumber
)
394 SwDBFormatData aFormatData
;
395 if(!pParam
->xFormatter
.is())
397 uno::Reference
<sdbc::XDataSource
> xSource
= SwDBManager::getDataSourceAsParent(
398 pParam
->xConnection
,pParam
->sDataSource
);
399 lcl_InitNumberFormatter(*pParam
, xSource
);
401 aFormatData
.aNullDate
= pParam
->aNullDate
;
402 aFormatData
.xFormatter
= pParam
->xFormatter
;
404 aFormatData
.aLocale
= LanguageTag( nLanguage
).getLocale();
406 rResult
= SwDBManager::GetDBField( rColumnProps
, aFormatData
, pNumber
);
409 static bool lcl_GetColumnCnt(SwDSParam
* pParam
, const OUString
& rColumnName
,
410 LanguageType nLanguage
, OUString
& rResult
, double* pNumber
)
412 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( pParam
->xResultSet
, uno::UNO_QUERY
);
413 uno::Reference
<container::XNameAccess
> xCols
;
416 xCols
= xColsSupp
->getColumns();
418 catch(const lang::DisposedException
&)
421 if(!xCols
.is() || !xCols
->hasByName(rColumnName
))
423 uno::Any aCol
= xCols
->getByName(rColumnName
);
424 uno::Reference
< beans::XPropertySet
> xColumnProps
;
425 aCol
>>= xColumnProps
;
426 lcl_GetColumnCnt( pParam
, xColumnProps
, nLanguage
, rResult
, pNumber
);
431 bool SwDBManager::Merge( const SwMergeDescriptor
& rMergeDesc
)
433 assert( !m_bInMerge
&& !m_pImpl
->pMergeData
&& "merge already activated!" );
435 SfxObjectShellLock xWorkObjSh
;
436 SwWrtShell
*pWorkShell
= nullptr;
437 rtl::Reference
<SwDoc
> pWorkDoc
;
438 SwDBManager
*pWorkDocOrigDBManager
= nullptr;
440 switch( rMergeDesc
.nMergeType
)
442 case DBMGR_MERGE_PRINTER
:
443 case DBMGR_MERGE_EMAIL
:
444 case DBMGR_MERGE_FILE
:
445 case DBMGR_MERGE_SHELL
:
447 SwDocShell
*pSourceDocSh
= rMergeDesc
.rSh
.GetView().GetDocShell();
448 if( pSourceDocSh
->IsModified() )
450 pWorkDocOrigDBManager
= this;
451 xWorkObjSh
= lcl_CreateWorkingDocument(
452 WorkingDocType::SOURCE
, rMergeDesc
.rSh
, nullptr,
453 &pWorkDocOrigDBManager
, nullptr, &pWorkShell
, &pWorkDoc
);
459 if( !xWorkObjSh
.Is() )
460 pWorkShell
= &rMergeDesc
.rSh
;
465 aData
.nCommandType
= sdb::CommandType::TABLE
;
466 uno::Reference
<sdbc::XResultSet
> xResSet
;
467 uno::Sequence
<uno::Any
> aSelection
;
468 uno::Reference
< sdbc::XConnection
> xConnection
;
470 aData
.sDataSource
= rMergeDesc
.rDescriptor
.getDataSource();
471 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::Command
] >>= aData
.sCommand
;
472 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::CommandType
] >>= aData
.nCommandType
;
474 if ( rMergeDesc
.rDescriptor
.has(svx::DataAccessDescriptorProperty::Cursor
) )
475 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::Cursor
] >>= xResSet
;
476 if ( rMergeDesc
.rDescriptor
.has(svx::DataAccessDescriptorProperty::Selection
) )
477 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::Selection
] >>= aSelection
;
478 if ( rMergeDesc
.rDescriptor
.has(svx::DataAccessDescriptorProperty::Connection
) )
479 rMergeDesc
.rDescriptor
[svx::DataAccessDescriptorProperty::Connection
] >>= xConnection
;
481 if((aData
.sDataSource
.isEmpty() || aData
.sCommand
.isEmpty()) && !xResSet
.is())
486 m_pImpl
->pMergeData
.reset(new SwDSParam(aData
, xResSet
, aSelection
));
487 SwDSParam
* pTemp
= FindDSData(aData
, false);
489 *pTemp
= *m_pImpl
->pMergeData
;
492 // calls from the calculator may have added a connection with an invalid commandtype
493 //"real" data base connections added here have to re-use the already available
494 //DSData and set the correct CommandType
495 aData
.nCommandType
= -1;
496 pTemp
= FindDSData(aData
, false);
498 *pTemp
= *m_pImpl
->pMergeData
;
501 m_DataSourceParams
.push_back(std::make_unique
<SwDSParam
>(*m_pImpl
->pMergeData
));
504 uno::Reference
<lang::XComponent
> xComponent(m_DataSourceParams
.back()->xConnection
, uno::UNO_QUERY
);
506 xComponent
->addEventListener(m_pImpl
->m_xDisposeListener
);
508 catch(const uno::Exception
&)
513 if(!m_pImpl
->pMergeData
->xConnection
.is())
514 m_pImpl
->pMergeData
->xConnection
= xConnection
;
515 // add an XEventListener
517 lcl_ToNextRecord(m_pImpl
->pMergeData
.get(), SwDBNextRecord::FIRST
);
519 uno::Reference
<sdbc::XDataSource
> xSource
= SwDBManager::getDataSourceAsParent(xConnection
,aData
.sDataSource
);
521 lcl_InitNumberFormatter(*m_pImpl
->pMergeData
, xSource
);
524 pWorkShell
->ChgDBData(aData
);
527 if (IsInitDBFields())
529 // with database fields without DB-Name, use DB-Name from Doc
530 std::vector
<OUString
> aDBNames
;
531 aDBNames
.emplace_back();
532 SwDBData aInsertData
= pWorkShell
->GetDBData();
533 OUString sDBName
= aInsertData
.sDataSource
534 + OUStringChar(DB_DELIM
) + aInsertData
.sCommand
535 + OUStringChar(DB_DELIM
)
536 + OUString::number(aInsertData
.nCommandType
);
537 pWorkShell
->ChangeDBFields( aDBNames
, sDBName
);
538 SetInitDBFields(false);
542 switch(rMergeDesc
.nMergeType
)
545 pWorkShell
->StartAllAction();
546 pWorkShell
->SwViewShell::UpdateFields( true );
547 pWorkShell
->SetModified();
548 pWorkShell
->EndAllAction();
551 case DBMGR_MERGE_PRINTER
:
552 case DBMGR_MERGE_EMAIL
:
553 case DBMGR_MERGE_FILE
:
554 case DBMGR_MERGE_SHELL
:
555 // save files and send them as e-Mail if required
556 bRet
= MergeMailFiles(pWorkShell
, rMergeDesc
);
560 // insert selected entries
561 // (was: InsertRecord)
562 ImportFromConnection(pWorkShell
);
566 m_pImpl
->pMergeData
.reset();
568 if( xWorkObjSh
.Is() )
570 pWorkDoc
->SetDBManager( pWorkDocOrigDBManager
);
571 xWorkObjSh
->DoClose();
579 void SwDBManager::ImportFromConnection( SwWrtShell
* pSh
)
581 if(!m_pImpl
->pMergeData
|| m_pImpl
->pMergeData
->bEndOfDB
)
584 pSh
->StartAllAction();
586 bool bGroupUndo(pSh
->DoesGroupUndo());
587 pSh
->DoGroupUndo(false);
589 if( pSh
->HasSelection() )
592 std::optional
<SwWait
> oWait
;
600 oWait
.emplace( *pSh
->GetView().GetDocShell(), true);
602 } while(ToNextMergeRecord());
605 pSh
->DoGroupUndo(bGroupUndo
);
610 void SwDBManager::ImportDBEntry(SwWrtShell
* pSh
)
612 if(!m_pImpl
->pMergeData
|| m_pImpl
->pMergeData
->bEndOfDB
)
615 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( m_pImpl
->pMergeData
->xResultSet
, uno::UNO_QUERY
);
616 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
618 uno::Sequence
<OUString
> aColNames
= xCols
->getElementNames();
619 const OUString
* pColNames
= aColNames
.getConstArray();
620 tools::Long nLength
= aColNames
.getLength();
621 for(tools::Long i
= 0; i
< nLength
; i
++)
623 uno::Any aCol
= xCols
->getByName(pColNames
[i
]);
624 uno::Reference
< beans::XPropertySet
> xColumnProp
;
625 aCol
>>= xColumnProp
;
626 SwDBFormatData aDBFormat
;
627 sStr
.append(GetDBField( xColumnProp
, aDBFormat
));
631 pSh
->SwEditShell::Insert2(sStr
.makeStringAndClear());
632 pSh
->SwFEShell::SplitNode(); // line feed
635 bool SwDBManager::GetTableNames(weld::ComboBox
& rBox
, const OUString
& rDBName
)
638 OUString
sOldTableName(rBox
.get_active_text());
640 SwDSParam
* pParam
= FindDSConnection(rDBName
, false);
641 uno::Reference
< sdbc::XConnection
> xConnection
;
642 if (pParam
&& pParam
->xConnection
.is())
643 xConnection
= pParam
->xConnection
;
646 if ( !rDBName
.isEmpty() )
647 xConnection
= RegisterConnection( rDBName
);
649 if (xConnection
.is())
651 uno::Reference
<sdbcx::XTablesSupplier
> xTSupplier(xConnection
, uno::UNO_QUERY
);
654 uno::Reference
<container::XNameAccess
> xTables
= xTSupplier
->getTables();
655 const uno::Sequence
<OUString
> aTables
= xTables
->getElementNames();
656 for (const OUString
& rTable
: aTables
)
657 rBox
.append(u
"0"_ustr
, rTable
);
659 uno::Reference
<sdb::XQueriesSupplier
> xQSupplier(xConnection
, uno::UNO_QUERY
);
662 uno::Reference
<container::XNameAccess
> xQueries
= xQSupplier
->getQueries();
663 const uno::Sequence
<OUString
> aQueries
= xQueries
->getElementNames();
664 for (const OUString
& rQuery
: aQueries
)
665 rBox
.append(u
"1"_ustr
, rQuery
);
667 if (!sOldTableName
.isEmpty())
668 rBox
.set_active_text(sOldTableName
);
674 // fill Listbox with column names of a database
675 void SwDBManager::GetColumnNames(weld::ComboBox
& rBox
,
676 const OUString
& rDBName
, const OUString
& rTableName
)
679 aData
.sDataSource
= rDBName
;
680 aData
.sCommand
= rTableName
;
681 aData
.nCommandType
= -1;
682 SwDSParam
* pParam
= FindDSData(aData
, false);
683 uno::Reference
< sdbc::XConnection
> xConnection
;
684 if(pParam
&& pParam
->xConnection
.is())
685 xConnection
= pParam
->xConnection
;
688 xConnection
= RegisterConnection( rDBName
);
690 GetColumnNames(rBox
, xConnection
, rTableName
);
693 void SwDBManager::GetColumnNames(weld::ComboBox
& rBox
,
694 uno::Reference
< sdbc::XConnection
> const & xConnection
,
695 const OUString
& rTableName
)
698 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp
= SwDBManager::GetColumnSupplier(xConnection
, rTableName
);
701 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
702 const uno::Sequence
<OUString
> aColNames
= xCols
->getElementNames();
703 for (const OUString
& rColName
: aColNames
)
705 rBox
.append_text(rColName
);
707 ::comphelper::disposeComponent( xColsSupp
);
711 SwDBManager::SwDBManager(SwDoc
* pDoc
)
712 : m_aMergeStatus( MergeStatus::Ok
)
713 , m_bInitDBFields(false)
715 , m_bMergeSilent(false)
716 , m_pImpl(new SwDBManager_Impl(*this))
717 , m_pMergeEvtSrc(nullptr)
722 void SwDBManager::ImplDestroy()
724 RevokeLastRegistrations();
726 // copy required, m_DataSourceParams can be modified while disposing components
727 std::vector
<uno::Reference
<sdbc::XConnection
>> aCopiedConnections
;
728 for (const auto & pParam
: m_DataSourceParams
)
730 if(pParam
->xConnection
.is())
732 aCopiedConnections
.push_back(pParam
->xConnection
);
735 for (const auto & xConnection
: aCopiedConnections
)
739 uno::Reference
<lang::XComponent
> xComp(xConnection
, uno::UNO_QUERY
);
743 catch(const uno::RuntimeException
&)
745 //may be disposed already since multiple entries may have used the same connection
750 SwDBManager::~SwDBManager()
752 suppress_fun_call_w_exception(ImplDestroy());
755 static void lcl_RemoveSectionLinks( SwWrtShell
& rWorkShell
)
757 //reset all links of the sections of synchronized labels
758 size_t nSections
= rWorkShell
.GetSectionFormatCount();
759 for (size_t nSection
= 0; nSection
< nSections
; ++nSection
)
761 SwSectionData
aSectionData( *rWorkShell
.GetSectionFormat( nSection
).GetSection() );
762 if( aSectionData
.GetType() == SectionType::FileLink
)
764 aSectionData
.SetType( SectionType::Content
);
765 aSectionData
.SetLinkFileName( OUString() );
766 rWorkShell
.UpdateSection( nSection
, aSectionData
);
769 rWorkShell
.SetLabelDoc( false );
772 static void lcl_SaveDebugDoc( SfxObjectShell
*xTargetDocShell
,
773 const char *name
, int no
= 0 )
775 static OUString sTempDirURL
;
776 if( sTempDirURL
.isEmpty() )
778 SvtPathOptions aPathOpt
;
779 utl::TempFileNamed
aTempDir( &aPathOpt
.GetTempPath(), true );
780 if( aTempDir
.IsValid() )
782 INetURLObject
aTempDirURL( aTempDir
.GetURL() );
783 sTempDirURL
= aTempDirURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
784 SAL_INFO( "sw.mailmerge", "Dump directory: " << sTempDirURL
);
787 if( sTempDirURL
.isEmpty() )
790 OUString basename
= OUString::createFromAscii( name
);
792 basename
+= OUString::number(no
) + "-";
793 // aTempFile is not deleted, but that seems to be intentional
794 utl::TempFileNamed
aTempFile( basename
, true, u
".odt", &sTempDirURL
);
795 INetURLObject
aTempFileURL( aTempFile
.GetURL() );
797 aTempFileURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
),
798 StreamMode::STD_READWRITE
);
799 bool bAnyError
= !xTargetDocShell
->DoSaveAs( aDstMed
);
800 // xObjectShell->DoSaveCompleted crashes the mail merge unit tests, so skip it
801 bAnyError
|= (ERRCODE_NONE
!= xTargetDocShell
->GetErrorIgnoreWarning());
803 SAL_WARN( "sw.mailmerge", "Error saving: " << aTempFile
.GetURL() );
805 SAL_INFO( "sw.mailmerge", "Saved doc as: " << aTempFile
.GetURL() );
808 static bool lcl_SaveDoc(
809 const INetURLObject
* pFileURL
,
810 const std::shared_ptr
<const SfxFilter
>& pStoreToFilter
,
811 const OUString
* pStoreToFilterOptions
,
812 const uno::Sequence
< beans::PropertyValue
>* pSaveToFilterData
,
813 const bool bIsPDFexport
,
814 SfxObjectShell
* xObjectShell
,
815 SwWrtShell
& rWorkShell
,
816 OUString
* const decodedURL
= nullptr )
818 OUString url
= pFileURL
->GetMainURL( INetURLObject::DecodeMechanism::NONE
);
822 SfxMedium
* pDstMed
= new SfxMedium( url
, StreamMode::STD_READWRITE
);
823 pDstMed
->SetFilter( pStoreToFilter
);
824 if( pStoreToFilterOptions
)
825 pDstMed
->GetItemSet().Put( SfxStringItem(SID_FILE_FILTEROPTIONS
,
826 *pStoreToFilterOptions
));
827 if( pSaveToFilterData
->hasElements() )
828 pDstMed
->GetItemSet().Put( SfxUnoAnyItem(SID_FILTER_DATA
,
829 uno::Any(*pSaveToFilterData
)));
831 // convert fields to text if we are exporting to PDF.
832 // this prevents a second merge while updating the fields
833 // in SwXTextDocument::getRendererCount()
835 rWorkShell
.ConvertFieldsToText();
837 bool bAnyError
= !xObjectShell
->DoSaveAs(*pDstMed
);
838 // Actually this should be a bool... so in case of email and individual
839 // files, where this is set, we skip the recently used handling
840 bAnyError
|= !xObjectShell
->DoSaveCompleted( pDstMed
, !decodedURL
);
841 bAnyError
|= (ERRCODE_NONE
!= xObjectShell
->GetErrorIgnoreWarning());
845 ErrorHandler::HandleError( xObjectShell
->GetErrorIgnoreWarning() );
850 static void lcl_PreparePrinterOptions(
851 const uno::Sequence
< beans::PropertyValue
>& rInPrintOptions
,
852 uno::Sequence
< beans::PropertyValue
>& rOutPrintOptions
)
854 // printing should be done synchronously otherwise the document
855 // might already become invalid during the process
857 rOutPrintOptions
= { comphelper::makePropertyValue(u
"Wait"_ustr
, true) };
859 // copy print options
860 sal_Int32 nIndex
= 1;
861 for( const beans::PropertyValue
& rOption
: rInPrintOptions
)
863 if( rOption
.Name
== "CopyCount" || rOption
.Name
== "FileName"
864 || rOption
.Name
== "Collate" || rOption
.Name
== "Pages"
865 || rOption
.Name
== "Wait" || rOption
.Name
== "PrinterName" )
868 rOutPrintOptions
.realloc( nIndex
+ 1 );
869 auto pOutPrintOptions
= rOutPrintOptions
.getArray();
870 pOutPrintOptions
[ nIndex
].Name
= rOption
.Name
;
871 pOutPrintOptions
[ nIndex
++ ].Value
= rOption
.Value
;
876 static void lcl_PrepareSaveFilterDataOptions(
877 const uno::Sequence
< beans::PropertyValue
>& rInSaveFilterDataptions
,
878 uno::Sequence
< beans::PropertyValue
>& rOutSaveFilterDataOptions
,
879 const OUString
& sPassword
)
881 rOutSaveFilterDataOptions
882 = { comphelper::makePropertyValue(u
"EncryptFile"_ustr
, true),
883 comphelper::makePropertyValue(u
"DocumentOpenPassword"_ustr
, sPassword
) };
885 // copy other options
886 sal_Int32 nIndex
= 2;
887 for( const beans::PropertyValue
& rOption
: rInSaveFilterDataptions
)
889 rOutSaveFilterDataOptions
.realloc( nIndex
+ 1 );
890 auto pOutSaveFilterDataOptions
= rOutSaveFilterDataOptions
.getArray();
891 pOutSaveFilterDataOptions
[ nIndex
].Name
= rOption
.Name
;
892 pOutSaveFilterDataOptions
[ nIndex
++ ].Value
= rOption
.Value
;
898 static SfxObjectShell
* lcl_CreateWorkingDocument(
900 const WorkingDocType aType
, const SwWrtShell
&rSourceWrtShell
,
902 const vcl::Window
*pSourceWindow
,
903 // optional in and output to swap the DB manager
904 SwDBManager
** const ppDBManager
,
906 SwView
** const pView
, SwWrtShell
** const pWrtShell
, rtl::Reference
<SwDoc
>* const pDoc
)
908 const SwDoc
*pSourceDoc
= rSourceWrtShell
.GetDoc();
909 SfxObjectShellRef xWorkObjectShell
= pSourceDoc
->CreateCopy( true, (aType
== WorkingDocType::TARGET
) );
910 SfxViewFrame
* pWorkFrame
= SfxViewFrame::LoadHiddenDocument( *xWorkObjectShell
, SFX_INTERFACE_NONE
);
914 // the created window has to be located at the same position as the source window
915 vcl::Window
& rTargetWindow
= pWorkFrame
->GetFrame().GetWindow();
916 rTargetWindow
.SetPosPixel( pSourceWindow
->GetPosPixel() );
919 SwView
* pWorkView
= static_cast< SwView
* >( pWorkFrame
->GetViewShell() );
921 if (SwWrtShell
* pWorkWrtShell
= pWorkView
->GetWrtShellPtr())
923 pWorkWrtShell
->GetViewOptions()->SetIdle( false );
924 pWorkView
->AttrChangedNotify(nullptr);// in order for SelectShell to be called
925 SwDoc
* pWorkDoc
= pWorkWrtShell
->GetDoc();
926 pWorkDoc
->GetIDocumentUndoRedo().DoUndo( false );
927 pWorkDoc
->ReplaceDocumentProperties( *pSourceDoc
);
929 // import print settings
930 const SwPrintData
&rPrintData
= pSourceDoc
->getIDocumentDeviceAccess().getPrintData();
931 pWorkDoc
->getIDocumentDeviceAccess().setPrintData(rPrintData
);
932 const JobSetup
*pJobSetup
= pSourceDoc
->getIDocumentDeviceAccess().getJobsetup();
934 pWorkDoc
->getIDocumentDeviceAccess().setJobsetup(*pJobSetup
);
936 if( aType
== WorkingDocType::TARGET
)
938 assert( !ppDBManager
);
939 pWorkDoc
->SetInMailMerge( true );
940 pWorkWrtShell
->SetLabelDoc( false );
944 // We have to swap the DBmanager of the new doc, so we also need input
945 assert(ppDBManager
&& *ppDBManager
);
946 SwDBManager
*pWorkDBManager
= pWorkDoc
->GetDBManager();
947 pWorkDoc
->SetDBManager( *ppDBManager
);
948 *ppDBManager
= pWorkDBManager
;
950 if( aType
== WorkingDocType::SOURCE
)
952 // the GetDBData call constructs the data, if it's missing - kind of const...
953 pWorkWrtShell
->ChgDBData( const_cast<SwDoc
*>(pSourceDoc
)->GetDBData() );
954 // some DocumentSettings are currently not copied by SwDoc::CreateCopy
955 pWorkWrtShell
->SetLabelDoc( rSourceWrtShell
.IsLabelDoc() );
956 pWorkDoc
->getIDocumentState().ResetModified();
959 pWorkDoc
->getIDocumentLinksAdministration().EmbedAllLinks();
962 if( pView
) *pView
= pWorkView
;
963 if( pWrtShell
) *pWrtShell
= pWorkWrtShell
;
964 if( pDoc
) *pDoc
= pWorkDoc
;
967 return xWorkObjectShell
.get();
970 static rtl::Reference
<SwMailMessage
> lcl_CreateMailFromDoc(
971 const SwMergeDescriptor
&rMergeDescriptor
,
972 const OUString
&sFileURL
, const OUString
&sMailRecipient
,
973 const OUString
&sMailBodyMimeType
, rtl_TextEncoding sMailEncoding
,
974 const OUString
&sAttachmentMimeType
)
976 rtl::Reference
<SwMailMessage
> pMessage
= new SwMailMessage
;
977 if( rMergeDescriptor
.pMailMergeConfigItem
->IsMailReplyTo() )
978 pMessage
->setReplyToAddress(rMergeDescriptor
.pMailMergeConfigItem
->GetMailReplyTo());
979 pMessage
->addRecipient( sMailRecipient
);
980 pMessage
->SetSenderAddress( rMergeDescriptor
.pMailMergeConfigItem
->GetMailAddress() );
982 OUStringBuffer sBody
;
983 if( rMergeDescriptor
.bSendAsAttachment
)
985 sBody
= rMergeDescriptor
.sMailBody
;
986 mail::MailAttachment aAttach
;
987 aAttach
.Data
= new SwMailTransferable( sFileURL
,
988 rMergeDescriptor
.sAttachmentName
, sAttachmentMimeType
);
989 aAttach
.ReadableName
= rMergeDescriptor
.sAttachmentName
;
990 pMessage
->addAttachment( aAttach
);
994 //read in the temporary file and use it as mail body
995 SfxMedium
aMedium( sFileURL
, StreamMode::READ
);
996 SvStream
* pInStream
= aMedium
.GetInStream();
997 assert( pInStream
&& "no output file created?" );
1001 pInStream
->SetStreamCharSet( sMailEncoding
);
1002 OStringBuffer sLine
;
1003 while ( pInStream
->ReadLine( sLine
) )
1005 sBody
.append(OStringToOUString( sLine
, sMailEncoding
) + "\n");
1008 pMessage
->setSubject( rMergeDescriptor
.sSubject
);
1009 uno::Reference
< datatransfer::XTransferable
> xBody
=
1010 new SwMailTransferable( sBody
.makeStringAndClear(), sMailBodyMimeType
);
1011 pMessage
->setBody( xBody
);
1013 for( const OUString
& sCcRecipient
: rMergeDescriptor
.aCopiesTo
)
1014 pMessage
->addCcRecipient( sCcRecipient
);
1015 for( const OUString
& sBccRecipient
: rMergeDescriptor
.aBlindCopiesTo
)
1016 pMessage
->addBccRecipient( sBccRecipient
);
1021 class SwDBManager::MailDispatcherListener_Impl
: public IMailDispatcherListener
1023 SwDBManager
&m_rDBManager
;
1026 explicit MailDispatcherListener_Impl( SwDBManager
&rDBManager
)
1027 : m_rDBManager( rDBManager
) {}
1029 virtual void idle() override
{}
1031 virtual void mailDelivered( uno::Reference
< mail::XMailMessage
> xMessage
) override
1033 std::unique_lock
aGuard( m_rDBManager
.m_pImpl
->m_aAllEmailSendMutex
);
1034 if ( m_rDBManager
.m_pImpl
->m_xLastMessage
== xMessage
)
1035 m_rDBManager
.m_pImpl
->m_xLastMessage
.clear();
1038 virtual void mailDeliveryError( ::rtl::Reference
<MailDispatcher
> xMailDispatcher
,
1039 uno::Reference
< mail::XMailMessage
>, const OUString
& ) override
1041 std::unique_lock
aGuard( m_rDBManager
.m_pImpl
->m_aAllEmailSendMutex
);
1042 m_rDBManager
.m_aMergeStatus
= MergeStatus::Error
;
1043 m_rDBManager
.m_pImpl
->m_xLastMessage
.clear();
1044 xMailDispatcher
->stop();
1049 * Please have a look at the README in the same directory, before you make
1050 * larger changes in this function!
1052 bool SwDBManager::MergeMailFiles(SwWrtShell
* pSourceShell
,
1053 const SwMergeDescriptor
& rMergeDescriptor
)
1055 // deconstruct mail merge type for better readability.
1056 // uppercase naming is intentional!
1057 const bool bMT_EMAIL
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_EMAIL
;
1058 const bool bMT_SHELL
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_SHELL
;
1059 const bool bMT_PRINTER
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_PRINTER
;
1060 const bool bMT_FILE
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_FILE
;
1062 //check if the doc is synchronized and contains at least one linked section
1063 const bool bSynchronizedDoc
= pSourceShell
->IsLabelDoc() && pSourceShell
->GetSectionFormatCount() > 1;
1064 const bool bNeedsTempFiles
= ( bMT_EMAIL
|| bMT_FILE
);
1065 const bool bIsMergeSilent
= IsMergeSilent();
1067 bool bCheckSingleFile_
= rMergeDescriptor
.bCreateSingleFile
;
1068 OUString sPrefix_
= rMergeDescriptor
.sPrefix
;
1071 assert( !rMergeDescriptor
.bPrefixIsFilename
);
1072 assert(!bCheckSingleFile_
);
1073 bCheckSingleFile_
= false;
1075 else if( bMT_SHELL
|| bMT_PRINTER
)
1077 assert(bCheckSingleFile_
);
1078 bCheckSingleFile_
= true;
1079 assert(sPrefix_
.isEmpty());
1082 const bool bCreateSingleFile
= bCheckSingleFile_
;
1083 const OUString sDescriptorPrefix
= sPrefix_
;
1085 // Setup for dumping debugging documents
1086 static const sal_Int32 nMaxDumpDocs
= []() {
1087 if (const char* sEnv
= getenv("SW_DEBUG_MAILMERGE_DOCS"))
1088 return OUString(sEnv
, strlen(sEnv
), osl_getThreadTextEncoding()).toInt32();
1090 return sal_Int32(0);
1093 ::rtl::Reference
< MailDispatcher
> xMailDispatcher
;
1094 ::rtl::Reference
< IMailDispatcherListener
> xMailListener
;
1095 OUString sMailBodyMimeType
;
1096 rtl_TextEncoding sMailEncoding
= ::osl_getThreadTextEncoding();
1098 uno::Reference
< beans::XPropertySet
> xColumnProp
;
1099 uno::Reference
< beans::XPropertySet
> xPasswordColumnProp
;
1101 // Check for (mandatory) email or (optional) filename column
1102 SwDBFormatData aColumnDBFormat
;
1103 bool bColumnName
= !rMergeDescriptor
.sDBcolumn
.isEmpty();
1104 bool bPasswordColumnName
= !rMergeDescriptor
.sDBPasswordColumn
.isEmpty();
1113 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( m_pImpl
->pMergeData
->xResultSet
, uno::UNO_QUERY
);
1114 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
1115 if( !xCols
->hasByName( rMergeDescriptor
.sDBcolumn
) )
1117 uno::Any aCol
= xCols
->getByName( rMergeDescriptor
.sDBcolumn
);
1118 aCol
>>= xColumnProp
;
1120 if(bPasswordColumnName
)
1122 aCol
= xCols
->getByName( rMergeDescriptor
.sDBPasswordColumn
);
1123 aCol
>>= xPasswordColumnProp
;
1126 aColumnDBFormat
.xFormatter
= m_pImpl
->pMergeData
->xFormatter
;
1127 aColumnDBFormat
.aNullDate
= m_pImpl
->pMergeData
->aNullDate
;
1131 // Reset internal mail accounting data
1133 std::unique_lock
aGuard(m_pImpl
->m_aAllEmailSendMutex
);
1134 m_pImpl
->m_xLastMessage
.clear();
1137 xMailDispatcher
.set( new MailDispatcher(rMergeDescriptor
.xSmtpServer
) );
1138 xMailListener
= new MailDispatcherListener_Impl( *this );
1139 xMailDispatcher
->addListener( xMailListener
);
1140 if(!rMergeDescriptor
.bSendAsAttachment
&& rMergeDescriptor
.bSendAsHTML
)
1142 sMailBodyMimeType
= "text/html; charset=utf-8";
1143 sMailEncoding
= RTL_TEXTENCODING_UTF8
;
1146 sMailBodyMimeType
= "text/plain; charset=UTF-8; format=flowed";
1150 SwDocShell
*pSourceDocSh
= pSourceShell
->GetView().GetDocShell();
1152 // setup the output format
1153 std::shared_ptr
<const SfxFilter
> pStoreToFilter
= SwIoSystem::GetFileFilter(
1154 pSourceDocSh
->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE
));
1155 SfxFilterContainer
* pFilterContainer
= SwDocShell::Factory().GetFilterContainer();
1156 const OUString
* pStoreToFilterOptions
= nullptr;
1158 // if a save_to filter is set then use it - otherwise use the default
1159 if( bMT_EMAIL
&& !rMergeDescriptor
.bSendAsAttachment
)
1161 OUString sExtension
= rMergeDescriptor
.bSendAsHTML
? u
"html"_ustr
: u
"txt"_ustr
;
1162 pStoreToFilter
= pFilterContainer
->GetFilter4Extension(sExtension
, SfxFilterFlags::EXPORT
);
1164 else if( !rMergeDescriptor
.sSaveToFilter
.isEmpty())
1166 std::shared_ptr
<const SfxFilter
> pFilter
=
1167 pFilterContainer
->GetFilter4FilterName( rMergeDescriptor
.sSaveToFilter
);
1170 pStoreToFilter
= std::move(pFilter
);
1171 if(!rMergeDescriptor
.sSaveToFilterOptions
.isEmpty())
1172 pStoreToFilterOptions
= &rMergeDescriptor
.sSaveToFilterOptions
;
1175 const bool bIsPDFexport
= pStoreToFilter
&& pStoreToFilter
->GetFilterName() == "writer_pdf_Export";
1176 const bool bIsMultiFile
= bMT_FILE
&& !bCreateSingleFile
;
1178 m_aMergeStatus
= MergeStatus::Ok
;
1180 // in case of creating a single resulting file this has to be created here
1181 SwView
* pTargetView
= rMergeDescriptor
.pMailMergeConfigItem
?
1182 rMergeDescriptor
.pMailMergeConfigItem
->GetTargetView() : nullptr;
1183 SwWrtShell
* pTargetShell
= nullptr;
1184 SfxObjectShellRef xTargetDocShell
;
1185 rtl::Reference
<SwDoc
> pTargetDoc
;
1187 std::unique_ptr
< utl::TempFileNamed
> aTempFile
;
1188 sal_uInt16 nStartingPageNo
= 0;
1190 std::shared_ptr
<weld::GenericDialogController
> xProgressDlg
;
1194 vcl::Window
*pSourceWindow
= nullptr;
1195 if( !bIsMergeSilent
)
1197 // construct the process dialog
1198 pSourceWindow
= &pSourceShell
->GetView().GetEditWin();
1200 xProgressDlg
= std::make_shared
<CreateMonitor
>(pSourceWindow
->GetFrameWeld());
1203 xProgressDlg
= std::make_shared
<PrintMonitor
>(pSourceWindow
->GetFrameWeld());
1204 static_cast<PrintMonitor
*>(xProgressDlg
.get())->set_title(
1205 pSourceDocSh
->GetTitle(22));
1207 weld::DialogController::runAsync(xProgressDlg
, [this, &xProgressDlg
](sal_Int32 nResult
){
1208 if (nResult
== RET_CANCEL
)
1210 xProgressDlg
.reset();
1213 Application::Reschedule( true );
1216 if( bCreateSingleFile
&& !pTargetView
)
1218 // create a target docshell to put the merged document into
1219 xTargetDocShell
= lcl_CreateWorkingDocument( WorkingDocType::TARGET
,
1220 *pSourceShell
, bMT_SHELL
? pSourceWindow
: nullptr,
1221 nullptr, &pTargetView
, &pTargetShell
, &pTargetDoc
);
1224 lcl_SaveDebugDoc( xTargetDocShell
.get(), "MergeDoc" );
1226 else if( pTargetView
)
1228 pTargetShell
= pTargetView
->GetWrtShellPtr();
1231 pTargetDoc
= pTargetShell
->GetDoc();
1232 xTargetDocShell
= pTargetView
->GetDocShell();
1236 if( bCreateSingleFile
)
1238 // determine the page style and number used at the start of the source document
1239 pSourceShell
->SttEndDoc(true);
1240 nStartingPageNo
= pSourceShell
->GetVirtPageNum();
1243 // Progress, to prohibit KeyInputs
1244 SfxProgress
aProgress(pSourceDocSh
, OUString(), 1);
1246 // lock all dispatchers
1247 SfxViewFrame
* pViewFrame
= SfxViewFrame::GetFirst(pSourceDocSh
);
1250 pViewFrame
->GetDispatcher()->Lock(true);
1251 pViewFrame
= SfxViewFrame::GetNext(*pViewFrame
, pSourceDocSh
);
1254 sal_Int32 nDocNo
= 1;
1256 // For single file mode, the number of pages in the target document so far, which is used
1257 // by AppendDoc() to adjust position of page-bound objects. Getting this information directly
1258 // from the target doc would require repeated layouts of the doc, which is expensive, but
1259 // it can be manually computed from the source documents (for which we do layouts, so the page
1260 // count is known, and there is a blank page between each of them in the target document).
1261 int targetDocPageCount
= 0;
1263 if( !bIsMergeSilent
&& !bMT_PRINTER
)
1265 sal_Int32 nRecordCount
= 1;
1266 lcl_getCountFromResultSet( nRecordCount
, m_pImpl
->pMergeData
.get() );
1268 // Synchronized docs don't auto-advance the record set, but there is a
1269 // "security" check, which will always advance the record set, if there
1270 // is no "next record" field in a synchronized doc => nRecordPerDoc > 0
1271 sal_Int32 nRecordPerDoc
= pSourceShell
->GetDoc()
1272 ->getIDocumentFieldsAccess().GetRecordsPerDocument();
1273 if ( bSynchronizedDoc
&& (nRecordPerDoc
> 1) )
1275 assert( nRecordPerDoc
> 0 );
1277 sal_Int32 nMaxDocs
= nRecordCount
/ nRecordPerDoc
;
1278 if ( 0 != nRecordCount
% nRecordPerDoc
)
1280 static_cast<CreateMonitor
*>(xProgressDlg
.get())->SetTotalCount(nMaxDocs
);
1283 sal_Int32 nStartRow
, nEndRow
;
1284 bool bFreezedLayouts
= false;
1285 // to collect temporary email files
1286 std::vector
< OUString
> aFilesToRemove
;
1288 // The SfxObjectShell will be closed explicitly later but
1289 // it is more safe to use SfxObjectShellLock here
1290 SfxObjectShellLock xWorkDocSh
;
1291 SwView
* pWorkView
= nullptr;
1292 rtl::Reference
<SwDoc
> pWorkDoc
;
1293 SwDBManager
* pWorkDocOrigDBManager
= nullptr;
1294 SwWrtShell
* pWorkShell
= nullptr;
1295 bool bWorkDocInitialized
= false;
1299 nStartRow
= m_pImpl
->pMergeData
? m_pImpl
->pMergeData
->xResultSet
->getRow() : 0;
1301 OUString sColumnData
;
1303 // Read the indicated data column, which should contain a valid mail
1304 // address or an optional file name
1305 if( bMT_EMAIL
|| bColumnName
)
1307 sColumnData
= GetDBField( xColumnProp
, aColumnDBFormat
);
1310 // create a new temporary file name - only done once in case of bCreateSingleFile
1311 if( bNeedsTempFiles
&& ( !bWorkDocInitialized
|| !bCreateSingleFile
))
1313 OUString sPrefix
= sDescriptorPrefix
;
1316 //#i97667# if the name is from a database field then it will be used _as is_
1317 if( bColumnName
&& !bMT_EMAIL
)
1319 if (!sColumnData
.isEmpty())
1320 sLeading
= sColumnData
;
1326 INetURLObject
aEntry( sPrefix
);
1327 sLeading
= aEntry
.GetBase();
1328 aEntry
.removeSegment();
1329 sPrefix
= aEntry
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1332 OUString
sExt(comphelper::string::stripStart(pStoreToFilter
->GetDefaultExtension(), '*'));
1333 aTempFile
.reset( new utl::TempFileNamed(sLeading
, sColumnData
.isEmpty(), sExt
, &sPrefix
, true) );
1334 if( !aTempFile
->IsValid() )
1336 ErrorHandler::HandleError( ERRCODE_IO_NOTSUPPORTED
);
1337 m_aMergeStatus
= MergeStatus::Error
;
1341 uno::Sequence
< beans::PropertyValue
> aSaveToFilterDataOptions( rMergeDescriptor
.aSaveToFilterData
);
1343 if( bMT_EMAIL
|| bPasswordColumnName
)
1345 OUString sPasswordColumnData
= GetDBField( xPasswordColumnProp
, aColumnDBFormat
);
1346 lcl_PrepareSaveFilterDataOptions( rMergeDescriptor
.aSaveToFilterData
, aSaveToFilterDataOptions
, sPasswordColumnData
);
1351 std::unique_ptr
< INetURLObject
> aTempFileURL
;
1352 if( bNeedsTempFiles
)
1353 aTempFileURL
.reset( new INetURLObject(aTempFile
->GetURL()));
1354 if( !bIsMergeSilent
) {
1356 static_cast<CreateMonitor
*>(xProgressDlg
.get())->SetCurrentPosition(nDocNo
);
1358 PrintMonitor
*pPrintMonDlg
= static_cast<PrintMonitor
*>(xProgressDlg
.get());
1359 pPrintMonDlg
->m_xPrinter
->set_label(bNeedsTempFiles
1360 ? aTempFileURL
->GetBase() : pSourceDocSh
->GetTitle( 2));
1361 OUString sStat
= SwResId(STR_STATSTR_LETTER
) + " " + OUString::number( nDocNo
);
1362 pPrintMonDlg
->m_xPrintInfo
->set_label(sStat
);
1364 //TODO xProgressDlg->queue_draw();
1367 Scheduler::ProcessEventsToIdle();
1369 // Create a copy of the source document and work with that one instead of the source.
1370 // If we're not in the single file mode (which requires modifying the document for the merging),
1371 // it is enough to do this just once. Currently PDF also has to be treated special.
1372 if( !bWorkDocInitialized
|| bCreateSingleFile
|| bIsPDFexport
|| bIsMultiFile
)
1374 assert( !xWorkDocSh
.Is());
1375 pWorkDocOrigDBManager
= this;
1376 xWorkDocSh
= lcl_CreateWorkingDocument( WorkingDocType::COPY
,
1377 *pSourceShell
, nullptr, &pWorkDocOrigDBManager
,
1378 &pWorkView
, &pWorkShell
, &pWorkDoc
);
1379 if ( (nMaxDumpDocs
< 0) || (nDocNo
<= nMaxDumpDocs
) )
1380 lcl_SaveDebugDoc( xWorkDocSh
, "WorkDoc", nDocNo
);
1382 // #i69458# lock fields to prevent access to the result set while calculating layout
1383 // tdf#92324: and do not unlock: keep document locked during printing to avoid
1384 // ExpFields update during printing, generation of preview, etc.
1385 pWorkShell
->LockExpFields();
1386 pWorkShell
->CalcLayout();
1387 // tdf#121168: Now force correct page descriptions applied to page frames. Without
1388 // this, e.g., page frames starting with sections could have page descriptions set
1389 // wrong. This would lead to wrong page styles applied in SwDoc::AppendDoc below.
1390 pWorkShell
->GetViewOptions()->SetIdle(true);
1391 for (auto aLayout
: pWorkShell
->GetDoc()->GetAllLayouts())
1393 aLayout
->FreezeLayout(false);
1394 aLayout
->AllCheckPageDescs();
1398 lcl_emitEvent(SfxEventHintId::SwEventFieldMerge
, STR_SW_EVENT_FIELD_MERGE
, xWorkDocSh
);
1400 // tdf#92324: Allow ExpFields update only by explicit instruction to avoid
1401 // database cursor movement on any other fields update, for example during
1402 // print preview and other operations
1403 if ( pWorkShell
->IsExpFieldsLocked() )
1404 pWorkShell
->UnlockExpFields();
1405 pWorkShell
->SwViewShell::UpdateFields();
1406 pWorkShell
->LockExpFields();
1408 lcl_emitEvent(SfxEventHintId::SwEventFieldMergeFinished
, STR_SW_EVENT_FIELD_MERGE_FINISHED
, xWorkDocSh
);
1410 // also emit MailMergeEvent on XInterface if possible
1411 const SwXMailMerge
*pEvtSrc
= GetMailMergeEvtSrc();
1414 rtl::Reference
< SwXMailMerge
> xRef(
1415 const_cast<SwXMailMerge
*>(pEvtSrc
) );
1416 text::MailMergeEvent
aEvt( static_cast<text::XMailMergeBroadcaster
*>(xRef
.get()), xWorkDocSh
->GetModel() );
1417 SolarMutexReleaser rel
;
1418 xRef
->LaunchMailMergeEvent( aEvt
);
1421 // working copy is merged - prepare final steps depending on merge options
1423 if( bCreateSingleFile
)
1425 assert( pTargetShell
&& "no target shell available!" );
1427 // prepare working copy and target to append
1429 pWorkDoc
->RemoveInvisibleContent();
1430 // remove of invisible content has influence on page count and so on fields for page count,
1431 // therefore layout has to be updated before fields are converted to text
1432 pWorkShell
->CalcLayout();
1433 pWorkShell
->ConvertFieldsToText();
1434 pWorkShell
->SetNumberingRestart();
1435 if( bSynchronizedDoc
)
1437 lcl_RemoveSectionLinks( *pWorkShell
);
1440 if ( (nMaxDumpDocs
< 0) || (nDocNo
<= nMaxDumpDocs
) )
1441 lcl_SaveDebugDoc( xWorkDocSh
, "WorkDoc", nDocNo
);
1443 // append the working document to the target document
1444 if( targetDocPageCount
% 2 == 1 )
1445 ++targetDocPageCount
; // Docs always start on odd pages (so offset must be even).
1446 SwNodeIndex appendedDocStart
= pTargetDoc
->AppendDoc( *pWorkDoc
,
1447 nStartingPageNo
, !bWorkDocInitialized
, targetDocPageCount
, nDocNo
);
1448 targetDocPageCount
+= pWorkShell
->GetPageCnt();
1450 if ( (nMaxDumpDocs
< 0) || (nDocNo
<= nMaxDumpDocs
) )
1451 lcl_SaveDebugDoc( xTargetDocShell
.get(), "MergeDoc" );
1455 SwDocMergeInfo aMergeInfo
;
1456 // Name of the mark is actually irrelevant, UNO bookmarks have internals names.
1457 aMergeInfo
.startPageInTarget
= pTargetDoc
->getIDocumentMarkAccess()->makeMark(
1458 SwPaM(appendedDocStart
), u
""_ustr
, IDocumentMarkAccess::MarkType::UNO_BOOKMARK
,
1459 ::sw::mark::InsertMode::New
);
1460 aMergeInfo
.nDBRow
= nStartRow
;
1461 rMergeDescriptor
.pMailMergeConfigItem
->AddMergedDocument( aMergeInfo
);
1466 assert( bNeedsTempFiles
);
1467 assert( pWorkShell
->IsExpFieldsLocked() );
1469 if (bIsMultiFile
&& pWorkDoc
->HasInvisibleContent())
1471 pWorkDoc
->RemoveInvisibleContent();
1472 pWorkShell
->CalcLayout();
1473 pWorkShell
->ConvertFieldsToText();
1476 // fields are locked, so it's fine to
1477 // restore the old / empty DB manager for save
1478 pWorkDoc
->SetDBManager( pWorkDocOrigDBManager
);
1480 // save merged document
1482 if( !lcl_SaveDoc( aTempFileURL
.get(), pStoreToFilter
, pStoreToFilterOptions
,
1483 &aSaveToFilterDataOptions
, bIsPDFexport
,
1484 xWorkDocSh
, *pWorkShell
, &sFileURL
) )
1486 m_aMergeStatus
= MergeStatus::Error
;
1489 // back to the MM DB manager
1490 pWorkDoc
->SetDBManager( this );
1492 if( bMT_EMAIL
&& !IsMergeError() )
1494 // schedule file for later removal
1495 aFilesToRemove
.push_back( sFileURL
);
1497 if( !SwMailMergeHelper::CheckMailAddress( sColumnData
) )
1499 OSL_FAIL("invalid e-Mail address in database column");
1503 uno::Reference
< mail::XMailMessage
> xMessage
= lcl_CreateMailFromDoc(
1504 rMergeDescriptor
, sFileURL
, sColumnData
, sMailBodyMimeType
,
1505 sMailEncoding
, pStoreToFilter
->GetMimeType() );
1508 std::unique_lock
aGuard( m_pImpl
->m_aAllEmailSendMutex
);
1509 m_pImpl
->m_xLastMessage
.set( xMessage
);
1510 xMailDispatcher
->enqueueMailMessage( xMessage
);
1511 if( !xMailDispatcher
->isStarted() )
1512 xMailDispatcher
->start();
1517 if( bCreateSingleFile
|| bIsPDFexport
|| bIsMultiFile
)
1519 pWorkDoc
->SetDBManager( pWorkDocOrigDBManager
);
1521 xWorkDocSh
->DoClose();
1522 xWorkDocSh
= nullptr;
1526 bWorkDocInitialized
= true;
1528 nEndRow
= m_pImpl
->pMergeData
? m_pImpl
->pMergeData
->xResultSet
->getRow() : 0;
1530 // Freeze the layouts of the target document after the first inserted
1531 // sub-document, to get the correct PageDesc.
1532 if(!bFreezedLayouts
&& bCreateSingleFile
)
1534 for ( auto aLayout
: pTargetShell
->GetDoc()->GetAllLayouts() )
1535 aLayout
->FreezeLayout(true);
1536 bFreezedLayouts
= true;
1538 } while( IsMergeOk() &&
1539 ((bSynchronizedDoc
&& (nStartRow
!= nEndRow
)) ? IsValidMergeRecord() : ToNextMergeRecord()));
1541 if ( xWorkDocSh
.Is() && pWorkView
->GetWrtShell().IsExpFieldsLocked() )
1543 // Unlock document fields after merge complete
1544 pWorkView
->GetWrtShell().UnlockExpFields();
1547 if( !bCreateSingleFile
)
1550 Printer::FinishPrintJob( pWorkView
->GetPrinterController());
1554 pWorkDoc
->SetDBManager(pWorkDocOrigDBManager
);
1555 if (xWorkDocSh
.Is())
1556 xWorkDocSh
->DoClose();
1559 else if( IsMergeOk() ) // && bCreateSingleFile
1561 Application::Reschedule( true );
1563 // sw::DocumentLayoutManager::CopyLayoutFormat() did not generate
1564 // unique fly names, do it here once.
1565 pTargetDoc
->SetInMailMerge(false);
1566 pTargetDoc
->SetAllUniqueFlyNames();
1568 // Unfreeze target document layouts and correct all PageDescs.
1569 SAL_INFO( "sw.pageframe", "(MergeMailFiles pTargetShell->CalcLayout in" );
1570 pTargetShell
->CalcLayout();
1571 SAL_INFO( "sw.pageframe", "MergeMailFiles pTargetShell->CalcLayout out)" );
1572 pTargetShell
->GetViewOptions()->SetIdle( true );
1573 pTargetDoc
->GetIDocumentUndoRedo().DoUndo( true );
1574 for ( auto aLayout
: pTargetShell
->GetDoc()->GetAllLayouts() )
1576 aLayout
->FreezeLayout(false);
1577 aLayout
->AllCheckPageDescs();
1580 Application::Reschedule( true );
1582 if( IsMergeOk() && bMT_FILE
)
1584 // save merged document
1585 assert( aTempFile
);
1586 INetURLObject aTempFileURL
;
1587 if (sDescriptorPrefix
.isEmpty() || !rMergeDescriptor
.bPrefixIsFilename
)
1588 aTempFileURL
.SetURL( aTempFile
->GetURL() );
1591 aTempFileURL
.SetURL(sDescriptorPrefix
);
1592 // remove the unneeded temporary file
1593 aTempFile
->EnableKillingFile();
1595 if( !lcl_SaveDoc( &aTempFileURL
, pStoreToFilter
,
1596 pStoreToFilterOptions
, &rMergeDescriptor
.aSaveToFilterData
,
1597 bIsPDFexport
, xTargetDocShell
.get(), *pTargetShell
) )
1599 m_aMergeStatus
= MergeStatus::Error
;
1602 else if( IsMergeOk() && bMT_PRINTER
)
1604 // print the target document
1605 uno::Sequence
< beans::PropertyValue
> aOptions( rMergeDescriptor
.aPrintOptions
);
1606 lcl_PreparePrinterOptions( rMergeDescriptor
.aPrintOptions
, aOptions
);
1607 pTargetView
->ExecPrint( aOptions
, bIsMergeSilent
, false/*bPrintAsync*/ );
1611 // we also show canceled documents, as long as there was no error
1612 if( !IsMergeError() && bMT_SHELL
)
1613 // leave docshell available for caller (e.g. MM wizard)
1614 rMergeDescriptor
.pMailMergeConfigItem
->SetTargetView( pTargetView
);
1615 else if( xTargetDocShell
.is() )
1616 xTargetDocShell
->DoClose();
1618 Application::Reschedule( true );
1622 xProgressDlg
->response(RET_OK
);
1625 // unlock all dispatchers
1626 pViewFrame
= SfxViewFrame::GetFirst(pSourceDocSh
);
1629 pViewFrame
->GetDispatcher()->Lock(false);
1630 pViewFrame
= SfxViewFrame::GetNext(*pViewFrame
, pSourceDocSh
);
1633 SwModule::get()->SetView(&pSourceShell
->GetView());
1635 if( xMailDispatcher
.is() )
1639 // TODO: Instead of polling via an AutoTimer, post an Idle event,
1640 // if the main loop has been made thread-safe.
1641 AutoTimer
aEmailDispatcherPollTimer("sw::SwDBManager aEmailDispatcherPollTimer");
1642 aEmailDispatcherPollTimer
.SetTimeout( 500 );
1643 aEmailDispatcherPollTimer
.Start();
1644 while( IsMergeOk() && m_pImpl
->m_xLastMessage
.is() && !Application::IsQuit())
1645 Application::Yield();
1646 aEmailDispatcherPollTimer
.Stop();
1648 xMailDispatcher
->stop();
1649 xMailDispatcher
->shutdown();
1652 // remove the temporary files
1653 // has to be done after xMailDispatcher is finished, as mails may be
1654 // delivered as message attachments!
1655 for( const OUString
&sFileURL
: aFilesToRemove
)
1656 SWUnoHelper::UCB_DeleteFile( sFileURL
);
1658 catch (const uno::Exception
&)
1662 xProgressDlg
->response(RET_CANCEL
);
1666 return !IsMergeError();
1669 void SwDBManager::MergeCancel()
1671 if (m_aMergeStatus
< MergeStatus::Cancel
)
1672 m_aMergeStatus
= MergeStatus::Cancel
;
1675 // determine the column's Numberformat and transfer to the forwarded Formatter,
1677 sal_uInt32
SwDBManager::GetColumnFormat( const OUString
& rDBName
,
1678 const OUString
& rTableName
,
1679 const OUString
& rColNm
,
1680 SvNumberFormatter
* pNFormatr
,
1681 LanguageType nLanguage
)
1683 sal_uInt32 nRet
= 0;
1686 uno::Reference
< sdbc::XDataSource
> xSource
;
1687 uno::Reference
< sdbc::XConnection
> xConnection
;
1688 bool bUseMergeData
= false;
1689 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp
;
1690 bool bDisposeConnection
= false;
1691 if(m_pImpl
->pMergeData
&&
1692 ((m_pImpl
->pMergeData
->sDataSource
== rDBName
&& m_pImpl
->pMergeData
->sCommand
== rTableName
) ||
1693 (rDBName
.isEmpty() && rTableName
.isEmpty())))
1695 xConnection
= m_pImpl
->pMergeData
->xConnection
;
1696 xSource
= SwDBManager::getDataSourceAsParent(xConnection
,rDBName
);
1697 bUseMergeData
= true;
1698 xColsSupp
.set(m_pImpl
->pMergeData
->xResultSet
, css::uno::UNO_QUERY
);
1700 if(!xConnection
.is())
1703 aData
.sDataSource
= rDBName
;
1704 aData
.sCommand
= rTableName
;
1705 aData
.nCommandType
= -1;
1706 SwDSParam
* pParam
= FindDSData(aData
, false);
1707 if(pParam
&& pParam
->xConnection
.is())
1709 xConnection
= pParam
->xConnection
;
1710 xColsSupp
.set(pParam
->xResultSet
, css::uno::UNO_QUERY
);
1714 xConnection
= RegisterConnection( rDBName
);
1715 bDisposeConnection
= true;
1718 m_pImpl
->pMergeData
->xConnection
= xConnection
;
1720 bool bDispose
= !xColsSupp
.is();
1723 xColsSupp
= SwDBManager::GetColumnSupplier(xConnection
, rTableName
);
1727 uno::Reference
<container::XNameAccess
> xCols
;
1730 xCols
= xColsSupp
->getColumns();
1732 catch (const uno::Exception
&)
1734 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Exception in getColumns()");
1736 if(!xCols
.is() || !xCols
->hasByName(rColNm
))
1738 uno::Any aCol
= xCols
->getByName(rColNm
);
1739 uno::Reference
< beans::XPropertySet
> xColumn
;
1741 nRet
= GetColumnFormat(xSource
, xConnection
, xColumn
, pNFormatr
, nLanguage
);
1744 ::comphelper::disposeComponent( xColsSupp
);
1746 if(bDisposeConnection
)
1748 ::comphelper::disposeComponent( xConnection
);
1752 nRet
= pNFormatr
->GetFormatIndex( NF_NUMBER_STANDARD
, LANGUAGE_SYSTEM
);
1757 sal_uInt32
SwDBManager::GetColumnFormat( uno::Reference
< sdbc::XDataSource
> const & xSource_in
,
1758 uno::Reference
< sdbc::XConnection
> const & xConnection
,
1759 uno::Reference
< beans::XPropertySet
> const & xColumn
,
1760 SvNumberFormatter
* pNFormatr
,
1761 LanguageType nLanguage
)
1763 auto xSource
= xSource_in
;
1765 // set the NumberFormat in the doc if applicable
1766 sal_uInt32 nRet
= 0;
1770 uno::Reference
<container::XChild
> xChild(xConnection
, uno::UNO_QUERY
);
1772 xSource
.set(xChild
->getParent(), uno::UNO_QUERY
);
1774 if(xSource
.is() && xConnection
.is() && xColumn
.is() && pNFormatr
)
1776 rtl::Reference
<SvNumberFormatsSupplierObj
> pNumFormat
= new SvNumberFormatsSupplierObj( pNFormatr
);
1777 uno::Reference
< util::XNumberFormats
> xDocNumberFormats
= pNumFormat
->getNumberFormats();
1778 uno::Reference
< util::XNumberFormatTypes
> xDocNumberFormatTypes(xDocNumberFormats
, uno::UNO_QUERY
);
1780 css::lang::Locale
aLocale( LanguageTag( nLanguage
).getLocale());
1782 //get the number formatter of the data source
1783 uno::Reference
<beans::XPropertySet
> xSourceProps(xSource
, uno::UNO_QUERY
);
1784 uno::Reference
< util::XNumberFormats
> xNumberFormats
;
1785 if(xSourceProps
.is())
1787 uno::Any aFormats
= xSourceProps
->getPropertyValue(u
"NumberFormatsSupplier"_ustr
);
1788 if(aFormats
.hasValue())
1790 uno::Reference
<util::XNumberFormatsSupplier
> xSuppl
;
1791 aFormats
>>= xSuppl
;
1794 xNumberFormats
= xSuppl
->getNumberFormats();
1798 bool bUseDefault
= true;
1801 uno::Any aFormatKey
= xColumn
->getPropertyValue(u
"FormatKey"_ustr
);
1802 if(aFormatKey
.hasValue())
1804 sal_Int32 nFormat
= 0;
1805 aFormatKey
>>= nFormat
;
1806 if(xNumberFormats
.is())
1810 uno::Reference
<beans::XPropertySet
> xNumProps
= xNumberFormats
->getByKey( nFormat
);
1811 uno::Any aFormatString
= xNumProps
->getPropertyValue(u
"FormatString"_ustr
);
1812 uno::Any aLocaleVal
= xNumProps
->getPropertyValue(u
"Locale"_ustr
);
1814 aFormatString
>>= sFormat
;
1816 aLocaleVal
>>= aLoc
;
1817 nFormat
= xDocNumberFormats
->queryKey( sFormat
, aLoc
, false );
1818 if(NUMBERFORMAT_ENTRY_NOT_FOUND
== sal::static_int_cast
< sal_uInt32
, sal_Int32
>(nFormat
))
1819 nFormat
= xDocNumberFormats
->addNew( sFormat
, aLoc
);
1822 bUseDefault
= false;
1824 catch (const uno::Exception
&)
1826 TOOLS_WARN_EXCEPTION("sw.mailmerge", "illegal number format key");
1831 catch(const uno::Exception
&)
1833 SAL_WARN("sw.mailmerge", "no FormatKey property found");
1836 nRet
= dbtools::getDefaultNumberFormat(xColumn
, xDocNumberFormatTypes
, aLocale
);
1841 sal_Int32
SwDBManager::GetColumnType( const OUString
& rDBName
,
1842 const OUString
& rTableName
,
1843 const OUString
& rColNm
)
1845 sal_Int32 nRet
= sdbc::DataType::SQLNULL
;
1847 aData
.sDataSource
= rDBName
;
1848 aData
.sCommand
= rTableName
;
1849 aData
.nCommandType
= -1;
1850 SwDSParam
* pParam
= FindDSData(aData
, false);
1851 uno::Reference
< sdbc::XConnection
> xConnection
;
1852 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp
;
1853 bool bDispose
= false;
1854 if(pParam
&& pParam
->xConnection
.is())
1856 xConnection
= pParam
->xConnection
;
1857 xColsSupp
.set( pParam
->xResultSet
, uno::UNO_QUERY
);
1861 xConnection
= RegisterConnection( rDBName
);
1863 if( !xColsSupp
.is() )
1865 xColsSupp
= SwDBManager::GetColumnSupplier(xConnection
, rTableName
);
1870 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
1871 if(xCols
->hasByName(rColNm
))
1873 uno::Any aCol
= xCols
->getByName(rColNm
);
1874 uno::Reference
<beans::XPropertySet
> xCol
;
1876 uno::Any aType
= xCol
->getPropertyValue(u
"Type"_ustr
);
1880 ::comphelper::disposeComponent( xColsSupp
);
1885 uno::Reference
< sdbc::XConnection
> SwDBManager::GetConnection(const OUString
& rDataSource
,
1886 uno::Reference
<sdbc::XDataSource
>& rxSource
, const SwView
*pView
)
1888 uno::Reference
< sdbc::XConnection
> xConnection
;
1889 const uno::Reference
< uno::XComponentContext
>& xContext( ::comphelper::getProcessComponentContext() );
1892 uno::Reference
<sdb::XCompletedConnection
> xComplConnection(dbtools::getDataSource(rDataSource
, xContext
), uno::UNO_QUERY
);
1893 if ( !xComplConnection
)
1895 rxSource
.set(xComplConnection
, uno::UNO_QUERY
);
1896 weld::Window
* pWindow
= pView
? pView
->GetFrameWeld() : nullptr;
1897 uno::Reference
< task::XInteractionHandler
> xHandler( task::InteractionHandler::createWithParent(xContext
, pWindow
? pWindow
->GetXWindow() : nullptr) );
1900 xConnection
= xComplConnection
->connectWithCompletion( xHandler
);
1902 catch(const uno::Exception
&)
1909 uno::Reference
< sdbcx::XColumnsSupplier
> SwDBManager::GetColumnSupplier(uno::Reference
<sdbc::XConnection
> const & xConnection
,
1910 const OUString
& rTableOrQuery
,
1911 SwDBSelect eTableOrQuery
)
1913 uno::Reference
< sdbcx::XColumnsSupplier
> xRet
;
1916 if(eTableOrQuery
== SwDBSelect::UNKNOWN
)
1918 //search for a table with the given command name
1919 uno::Reference
<sdbcx::XTablesSupplier
> xTSupplier(xConnection
, uno::UNO_QUERY
);
1922 uno::Reference
<container::XNameAccess
> xTables
= xTSupplier
->getTables();
1923 eTableOrQuery
= xTables
->hasByName(rTableOrQuery
) ?
1924 SwDBSelect::TABLE
: SwDBSelect::QUERY
;
1927 sal_Int32 nCommandType
= SwDBSelect::TABLE
== eTableOrQuery
?
1928 sdb::CommandType::TABLE
: sdb::CommandType::QUERY
;
1929 uno::Reference
< lang::XMultiServiceFactory
> xMgr( ::comphelper::getProcessServiceFactory() );
1930 uno::Reference
<sdbc::XRowSet
> xRowSet(xMgr
->createInstance(u
"com.sun.star.sdb.RowSet"_ustr
), uno::UNO_QUERY
);
1932 OUString sDataSource
;
1933 uno::Reference
<sdbc::XDataSource
> xSource
= SwDBManager::getDataSourceAsParent(xConnection
, sDataSource
);
1934 uno::Reference
<beans::XPropertySet
> xSourceProperties(xSource
, uno::UNO_QUERY
);
1935 if(xSourceProperties
.is())
1937 xSourceProperties
->getPropertyValue(u
"Name"_ustr
) >>= sDataSource
;
1940 uno::Reference
<beans::XPropertySet
> xRowProperties(xRowSet
, uno::UNO_QUERY
);
1941 xRowProperties
->setPropertyValue(u
"DataSourceName"_ustr
, uno::Any(sDataSource
));
1942 xRowProperties
->setPropertyValue(u
"Command"_ustr
, uno::Any(rTableOrQuery
));
1943 xRowProperties
->setPropertyValue(u
"CommandType"_ustr
, uno::Any(nCommandType
));
1944 xRowProperties
->setPropertyValue(u
"FetchSize"_ustr
, uno::Any(sal_Int32(10)));
1945 xRowProperties
->setPropertyValue(u
"ActiveConnection"_ustr
, uno::Any(xConnection
));
1947 xRet
.set( xRowSet
, uno::UNO_QUERY
);
1949 catch (const uno::Exception
&)
1951 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Exception in SwDBManager::GetColumnSupplier");
1957 OUString
SwDBManager::GetDBField(uno::Reference
<beans::XPropertySet
> const & xColumnProps
,
1958 const SwDBFormatData
& rDBFormatData
,
1961 uno::Reference
< sdb::XColumn
> xColumn(xColumnProps
, uno::UNO_QUERY
);
1963 assert( xColumn
.is() && "SwDBManager::::ImportDBField: illegal arguments" );
1967 uno::Any aType
= xColumnProps
->getPropertyValue(u
"Type"_ustr
);
1968 sal_Int32 eDataType
= sdbc::DataType::SQLNULL
;
1969 aType
>>= eDataType
;
1972 case sdbc::DataType::CHAR
:
1973 case sdbc::DataType::VARCHAR
:
1974 case sdbc::DataType::LONGVARCHAR
:
1977 sRet
= xColumn
->getString();
1978 sRet
= sRet
.replace( '\xb', '\n' ); // MSWord uses \xb as a newline
1980 catch(const sdbc::SQLException
&)
1984 case sdbc::DataType::BIT
:
1985 case sdbc::DataType::BOOLEAN
:
1986 case sdbc::DataType::TINYINT
:
1987 case sdbc::DataType::SMALLINT
:
1988 case sdbc::DataType::INTEGER
:
1989 case sdbc::DataType::BIGINT
:
1990 case sdbc::DataType::FLOAT
:
1991 case sdbc::DataType::REAL
:
1992 case sdbc::DataType::DOUBLE
:
1993 case sdbc::DataType::NUMERIC
:
1994 case sdbc::DataType::DECIMAL
:
1995 case sdbc::DataType::DATE
:
1996 case sdbc::DataType::TIME
:
1997 case sdbc::DataType::TIMESTAMP
:
2002 sRet
= dbtools::DBTypeConversion::getFormattedValue(
2004 rDBFormatData
.xFormatter
,
2005 rDBFormatData
.aLocale
,
2006 rDBFormatData
.aNullDate
);
2009 double fVal
= xColumn
->getDouble();
2010 if(!xColumn
->wasNull())
2016 catch (const uno::Exception
&)
2018 TOOLS_WARN_EXCEPTION("sw.mailmerge", "");
2028 // checks if a desired data source table or query is open
2029 bool SwDBManager::IsDataSourceOpen(const OUString
& rDataSource
,
2030 const OUString
& rTableOrQuery
, bool bMergeShell
)
2032 if(m_pImpl
->pMergeData
)
2034 return ((rDataSource
== m_pImpl
->pMergeData
->sDataSource
2035 && rTableOrQuery
== m_pImpl
->pMergeData
->sCommand
)
2036 || (rDataSource
.isEmpty() && rTableOrQuery
.isEmpty()))
2037 && m_pImpl
->pMergeData
->xResultSet
.is();
2039 else if(!bMergeShell
)
2042 aData
.sDataSource
= rDataSource
;
2043 aData
.sCommand
= rTableOrQuery
;
2044 aData
.nCommandType
= -1;
2045 SwDSParam
* pFound
= FindDSData(aData
, false);
2046 return (pFound
&& pFound
->xResultSet
.is());
2051 // read column data at a specified position
2052 bool SwDBManager::GetColumnCnt(const OUString
& rSourceName
, const OUString
& rTableName
,
2053 const OUString
& rColumnName
, sal_uInt32 nAbsRecordId
,
2054 LanguageType nLanguage
,
2055 OUString
& rResult
, double* pNumber
)
2058 SwDSParam
* pFound
= nullptr;
2059 //check if it's the merge data source
2060 if(m_pImpl
->pMergeData
&&
2061 rSourceName
== m_pImpl
->pMergeData
->sDataSource
&&
2062 rTableName
== m_pImpl
->pMergeData
->sCommand
)
2064 pFound
= m_pImpl
->pMergeData
.get();
2069 aData
.sDataSource
= rSourceName
;
2070 aData
.sCommand
= rTableName
;
2071 aData
.nCommandType
= -1;
2072 pFound
= FindDSData(aData
, false);
2076 //check validity of supplied record Id
2077 if(pFound
->aSelection
.hasElements())
2079 //the destination has to be an element of the selection
2080 bool bFound
= std::any_of(std::cbegin(pFound
->aSelection
), std::cend(pFound
->aSelection
),
2081 [nAbsRecordId
](const uno::Any
& rSelection
) {
2082 sal_Int32 nSelection
= 0;
2083 rSelection
>>= nSelection
;
2084 return nSelection
== static_cast<sal_Int32
>(nAbsRecordId
);
2089 if( pFound
->HasValidRecord() )
2091 sal_Int32 nOldRow
= 0;
2094 nOldRow
= pFound
->xResultSet
->getRow();
2096 catch(const uno::Exception
&)
2100 //position to the desired index
2102 if ( nOldRow
!= static_cast<sal_Int32
>(nAbsRecordId
) )
2103 bMove
= lcl_MoveAbsolute(pFound
, nAbsRecordId
);
2105 bRet
= lcl_GetColumnCnt(pFound
, rColumnName
, nLanguage
, rResult
, pNumber
);
2106 if ( nOldRow
!= static_cast<sal_Int32
>(nAbsRecordId
) )
2107 lcl_MoveAbsolute(pFound
, nOldRow
);
2112 // reads the column data at the current position
2113 bool SwDBManager::GetMergeColumnCnt(const OUString
& rColumnName
, LanguageType nLanguage
,
2114 OUString
&rResult
, double *pNumber
)
2116 if( !IsValidMergeRecord() )
2122 bool bRet
= lcl_GetColumnCnt(m_pImpl
->pMergeData
.get(), rColumnName
, nLanguage
, rResult
, pNumber
);
2126 bool SwDBManager::ToNextMergeRecord()
2128 assert( m_pImpl
->pMergeData
&& m_pImpl
->pMergeData
->xResultSet
.is() && "no data source in merge" );
2129 return lcl_ToNextRecord( m_pImpl
->pMergeData
.get() );
2132 bool SwDBManager::FillCalcWithMergeData( SvNumberFormatter
*pDocFormatter
,
2133 LanguageType nLanguage
, SwCalc
&rCalc
)
2135 if( !IsValidMergeRecord() )
2138 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( m_pImpl
->pMergeData
->xResultSet
, uno::UNO_QUERY
);
2139 if( !xColsSupp
.is() )
2143 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
2144 const uno::Sequence
<OUString
> aColNames
= xCols
->getElementNames();
2147 // add the "record number" variable, as SwCalc::VarLook would.
2148 rCalc
.VarChange( GetAppCharClass().lowercase(
2149 SwFieldType::GetTypeStr(SwFieldTypesEnum::DatabaseSetNumber
) ), GetSelectedRecordId() );
2151 for( const OUString
& rColName
: aColNames
)
2153 // get the column type
2154 sal_Int32 nColumnType
= sdbc::DataType::SQLNULL
;
2155 uno::Any aCol
= xCols
->getByName( rColName
);
2156 uno::Reference
<beans::XPropertySet
> xColumnProps
;
2157 aCol
>>= xColumnProps
;
2158 uno::Any aType
= xColumnProps
->getPropertyValue( u
"Type"_ustr
);
2159 aType
>>= nColumnType
;
2160 double aNumber
= DBL_MAX
;
2162 lcl_GetColumnCnt( m_pImpl
->pMergeData
.get(), xColumnProps
, nLanguage
, aString
, &aNumber
);
2164 sal_uInt32 nFormat
= GetColumnFormat( m_pImpl
->pMergeData
->sDataSource
,
2165 m_pImpl
->pMergeData
->sCommand
,
2166 rColName
, pDocFormatter
, nLanguage
);
2167 // aNumber is overwritten by SwDBField::FormatValue, so store initial status
2168 bool colIsNumber
= aNumber
!= DBL_MAX
;
2169 bool bValidValue
= SwDBField::FormatValue( pDocFormatter
, aString
, nFormat
,
2170 aNumber
, nColumnType
);
2176 aValue
.PutDouble( aNumber
);
2177 aValue
.SetDBvalue( true );
2178 SAL_INFO( "sw.ui", "'" << rColName
<< "': " << aNumber
<< " / " << aString
);
2179 rCalc
.VarChange( rColName
, aValue
);
2185 aValue
.PutString( aString
);
2186 aValue
.SetDBvalue( true );
2187 SAL_INFO( "sw.ui", "'" << rColName
<< "': " << aString
);
2188 rCalc
.VarChange( rColName
, aValue
);
2196 void SwDBManager::ToNextRecord(
2197 const OUString
& rDataSource
, const OUString
& rCommand
)
2199 SwDSParam
* pFound
= nullptr;
2200 if(m_pImpl
->pMergeData
&&
2201 rDataSource
== m_pImpl
->pMergeData
->sDataSource
&&
2202 rCommand
== m_pImpl
->pMergeData
->sCommand
)
2204 pFound
= m_pImpl
->pMergeData
.get();
2209 aData
.sDataSource
= rDataSource
;
2210 aData
.sCommand
= rCommand
;
2211 aData
.nCommandType
= -1;
2212 pFound
= FindDSData(aData
, false);
2214 lcl_ToNextRecord( pFound
);
2217 static bool lcl_ToNextRecord( SwDSParam
* pParam
, const SwDBNextRecord action
)
2221 assert( SwDBNextRecord::NEXT
== action
||
2222 (SwDBNextRecord::FIRST
== action
&& pParam
) );
2223 if( nullptr == pParam
)
2226 if( action
== SwDBNextRecord::FIRST
)
2228 pParam
->nSelectionIndex
= 0;
2229 pParam
->bEndOfDB
= false;
2232 if( !pParam
->HasValidRecord() )
2237 if( pParam
->aSelection
.hasElements() )
2239 if( pParam
->nSelectionIndex
>= pParam
->aSelection
.getLength() )
2240 pParam
->bEndOfDB
= true;
2244 pParam
->aSelection
.getConstArray()[ pParam
->nSelectionIndex
] >>= nPos
;
2245 pParam
->bEndOfDB
= !pParam
->xResultSet
->absolute( nPos
);
2248 else if( action
== SwDBNextRecord::FIRST
)
2250 pParam
->bEndOfDB
= !pParam
->xResultSet
->first();
2254 sal_Int32 nBefore
= pParam
->xResultSet
->getRow();
2255 pParam
->bEndOfDB
= !pParam
->xResultSet
->next();
2256 if( !pParam
->bEndOfDB
&& nBefore
== pParam
->xResultSet
->getRow() )
2258 // next returned true but it didn't move
2259 ::dbtools::throwFunctionSequenceException( pParam
->xResultSet
);
2263 ++pParam
->nSelectionIndex
;
2264 bRet
= !pParam
->bEndOfDB
;
2266 catch( const uno::Exception
& )
2268 // we allow merging with empty databases, so don't warn on init
2269 TOOLS_WARN_EXCEPTION_IF(action
== SwDBNextRecord::NEXT
,
2270 "sw.mailmerge", "exception in ToNextRecord()");
2271 pParam
->bEndOfDB
= true;
2277 // synchronized labels contain a next record field at their end
2278 // to assure that the next page can be created in mail merge
2279 // the cursor position must be validated
2280 bool SwDBManager::IsValidMergeRecord() const
2282 return( m_pImpl
->pMergeData
&& m_pImpl
->pMergeData
->HasValidRecord() );
2285 sal_uInt32
SwDBManager::GetSelectedRecordId()
2287 sal_uInt32 nRet
= 0;
2288 assert( m_pImpl
->pMergeData
&&
2289 m_pImpl
->pMergeData
->xResultSet
.is() && "no data source in merge" );
2290 if(!m_pImpl
->pMergeData
|| !m_pImpl
->pMergeData
->xResultSet
.is())
2294 nRet
= m_pImpl
->pMergeData
->xResultSet
->getRow();
2296 catch(const uno::Exception
&)
2302 bool SwDBManager::ToRecordId(sal_Int32 nSet
)
2304 assert( m_pImpl
->pMergeData
&&
2305 m_pImpl
->pMergeData
->xResultSet
.is() && "no data source in merge" );
2306 if(!m_pImpl
->pMergeData
|| !m_pImpl
->pMergeData
->xResultSet
.is()|| nSet
< 0)
2309 sal_Int32 nAbsPos
= nSet
;
2310 assert(nAbsPos
>= 0);
2311 bRet
= lcl_MoveAbsolute(m_pImpl
->pMergeData
.get(), nAbsPos
);
2312 m_pImpl
->pMergeData
->bEndOfDB
= !bRet
;
2316 bool SwDBManager::OpenDataSource(const OUString
& rDataSource
, const OUString
& rTableOrQuery
)
2319 aData
.sDataSource
= rDataSource
;
2320 aData
.sCommand
= rTableOrQuery
;
2321 aData
.nCommandType
= -1;
2323 SwDSParam
* pFound
= FindDSData(aData
, true);
2324 if(pFound
->xResultSet
.is())
2326 SwDSParam
* pParam
= FindDSConnection(rDataSource
, false);
2327 if(pParam
&& pParam
->xConnection
.is())
2328 pFound
->xConnection
= pParam
->xConnection
;
2329 if(pFound
->xConnection
.is())
2333 uno::Reference
< sdbc::XDatabaseMetaData
> xMetaData
= pFound
->xConnection
->getMetaData();
2336 pFound
->bScrollable
= xMetaData
2337 ->supportsResultSetType(sal_Int32(sdbc::ResultSetType::SCROLL_INSENSITIVE
));
2339 catch(const uno::Exception
&)
2341 // DB driver may not be ODBC 3.0 compliant
2342 pFound
->bScrollable
= true;
2344 pFound
->xStatement
= pFound
->xConnection
->createStatement();
2345 OUString aQuoteChar
= xMetaData
->getIdentifierQuoteString();
2346 OUString sStatement
= "SELECT * FROM " + aQuoteChar
+ rTableOrQuery
+ aQuoteChar
;
2347 pFound
->xResultSet
= pFound
->xStatement
->executeQuery( sStatement
);
2349 //after executeQuery the cursor must be positioned
2350 pFound
->bEndOfDB
= !pFound
->xResultSet
->next();
2351 ++pFound
->nSelectionIndex
;
2353 catch (const uno::Exception
&)
2355 pFound
->xResultSet
= nullptr;
2356 pFound
->xStatement
= nullptr;
2357 pFound
->xConnection
= nullptr;
2360 return pFound
->xResultSet
.is();
2363 uno::Reference
< sdbc::XConnection
> const & SwDBManager::RegisterConnection(OUString
const& rDataSource
)
2365 SwDSParam
* pFound
= SwDBManager::FindDSConnection(rDataSource
, true);
2366 uno::Reference
< sdbc::XDataSource
> xSource
;
2367 if(!pFound
->xConnection
.is())
2369 SwView
* pView
= (m_pDoc
&& m_pDoc
->GetDocShell()) ? m_pDoc
->GetDocShell()->GetView() : nullptr;
2370 pFound
->xConnection
= SwDBManager::GetConnection(rDataSource
, xSource
, pView
);
2373 uno::Reference
<lang::XComponent
> xComponent(pFound
->xConnection
, uno::UNO_QUERY
);
2375 xComponent
->addEventListener(m_pImpl
->m_xDisposeListener
);
2377 catch(const uno::Exception
&)
2381 return pFound
->xConnection
;
2384 sal_uInt32
SwDBManager::GetSelectedRecordId(
2385 const OUString
& rDataSource
, const OUString
& rTableOrQuery
, sal_Int32 nCommandType
)
2387 sal_uInt32 nRet
= 0xffffffff;
2388 //check for merge data source first
2389 if(m_pImpl
->pMergeData
&&
2390 ((rDataSource
== m_pImpl
->pMergeData
->sDataSource
&&
2391 rTableOrQuery
== m_pImpl
->pMergeData
->sCommand
) ||
2392 (rDataSource
.isEmpty() && rTableOrQuery
.isEmpty())) &&
2393 (nCommandType
== -1 || nCommandType
== m_pImpl
->pMergeData
->nCommandType
) &&
2394 m_pImpl
->pMergeData
->xResultSet
.is())
2396 nRet
= GetSelectedRecordId();
2401 aData
.sDataSource
= rDataSource
;
2402 aData
.sCommand
= rTableOrQuery
;
2403 aData
.nCommandType
= nCommandType
;
2404 SwDSParam
* pFound
= FindDSData(aData
, false);
2405 if(pFound
&& pFound
->xResultSet
.is())
2408 { //if a selection array is set the current row at the result set may not be set yet
2409 if(pFound
->aSelection
.hasElements())
2411 sal_Int32 nSelIndex
= pFound
->nSelectionIndex
;
2412 if(nSelIndex
>= pFound
->aSelection
.getLength())
2413 nSelIndex
= pFound
->aSelection
.getLength() -1;
2414 pFound
->aSelection
.getConstArray()[nSelIndex
] >>= nRet
;
2418 nRet
= pFound
->xResultSet
->getRow();
2420 catch(const uno::Exception
&)
2428 // close all data sources - after fields were updated
2429 void SwDBManager::CloseAll(bool bIncludingMerge
)
2431 //the only thing done here is to reset the selection index
2432 //all connections stay open
2433 for (auto & pParam
: m_DataSourceParams
)
2435 if (bIncludingMerge
|| pParam
.get() != m_pImpl
->pMergeData
.get())
2437 pParam
->nSelectionIndex
= 0;
2438 pParam
->bEndOfDB
= false;
2441 if(!m_bInMerge
&& pParam
->xResultSet
.is())
2442 pParam
->xResultSet
->first();
2444 catch(const uno::Exception
&)
2450 SwDSParam
* SwDBManager::FindDSData(const SwDBData
& rData
, bool bCreate
)
2452 //prefer merge data if available
2453 if(m_pImpl
->pMergeData
&&
2454 ((rData
.sDataSource
== m_pImpl
->pMergeData
->sDataSource
&&
2455 rData
.sCommand
== m_pImpl
->pMergeData
->sCommand
) ||
2456 (rData
.sDataSource
.isEmpty() && rData
.sCommand
.isEmpty())) &&
2457 (rData
.nCommandType
== -1 || rData
.nCommandType
== m_pImpl
->pMergeData
->nCommandType
||
2458 (bCreate
&& m_pImpl
->pMergeData
->nCommandType
== -1)))
2460 return m_pImpl
->pMergeData
.get();
2463 SwDSParam
* pFound
= nullptr;
2464 for (size_t nPos
= m_DataSourceParams
.size(); nPos
; nPos
--)
2466 SwDSParam
* pParam
= m_DataSourceParams
[nPos
- 1].get();
2467 if(rData
.sDataSource
== pParam
->sDataSource
&&
2468 rData
.sCommand
== pParam
->sCommand
&&
2469 (rData
.nCommandType
== -1 || rData
.nCommandType
== pParam
->nCommandType
||
2470 (bCreate
&& pParam
->nCommandType
== -1)))
2472 // calls from the calculator may add a connection with an invalid commandtype
2473 //later added "real" data base connections have to re-use the already available
2474 //DSData and set the correct CommandType
2475 if(bCreate
&& pParam
->nCommandType
== -1)
2476 pParam
->nCommandType
= rData
.nCommandType
;
2481 if(bCreate
&& !pFound
)
2483 pFound
= new SwDSParam(rData
);
2484 m_DataSourceParams
.push_back(std::unique_ptr
<SwDSParam
>(pFound
));
2487 uno::Reference
<lang::XComponent
> xComponent(pFound
->xConnection
, uno::UNO_QUERY
);
2489 xComponent
->addEventListener(m_pImpl
->m_xDisposeListener
);
2491 catch(const uno::Exception
&)
2498 SwDSParam
* SwDBManager::FindDSConnection(const OUString
& rDataSource
, bool bCreate
)
2500 //prefer merge data if available
2501 if(m_pImpl
->pMergeData
&& rDataSource
== m_pImpl
->pMergeData
->sDataSource
)
2503 SetAsUsed(rDataSource
);
2504 return m_pImpl
->pMergeData
.get();
2506 SwDSParam
* pFound
= nullptr;
2507 for (const auto & pParam
: m_DataSourceParams
)
2509 if(rDataSource
== pParam
->sDataSource
)
2511 SetAsUsed(rDataSource
);
2512 pFound
= pParam
.get();
2516 if(bCreate
&& !pFound
)
2519 aData
.sDataSource
= rDataSource
;
2520 pFound
= new SwDSParam(aData
);
2521 SetAsUsed(rDataSource
);
2522 m_DataSourceParams
.push_back(std::unique_ptr
<SwDSParam
>(pFound
));
2525 uno::Reference
<lang::XComponent
> xComponent(pFound
->xConnection
, uno::UNO_QUERY
);
2527 xComponent
->addEventListener(m_pImpl
->m_xDisposeListener
);
2529 catch(const uno::Exception
&)
2536 const SwDBData
& SwDBManager::GetAddressDBName()
2538 return SwModule::get()->GetDBConfig()->GetAddressSource();
2541 uno::Sequence
<OUString
> SwDBManager::GetExistingDatabaseNames()
2543 const uno::Reference
<uno::XComponentContext
>& xContext( ::comphelper::getProcessComponentContext() );
2544 uno::Reference
<sdb::XDatabaseContext
> xDBContext
= sdb::DatabaseContext::create(xContext
);
2545 return xDBContext
->getElementNames();
2550 DBConnURIType
GetDBunoType(const INetURLObject
&rURL
)
2552 OUString
sExt(rURL
.GetFileExtension());
2553 DBConnURIType type
= DBConnURIType::UNKNOWN
;
2557 type
= DBConnURIType::ODB
;
2559 else if (sExt
.equalsIgnoreAsciiCase("sxc")
2560 || sExt
.equalsIgnoreAsciiCase("ods")
2561 || sExt
.equalsIgnoreAsciiCase("xls")
2562 || sExt
.equalsIgnoreAsciiCase("xlsx"))
2564 type
= DBConnURIType::CALC
;
2566 else if (sExt
.equalsIgnoreAsciiCase("sxw") || sExt
.equalsIgnoreAsciiCase("odt") || sExt
.equalsIgnoreAsciiCase("doc") || sExt
.equalsIgnoreAsciiCase("docx"))
2568 type
= DBConnURIType::WRITER
;
2570 else if (sExt
.equalsIgnoreAsciiCase("dbf"))
2572 type
= DBConnURIType::DBASE
;
2574 else if (sExt
.equalsIgnoreAsciiCase("csv") || sExt
.equalsIgnoreAsciiCase("txt"))
2576 type
= DBConnURIType::FLAT
;
2579 else if (sExt
.equalsIgnoreAsciiCase("accdb") || sExt
.equalsIgnoreAsciiCase("accde")
2580 || sExt
.equalsIgnoreAsciiCase("mdb") || sExt
.equalsIgnoreAsciiCase("mde"))
2582 type
= DBConnURIType::MSACE
;
2591 uno::Any
GetDBunoURI(const INetURLObject
&rURL
, DBConnURIType
& rType
)
2595 if (rType
== DBConnURIType::UNKNOWN
)
2596 rType
= GetDBunoType(rURL
);
2599 case DBConnURIType::UNKNOWN
:
2600 case DBConnURIType::ODB
:
2602 case DBConnURIType::CALC
:
2604 OUString sDBURL
= "sdbc:calc:" +
2605 rURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2609 case DBConnURIType::WRITER
:
2611 OUString sDBURL
= "sdbc:writer:" +
2612 rURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2616 case DBConnURIType::DBASE
:
2618 INetURLObject
aUrlTmp(rURL
);
2619 aUrlTmp
.removeSegment();
2620 aUrlTmp
.removeFinalSlash();
2621 OUString sDBURL
= "sdbc:dbase:" +
2622 aUrlTmp
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2626 case DBConnURIType::FLAT
:
2628 INetURLObject
aUrlTmp(rURL
);
2629 aUrlTmp
.removeSegment();
2630 aUrlTmp
.removeFinalSlash();
2631 OUString sDBURL
= "sdbc:flat:" +
2632 //only the 'path' has to be added
2633 aUrlTmp
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2637 case DBConnURIType::MSACE
:
2640 OUString
sDBURL("sdbc:ado:PROVIDER=Microsoft.ACE.OLEDB.12.0;DATA SOURCE=" + rURL
.PathToFileName());
2649 /// Returns the URL of this SwDoc.
2650 OUString
getOwnURL(SfxObjectShell
const * pDocShell
)
2657 const INetURLObject
& rURLObject
= pDocShell
->GetMedium()->GetURLObject();
2658 aRet
= rURLObject
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2663 Loads a data source from file and registers it.
2665 In case of success it returns the registered name, otherwise an empty string.
2666 Optionally add a prefix to the registered DB name.
2668 OUString
LoadAndRegisterDataSource_Impl(DBConnURIType type
, const uno::Reference
< beans::XPropertySet
> *pSettings
,
2669 const INetURLObject
&rURL
, const OUString
*pDestDir
, SfxObjectShell
* pDocShell
)
2671 OUString
sExt(rURL
.GetFileExtension());
2672 uno::Any aTableFilterAny
;
2673 uno::Any aSuppressVersionsAny
;
2678 uno::Any aURLAny
= GetDBunoURI(rURL
, type
);
2680 case DBConnURIType::UNKNOWN
:
2681 case DBConnURIType::CALC
:
2682 case DBConnURIType::WRITER
:
2684 case DBConnURIType::ODB
:
2687 case DBConnURIType::FLAT
:
2688 case DBConnURIType::DBASE
:
2689 //set the filter to the file name without extension
2691 uno::Sequence
<OUString
> aFilters
{ rURL
.getBase(INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
) };
2692 aTableFilterAny
<<= aFilters
;
2695 case DBConnURIType::MSACE
:
2696 aSuppressVersionsAny
<<= true;
2702 const uno::Reference
<uno::XComponentContext
>& xContext(::comphelper::getProcessComponentContext());
2703 uno::Reference
<sdb::XDatabaseContext
> xDBContext
= sdb::DatabaseContext::create(xContext
);
2705 OUString sNewName
= rURL
.getName(
2706 INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::Unambiguous
);
2707 sal_Int32 nExtLen
= sExt
.getLength();
2708 sNewName
= sNewName
.replaceAt(sNewName
.getLength() - nExtLen
- 1, nExtLen
+ 1, u
"");
2710 //find a unique name if sNewName already exists
2712 sal_Int32 nIndex
= 0;
2713 while (xDBContext
->hasByName(sFind
))
2714 sFind
= sNewName
+ OUString::number(++nIndex
);
2716 uno::Reference
<uno::XInterface
> xNewInstance
;
2720 uno::Any aDataSource
= xDBContext
->getByName(rURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
));
2721 aDataSource
>>= xNewInstance
;
2725 xNewInstance
= xDBContext
->createInstance();
2726 uno::Reference
<beans::XPropertySet
> xDataProperties(xNewInstance
, uno::UNO_QUERY
);
2728 if (aURLAny
.hasValue())
2729 xDataProperties
->setPropertyValue(u
"URL"_ustr
, aURLAny
);
2730 if (aTableFilterAny
.hasValue())
2731 xDataProperties
->setPropertyValue(u
"TableFilter"_ustr
, aTableFilterAny
);
2732 if (aSuppressVersionsAny
.hasValue())
2733 xDataProperties
->setPropertyValue(u
"SuppressVersionColumns"_ustr
, aSuppressVersionsAny
);
2734 if (aInfoAny
.hasValue())
2735 xDataProperties
->setPropertyValue(u
"Info"_ustr
, aInfoAny
);
2737 if (DBConnURIType::FLAT
== type
&& pSettings
)
2739 uno::Any aSettings
= xDataProperties
->getPropertyValue(u
"Settings"_ustr
);
2740 uno::Reference
< beans::XPropertySet
> xDSSettings
;
2741 aSettings
>>= xDSSettings
;
2742 ::comphelper::copyProperties(*pSettings
, xDSSettings
);
2743 xDSSettings
->setPropertyValue(u
"Extension"_ustr
, uno::Any(sExt
));
2746 uno::Reference
<sdb::XDocumentDataSource
> xDS(xNewInstance
, uno::UNO_QUERY_THROW
);
2747 uno::Reference
<frame::XStorable
> xStore(xDS
->getDatabaseDocument(), uno::UNO_QUERY_THROW
);
2748 OUString aOwnURL
= getOwnURL(pDocShell
);
2749 if (aOwnURL
.isEmpty())
2751 // Cannot embed, as embedded data source would need the URL of the parent document.
2752 OUString
sHomePath(SvtPathOptions().GetWorkPath());
2753 const OUString sTmpName
= utl::CreateTempURL(sNewName
, true, u
".odb", pDestDir
? pDestDir
: &sHomePath
);
2754 xStore
->storeAsURL(sTmpName
, uno::Sequence
<beans::PropertyValue
>());
2759 OUString aStreamRelPath
= u
"EmbeddedDatabase"_ustr
;
2760 uno::Reference
<embed::XStorage
> xStorage
= pDocShell
->GetStorage();
2762 // Refer to the sub-storage name in the document settings, so
2763 // we can load it again next time the file is imported.
2764 uno::Reference
<lang::XMultiServiceFactory
> xFactory(pDocShell
->GetModel(), uno::UNO_QUERY
);
2765 uno::Reference
<beans::XPropertySet
> xPropertySet(xFactory
->createInstance(u
"com.sun.star.document.Settings"_ustr
), uno::UNO_QUERY
);
2766 xPropertySet
->setPropertyValue(u
"EmbeddedDatabaseName"_ustr
, uno::Any(aStreamRelPath
));
2768 // Store it only after setting the above property, so that only one data source gets registered.
2769 SwDBManager::StoreEmbeddedDataSource(xStore
, xStorage
, aStreamRelPath
, aOwnURL
);
2772 xDBContext
->registerObject(sFind
, xNewInstance
);
2774 catch (const uno::Exception
&)
2782 OUString
SwDBManager::LoadAndRegisterDataSource(weld::Window
* pParent
, SwDocShell
* pDocShell
)
2784 sfx2::FileDialogHelper
aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE
, FileDialogFlags::NONE
, pParent
);
2785 aDlgHelper
.SetContext(sfx2::FileDialogHelper::WriterRegisterDataSource
);
2786 uno::Reference
< ui::dialogs::XFilePicker3
> xFP
= aDlgHelper
.GetFilePicker();
2788 OUString
sFilterAll(SwResId(STR_FILTER_ALL
));
2789 OUString
sFilterAllData(SwResId(STR_FILTER_ALL_DATA
));
2791 const std::vector
<std::pair
<OUString
, OUString
>> filters
{
2792 { SwResId(STR_FILTER_SXB
), "*.odb" },
2793 { SwResId(STR_FILTER_SXC
), "*.ods;*.sxc" },
2794 { SwResId(STR_FILTER_SXW
), "*.odt;*.sxw" },
2795 { SwResId(STR_FILTER_DBF
), "*.dbf" },
2796 { SwResId(STR_FILTER_XLS
), "*.xls;*.xlsx" },
2797 { SwResId(STR_FILTER_DOC
), "*.doc;*.docx" },
2798 { SwResId(STR_FILTER_TXT
), "*.txt" },
2799 { SwResId(STR_FILTER_CSV
), "*.csv" },
2801 { SwResId(STR_FILTER_ACCDB
), "*.accdb;*.accde;*.mdb;*.mde" },
2805 OUStringBuffer sAllDataFilter
;
2806 for (const auto& [name
, filter
] : filters
)
2809 if (!sAllDataFilter
.isEmpty())
2810 sAllDataFilter
.append(';');
2811 sAllDataFilter
.append(filter
);
2814 xFP
->appendFilter( sFilterAll
, u
"*"_ustr
);
2815 xFP
->appendFilter( sFilterAllData
, sAllDataFilter
.makeStringAndClear());
2817 // Similar to sfx2::addExtension from sfx2/source/dialog/filtergrouping.cxx
2818 for (const auto& [name
, filter
] : filters
)
2819 xFP
->appendFilter(name
+ " (" + filter
+ ")", filter
);
2821 xFP
->setCurrentFilter( sFilterAll
) ;
2823 if( ERRCODE_NONE
== aDlgHelper
.Execute() )
2825 uno::Reference
< beans::XPropertySet
> aSettings
;
2826 const INetURLObject
aURL( xFP
->getSelectedFiles()[0] );
2827 const DBConnURIType type
= GetDBunoType( aURL
);
2829 if( DBConnURIType::FLAT
== type
)
2831 const uno::Reference
<uno::XComponentContext
>& xContext( ::comphelper::getProcessComponentContext() );
2832 uno::Reference
< sdb::XTextConnectionSettings
> xSettingsDlg
= sdb::TextConnectionSettings::create(xContext
);
2833 if( xSettingsDlg
->execute() )
2834 aSettings
.set( uno::Reference
< beans::XPropertySet
>( xSettingsDlg
, uno::UNO_QUERY_THROW
) );
2836 sFind
= LoadAndRegisterDataSource_Impl( type
, DBConnURIType::FLAT
== type
? &aSettings
: nullptr, aURL
, nullptr, pDocShell
);
2838 s_aUncommittedRegistrations
.push_back(std::pair
<SwDocShell
*, OUString
>(pDocShell
, sFind
));
2843 void SwDBManager::StoreEmbeddedDataSource(const uno::Reference
<frame::XStorable
>& xStorable
,
2844 const uno::Reference
<embed::XStorage
>& xStorage
,
2845 const OUString
& rStreamRelPath
,
2846 const OUString
& rOwnURL
, bool bCopyTo
)
2848 // Construct vnd.sun.star.pkg:// URL for later loading, and TargetStorage/StreamRelPath for storing.
2849 OUString
const sTmpName
= ConstructVndSunStarPkgUrl(rOwnURL
, rStreamRelPath
);
2851 uno::Sequence
<beans::PropertyValue
> aSequence
= comphelper::InitPropertySequence(
2853 {"TargetStorage", uno::Any(xStorage
)},
2854 {"StreamRelPath", uno::Any(rStreamRelPath
)},
2855 {"BaseURI", uno::Any(rOwnURL
)}
2858 xStorable
->storeToURL(sTmpName
, aSequence
);
2860 xStorable
->storeAsURL(sTmpName
, aSequence
);
2863 OUString
SwDBManager::LoadAndRegisterDataSource(std::u16string_view rURI
, const OUString
*pDestDir
)
2865 return LoadAndRegisterDataSource_Impl( DBConnURIType::UNKNOWN
, nullptr, INetURLObject(rURI
), pDestDir
, nullptr );
2870 // tdf#117824 switch the embedded database away from using its current storage and point it to temporary storage
2871 // which allows the original storage to be deleted
2872 void switchEmbeddedDatabaseStorage(const uno::Reference
<sdb::XDatabaseContext
>& rDatabaseContext
, const OUString
& rName
)
2874 uno::Reference
<sdb::XDocumentDataSource
> xDS(rDatabaseContext
->getByName(rName
), uno::UNO_QUERY
);
2877 uno::Reference
<document::XStorageBasedDocument
> xStorageDoc(xDS
->getDatabaseDocument(), uno::UNO_QUERY
);
2880 xStorageDoc
->switchToStorage(comphelper::OStorageHelper::GetTemporaryStorage());
2884 void SwDBManager::RevokeDataSource(const OUString
& rName
)
2886 uno::Reference
<sdb::XDatabaseContext
> xDatabaseContext
= sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
2887 if (xDatabaseContext
->hasByName(rName
))
2889 switchEmbeddedDatabaseStorage(xDatabaseContext
, rName
);
2890 xDatabaseContext
->revokeObject(rName
);
2894 void SwDBManager::LoadAndRegisterEmbeddedDataSource(const SwDBData
& rData
, const SwDocShell
& rDocShell
)
2896 uno::Reference
<sdb::XDatabaseContext
> xDatabaseContext
= sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
2898 OUString sDataSource
= rData
.sDataSource
;
2900 // Fallback, just in case the document would contain an embedded data source, but no DB fields.
2901 if (sDataSource
.isEmpty())
2902 sDataSource
= "EmbeddedDatabase";
2904 SwDBManager::RevokeDataSource( sDataSource
);
2906 // Encode the stream name and the real path into a single URL.
2907 const INetURLObject
& rURLObject
= rDocShell
.GetMedium()->GetURLObject();
2908 OUString
const aURL
= ConstructVndSunStarPkgUrl(
2909 rURLObject
.GetMainURL(INetURLObject::DecodeMechanism::NONE
),
2912 uno::Reference
<uno::XInterface
> xDataSource(xDatabaseContext
->getByName(aURL
), uno::UNO_QUERY
);
2913 xDatabaseContext
->registerObject( sDataSource
, xDataSource
);
2915 // temp file - don't remember connection
2916 if (rData
.sDataSource
.isEmpty())
2917 s_aUncommittedRegistrations
.push_back(std::pair
<SwDocShell
*, OUString
>(nullptr, sDataSource
));
2920 void SwDBManager::ExecuteFormLetter( SwWrtShell
& rSh
,
2921 const uno::Sequence
<beans::PropertyValue
>& rProperties
)
2923 //prevent second call
2924 if(m_pImpl
->pMergeDialog
)
2926 OUString sDataSource
, sDataTableOrQuery
;
2927 uno::Sequence
<uno::Any
> aSelection
;
2929 sal_Int32 nCmdType
= sdb::CommandType::TABLE
;
2930 uno::Reference
< sdbc::XConnection
> xConnection
;
2932 svx::ODataAccessDescriptor
aDescriptor(rProperties
);
2933 sDataSource
= aDescriptor
.getDataSource();
2934 OSL_VERIFY(aDescriptor
[svx::DataAccessDescriptorProperty::Command
] >>= sDataTableOrQuery
);
2935 OSL_VERIFY(aDescriptor
[svx::DataAccessDescriptorProperty::CommandType
] >>= nCmdType
);
2937 if ( aDescriptor
.has(svx::DataAccessDescriptorProperty::Selection
) )
2938 aDescriptor
[svx::DataAccessDescriptorProperty::Selection
] >>= aSelection
;
2939 if ( aDescriptor
.has(svx::DataAccessDescriptorProperty::Connection
) )
2940 aDescriptor
[svx::DataAccessDescriptorProperty::Connection
] >>= xConnection
;
2942 if(sDataSource
.isEmpty() || sDataTableOrQuery
.isEmpty())
2944 OSL_FAIL("PropertyValues missing or unset");
2948 //always create a connection for the dialog and dispose it after the dialog has been closed
2949 SwDSParam
* pFound
= nullptr;
2950 if(!xConnection
.is())
2952 xConnection
= SwDBManager::RegisterConnection(sDataSource
);
2953 pFound
= FindDSConnection(sDataSource
, true);
2955 SwAbstractDialogFactory
* pFact
= SwAbstractDialogFactory::Create();
2956 m_pImpl
->pMergeDialog
= pFact
->CreateMailMergeDlg(rSh
.GetView().GetViewFrame().GetFrameWeld(), rSh
,
2961 if(m_pImpl
->pMergeDialog
->Execute() == RET_OK
)
2963 aDescriptor
[svx::DataAccessDescriptorProperty::Selection
] <<= m_pImpl
->pMergeDialog
->GetSelection();
2965 uno::Reference
<sdbc::XResultSet
> xResSet
= m_pImpl
->pMergeDialog
->GetResultSet();
2967 aDescriptor
[svx::DataAccessDescriptorProperty::Cursor
] <<= xResSet
;
2969 // SfxObjectShellRef is ok, since there should be no control over the document lifetime here
2970 SfxObjectShellRef xDocShell
= rSh
.GetView().GetViewFrame().GetObjectShell();
2972 lcl_emitEvent(SfxEventHintId::SwMailMerge
, STR_SW_EVENT_MAIL_MERGE
, xDocShell
.get());
2974 // prepare mail merge descriptor
2975 SwMergeDescriptor
aMergeDesc( m_pImpl
->pMergeDialog
->GetMergeType(), rSh
, aDescriptor
);
2976 aMergeDesc
.sSaveToFilter
= m_pImpl
->pMergeDialog
->GetSaveFilter();
2977 aMergeDesc
.bCreateSingleFile
= m_pImpl
->pMergeDialog
->IsSaveSingleDoc();
2978 aMergeDesc
.bPrefixIsFilename
= aMergeDesc
.bCreateSingleFile
;
2979 aMergeDesc
.sPrefix
= m_pImpl
->pMergeDialog
->GetTargetURL();
2981 if(!aMergeDesc
.bCreateSingleFile
)
2983 if(m_pImpl
->pMergeDialog
->IsGenerateFromDataBase())
2984 aMergeDesc
.sDBcolumn
= m_pImpl
->pMergeDialog
->GetColumnName();
2986 if(m_pImpl
->pMergeDialog
->IsFileEncryptedFromDataBase())
2987 aMergeDesc
.sDBPasswordColumn
= m_pImpl
->pMergeDialog
->GetPasswordColumnName();
2990 Merge( aMergeDesc
);
2992 lcl_emitEvent(SfxEventHintId::SwMailMergeEnd
, STR_SW_EVENT_MAIL_MERGE_END
, xDocShell
.get());
2994 // reset the cursor inside
2996 aDescriptor
[svx::DataAccessDescriptorProperty::Cursor
] <<= xResSet
;
3000 for (const auto & pParam
: m_DataSourceParams
)
3002 if (pParam
.get() == pFound
)
3006 uno::Reference
<lang::XComponent
> xComp(pParam
->xConnection
, uno::UNO_QUERY
);
3010 catch(const uno::RuntimeException
&)
3012 //may be disposed already since multiple entries may have used the same connection
3016 //pFound doesn't need to be removed/deleted -
3017 //this has been done by the SwConnectionDisposedListener_Impl already
3020 m_pImpl
->pMergeDialog
.disposeAndClear();
3023 void SwDBManager::InsertText(SwWrtShell
& rSh
,
3024 const uno::Sequence
< beans::PropertyValue
>& rProperties
)
3026 OUString sDataSource
, sDataTableOrQuery
;
3027 uno::Reference
<sdbc::XResultSet
> xResSet
;
3028 uno::Sequence
<uno::Any
> aSelection
;
3029 sal_Int16 nCmdType
= sdb::CommandType::TABLE
;
3030 uno::Reference
< sdbc::XConnection
> xConnection
;
3031 for(const beans::PropertyValue
& rValue
: rProperties
)
3033 if ( rValue
.Name
== "DataSourceName" )
3034 rValue
.Value
>>= sDataSource
;
3035 else if ( rValue
.Name
== "Command" )
3036 rValue
.Value
>>= sDataTableOrQuery
;
3037 else if ( rValue
.Name
== "Cursor" )
3038 rValue
.Value
>>= xResSet
;
3039 else if ( rValue
.Name
== "Selection" )
3040 rValue
.Value
>>= aSelection
;
3041 else if ( rValue
.Name
== "CommandType" )
3042 rValue
.Value
>>= nCmdType
;
3043 else if ( rValue
.Name
== "ActiveConnection" )
3044 rValue
.Value
>>= xConnection
;
3046 if(sDataSource
.isEmpty() || sDataTableOrQuery
.isEmpty() || !xResSet
.is())
3048 OSL_FAIL("PropertyValues missing or unset");
3051 const uno::Reference
< uno::XComponentContext
>& xContext( ::comphelper::getProcessComponentContext() );
3052 uno::Reference
<sdbc::XDataSource
> xSource
;
3053 uno::Reference
<container::XChild
> xChild(xConnection
, uno::UNO_QUERY
);
3055 xSource
.set(xChild
->getParent(), uno::UNO_QUERY
);
3057 xSource
= dbtools::getDataSource(sDataSource
, xContext
);
3058 uno::Reference
< sdbcx::XColumnsSupplier
> xColSupp( xResSet
, uno::UNO_QUERY
);
3060 aDBData
.sDataSource
= sDataSource
;
3061 aDBData
.sCommand
= sDataTableOrQuery
;
3062 aDBData
.nCommandType
= nCmdType
;
3064 SwAbstractDialogFactory
* pFact
= SwAbstractDialogFactory::Create();
3065 VclPtr
<AbstractSwInsertDBColAutoPilot
> pDlg(pFact
->CreateSwInsertDBColAutoPilot( rSh
.GetView(),
3069 pDlg
->StartExecuteAsync(
3070 [xConnection
, xSource
, pDlg
, xResSet
, aSelection
] (sal_Int32 nResult
)->void
3072 if (nResult
== RET_OK
)
3075 auto xTmpConnection
= xConnection
;
3076 if(!xTmpConnection
.is())
3077 xTmpConnection
= xSource
->getConnection(sDummy
, sDummy
);
3080 pDlg
->DataToDoc( aSelection
, xSource
, xTmpConnection
, xResSet
);
3082 catch (const uno::Exception
&)
3084 TOOLS_WARN_EXCEPTION("sw.mailmerge", "");
3086 pDlg
->disposeOnce();
3093 uno::Reference
<sdbc::XDataSource
> SwDBManager::getDataSourceAsParent(const uno::Reference
< sdbc::XConnection
>& _xConnection
,const OUString
& _sDataSourceName
)
3095 uno::Reference
<sdbc::XDataSource
> xSource
;
3098 uno::Reference
<container::XChild
> xChild(_xConnection
, uno::UNO_QUERY
);
3100 xSource
.set(xChild
->getParent(), uno::UNO_QUERY
);
3101 if ( !xSource
.is() )
3102 xSource
= dbtools::getDataSource(_sDataSourceName
, ::comphelper::getProcessComponentContext());
3104 catch (const uno::Exception
&)
3106 TOOLS_WARN_EXCEPTION("sw.mailmerge", "getDataSourceAsParent()");
3111 uno::Reference
<sdbc::XResultSet
> SwDBManager::createCursor(const OUString
& _sDataSourceName
,
3112 const OUString
& _sCommand
,
3113 sal_Int32 _nCommandType
,
3114 const uno::Reference
<sdbc::XConnection
>& _xConnection
,
3115 const SwView
* pView
)
3117 uno::Reference
<sdbc::XResultSet
> xResultSet
;
3120 uno::Reference
< lang::XMultiServiceFactory
> xMgr( ::comphelper::getProcessServiceFactory() );
3123 uno::Reference
<uno::XInterface
> xInstance
= xMgr
->createInstance(u
"com.sun.star.sdb.RowSet"_ustr
);
3124 uno::Reference
<beans::XPropertySet
> xRowSetPropSet(xInstance
, uno::UNO_QUERY
);
3125 if(xRowSetPropSet
.is())
3127 xRowSetPropSet
->setPropertyValue(u
"DataSourceName"_ustr
, uno::Any(_sDataSourceName
));
3128 xRowSetPropSet
->setPropertyValue(u
"ActiveConnection"_ustr
, uno::Any(_xConnection
));
3129 xRowSetPropSet
->setPropertyValue(u
"Command"_ustr
, uno::Any(_sCommand
));
3130 xRowSetPropSet
->setPropertyValue(u
"CommandType"_ustr
, uno::Any(_nCommandType
));
3132 uno::Reference
< sdb::XCompletedExecution
> xRowSet(xInstance
, uno::UNO_QUERY
);
3136 weld::Window
* pWindow
= pView
? pView
->GetFrameWeld() : nullptr;
3137 uno::Reference
< task::XInteractionHandler
> xHandler( task::InteractionHandler::createWithParent(comphelper::getComponentContext(xMgr
), pWindow
? pWindow
->GetXWindow() : nullptr), uno::UNO_QUERY_THROW
);
3138 xRowSet
->executeWithCompletion(xHandler
);
3140 xResultSet
.set(xRowSet
, uno::UNO_QUERY
);
3144 catch (const uno::Exception
&)
3146 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Caught exception while creating a new RowSet");
3151 void SwDBManager::setEmbeddedName(const OUString
& rEmbeddedName
, SwDocShell
& rDocShell
)
3153 bool bLoad
= m_sEmbeddedName
!= rEmbeddedName
&& !rEmbeddedName
.isEmpty();
3154 bool bRegisterListener
= m_sEmbeddedName
.isEmpty() && !rEmbeddedName
.isEmpty();
3156 m_sEmbeddedName
= rEmbeddedName
;
3160 uno::Reference
<embed::XStorage
> xStorage
= rDocShell
.GetStorage();
3161 // It's OK that we don't have the named sub-storage yet, in case
3162 // we're in the process of creating it.
3163 if (xStorage
->hasByName(rEmbeddedName
))
3164 LoadAndRegisterEmbeddedDataSource(rDocShell
.GetDoc()->GetDBData(), rDocShell
);
3167 if (bRegisterListener
)
3168 // Register a remove listener, so we know when the embedded data source is removed.
3169 m_pImpl
->m_xDataSourceRemovedListener
= new SwDataSourceRemovedListener(*this);
3172 const OUString
& SwDBManager::getEmbeddedName() const
3174 return m_sEmbeddedName
;
3177 SwDoc
* SwDBManager::getDoc() const
3182 void SwDBManager::releaseRevokeListener()
3184 if (m_pImpl
->m_xDataSourceRemovedListener
.is())
3186 m_pImpl
->m_xDataSourceRemovedListener
->Dispose();
3187 m_pImpl
->m_xDataSourceRemovedListener
.clear();
3191 SwDBManager::ConnectionDisposedListener_Impl::ConnectionDisposedListener_Impl(SwDBManager
& rManager
)
3192 : m_pDBManager(&rManager
)
3196 void SwDBManager::ConnectionDisposedListener_Impl::disposing( const lang::EventObject
& rSource
)
3198 ::SolarMutexGuard aGuard
;
3200 if (!m_pDBManager
) return; // we're disposed too!
3202 uno::Reference
<sdbc::XConnection
> xSource(rSource
.Source
, uno::UNO_QUERY
);
3203 for (size_t nPos
= m_pDBManager
->m_DataSourceParams
.size(); nPos
; nPos
--)
3205 SwDSParam
* pParam
= m_pDBManager
->m_DataSourceParams
[nPos
- 1].get();
3206 if(pParam
->xConnection
.is() &&
3207 (xSource
== pParam
->xConnection
))
3209 m_pDBManager
->m_DataSourceParams
.erase(
3210 m_pDBManager
->m_DataSourceParams
.begin() + nPos
- 1);
3215 std::shared_ptr
<SwMailMergeConfigItem
> SwDBManager::PerformMailMerge(SwView
const * pView
)
3217 std::shared_ptr
<SwMailMergeConfigItem
> xConfigItem
= pView
->GetMailMergeConfigItem();
3221 svx::ODataAccessDescriptor aDescriptor
;
3222 aDescriptor
.setDataSource(xConfigItem
->GetCurrentDBData().sDataSource
);
3223 aDescriptor
[ svx::DataAccessDescriptorProperty::Connection
] <<= xConfigItem
->GetConnection().getTyped();
3224 aDescriptor
[ svx::DataAccessDescriptorProperty::Cursor
] <<= xConfigItem
->GetResultSet();
3225 aDescriptor
[ svx::DataAccessDescriptorProperty::Command
] <<= xConfigItem
->GetCurrentDBData().sCommand
;
3226 aDescriptor
[ svx::DataAccessDescriptorProperty::CommandType
] <<= xConfigItem
->GetCurrentDBData().nCommandType
;
3227 aDescriptor
[ svx::DataAccessDescriptorProperty::Selection
] <<= xConfigItem
->GetSelection();
3229 SwWrtShell
& rSh
= pView
->GetWrtShell();
3230 xConfigItem
->SetTargetView(nullptr);
3232 SwMergeDescriptor
aMergeDesc(DBMGR_MERGE_SHELL
, rSh
, aDescriptor
);
3233 aMergeDesc
.pMailMergeConfigItem
= xConfigItem
.get();
3234 aMergeDesc
.bCreateSingleFile
= true;
3235 rSh
.GetDBManager()->Merge(aMergeDesc
);
3240 void SwDBManager::RevokeLastRegistrations()
3242 if (s_aUncommittedRegistrations
.empty())
3245 SwView
* pView
= ( m_pDoc
&& m_pDoc
->GetDocShell() ) ? m_pDoc
->GetDocShell()->GetView() : nullptr;
3248 const std::shared_ptr
<SwMailMergeConfigItem
>& xConfigItem
= pView
->GetMailMergeConfigItem();
3251 xConfigItem
->DisposeResultSet();
3252 xConfigItem
->DocumentReloaded();
3256 for (auto it
= s_aUncommittedRegistrations
.begin(); it
!= s_aUncommittedRegistrations
.end();)
3258 if ((m_pDoc
&& it
->first
== m_pDoc
->GetDocShell()) || it
->first
== nullptr)
3260 RevokeDataSource(it
->second
);
3261 it
= s_aUncommittedRegistrations
.erase(it
);
3268 void SwDBManager::CommitLastRegistrations()
3270 for (auto aIt
= s_aUncommittedRegistrations
.begin(); aIt
!= s_aUncommittedRegistrations
.end();)
3272 if (aIt
->first
== m_pDoc
->GetDocShell() || aIt
->first
== nullptr)
3274 m_aNotUsedConnections
.push_back(aIt
->second
);
3275 aIt
= s_aUncommittedRegistrations
.erase(aIt
);
3282 void SwDBManager::SetAsUsed(const OUString
& rName
)
3284 auto aFound
= std::find(m_aNotUsedConnections
.begin(), m_aNotUsedConnections
.end(), rName
);
3285 if (aFound
!= m_aNotUsedConnections
.end())
3286 m_aNotUsedConnections
.erase(aFound
);
3289 void SwDBManager::RevokeNotUsedConnections()
3291 for (auto aIt
= m_aNotUsedConnections
.begin(); aIt
!= m_aNotUsedConnections
.end();)
3293 RevokeDataSource(*aIt
);
3294 aIt
= m_aNotUsedConnections
.erase(aIt
);
3298 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */