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 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("IsRowCountFinal");
194 pParam
->xResultSet
->last();
195 pParam
->xResultSet
->first();
197 uno::Any aCount
= xPrSet
->getPropertyValue("RowCount");
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 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 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("NumberFormatsSupplier");
339 if(!aFormats
.hasValue())
342 uno::Reference
<util::XNumberFormatsSupplier
> xSuppl
;
346 uno::Reference
< beans::XPropertySet
> xSettings
= xSuppl
->getNumberFormatSettings();
347 uno::Any aNull
= xSettings
->getPropertyValue("NullDate");
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
);
523 pWorkShell
->ChgDBData(aData
);
526 if (IsInitDBFields())
528 // with database fields without DB-Name, use DB-Name from Doc
529 std::vector
<OUString
> aDBNames
;
530 aDBNames
.emplace_back();
531 SwDBData aInsertData
= pWorkShell
->GetDBData();
532 OUString sDBName
= aInsertData
.sDataSource
533 + OUStringChar(DB_DELIM
) + aInsertData
.sCommand
534 + OUStringChar(DB_DELIM
)
535 + OUString::number(aInsertData
.nCommandType
);
536 pWorkShell
->ChangeDBFields( aDBNames
, sDBName
);
537 SetInitDBFields(false);
541 switch(rMergeDesc
.nMergeType
)
544 pWorkShell
->StartAllAction();
545 pWorkShell
->SwViewShell::UpdateFields( true );
546 pWorkShell
->SetModified();
547 pWorkShell
->EndAllAction();
550 case DBMGR_MERGE_PRINTER
:
551 case DBMGR_MERGE_EMAIL
:
552 case DBMGR_MERGE_FILE
:
553 case DBMGR_MERGE_SHELL
:
554 // save files and send them as e-Mail if required
555 bRet
= MergeMailFiles(pWorkShell
, rMergeDesc
);
559 // insert selected entries
560 // (was: InsertRecord)
561 ImportFromConnection(pWorkShell
);
565 m_pImpl
->pMergeData
.reset();
567 if( xWorkObjSh
.Is() )
569 pWorkDoc
->SetDBManager( pWorkDocOrigDBManager
);
570 xWorkObjSh
->DoClose();
578 void SwDBManager::ImportFromConnection( SwWrtShell
* pSh
)
580 if(!m_pImpl
->pMergeData
|| m_pImpl
->pMergeData
->bEndOfDB
)
583 pSh
->StartAllAction();
585 bool bGroupUndo(pSh
->DoesGroupUndo());
586 pSh
->DoGroupUndo(false);
588 if( pSh
->HasSelection() )
591 std::optional
<SwWait
> oWait
;
599 oWait
.emplace( *pSh
->GetView().GetDocShell(), true);
601 } while(ToNextMergeRecord());
604 pSh
->DoGroupUndo(bGroupUndo
);
609 void SwDBManager::ImportDBEntry(SwWrtShell
* pSh
)
611 if(!m_pImpl
->pMergeData
|| m_pImpl
->pMergeData
->bEndOfDB
)
614 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( m_pImpl
->pMergeData
->xResultSet
, uno::UNO_QUERY
);
615 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
617 uno::Sequence
<OUString
> aColNames
= xCols
->getElementNames();
618 const OUString
* pColNames
= aColNames
.getConstArray();
619 tools::Long nLength
= aColNames
.getLength();
620 for(tools::Long i
= 0; i
< nLength
; i
++)
622 uno::Any aCol
= xCols
->getByName(pColNames
[i
]);
623 uno::Reference
< beans::XPropertySet
> xColumnProp
;
624 aCol
>>= xColumnProp
;
625 SwDBFormatData aDBFormat
;
626 sStr
.append(GetDBField( xColumnProp
, aDBFormat
));
630 pSh
->SwEditShell::Insert2(sStr
.makeStringAndClear());
631 pSh
->SwFEShell::SplitNode(); // line feed
634 bool SwDBManager::GetTableNames(weld::ComboBox
& rBox
, const OUString
& rDBName
)
637 OUString
sOldTableName(rBox
.get_active_text());
639 SwDSParam
* pParam
= FindDSConnection(rDBName
, false);
640 uno::Reference
< sdbc::XConnection
> xConnection
;
641 if (pParam
&& pParam
->xConnection
.is())
642 xConnection
= pParam
->xConnection
;
645 if ( !rDBName
.isEmpty() )
646 xConnection
= RegisterConnection( rDBName
);
648 if (xConnection
.is())
650 uno::Reference
<sdbcx::XTablesSupplier
> xTSupplier(xConnection
, uno::UNO_QUERY
);
653 uno::Reference
<container::XNameAccess
> xTables
= xTSupplier
->getTables();
654 const uno::Sequence
<OUString
> aTables
= xTables
->getElementNames();
655 for (const OUString
& rTable
: aTables
)
656 rBox
.append("0", rTable
);
658 uno::Reference
<sdb::XQueriesSupplier
> xQSupplier(xConnection
, uno::UNO_QUERY
);
661 uno::Reference
<container::XNameAccess
> xQueries
= xQSupplier
->getQueries();
662 const uno::Sequence
<OUString
> aQueries
= xQueries
->getElementNames();
663 for (const OUString
& rQuery
: aQueries
)
664 rBox
.append("1", rQuery
);
666 if (!sOldTableName
.isEmpty())
667 rBox
.set_active_text(sOldTableName
);
673 // fill Listbox with column names of a database
674 void SwDBManager::GetColumnNames(weld::ComboBox
& rBox
,
675 const OUString
& rDBName
, const OUString
& rTableName
)
678 aData
.sDataSource
= rDBName
;
679 aData
.sCommand
= rTableName
;
680 aData
.nCommandType
= -1;
681 SwDSParam
* pParam
= FindDSData(aData
, false);
682 uno::Reference
< sdbc::XConnection
> xConnection
;
683 if(pParam
&& pParam
->xConnection
.is())
684 xConnection
= pParam
->xConnection
;
687 xConnection
= RegisterConnection( rDBName
);
689 GetColumnNames(rBox
, xConnection
, rTableName
);
692 void SwDBManager::GetColumnNames(weld::ComboBox
& rBox
,
693 uno::Reference
< sdbc::XConnection
> const & xConnection
,
694 const OUString
& rTableName
)
697 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp
= SwDBManager::GetColumnSupplier(xConnection
, rTableName
);
700 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
701 const uno::Sequence
<OUString
> aColNames
= xCols
->getElementNames();
702 for (const OUString
& rColName
: aColNames
)
704 rBox
.append_text(rColName
);
706 ::comphelper::disposeComponent( xColsSupp
);
710 SwDBManager::SwDBManager(SwDoc
* pDoc
)
711 : m_aMergeStatus( MergeStatus::Ok
)
712 , m_bInitDBFields(false)
714 , m_bMergeSilent(false)
715 , m_pImpl(new SwDBManager_Impl(*this))
716 , m_pMergeEvtSrc(nullptr)
721 void SwDBManager::ImplDestroy()
723 RevokeLastRegistrations();
725 // copy required, m_DataSourceParams can be modified while disposing components
726 std::vector
<uno::Reference
<sdbc::XConnection
>> aCopiedConnections
;
727 for (const auto & pParam
: m_DataSourceParams
)
729 if(pParam
->xConnection
.is())
731 aCopiedConnections
.push_back(pParam
->xConnection
);
734 for (const auto & xConnection
: aCopiedConnections
)
738 uno::Reference
<lang::XComponent
> xComp(xConnection
, uno::UNO_QUERY
);
742 catch(const uno::RuntimeException
&)
744 //may be disposed already since multiple entries may have used the same connection
749 SwDBManager::~SwDBManager()
751 suppress_fun_call_w_exception(ImplDestroy());
754 static void lcl_RemoveSectionLinks( SwWrtShell
& rWorkShell
)
756 //reset all links of the sections of synchronized labels
757 size_t nSections
= rWorkShell
.GetSectionFormatCount();
758 for (size_t nSection
= 0; nSection
< nSections
; ++nSection
)
760 SwSectionData
aSectionData( *rWorkShell
.GetSectionFormat( nSection
).GetSection() );
761 if( aSectionData
.GetType() == SectionType::FileLink
)
763 aSectionData
.SetType( SectionType::Content
);
764 aSectionData
.SetLinkFileName( OUString() );
765 rWorkShell
.UpdateSection( nSection
, aSectionData
);
768 rWorkShell
.SetLabelDoc( false );
771 static void lcl_SaveDebugDoc( SfxObjectShell
*xTargetDocShell
,
772 const char *name
, int no
= 0 )
774 static OUString sTempDirURL
;
775 if( sTempDirURL
.isEmpty() )
777 SvtPathOptions aPathOpt
;
778 utl::TempFileNamed
aTempDir( &aPathOpt
.GetTempPath(), true );
779 if( aTempDir
.IsValid() )
781 INetURLObject
aTempDirURL( aTempDir
.GetURL() );
782 sTempDirURL
= aTempDirURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
783 SAL_INFO( "sw.mailmerge", "Dump directory: " << sTempDirURL
);
786 if( sTempDirURL
.isEmpty() )
789 OUString basename
= OUString::createFromAscii( name
);
791 basename
+= OUString::number(no
) + "-";
792 // aTempFile is not deleted, but that seems to be intentional
793 utl::TempFileNamed
aTempFile( basename
, true, u
".odt", &sTempDirURL
);
794 INetURLObject
aTempFileURL( aTempFile
.GetURL() );
795 auto pDstMed
= std::make_unique
<SfxMedium
>(
796 aTempFileURL
.GetMainURL( INetURLObject::DecodeMechanism::NONE
),
797 StreamMode::STD_READWRITE
);
798 bool bAnyError
= !xTargetDocShell
->DoSaveAs( *pDstMed
);
799 // xObjectShell->DoSaveCompleted crashes the mail merge unit tests, so skip it
800 bAnyError
|= (ERRCODE_NONE
!= xTargetDocShell
->GetError());
802 SAL_WARN( "sw.mailmerge", "Error saving: " << aTempFile
.GetURL() );
804 SAL_INFO( "sw.mailmerge", "Saved doc as: " << aTempFile
.GetURL() );
807 static bool lcl_SaveDoc(
808 const INetURLObject
* pFileURL
,
809 const std::shared_ptr
<const SfxFilter
>& pStoreToFilter
,
810 const OUString
* pStoreToFilterOptions
,
811 const uno::Sequence
< beans::PropertyValue
>* pSaveToFilterData
,
812 const bool bIsPDFexport
,
813 SfxObjectShell
* xObjectShell
,
814 SwWrtShell
& rWorkShell
,
815 OUString
* const decodedURL
= nullptr )
817 OUString url
= pFileURL
->GetMainURL( INetURLObject::DecodeMechanism::NONE
);
821 SfxMedium
* pDstMed
= new SfxMedium( url
, StreamMode::STD_READWRITE
);
822 pDstMed
->SetFilter( pStoreToFilter
);
823 if( pDstMed
->GetItemSet() )
825 if( pStoreToFilterOptions
)
826 pDstMed
->GetItemSet()->Put( SfxStringItem(SID_FILE_FILTEROPTIONS
,
827 *pStoreToFilterOptions
));
828 if( pSaveToFilterData
->hasElements() )
829 pDstMed
->GetItemSet()->Put( SfxUnoAnyItem(SID_FILTER_DATA
,
830 uno::Any(*pSaveToFilterData
)));
833 // convert fields to text if we are exporting to PDF.
834 // this prevents a second merge while updating the fields
835 // in SwXTextDocument::getRendererCount()
837 rWorkShell
.ConvertFieldsToText();
839 bool bAnyError
= !xObjectShell
->DoSaveAs(*pDstMed
);
840 // Actually this should be a bool... so in case of email and individual
841 // files, where this is set, we skip the recently used handling
842 bAnyError
|= !xObjectShell
->DoSaveCompleted( pDstMed
, !decodedURL
);
843 bAnyError
|= (ERRCODE_NONE
!= xObjectShell
->GetError());
847 ErrorHandler::HandleError( xObjectShell
->GetError() );
852 static void lcl_PreparePrinterOptions(
853 const uno::Sequence
< beans::PropertyValue
>& rInPrintOptions
,
854 uno::Sequence
< beans::PropertyValue
>& rOutPrintOptions
)
856 // printing should be done synchronously otherwise the document
857 // might already become invalid during the process
859 rOutPrintOptions
= { comphelper::makePropertyValue("Wait", true) };
861 // copy print options
862 sal_Int32 nIndex
= 1;
863 for( const beans::PropertyValue
& rOption
: rInPrintOptions
)
865 if( rOption
.Name
== "CopyCount" || rOption
.Name
== "FileName"
866 || rOption
.Name
== "Collate" || rOption
.Name
== "Pages"
867 || rOption
.Name
== "Wait" || rOption
.Name
== "PrinterName" )
870 rOutPrintOptions
.realloc( nIndex
+ 1 );
871 auto pOutPrintOptions
= rOutPrintOptions
.getArray();
872 pOutPrintOptions
[ nIndex
].Name
= rOption
.Name
;
873 pOutPrintOptions
[ nIndex
++ ].Value
= rOption
.Value
;
878 static void lcl_PrepareSaveFilterDataOptions(
879 const uno::Sequence
< beans::PropertyValue
>& rInSaveFilterDataptions
,
880 uno::Sequence
< beans::PropertyValue
>& rOutSaveFilterDataOptions
,
881 const OUString
& sPassword
)
883 rOutSaveFilterDataOptions
884 = { comphelper::makePropertyValue("EncryptFile", true),
885 comphelper::makePropertyValue("DocumentOpenPassword", sPassword
) };
887 // copy other options
888 sal_Int32 nIndex
= 2;
889 for( const beans::PropertyValue
& rOption
: rInSaveFilterDataptions
)
891 rOutSaveFilterDataOptions
.realloc( nIndex
+ 1 );
892 auto pOutSaveFilterDataOptions
= rOutSaveFilterDataOptions
.getArray();
893 pOutSaveFilterDataOptions
[ nIndex
].Name
= rOption
.Name
;
894 pOutSaveFilterDataOptions
[ nIndex
++ ].Value
= rOption
.Value
;
900 static SfxObjectShell
* lcl_CreateWorkingDocument(
902 const WorkingDocType aType
, const SwWrtShell
&rSourceWrtShell
,
904 const vcl::Window
*pSourceWindow
,
905 // optional in and output to swap the DB manager
906 SwDBManager
** const ppDBManager
,
908 SwView
** const pView
, SwWrtShell
** const pWrtShell
, rtl::Reference
<SwDoc
>* const pDoc
)
910 const SwDoc
*pSourceDoc
= rSourceWrtShell
.GetDoc();
911 SfxObjectShellRef xWorkObjectShell
= pSourceDoc
->CreateCopy( true, (aType
== WorkingDocType::TARGET
) );
912 SfxViewFrame
* pWorkFrame
= SfxViewFrame::LoadHiddenDocument( *xWorkObjectShell
, SFX_INTERFACE_NONE
);
916 // the created window has to be located at the same position as the source window
917 vcl::Window
& rTargetWindow
= pWorkFrame
->GetFrame().GetWindow();
918 rTargetWindow
.SetPosPixel( pSourceWindow
->GetPosPixel() );
921 SwView
* pWorkView
= static_cast< SwView
* >( pWorkFrame
->GetViewShell() );
923 if (SwWrtShell
* pWorkWrtShell
= pWorkView
->GetWrtShellPtr())
925 pWorkWrtShell
->GetViewOptions()->SetIdle( false );
926 pWorkView
->AttrChangedNotify(nullptr);// in order for SelectShell to be called
927 SwDoc
* pWorkDoc
= pWorkWrtShell
->GetDoc();
928 pWorkDoc
->GetIDocumentUndoRedo().DoUndo( false );
929 pWorkDoc
->ReplaceDocumentProperties( *pSourceDoc
);
931 // import print settings
932 const SwPrintData
&rPrintData
= pSourceDoc
->getIDocumentDeviceAccess().getPrintData();
933 pWorkDoc
->getIDocumentDeviceAccess().setPrintData(rPrintData
);
934 const JobSetup
*pJobSetup
= pSourceDoc
->getIDocumentDeviceAccess().getJobsetup();
936 pWorkDoc
->getIDocumentDeviceAccess().setJobsetup(*pJobSetup
);
938 if( aType
== WorkingDocType::TARGET
)
940 assert( !ppDBManager
);
941 pWorkDoc
->SetInMailMerge( true );
942 pWorkWrtShell
->SetLabelDoc( false );
946 // We have to swap the DBmanager of the new doc, so we also need input
947 assert(ppDBManager
&& *ppDBManager
);
948 SwDBManager
*pWorkDBManager
= pWorkDoc
->GetDBManager();
949 pWorkDoc
->SetDBManager( *ppDBManager
);
950 *ppDBManager
= pWorkDBManager
;
952 if( aType
== WorkingDocType::SOURCE
)
954 // the GetDBData call constructs the data, if it's missing - kind of const...
955 pWorkWrtShell
->ChgDBData( const_cast<SwDoc
*>(pSourceDoc
)->GetDBData() );
956 // some DocumentSettings are currently not copied by SwDoc::CreateCopy
957 pWorkWrtShell
->SetLabelDoc( rSourceWrtShell
.IsLabelDoc() );
958 pWorkDoc
->getIDocumentState().ResetModified();
961 pWorkDoc
->getIDocumentLinksAdministration().EmbedAllLinks();
964 if( pView
) *pView
= pWorkView
;
965 if( pWrtShell
) *pWrtShell
= pWorkWrtShell
;
966 if( pDoc
) *pDoc
= pWorkDoc
;
969 return xWorkObjectShell
.get();
972 static rtl::Reference
<SwMailMessage
> lcl_CreateMailFromDoc(
973 const SwMergeDescriptor
&rMergeDescriptor
,
974 const OUString
&sFileURL
, const OUString
&sMailRecipient
,
975 const OUString
&sMailBodyMimeType
, rtl_TextEncoding sMailEncoding
,
976 const OUString
&sAttachmentMimeType
)
978 rtl::Reference
<SwMailMessage
> pMessage
= new SwMailMessage
;
979 if( rMergeDescriptor
.pMailMergeConfigItem
->IsMailReplyTo() )
980 pMessage
->setReplyToAddress(rMergeDescriptor
.pMailMergeConfigItem
->GetMailReplyTo());
981 pMessage
->addRecipient( sMailRecipient
);
982 pMessage
->SetSenderAddress( rMergeDescriptor
.pMailMergeConfigItem
->GetMailAddress() );
984 OUStringBuffer sBody
;
985 if( rMergeDescriptor
.bSendAsAttachment
)
987 sBody
= rMergeDescriptor
.sMailBody
;
988 mail::MailAttachment aAttach
;
989 aAttach
.Data
= new SwMailTransferable( sFileURL
,
990 rMergeDescriptor
.sAttachmentName
, sAttachmentMimeType
);
991 aAttach
.ReadableName
= rMergeDescriptor
.sAttachmentName
;
992 pMessage
->addAttachment( aAttach
);
996 //read in the temporary file and use it as mail body
997 SfxMedium
aMedium( sFileURL
, StreamMode::READ
);
998 SvStream
* pInStream
= aMedium
.GetInStream();
999 assert( pInStream
&& "no output file created?" );
1003 pInStream
->SetStreamCharSet( sMailEncoding
);
1004 OStringBuffer sLine
;
1005 while ( pInStream
->ReadLine( sLine
) )
1007 sBody
.append(OStringToOUString( sLine
, sMailEncoding
) + "\n");
1010 pMessage
->setSubject( rMergeDescriptor
.sSubject
);
1011 uno::Reference
< datatransfer::XTransferable
> xBody
=
1012 new SwMailTransferable( sBody
.makeStringAndClear(), sMailBodyMimeType
);
1013 pMessage
->setBody( xBody
);
1015 for( const OUString
& sCcRecipient
: rMergeDescriptor
.aCopiesTo
)
1016 pMessage
->addCcRecipient( sCcRecipient
);
1017 for( const OUString
& sBccRecipient
: rMergeDescriptor
.aBlindCopiesTo
)
1018 pMessage
->addBccRecipient( sBccRecipient
);
1023 class SwDBManager::MailDispatcherListener_Impl
: public IMailDispatcherListener
1025 SwDBManager
&m_rDBManager
;
1028 explicit MailDispatcherListener_Impl( SwDBManager
&rDBManager
)
1029 : m_rDBManager( rDBManager
) {}
1031 virtual void idle() override
{}
1033 virtual void mailDelivered( uno::Reference
< mail::XMailMessage
> xMessage
) override
1035 std::unique_lock
aGuard( m_rDBManager
.m_pImpl
->m_aAllEmailSendMutex
);
1036 if ( m_rDBManager
.m_pImpl
->m_xLastMessage
== xMessage
)
1037 m_rDBManager
.m_pImpl
->m_xLastMessage
.clear();
1040 virtual void mailDeliveryError( ::rtl::Reference
<MailDispatcher
> xMailDispatcher
,
1041 uno::Reference
< mail::XMailMessage
>, const OUString
& ) override
1043 std::unique_lock
aGuard( m_rDBManager
.m_pImpl
->m_aAllEmailSendMutex
);
1044 m_rDBManager
.m_aMergeStatus
= MergeStatus::Error
;
1045 m_rDBManager
.m_pImpl
->m_xLastMessage
.clear();
1046 xMailDispatcher
->stop();
1051 * Please have a look at the README in the same directory, before you make
1052 * larger changes in this function!
1054 bool SwDBManager::MergeMailFiles(SwWrtShell
* pSourceShell
,
1055 const SwMergeDescriptor
& rMergeDescriptor
)
1057 // deconstruct mail merge type for better readability.
1058 // uppercase naming is intentional!
1059 const bool bMT_EMAIL
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_EMAIL
;
1060 const bool bMT_SHELL
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_SHELL
;
1061 const bool bMT_PRINTER
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_PRINTER
;
1062 const bool bMT_FILE
= rMergeDescriptor
.nMergeType
== DBMGR_MERGE_FILE
;
1064 //check if the doc is synchronized and contains at least one linked section
1065 const bool bSynchronizedDoc
= pSourceShell
->IsLabelDoc() && pSourceShell
->GetSectionFormatCount() > 1;
1066 const bool bNeedsTempFiles
= ( bMT_EMAIL
|| bMT_FILE
);
1067 const bool bIsMergeSilent
= IsMergeSilent();
1069 bool bCheckSingleFile_
= rMergeDescriptor
.bCreateSingleFile
;
1070 OUString sPrefix_
= rMergeDescriptor
.sPrefix
;
1073 assert( !rMergeDescriptor
.bPrefixIsFilename
);
1074 assert(!bCheckSingleFile_
);
1075 bCheckSingleFile_
= false;
1077 else if( bMT_SHELL
|| bMT_PRINTER
)
1079 assert(bCheckSingleFile_
);
1080 bCheckSingleFile_
= true;
1081 assert(sPrefix_
.isEmpty());
1084 const bool bCreateSingleFile
= bCheckSingleFile_
;
1085 const OUString sDescriptorPrefix
= sPrefix_
;
1087 // Setup for dumping debugging documents
1088 static const sal_Int32 nMaxDumpDocs
= []() {
1089 if (const char* sEnv
= getenv("SW_DEBUG_MAILMERGE_DOCS"))
1090 return OUString(sEnv
, strlen(sEnv
), osl_getThreadTextEncoding()).toInt32();
1092 return sal_Int32(0);
1095 ::rtl::Reference
< MailDispatcher
> xMailDispatcher
;
1096 ::rtl::Reference
< IMailDispatcherListener
> xMailListener
;
1097 OUString sMailBodyMimeType
;
1098 rtl_TextEncoding sMailEncoding
= ::osl_getThreadTextEncoding();
1100 uno::Reference
< beans::XPropertySet
> xColumnProp
;
1101 uno::Reference
< beans::XPropertySet
> xPasswordColumnProp
;
1103 // Check for (mandatory) email or (optional) filename column
1104 SwDBFormatData aColumnDBFormat
;
1105 bool bColumnName
= !rMergeDescriptor
.sDBcolumn
.isEmpty();
1106 bool bPasswordColumnName
= !rMergeDescriptor
.sDBPasswordColumn
.isEmpty();
1115 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( m_pImpl
->pMergeData
->xResultSet
, uno::UNO_QUERY
);
1116 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
1117 if( !xCols
->hasByName( rMergeDescriptor
.sDBcolumn
) )
1119 uno::Any aCol
= xCols
->getByName( rMergeDescriptor
.sDBcolumn
);
1120 aCol
>>= xColumnProp
;
1122 if(bPasswordColumnName
)
1124 aCol
= xCols
->getByName( rMergeDescriptor
.sDBPasswordColumn
);
1125 aCol
>>= xPasswordColumnProp
;
1128 aColumnDBFormat
.xFormatter
= m_pImpl
->pMergeData
->xFormatter
;
1129 aColumnDBFormat
.aNullDate
= m_pImpl
->pMergeData
->aNullDate
;
1133 // Reset internal mail accounting data
1134 m_pImpl
->m_xLastMessage
.clear();
1136 xMailDispatcher
.set( new MailDispatcher(rMergeDescriptor
.xSmtpServer
) );
1137 xMailListener
= new MailDispatcherListener_Impl( *this );
1138 xMailDispatcher
->addListener( xMailListener
);
1139 if(!rMergeDescriptor
.bSendAsAttachment
&& rMergeDescriptor
.bSendAsHTML
)
1141 sMailBodyMimeType
= "text/html; charset=utf-8";
1142 sMailEncoding
= RTL_TEXTENCODING_UTF8
;
1145 sMailBodyMimeType
= "text/plain; charset=UTF-8; format=flowed";
1149 SwDocShell
*pSourceDocSh
= pSourceShell
->GetView().GetDocShell();
1151 // setup the output format
1152 std::shared_ptr
<const SfxFilter
> pStoreToFilter
= SwIoSystem::GetFileFilter(
1153 pSourceDocSh
->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE
));
1154 SfxFilterContainer
* pFilterContainer
= SwDocShell::Factory().GetFilterContainer();
1155 const OUString
* pStoreToFilterOptions
= nullptr;
1157 // if a save_to filter is set then use it - otherwise use the default
1158 if( bMT_EMAIL
&& !rMergeDescriptor
.bSendAsAttachment
)
1160 OUString sExtension
= rMergeDescriptor
.bSendAsHTML
? OUString("html") : OUString("txt");
1161 pStoreToFilter
= pFilterContainer
->GetFilter4Extension(sExtension
, SfxFilterFlags::EXPORT
);
1163 else if( !rMergeDescriptor
.sSaveToFilter
.isEmpty())
1165 std::shared_ptr
<const SfxFilter
> pFilter
=
1166 pFilterContainer
->GetFilter4FilterName( rMergeDescriptor
.sSaveToFilter
);
1169 pStoreToFilter
= pFilter
;
1170 if(!rMergeDescriptor
.sSaveToFilterOptions
.isEmpty())
1171 pStoreToFilterOptions
= &rMergeDescriptor
.sSaveToFilterOptions
;
1174 const bool bIsPDFexport
= pStoreToFilter
&& pStoreToFilter
->GetFilterName() == "writer_pdf_Export";
1175 const bool bIsMultiFile
= bMT_FILE
&& !bCreateSingleFile
;
1177 m_aMergeStatus
= MergeStatus::Ok
;
1179 // in case of creating a single resulting file this has to be created here
1180 SwView
* pTargetView
= rMergeDescriptor
.pMailMergeConfigItem
?
1181 rMergeDescriptor
.pMailMergeConfigItem
->GetTargetView() : nullptr;
1182 SwWrtShell
* pTargetShell
= nullptr;
1183 SfxObjectShellRef xTargetDocShell
;
1184 rtl::Reference
<SwDoc
> pTargetDoc
;
1186 std::unique_ptr
< utl::TempFileNamed
> aTempFile
;
1187 sal_uInt16 nStartingPageNo
= 0;
1189 std::shared_ptr
<weld::GenericDialogController
> xProgressDlg
;
1193 vcl::Window
*pSourceWindow
= nullptr;
1194 if( !bIsMergeSilent
)
1196 // construct the process dialog
1197 pSourceWindow
= &pSourceShell
->GetView().GetEditWin();
1199 xProgressDlg
= std::make_shared
<CreateMonitor
>(pSourceWindow
->GetFrameWeld());
1202 xProgressDlg
= std::make_shared
<PrintMonitor
>(pSourceWindow
->GetFrameWeld());
1203 static_cast<PrintMonitor
*>(xProgressDlg
.get())->set_title(
1204 pSourceDocSh
->GetTitle(22));
1206 weld::DialogController::runAsync(xProgressDlg
, [this, &xProgressDlg
](sal_Int32 nResult
){
1207 if (nResult
== RET_CANCEL
)
1209 xProgressDlg
.reset();
1212 Application::Reschedule( true );
1215 if( bCreateSingleFile
&& !pTargetView
)
1217 // create a target docshell to put the merged document into
1218 xTargetDocShell
= lcl_CreateWorkingDocument( WorkingDocType::TARGET
,
1219 *pSourceShell
, bMT_SHELL
? pSourceWindow
: nullptr,
1220 nullptr, &pTargetView
, &pTargetShell
, &pTargetDoc
);
1223 lcl_SaveDebugDoc( xTargetDocShell
.get(), "MergeDoc" );
1225 else if( pTargetView
)
1227 pTargetShell
= pTargetView
->GetWrtShellPtr();
1230 pTargetDoc
= pTargetShell
->GetDoc();
1231 xTargetDocShell
= pTargetView
->GetDocShell();
1235 if( bCreateSingleFile
)
1237 // determine the page style and number used at the start of the source document
1238 pSourceShell
->SttEndDoc(true);
1239 nStartingPageNo
= pSourceShell
->GetVirtPageNum();
1242 // Progress, to prohibit KeyInputs
1243 SfxProgress
aProgress(pSourceDocSh
, OUString(), 1);
1245 // lock all dispatchers
1246 SfxViewFrame
* pViewFrame
= SfxViewFrame::GetFirst(pSourceDocSh
);
1249 pViewFrame
->GetDispatcher()->Lock(true);
1250 pViewFrame
= SfxViewFrame::GetNext(*pViewFrame
, pSourceDocSh
);
1253 sal_Int32 nDocNo
= 1;
1255 // For single file mode, the number of pages in the target document so far, which is used
1256 // by AppendDoc() to adjust position of page-bound objects. Getting this information directly
1257 // from the target doc would require repeated layouts of the doc, which is expensive, but
1258 // it can be manually computed from the source documents (for which we do layouts, so the page
1259 // count is known, and there is a blank page between each of them in the target document).
1260 int targetDocPageCount
= 0;
1262 if( !bIsMergeSilent
&& !bMT_PRINTER
)
1264 sal_Int32 nRecordCount
= 1;
1265 lcl_getCountFromResultSet( nRecordCount
, m_pImpl
->pMergeData
.get() );
1267 // Synchronized docs don't auto-advance the record set, but there is a
1268 // "security" check, which will always advance the record set, if there
1269 // is no "next record" field in a synchronized doc => nRecordPerDoc > 0
1270 sal_Int32 nRecordPerDoc
= pSourceShell
->GetDoc()
1271 ->getIDocumentFieldsAccess().GetRecordsPerDocument();
1272 if ( bSynchronizedDoc
&& (nRecordPerDoc
> 1) )
1274 assert( nRecordPerDoc
> 0 );
1276 sal_Int32 nMaxDocs
= nRecordCount
/ nRecordPerDoc
;
1277 if ( 0 != nRecordCount
% nRecordPerDoc
)
1279 static_cast<CreateMonitor
*>(xProgressDlg
.get())->SetTotalCount(nMaxDocs
);
1282 sal_Int32 nStartRow
, nEndRow
;
1283 bool bFreezedLayouts
= false;
1284 // to collect temporary email files
1285 std::vector
< OUString
> aFilesToRemove
;
1287 // The SfxObjectShell will be closed explicitly later but
1288 // it is more safe to use SfxObjectShellLock here
1289 SfxObjectShellLock xWorkDocSh
;
1290 SwView
* pWorkView
= nullptr;
1291 rtl::Reference
<SwDoc
> pWorkDoc
;
1292 SwDBManager
* pWorkDocOrigDBManager
= nullptr;
1293 SwWrtShell
* pWorkShell
= nullptr;
1294 bool bWorkDocInitialized
= false;
1298 nStartRow
= m_pImpl
->pMergeData
? m_pImpl
->pMergeData
->xResultSet
->getRow() : 0;
1300 OUString sColumnData
;
1302 // Read the indicated data column, which should contain a valid mail
1303 // address or an optional file name
1304 if( bMT_EMAIL
|| bColumnName
)
1306 sColumnData
= GetDBField( xColumnProp
, aColumnDBFormat
);
1309 // create a new temporary file name - only done once in case of bCreateSingleFile
1310 if( bNeedsTempFiles
&& ( !bWorkDocInitialized
|| !bCreateSingleFile
))
1312 OUString sPrefix
= sDescriptorPrefix
;
1315 //#i97667# if the name is from a database field then it will be used _as is_
1316 if( bColumnName
&& !bMT_EMAIL
)
1318 if (!sColumnData
.isEmpty())
1319 sLeading
= sColumnData
;
1325 INetURLObject
aEntry( sPrefix
);
1326 sLeading
= aEntry
.GetBase();
1327 aEntry
.removeSegment();
1328 sPrefix
= aEntry
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1331 OUString
sExt(comphelper::string::stripStart(pStoreToFilter
->GetDefaultExtension(), '*'));
1332 aTempFile
.reset( new utl::TempFileNamed(sLeading
, sColumnData
.isEmpty(), sExt
, &sPrefix
, true) );
1333 if( !aTempFile
->IsValid() )
1335 ErrorHandler::HandleError( ERRCODE_IO_NOTSUPPORTED
);
1336 m_aMergeStatus
= MergeStatus::Error
;
1340 uno::Sequence
< beans::PropertyValue
> aSaveToFilterDataOptions( rMergeDescriptor
.aSaveToFilterData
);
1342 if( bMT_EMAIL
|| bPasswordColumnName
)
1344 OUString sPasswordColumnData
= GetDBField( xPasswordColumnProp
, aColumnDBFormat
);
1345 lcl_PrepareSaveFilterDataOptions( rMergeDescriptor
.aSaveToFilterData
, aSaveToFilterDataOptions
, sPasswordColumnData
);
1350 std::unique_ptr
< INetURLObject
> aTempFileURL
;
1351 if( bNeedsTempFiles
)
1352 aTempFileURL
.reset( new INetURLObject(aTempFile
->GetURL()));
1353 if( !bIsMergeSilent
) {
1355 static_cast<CreateMonitor
*>(xProgressDlg
.get())->SetCurrentPosition(nDocNo
);
1357 PrintMonitor
*pPrintMonDlg
= static_cast<PrintMonitor
*>(xProgressDlg
.get());
1358 pPrintMonDlg
->m_xPrinter
->set_label(bNeedsTempFiles
1359 ? aTempFileURL
->GetBase() : pSourceDocSh
->GetTitle( 2));
1360 OUString sStat
= SwResId(STR_STATSTR_LETTER
) + " " + OUString::number( nDocNo
);
1361 pPrintMonDlg
->m_xPrintInfo
->set_label(sStat
);
1363 //TODO xProgressDlg->queue_draw();
1366 Scheduler::ProcessEventsToIdle();
1368 // Create a copy of the source document and work with that one instead of the source.
1369 // If we're not in the single file mode (which requires modifying the document for the merging),
1370 // it is enough to do this just once. Currently PDF also has to be treated special.
1371 if( !bWorkDocInitialized
|| bCreateSingleFile
|| bIsPDFexport
|| bIsMultiFile
)
1373 assert( !xWorkDocSh
.Is());
1374 pWorkDocOrigDBManager
= this;
1375 xWorkDocSh
= lcl_CreateWorkingDocument( WorkingDocType::COPY
,
1376 *pSourceShell
, nullptr, &pWorkDocOrigDBManager
,
1377 &pWorkView
, &pWorkShell
, &pWorkDoc
);
1378 if ( (nMaxDumpDocs
< 0) || (nDocNo
<= nMaxDumpDocs
) )
1379 lcl_SaveDebugDoc( xWorkDocSh
, "WorkDoc", nDocNo
);
1381 // #i69458# lock fields to prevent access to the result set while calculating layout
1382 // tdf#92324: and do not unlock: keep document locked during printing to avoid
1383 // ExpFields update during printing, generation of preview, etc.
1384 pWorkShell
->LockExpFields();
1385 pWorkShell
->CalcLayout();
1386 // tdf#121168: Now force correct page descriptions applied to page frames. Without
1387 // this, e.g., page frames starting with sections could have page descriptions set
1388 // wrong. This would lead to wrong page styles applied in SwDoc::AppendDoc below.
1389 pWorkShell
->GetViewOptions()->SetIdle(true);
1390 for (auto aLayout
: pWorkShell
->GetDoc()->GetAllLayouts())
1392 aLayout
->FreezeLayout(false);
1393 aLayout
->AllCheckPageDescs();
1397 lcl_emitEvent(SfxEventHintId::SwEventFieldMerge
, STR_SW_EVENT_FIELD_MERGE
, xWorkDocSh
);
1399 // tdf#92324: Allow ExpFields update only by explicit instruction to avoid
1400 // database cursor movement on any other fields update, for example during
1401 // print preview and other operations
1402 if ( pWorkShell
->IsExpFieldsLocked() )
1403 pWorkShell
->UnlockExpFields();
1404 pWorkShell
->SwViewShell::UpdateFields();
1405 pWorkShell
->LockExpFields();
1407 lcl_emitEvent(SfxEventHintId::SwEventFieldMergeFinished
, STR_SW_EVENT_FIELD_MERGE_FINISHED
, xWorkDocSh
);
1409 // also emit MailMergeEvent on XInterface if possible
1410 const SwXMailMerge
*pEvtSrc
= GetMailMergeEvtSrc();
1413 rtl::Reference
< SwXMailMerge
> xRef(
1414 const_cast<SwXMailMerge
*>(pEvtSrc
) );
1415 text::MailMergeEvent
aEvt( static_cast<text::XMailMergeBroadcaster
*>(xRef
.get()), xWorkDocSh
->GetModel() );
1416 SolarMutexReleaser rel
;
1417 xRef
->LaunchMailMergeEvent( aEvt
);
1420 // working copy is merged - prepare final steps depending on merge options
1422 if( bCreateSingleFile
)
1424 assert( pTargetShell
&& "no target shell available!" );
1426 // prepare working copy and target to append
1428 pWorkDoc
->RemoveInvisibleContent();
1429 // remove of invisible content has influence on page count and so on fields for page count,
1430 // therefore layout has to be updated before fields are converted to text
1431 pWorkShell
->CalcLayout();
1432 pWorkShell
->ConvertFieldsToText();
1433 pWorkShell
->SetNumberingRestart();
1434 if( bSynchronizedDoc
)
1436 lcl_RemoveSectionLinks( *pWorkShell
);
1439 if ( (nMaxDumpDocs
< 0) || (nDocNo
<= nMaxDumpDocs
) )
1440 lcl_SaveDebugDoc( xWorkDocSh
, "WorkDoc", nDocNo
);
1442 // append the working document to the target document
1443 if( targetDocPageCount
% 2 == 1 )
1444 ++targetDocPageCount
; // Docs always start on odd pages (so offset must be even).
1445 SwNodeIndex appendedDocStart
= pTargetDoc
->AppendDoc( *pWorkDoc
,
1446 nStartingPageNo
, !bWorkDocInitialized
, targetDocPageCount
, nDocNo
);
1447 targetDocPageCount
+= pWorkShell
->GetPageCnt();
1449 if ( (nMaxDumpDocs
< 0) || (nDocNo
<= nMaxDumpDocs
) )
1450 lcl_SaveDebugDoc( xTargetDocShell
.get(), "MergeDoc" );
1454 SwDocMergeInfo aMergeInfo
;
1455 // Name of the mark is actually irrelevant, UNO bookmarks have internals names.
1456 aMergeInfo
.startPageInTarget
= pTargetDoc
->getIDocumentMarkAccess()->makeMark(
1457 SwPaM(appendedDocStart
), "", IDocumentMarkAccess::MarkType::UNO_BOOKMARK
,
1458 ::sw::mark::InsertMode::New
);
1459 aMergeInfo
.nDBRow
= nStartRow
;
1460 rMergeDescriptor
.pMailMergeConfigItem
->AddMergedDocument( aMergeInfo
);
1465 assert( bNeedsTempFiles
);
1466 assert( pWorkShell
->IsExpFieldsLocked() );
1468 if (bIsMultiFile
&& pWorkDoc
->HasInvisibleContent())
1470 pWorkDoc
->RemoveInvisibleContent();
1471 pWorkShell
->CalcLayout();
1472 pWorkShell
->ConvertFieldsToText();
1475 // fields are locked, so it's fine to
1476 // restore the old / empty DB manager for save
1477 pWorkDoc
->SetDBManager( pWorkDocOrigDBManager
);
1479 // save merged document
1481 if( !lcl_SaveDoc( aTempFileURL
.get(), pStoreToFilter
, pStoreToFilterOptions
,
1482 &aSaveToFilterDataOptions
, bIsPDFexport
,
1483 xWorkDocSh
, *pWorkShell
, &sFileURL
) )
1485 m_aMergeStatus
= MergeStatus::Error
;
1488 // back to the MM DB manager
1489 pWorkDoc
->SetDBManager( this );
1491 if( bMT_EMAIL
&& !IsMergeError() )
1493 // schedule file for later removal
1494 aFilesToRemove
.push_back( sFileURL
);
1496 if( !SwMailMergeHelper::CheckMailAddress( sColumnData
) )
1498 OSL_FAIL("invalid e-Mail address in database column");
1502 uno::Reference
< mail::XMailMessage
> xMessage
= lcl_CreateMailFromDoc(
1503 rMergeDescriptor
, sFileURL
, sColumnData
, sMailBodyMimeType
,
1504 sMailEncoding
, pStoreToFilter
->GetMimeType() );
1507 std::unique_lock
aGuard( m_pImpl
->m_aAllEmailSendMutex
);
1508 m_pImpl
->m_xLastMessage
.set( xMessage
);
1509 xMailDispatcher
->enqueueMailMessage( xMessage
);
1510 if( !xMailDispatcher
->isStarted() )
1511 xMailDispatcher
->start();
1516 if( bCreateSingleFile
|| bIsPDFexport
|| bIsMultiFile
)
1518 pWorkDoc
->SetDBManager( pWorkDocOrigDBManager
);
1520 xWorkDocSh
->DoClose();
1521 xWorkDocSh
= nullptr;
1525 bWorkDocInitialized
= true;
1527 nEndRow
= m_pImpl
->pMergeData
? m_pImpl
->pMergeData
->xResultSet
->getRow() : 0;
1529 // Freeze the layouts of the target document after the first inserted
1530 // sub-document, to get the correct PageDesc.
1531 if(!bFreezedLayouts
&& bCreateSingleFile
)
1533 for ( auto aLayout
: pTargetShell
->GetDoc()->GetAllLayouts() )
1534 aLayout
->FreezeLayout(true);
1535 bFreezedLayouts
= true;
1537 } while( IsMergeOk() &&
1538 ((bSynchronizedDoc
&& (nStartRow
!= nEndRow
)) ? IsValidMergeRecord() : ToNextMergeRecord()));
1540 if ( xWorkDocSh
.Is() && pWorkView
->GetWrtShell().IsExpFieldsLocked() )
1542 // Unlock document fields after merge complete
1543 pWorkView
->GetWrtShell().UnlockExpFields();
1546 if( !bCreateSingleFile
)
1549 Printer::FinishPrintJob( pWorkView
->GetPrinterController());
1553 pWorkDoc
->SetDBManager(pWorkDocOrigDBManager
);
1554 if (xWorkDocSh
.Is())
1555 xWorkDocSh
->DoClose();
1558 else if( IsMergeOk() ) // && bCreateSingleFile
1560 Application::Reschedule( true );
1562 // sw::DocumentLayoutManager::CopyLayoutFormat() did not generate
1563 // unique fly names, do it here once.
1564 pTargetDoc
->SetInMailMerge(false);
1565 pTargetDoc
->SetAllUniqueFlyNames();
1567 // Unfreeze target document layouts and correct all PageDescs.
1568 SAL_INFO( "sw.pageframe", "(MergeMailFiles pTargetShell->CalcLayout in" );
1569 pTargetShell
->CalcLayout();
1570 SAL_INFO( "sw.pageframe", "MergeMailFiles pTargetShell->CalcLayout out)" );
1571 pTargetShell
->GetViewOptions()->SetIdle( true );
1572 pTargetDoc
->GetIDocumentUndoRedo().DoUndo( true );
1573 for ( auto aLayout
: pTargetShell
->GetDoc()->GetAllLayouts() )
1575 aLayout
->FreezeLayout(false);
1576 aLayout
->AllCheckPageDescs();
1579 Application::Reschedule( true );
1581 if( IsMergeOk() && bMT_FILE
)
1583 // save merged document
1584 assert( aTempFile
);
1585 INetURLObject aTempFileURL
;
1586 if (sDescriptorPrefix
.isEmpty() || !rMergeDescriptor
.bPrefixIsFilename
)
1587 aTempFileURL
.SetURL( aTempFile
->GetURL() );
1590 aTempFileURL
.SetURL(sDescriptorPrefix
);
1591 // remove the unneeded temporary file
1592 aTempFile
->EnableKillingFile();
1594 if( !lcl_SaveDoc( &aTempFileURL
, pStoreToFilter
,
1595 pStoreToFilterOptions
, &rMergeDescriptor
.aSaveToFilterData
,
1596 bIsPDFexport
, xTargetDocShell
.get(), *pTargetShell
) )
1598 m_aMergeStatus
= MergeStatus::Error
;
1601 else if( IsMergeOk() && bMT_PRINTER
)
1603 // print the target document
1604 uno::Sequence
< beans::PropertyValue
> aOptions( rMergeDescriptor
.aPrintOptions
);
1605 lcl_PreparePrinterOptions( rMergeDescriptor
.aPrintOptions
, aOptions
);
1606 pTargetView
->ExecPrint( aOptions
, bIsMergeSilent
, false/*bPrintAsync*/ );
1610 // we also show canceled documents, as long as there was no error
1611 if( !IsMergeError() && bMT_SHELL
)
1612 // leave docshell available for caller (e.g. MM wizard)
1613 rMergeDescriptor
.pMailMergeConfigItem
->SetTargetView( pTargetView
);
1614 else if( xTargetDocShell
.is() )
1615 xTargetDocShell
->DoClose();
1617 Application::Reschedule( true );
1621 xProgressDlg
->response(RET_OK
);
1624 // unlock all dispatchers
1625 pViewFrame
= SfxViewFrame::GetFirst(pSourceDocSh
);
1628 pViewFrame
->GetDispatcher()->Lock(false);
1629 pViewFrame
= SfxViewFrame::GetNext(*pViewFrame
, pSourceDocSh
);
1632 SW_MOD()->SetView(&pSourceShell
->GetView());
1634 if( xMailDispatcher
.is() )
1638 // TODO: Instead of polling via an AutoTimer, post an Idle event,
1639 // if the main loop has been made thread-safe.
1640 AutoTimer
aEmailDispatcherPollTimer("sw::SwDBManager aEmailDispatcherPollTimer");
1641 aEmailDispatcherPollTimer
.SetTimeout( 500 );
1642 aEmailDispatcherPollTimer
.Start();
1643 while( IsMergeOk() && m_pImpl
->m_xLastMessage
.is() && !Application::IsQuit())
1644 Application::Yield();
1645 aEmailDispatcherPollTimer
.Stop();
1647 xMailDispatcher
->stop();
1648 xMailDispatcher
->shutdown();
1651 // remove the temporary files
1652 // has to be done after xMailDispatcher is finished, as mails may be
1653 // delivered as message attachments!
1654 for( const OUString
&sFileURL
: aFilesToRemove
)
1655 SWUnoHelper::UCB_DeleteFile( sFileURL
);
1657 catch (const uno::Exception
&)
1661 xProgressDlg
->response(RET_CANCEL
);
1665 return !IsMergeError();
1668 void SwDBManager::MergeCancel()
1670 if (m_aMergeStatus
< MergeStatus::Cancel
)
1671 m_aMergeStatus
= MergeStatus::Cancel
;
1674 // determine the column's Numberformat and transfer to the forwarded Formatter,
1676 sal_uLong
SwDBManager::GetColumnFormat( const OUString
& rDBName
,
1677 const OUString
& rTableName
,
1678 const OUString
& rColNm
,
1679 SvNumberFormatter
* pNFormatr
,
1680 LanguageType nLanguage
)
1685 uno::Reference
< sdbc::XDataSource
> xSource
;
1686 uno::Reference
< sdbc::XConnection
> xConnection
;
1687 bool bUseMergeData
= false;
1688 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp
;
1689 bool bDisposeConnection
= false;
1690 if(m_pImpl
->pMergeData
&&
1691 ((m_pImpl
->pMergeData
->sDataSource
== rDBName
&& m_pImpl
->pMergeData
->sCommand
== rTableName
) ||
1692 (rDBName
.isEmpty() && rTableName
.isEmpty())))
1694 xConnection
= m_pImpl
->pMergeData
->xConnection
;
1695 xSource
= SwDBManager::getDataSourceAsParent(xConnection
,rDBName
);
1696 bUseMergeData
= true;
1697 xColsSupp
.set(m_pImpl
->pMergeData
->xResultSet
, css::uno::UNO_QUERY
);
1699 if(!xConnection
.is())
1702 aData
.sDataSource
= rDBName
;
1703 aData
.sCommand
= rTableName
;
1704 aData
.nCommandType
= -1;
1705 SwDSParam
* pParam
= FindDSData(aData
, false);
1706 if(pParam
&& pParam
->xConnection
.is())
1708 xConnection
= pParam
->xConnection
;
1709 xColsSupp
.set(pParam
->xResultSet
, css::uno::UNO_QUERY
);
1713 xConnection
= RegisterConnection( rDBName
);
1714 bDisposeConnection
= true;
1717 m_pImpl
->pMergeData
->xConnection
= xConnection
;
1719 bool bDispose
= !xColsSupp
.is();
1722 xColsSupp
= SwDBManager::GetColumnSupplier(xConnection
, rTableName
);
1726 uno::Reference
<container::XNameAccess
> xCols
;
1729 xCols
= xColsSupp
->getColumns();
1731 catch (const uno::Exception
&)
1733 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Exception in getColumns()");
1735 if(!xCols
.is() || !xCols
->hasByName(rColNm
))
1737 uno::Any aCol
= xCols
->getByName(rColNm
);
1738 uno::Reference
< beans::XPropertySet
> xColumn
;
1740 nRet
= GetColumnFormat(xSource
, xConnection
, xColumn
, pNFormatr
, nLanguage
);
1743 ::comphelper::disposeComponent( xColsSupp
);
1745 if(bDisposeConnection
)
1747 ::comphelper::disposeComponent( xConnection
);
1751 nRet
= pNFormatr
->GetFormatIndex( NF_NUMBER_STANDARD
, LANGUAGE_SYSTEM
);
1756 sal_uLong
SwDBManager::GetColumnFormat( uno::Reference
< sdbc::XDataSource
> const & xSource_in
,
1757 uno::Reference
< sdbc::XConnection
> const & xConnection
,
1758 uno::Reference
< beans::XPropertySet
> const & xColumn
,
1759 SvNumberFormatter
* pNFormatr
,
1760 LanguageType nLanguage
)
1762 auto xSource
= xSource_in
;
1764 // set the NumberFormat in the doc if applicable
1769 uno::Reference
<container::XChild
> xChild(xConnection
, uno::UNO_QUERY
);
1771 xSource
.set(xChild
->getParent(), uno::UNO_QUERY
);
1773 if(xSource
.is() && xConnection
.is() && xColumn
.is() && pNFormatr
)
1775 rtl::Reference
<SvNumberFormatsSupplierObj
> pNumFormat
= new SvNumberFormatsSupplierObj( pNFormatr
);
1776 uno::Reference
< util::XNumberFormats
> xDocNumberFormats
= pNumFormat
->getNumberFormats();
1777 uno::Reference
< util::XNumberFormatTypes
> xDocNumberFormatTypes(xDocNumberFormats
, uno::UNO_QUERY
);
1779 css::lang::Locale
aLocale( LanguageTag( nLanguage
).getLocale());
1781 //get the number formatter of the data source
1782 uno::Reference
<beans::XPropertySet
> xSourceProps(xSource
, uno::UNO_QUERY
);
1783 uno::Reference
< util::XNumberFormats
> xNumberFormats
;
1784 if(xSourceProps
.is())
1786 uno::Any aFormats
= xSourceProps
->getPropertyValue("NumberFormatsSupplier");
1787 if(aFormats
.hasValue())
1789 uno::Reference
<util::XNumberFormatsSupplier
> xSuppl
;
1790 aFormats
>>= xSuppl
;
1793 xNumberFormats
= xSuppl
->getNumberFormats();
1797 bool bUseDefault
= true;
1800 uno::Any aFormatKey
= xColumn
->getPropertyValue("FormatKey");
1801 if(aFormatKey
.hasValue())
1803 sal_Int32 nFormat
= 0;
1804 aFormatKey
>>= nFormat
;
1805 if(xNumberFormats
.is())
1809 uno::Reference
<beans::XPropertySet
> xNumProps
= xNumberFormats
->getByKey( nFormat
);
1810 uno::Any aFormatString
= xNumProps
->getPropertyValue("FormatString");
1811 uno::Any aLocaleVal
= xNumProps
->getPropertyValue("Locale");
1813 aFormatString
>>= sFormat
;
1815 aLocaleVal
>>= aLoc
;
1816 nFormat
= xDocNumberFormats
->queryKey( sFormat
, aLoc
, false );
1817 if(NUMBERFORMAT_ENTRY_NOT_FOUND
== sal::static_int_cast
< sal_uInt32
, sal_Int32
>(nFormat
))
1818 nFormat
= xDocNumberFormats
->addNew( sFormat
, aLoc
);
1820 bUseDefault
= false;
1822 catch (const uno::Exception
&)
1824 TOOLS_WARN_EXCEPTION("sw.mailmerge", "illegal number format key");
1829 catch(const uno::Exception
&)
1831 SAL_WARN("sw.mailmerge", "no FormatKey property found");
1834 nRet
= dbtools::getDefaultNumberFormat(xColumn
, xDocNumberFormatTypes
, aLocale
);
1839 sal_Int32
SwDBManager::GetColumnType( const OUString
& rDBName
,
1840 const OUString
& rTableName
,
1841 const OUString
& rColNm
)
1843 sal_Int32 nRet
= sdbc::DataType::SQLNULL
;
1845 aData
.sDataSource
= rDBName
;
1846 aData
.sCommand
= rTableName
;
1847 aData
.nCommandType
= -1;
1848 SwDSParam
* pParam
= FindDSData(aData
, false);
1849 uno::Reference
< sdbc::XConnection
> xConnection
;
1850 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp
;
1851 bool bDispose
= false;
1852 if(pParam
&& pParam
->xConnection
.is())
1854 xConnection
= pParam
->xConnection
;
1855 xColsSupp
.set( pParam
->xResultSet
, uno::UNO_QUERY
);
1859 xConnection
= RegisterConnection( rDBName
);
1861 if( !xColsSupp
.is() )
1863 xColsSupp
= SwDBManager::GetColumnSupplier(xConnection
, rTableName
);
1868 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
1869 if(xCols
->hasByName(rColNm
))
1871 uno::Any aCol
= xCols
->getByName(rColNm
);
1872 uno::Reference
<beans::XPropertySet
> xCol
;
1874 uno::Any aType
= xCol
->getPropertyValue("Type");
1878 ::comphelper::disposeComponent( xColsSupp
);
1883 uno::Reference
< sdbc::XConnection
> SwDBManager::GetConnection(const OUString
& rDataSource
,
1884 uno::Reference
<sdbc::XDataSource
>& rxSource
, const SwView
*pView
)
1886 uno::Reference
< sdbc::XConnection
> xConnection
;
1887 uno::Reference
< uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
1890 uno::Reference
<sdb::XCompletedConnection
> xComplConnection(dbtools::getDataSource(rDataSource
, xContext
), uno::UNO_QUERY
);
1891 if ( xComplConnection
.is() )
1893 rxSource
.set(xComplConnection
, uno::UNO_QUERY
);
1894 weld::Window
* pWindow
= pView
? pView
->GetFrameWeld() : nullptr;
1895 uno::Reference
< task::XInteractionHandler
> xHandler( task::InteractionHandler::createWithParent(xContext
, pWindow
? pWindow
->GetXWindow() : nullptr), uno::UNO_QUERY_THROW
);
1896 xConnection
= xComplConnection
->connectWithCompletion( xHandler
);
1899 catch(const uno::Exception
&)
1906 uno::Reference
< sdbcx::XColumnsSupplier
> SwDBManager::GetColumnSupplier(uno::Reference
<sdbc::XConnection
> const & xConnection
,
1907 const OUString
& rTableOrQuery
,
1908 SwDBSelect eTableOrQuery
)
1910 uno::Reference
< sdbcx::XColumnsSupplier
> xRet
;
1913 if(eTableOrQuery
== SwDBSelect::UNKNOWN
)
1915 //search for a table with the given command name
1916 uno::Reference
<sdbcx::XTablesSupplier
> xTSupplier(xConnection
, uno::UNO_QUERY
);
1919 uno::Reference
<container::XNameAccess
> xTables
= xTSupplier
->getTables();
1920 eTableOrQuery
= xTables
->hasByName(rTableOrQuery
) ?
1921 SwDBSelect::TABLE
: SwDBSelect::QUERY
;
1924 sal_Int32 nCommandType
= SwDBSelect::TABLE
== eTableOrQuery
?
1925 sdb::CommandType::TABLE
: sdb::CommandType::QUERY
;
1926 uno::Reference
< lang::XMultiServiceFactory
> xMgr( ::comphelper::getProcessServiceFactory() );
1927 uno::Reference
<sdbc::XRowSet
> xRowSet(xMgr
->createInstance("com.sun.star.sdb.RowSet"), uno::UNO_QUERY
);
1929 OUString sDataSource
;
1930 uno::Reference
<sdbc::XDataSource
> xSource
= SwDBManager::getDataSourceAsParent(xConnection
, sDataSource
);
1931 uno::Reference
<beans::XPropertySet
> xSourceProperties(xSource
, uno::UNO_QUERY
);
1932 if(xSourceProperties
.is())
1934 xSourceProperties
->getPropertyValue("Name") >>= sDataSource
;
1937 uno::Reference
<beans::XPropertySet
> xRowProperties(xRowSet
, uno::UNO_QUERY
);
1938 xRowProperties
->setPropertyValue("DataSourceName", uno::Any(sDataSource
));
1939 xRowProperties
->setPropertyValue("Command", uno::Any(rTableOrQuery
));
1940 xRowProperties
->setPropertyValue("CommandType", uno::Any(nCommandType
));
1941 xRowProperties
->setPropertyValue("FetchSize", uno::Any(sal_Int32(10)));
1942 xRowProperties
->setPropertyValue("ActiveConnection", uno::Any(xConnection
));
1944 xRet
.set( xRowSet
, uno::UNO_QUERY
);
1946 catch (const uno::Exception
&)
1948 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Exception in SwDBManager::GetColumnSupplier");
1954 OUString
SwDBManager::GetDBField(uno::Reference
<beans::XPropertySet
> const & xColumnProps
,
1955 const SwDBFormatData
& rDBFormatData
,
1958 uno::Reference
< sdb::XColumn
> xColumn(xColumnProps
, uno::UNO_QUERY
);
1960 assert( xColumn
.is() && "SwDBManager::::ImportDBField: illegal arguments" );
1964 uno::Any aType
= xColumnProps
->getPropertyValue("Type");
1965 sal_Int32 eDataType
= sdbc::DataType::SQLNULL
;
1966 aType
>>= eDataType
;
1969 case sdbc::DataType::CHAR
:
1970 case sdbc::DataType::VARCHAR
:
1971 case sdbc::DataType::LONGVARCHAR
:
1974 sRet
= xColumn
->getString();
1975 sRet
= sRet
.replace( '\xb', '\n' ); // MSWord uses \xb as a newline
1977 catch(const sdbc::SQLException
&)
1981 case sdbc::DataType::BIT
:
1982 case sdbc::DataType::BOOLEAN
:
1983 case sdbc::DataType::TINYINT
:
1984 case sdbc::DataType::SMALLINT
:
1985 case sdbc::DataType::INTEGER
:
1986 case sdbc::DataType::BIGINT
:
1987 case sdbc::DataType::FLOAT
:
1988 case sdbc::DataType::REAL
:
1989 case sdbc::DataType::DOUBLE
:
1990 case sdbc::DataType::NUMERIC
:
1991 case sdbc::DataType::DECIMAL
:
1992 case sdbc::DataType::DATE
:
1993 case sdbc::DataType::TIME
:
1994 case sdbc::DataType::TIMESTAMP
:
1999 sRet
= dbtools::DBTypeConversion::getFormattedValue(
2001 rDBFormatData
.xFormatter
,
2002 rDBFormatData
.aLocale
,
2003 rDBFormatData
.aNullDate
);
2006 double fVal
= xColumn
->getDouble();
2007 if(!xColumn
->wasNull())
2013 catch (const uno::Exception
&)
2015 TOOLS_WARN_EXCEPTION("sw.mailmerge", "");
2025 // checks if a desired data source table or query is open
2026 bool SwDBManager::IsDataSourceOpen(const OUString
& rDataSource
,
2027 const OUString
& rTableOrQuery
, bool bMergeShell
)
2029 if(m_pImpl
->pMergeData
)
2031 return ((rDataSource
== m_pImpl
->pMergeData
->sDataSource
2032 && rTableOrQuery
== m_pImpl
->pMergeData
->sCommand
)
2033 || (rDataSource
.isEmpty() && rTableOrQuery
.isEmpty()))
2034 && m_pImpl
->pMergeData
->xResultSet
.is();
2036 else if(!bMergeShell
)
2039 aData
.sDataSource
= rDataSource
;
2040 aData
.sCommand
= rTableOrQuery
;
2041 aData
.nCommandType
= -1;
2042 SwDSParam
* pFound
= FindDSData(aData
, false);
2043 return (pFound
&& pFound
->xResultSet
.is());
2048 // read column data at a specified position
2049 bool SwDBManager::GetColumnCnt(const OUString
& rSourceName
, const OUString
& rTableName
,
2050 const OUString
& rColumnName
, sal_uInt32 nAbsRecordId
,
2051 LanguageType nLanguage
,
2052 OUString
& rResult
, double* pNumber
)
2055 SwDSParam
* pFound
= nullptr;
2056 //check if it's the merge data source
2057 if(m_pImpl
->pMergeData
&&
2058 rSourceName
== m_pImpl
->pMergeData
->sDataSource
&&
2059 rTableName
== m_pImpl
->pMergeData
->sCommand
)
2061 pFound
= m_pImpl
->pMergeData
.get();
2066 aData
.sDataSource
= rSourceName
;
2067 aData
.sCommand
= rTableName
;
2068 aData
.nCommandType
= -1;
2069 pFound
= FindDSData(aData
, false);
2073 //check validity of supplied record Id
2074 if(pFound
->aSelection
.hasElements())
2076 //the destination has to be an element of the selection
2077 bool bFound
= std::any_of(std::cbegin(pFound
->aSelection
), std::cend(pFound
->aSelection
),
2078 [nAbsRecordId
](const uno::Any
& rSelection
) {
2079 sal_Int32 nSelection
= 0;
2080 rSelection
>>= nSelection
;
2081 return nSelection
== static_cast<sal_Int32
>(nAbsRecordId
);
2086 if( pFound
->HasValidRecord() )
2088 sal_Int32 nOldRow
= 0;
2091 nOldRow
= pFound
->xResultSet
->getRow();
2093 catch(const uno::Exception
&)
2097 //position to the desired index
2099 if ( nOldRow
!= static_cast<sal_Int32
>(nAbsRecordId
) )
2100 bMove
= lcl_MoveAbsolute(pFound
, nAbsRecordId
);
2102 bRet
= lcl_GetColumnCnt(pFound
, rColumnName
, nLanguage
, rResult
, pNumber
);
2103 if ( nOldRow
!= static_cast<sal_Int32
>(nAbsRecordId
) )
2104 lcl_MoveAbsolute(pFound
, nOldRow
);
2109 // reads the column data at the current position
2110 bool SwDBManager::GetMergeColumnCnt(const OUString
& rColumnName
, LanguageType nLanguage
,
2111 OUString
&rResult
, double *pNumber
)
2113 if( !IsValidMergeRecord() )
2119 bool bRet
= lcl_GetColumnCnt(m_pImpl
->pMergeData
.get(), rColumnName
, nLanguage
, rResult
, pNumber
);
2123 bool SwDBManager::ToNextMergeRecord()
2125 assert( m_pImpl
->pMergeData
&& m_pImpl
->pMergeData
->xResultSet
.is() && "no data source in merge" );
2126 return lcl_ToNextRecord( m_pImpl
->pMergeData
.get() );
2129 bool SwDBManager::FillCalcWithMergeData( SvNumberFormatter
*pDocFormatter
,
2130 LanguageType nLanguage
, SwCalc
&rCalc
)
2132 if( !IsValidMergeRecord() )
2135 uno::Reference
< sdbcx::XColumnsSupplier
> xColsSupp( m_pImpl
->pMergeData
->xResultSet
, uno::UNO_QUERY
);
2136 if( !xColsSupp
.is() )
2140 uno::Reference
<container::XNameAccess
> xCols
= xColsSupp
->getColumns();
2141 const uno::Sequence
<OUString
> aColNames
= xCols
->getElementNames();
2144 // add the "record number" variable, as SwCalc::VarLook would.
2145 rCalc
.VarChange( GetAppCharClass().lowercase(
2146 SwFieldType::GetTypeStr(SwFieldTypesEnum::DatabaseSetNumber
) ), GetSelectedRecordId() );
2148 for( const OUString
& rColName
: aColNames
)
2150 // get the column type
2151 sal_Int32 nColumnType
= sdbc::DataType::SQLNULL
;
2152 uno::Any aCol
= xCols
->getByName( rColName
);
2153 uno::Reference
<beans::XPropertySet
> xColumnProps
;
2154 aCol
>>= xColumnProps
;
2155 uno::Any aType
= xColumnProps
->getPropertyValue( "Type" );
2156 aType
>>= nColumnType
;
2157 double aNumber
= DBL_MAX
;
2159 lcl_GetColumnCnt( m_pImpl
->pMergeData
.get(), xColumnProps
, nLanguage
, aString
, &aNumber
);
2161 sal_uInt32 nFormat
= GetColumnFormat( m_pImpl
->pMergeData
->sDataSource
,
2162 m_pImpl
->pMergeData
->sCommand
,
2163 rColName
, pDocFormatter
, nLanguage
);
2164 // aNumber is overwritten by SwDBField::FormatValue, so store initial status
2165 bool colIsNumber
= aNumber
!= DBL_MAX
;
2166 bool bValidValue
= SwDBField::FormatValue( pDocFormatter
, aString
, nFormat
,
2167 aNumber
, nColumnType
);
2173 aValue
.PutDouble( aNumber
);
2174 aValue
.SetDBvalue( true );
2175 SAL_INFO( "sw.ui", "'" << rColName
<< "': " << aNumber
<< " / " << aString
);
2176 rCalc
.VarChange( rColName
, aValue
);
2182 aValue
.PutString( aString
);
2183 aValue
.SetDBvalue( true );
2184 SAL_INFO( "sw.ui", "'" << rColName
<< "': " << aString
);
2185 rCalc
.VarChange( rColName
, aValue
);
2193 void SwDBManager::ToNextRecord(
2194 const OUString
& rDataSource
, const OUString
& rCommand
)
2196 SwDSParam
* pFound
= nullptr;
2197 if(m_pImpl
->pMergeData
&&
2198 rDataSource
== m_pImpl
->pMergeData
->sDataSource
&&
2199 rCommand
== m_pImpl
->pMergeData
->sCommand
)
2201 pFound
= m_pImpl
->pMergeData
.get();
2206 aData
.sDataSource
= rDataSource
;
2207 aData
.sCommand
= rCommand
;
2208 aData
.nCommandType
= -1;
2209 pFound
= FindDSData(aData
, false);
2211 lcl_ToNextRecord( pFound
);
2214 static bool lcl_ToNextRecord( SwDSParam
* pParam
, const SwDBNextRecord action
)
2218 assert( SwDBNextRecord::NEXT
== action
||
2219 (SwDBNextRecord::FIRST
== action
&& pParam
) );
2220 if( nullptr == pParam
)
2223 if( action
== SwDBNextRecord::FIRST
)
2225 pParam
->nSelectionIndex
= 0;
2226 pParam
->bEndOfDB
= false;
2229 if( !pParam
->HasValidRecord() )
2234 if( pParam
->aSelection
.hasElements() )
2236 if( pParam
->nSelectionIndex
>= pParam
->aSelection
.getLength() )
2237 pParam
->bEndOfDB
= true;
2241 pParam
->aSelection
.getConstArray()[ pParam
->nSelectionIndex
] >>= nPos
;
2242 pParam
->bEndOfDB
= !pParam
->xResultSet
->absolute( nPos
);
2245 else if( action
== SwDBNextRecord::FIRST
)
2247 pParam
->bEndOfDB
= !pParam
->xResultSet
->first();
2251 sal_Int32 nBefore
= pParam
->xResultSet
->getRow();
2252 pParam
->bEndOfDB
= !pParam
->xResultSet
->next();
2253 if( !pParam
->bEndOfDB
&& nBefore
== pParam
->xResultSet
->getRow() )
2255 // next returned true but it didn't move
2256 ::dbtools::throwFunctionSequenceException( pParam
->xResultSet
);
2260 ++pParam
->nSelectionIndex
;
2261 bRet
= !pParam
->bEndOfDB
;
2263 catch( const uno::Exception
& )
2265 // we allow merging with empty databases, so don't warn on init
2266 TOOLS_WARN_EXCEPTION_IF(action
== SwDBNextRecord::NEXT
,
2267 "sw.mailmerge", "exception in ToNextRecord()");
2268 pParam
->bEndOfDB
= true;
2274 // synchronized labels contain a next record field at their end
2275 // to assure that the next page can be created in mail merge
2276 // the cursor position must be validated
2277 bool SwDBManager::IsValidMergeRecord() const
2279 return( m_pImpl
->pMergeData
&& m_pImpl
->pMergeData
->HasValidRecord() );
2282 sal_uInt32
SwDBManager::GetSelectedRecordId()
2284 sal_uInt32 nRet
= 0;
2285 assert( m_pImpl
->pMergeData
&&
2286 m_pImpl
->pMergeData
->xResultSet
.is() && "no data source in merge" );
2287 if(!m_pImpl
->pMergeData
|| !m_pImpl
->pMergeData
->xResultSet
.is())
2291 nRet
= m_pImpl
->pMergeData
->xResultSet
->getRow();
2293 catch(const uno::Exception
&)
2299 bool SwDBManager::ToRecordId(sal_Int32 nSet
)
2301 assert( m_pImpl
->pMergeData
&&
2302 m_pImpl
->pMergeData
->xResultSet
.is() && "no data source in merge" );
2303 if(!m_pImpl
->pMergeData
|| !m_pImpl
->pMergeData
->xResultSet
.is()|| nSet
< 0)
2306 sal_Int32 nAbsPos
= nSet
;
2307 assert(nAbsPos
>= 0);
2308 bRet
= lcl_MoveAbsolute(m_pImpl
->pMergeData
.get(), nAbsPos
);
2309 m_pImpl
->pMergeData
->bEndOfDB
= !bRet
;
2313 bool SwDBManager::OpenDataSource(const OUString
& rDataSource
, const OUString
& rTableOrQuery
)
2316 aData
.sDataSource
= rDataSource
;
2317 aData
.sCommand
= rTableOrQuery
;
2318 aData
.nCommandType
= -1;
2320 SwDSParam
* pFound
= FindDSData(aData
, true);
2321 if(pFound
->xResultSet
.is())
2323 SwDSParam
* pParam
= FindDSConnection(rDataSource
, false);
2324 if(pParam
&& pParam
->xConnection
.is())
2325 pFound
->xConnection
= pParam
->xConnection
;
2326 if(pFound
->xConnection
.is())
2330 uno::Reference
< sdbc::XDatabaseMetaData
> xMetaData
= pFound
->xConnection
->getMetaData();
2333 pFound
->bScrollable
= xMetaData
2334 ->supportsResultSetType(sal_Int32(sdbc::ResultSetType::SCROLL_INSENSITIVE
));
2336 catch(const uno::Exception
&)
2338 // DB driver may not be ODBC 3.0 compliant
2339 pFound
->bScrollable
= true;
2341 pFound
->xStatement
= pFound
->xConnection
->createStatement();
2342 OUString aQuoteChar
= xMetaData
->getIdentifierQuoteString();
2343 OUString sStatement
= "SELECT * FROM " + aQuoteChar
+ rTableOrQuery
+ aQuoteChar
;
2344 pFound
->xResultSet
= pFound
->xStatement
->executeQuery( sStatement
);
2346 //after executeQuery the cursor must be positioned
2347 pFound
->bEndOfDB
= !pFound
->xResultSet
->next();
2348 ++pFound
->nSelectionIndex
;
2350 catch (const uno::Exception
&)
2352 pFound
->xResultSet
= nullptr;
2353 pFound
->xStatement
= nullptr;
2354 pFound
->xConnection
= nullptr;
2357 return pFound
->xResultSet
.is();
2360 uno::Reference
< sdbc::XConnection
> const & SwDBManager::RegisterConnection(OUString
const& rDataSource
)
2362 SwDSParam
* pFound
= SwDBManager::FindDSConnection(rDataSource
, true);
2363 uno::Reference
< sdbc::XDataSource
> xSource
;
2364 if(!pFound
->xConnection
.is())
2366 SwView
* pView
= (m_pDoc
&& m_pDoc
->GetDocShell()) ? m_pDoc
->GetDocShell()->GetView() : nullptr;
2367 pFound
->xConnection
= SwDBManager::GetConnection(rDataSource
, xSource
, pView
);
2370 uno::Reference
<lang::XComponent
> xComponent(pFound
->xConnection
, uno::UNO_QUERY
);
2372 xComponent
->addEventListener(m_pImpl
->m_xDisposeListener
);
2374 catch(const uno::Exception
&)
2378 return pFound
->xConnection
;
2381 sal_uInt32
SwDBManager::GetSelectedRecordId(
2382 const OUString
& rDataSource
, const OUString
& rTableOrQuery
, sal_Int32 nCommandType
)
2384 sal_uInt32 nRet
= 0xffffffff;
2385 //check for merge data source first
2386 if(m_pImpl
->pMergeData
&&
2387 ((rDataSource
== m_pImpl
->pMergeData
->sDataSource
&&
2388 rTableOrQuery
== m_pImpl
->pMergeData
->sCommand
) ||
2389 (rDataSource
.isEmpty() && rTableOrQuery
.isEmpty())) &&
2390 (nCommandType
== -1 || nCommandType
== m_pImpl
->pMergeData
->nCommandType
) &&
2391 m_pImpl
->pMergeData
->xResultSet
.is())
2393 nRet
= GetSelectedRecordId();
2398 aData
.sDataSource
= rDataSource
;
2399 aData
.sCommand
= rTableOrQuery
;
2400 aData
.nCommandType
= nCommandType
;
2401 SwDSParam
* pFound
= FindDSData(aData
, false);
2402 if(pFound
&& pFound
->xResultSet
.is())
2405 { //if a selection array is set the current row at the result set may not be set yet
2406 if(pFound
->aSelection
.hasElements())
2408 sal_Int32 nSelIndex
= pFound
->nSelectionIndex
;
2409 if(nSelIndex
>= pFound
->aSelection
.getLength())
2410 nSelIndex
= pFound
->aSelection
.getLength() -1;
2411 pFound
->aSelection
.getConstArray()[nSelIndex
] >>= nRet
;
2415 nRet
= pFound
->xResultSet
->getRow();
2417 catch(const uno::Exception
&)
2425 // close all data sources - after fields were updated
2426 void SwDBManager::CloseAll(bool bIncludingMerge
)
2428 //the only thing done here is to reset the selection index
2429 //all connections stay open
2430 for (auto & pParam
: m_DataSourceParams
)
2432 if (bIncludingMerge
|| pParam
.get() != m_pImpl
->pMergeData
.get())
2434 pParam
->nSelectionIndex
= 0;
2435 pParam
->bEndOfDB
= false;
2438 if(!m_bInMerge
&& pParam
->xResultSet
.is())
2439 pParam
->xResultSet
->first();
2441 catch(const uno::Exception
&)
2447 SwDSParam
* SwDBManager::FindDSData(const SwDBData
& rData
, bool bCreate
)
2449 //prefer merge data if available
2450 if(m_pImpl
->pMergeData
&&
2451 ((rData
.sDataSource
== m_pImpl
->pMergeData
->sDataSource
&&
2452 rData
.sCommand
== m_pImpl
->pMergeData
->sCommand
) ||
2453 (rData
.sDataSource
.isEmpty() && rData
.sCommand
.isEmpty())) &&
2454 (rData
.nCommandType
== -1 || rData
.nCommandType
== m_pImpl
->pMergeData
->nCommandType
||
2455 (bCreate
&& m_pImpl
->pMergeData
->nCommandType
== -1)))
2457 return m_pImpl
->pMergeData
.get();
2460 SwDSParam
* pFound
= nullptr;
2461 for (size_t nPos
= m_DataSourceParams
.size(); nPos
; nPos
--)
2463 SwDSParam
* pParam
= m_DataSourceParams
[nPos
- 1].get();
2464 if(rData
.sDataSource
== pParam
->sDataSource
&&
2465 rData
.sCommand
== pParam
->sCommand
&&
2466 (rData
.nCommandType
== -1 || rData
.nCommandType
== pParam
->nCommandType
||
2467 (bCreate
&& pParam
->nCommandType
== -1)))
2469 // calls from the calculator may add a connection with an invalid commandtype
2470 //later added "real" data base connections have to re-use the already available
2471 //DSData and set the correct CommandType
2472 if(bCreate
&& pParam
->nCommandType
== -1)
2473 pParam
->nCommandType
= rData
.nCommandType
;
2478 if(bCreate
&& !pFound
)
2480 pFound
= new SwDSParam(rData
);
2481 m_DataSourceParams
.push_back(std::unique_ptr
<SwDSParam
>(pFound
));
2484 uno::Reference
<lang::XComponent
> xComponent(pFound
->xConnection
, uno::UNO_QUERY
);
2486 xComponent
->addEventListener(m_pImpl
->m_xDisposeListener
);
2488 catch(const uno::Exception
&)
2495 SwDSParam
* SwDBManager::FindDSConnection(const OUString
& rDataSource
, bool bCreate
)
2497 //prefer merge data if available
2498 if(m_pImpl
->pMergeData
&& rDataSource
== m_pImpl
->pMergeData
->sDataSource
)
2500 SetAsUsed(rDataSource
);
2501 return m_pImpl
->pMergeData
.get();
2503 SwDSParam
* pFound
= nullptr;
2504 for (const auto & pParam
: m_DataSourceParams
)
2506 if(rDataSource
== pParam
->sDataSource
)
2508 SetAsUsed(rDataSource
);
2509 pFound
= pParam
.get();
2513 if(bCreate
&& !pFound
)
2516 aData
.sDataSource
= rDataSource
;
2517 pFound
= new SwDSParam(aData
);
2518 SetAsUsed(rDataSource
);
2519 m_DataSourceParams
.push_back(std::unique_ptr
<SwDSParam
>(pFound
));
2522 uno::Reference
<lang::XComponent
> xComponent(pFound
->xConnection
, uno::UNO_QUERY
);
2524 xComponent
->addEventListener(m_pImpl
->m_xDisposeListener
);
2526 catch(const uno::Exception
&)
2533 const SwDBData
& SwDBManager::GetAddressDBName()
2535 return SW_MOD()->GetDBConfig()->GetAddressSource();
2538 uno::Sequence
<OUString
> SwDBManager::GetExistingDatabaseNames()
2540 uno::Reference
<uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
2541 uno::Reference
<sdb::XDatabaseContext
> xDBContext
= sdb::DatabaseContext::create(xContext
);
2542 return xDBContext
->getElementNames();
2547 DBConnURIType
GetDBunoType(const INetURLObject
&rURL
)
2549 OUString
sExt(rURL
.GetFileExtension());
2550 DBConnURIType type
= DBConnURIType::UNKNOWN
;
2554 type
= DBConnURIType::ODB
;
2556 else if (sExt
.equalsIgnoreAsciiCase("sxc")
2557 || sExt
.equalsIgnoreAsciiCase("ods")
2558 || sExt
.equalsIgnoreAsciiCase("xls")
2559 || sExt
.equalsIgnoreAsciiCase("xlsx"))
2561 type
= DBConnURIType::CALC
;
2563 else if (sExt
.equalsIgnoreAsciiCase("sxw") || sExt
.equalsIgnoreAsciiCase("odt") || sExt
.equalsIgnoreAsciiCase("doc") || sExt
.equalsIgnoreAsciiCase("docx"))
2565 type
= DBConnURIType::WRITER
;
2567 else if (sExt
.equalsIgnoreAsciiCase("dbf"))
2569 type
= DBConnURIType::DBASE
;
2571 else if (sExt
.equalsIgnoreAsciiCase("csv") || sExt
.equalsIgnoreAsciiCase("txt"))
2573 type
= DBConnURIType::FLAT
;
2576 else if (sExt
.equalsIgnoreAsciiCase("mdb") || sExt
.equalsIgnoreAsciiCase("mde"))
2578 type
= DBConnURIType::MSJET
;
2580 else if (sExt
.equalsIgnoreAsciiCase("accdb") || sExt
.equalsIgnoreAsciiCase("accde"))
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::MSJET
:
2640 OUString
sDBURL("sdbc:ado:access:PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=" + rURL
.PathToFileName());
2645 case DBConnURIType::MSACE
:
2648 OUString
sDBURL("sdbc:ado:PROVIDER=Microsoft.ACE.OLEDB.12.0;DATA SOURCE=" + rURL
.PathToFileName());
2657 /// Returns the URL of this SwDoc.
2658 OUString
getOwnURL(SfxObjectShell
const * pDocShell
)
2665 const INetURLObject
& rURLObject
= pDocShell
->GetMedium()->GetURLObject();
2666 aRet
= rURLObject
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2671 Loads a data source from file and registers it.
2673 In case of success it returns the registered name, otherwise an empty string.
2674 Optionally add a prefix to the registered DB name.
2676 OUString
LoadAndRegisterDataSource_Impl(DBConnURIType type
, const uno::Reference
< beans::XPropertySet
> *pSettings
,
2677 const INetURLObject
&rURL
, const OUString
*pDestDir
, SfxObjectShell
* pDocShell
)
2679 OUString
sExt(rURL
.GetFileExtension());
2680 uno::Any aTableFilterAny
;
2681 uno::Any aSuppressVersionsAny
;
2686 uno::Any aURLAny
= GetDBunoURI(rURL
, type
);
2688 case DBConnURIType::UNKNOWN
:
2689 case DBConnURIType::CALC
:
2690 case DBConnURIType::WRITER
:
2692 case DBConnURIType::ODB
:
2695 case DBConnURIType::FLAT
:
2696 case DBConnURIType::DBASE
:
2697 //set the filter to the file name without extension
2699 uno::Sequence
<OUString
> aFilters
{ rURL
.getBase(INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
) };
2700 aTableFilterAny
<<= aFilters
;
2703 case DBConnURIType::MSJET
:
2704 case DBConnURIType::MSACE
:
2705 aSuppressVersionsAny
<<= true;
2711 uno::Reference
<uno::XComponentContext
> xContext(::comphelper::getProcessComponentContext());
2712 uno::Reference
<sdb::XDatabaseContext
> xDBContext
= sdb::DatabaseContext::create(xContext
);
2714 OUString sNewName
= rURL
.getName(
2715 INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::Unambiguous
);
2716 sal_Int32 nExtLen
= sExt
.getLength();
2717 sNewName
= sNewName
.replaceAt(sNewName
.getLength() - nExtLen
- 1, nExtLen
+ 1, u
"");
2719 //find a unique name if sNewName already exists
2721 sal_Int32 nIndex
= 0;
2722 while (xDBContext
->hasByName(sFind
))
2723 sFind
= sNewName
+ OUString::number(++nIndex
);
2725 uno::Reference
<uno::XInterface
> xNewInstance
;
2729 uno::Any aDataSource
= xDBContext
->getByName(rURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
));
2730 aDataSource
>>= xNewInstance
;
2734 xNewInstance
= xDBContext
->createInstance();
2735 uno::Reference
<beans::XPropertySet
> xDataProperties(xNewInstance
, uno::UNO_QUERY
);
2737 if (aURLAny
.hasValue())
2738 xDataProperties
->setPropertyValue("URL", aURLAny
);
2739 if (aTableFilterAny
.hasValue())
2740 xDataProperties
->setPropertyValue("TableFilter", aTableFilterAny
);
2741 if (aSuppressVersionsAny
.hasValue())
2742 xDataProperties
->setPropertyValue("SuppressVersionColumns", aSuppressVersionsAny
);
2743 if (aInfoAny
.hasValue())
2744 xDataProperties
->setPropertyValue("Info", aInfoAny
);
2746 if (DBConnURIType::FLAT
== type
&& pSettings
)
2748 uno::Any aSettings
= xDataProperties
->getPropertyValue("Settings");
2749 uno::Reference
< beans::XPropertySet
> xDSSettings
;
2750 aSettings
>>= xDSSettings
;
2751 ::comphelper::copyProperties(*pSettings
, xDSSettings
);
2752 xDSSettings
->setPropertyValue("Extension", uno::Any(sExt
));
2755 uno::Reference
<sdb::XDocumentDataSource
> xDS(xNewInstance
, uno::UNO_QUERY_THROW
);
2756 uno::Reference
<frame::XStorable
> xStore(xDS
->getDatabaseDocument(), uno::UNO_QUERY_THROW
);
2757 OUString aOwnURL
= getOwnURL(pDocShell
);
2758 if (aOwnURL
.isEmpty())
2760 // Cannot embed, as embedded data source would need the URL of the parent document.
2761 OUString
sHomePath(SvtPathOptions().GetWorkPath());
2762 const OUString sTmpName
= utl::CreateTempURL(sNewName
, true, u
".odb", pDestDir
? pDestDir
: &sHomePath
);
2763 xStore
->storeAsURL(sTmpName
, uno::Sequence
<beans::PropertyValue
>());
2768 OUString aStreamRelPath
= "EmbeddedDatabase";
2769 uno::Reference
<embed::XStorage
> xStorage
= pDocShell
->GetStorage();
2771 // Refer to the sub-storage name in the document settings, so
2772 // we can load it again next time the file is imported.
2773 uno::Reference
<lang::XMultiServiceFactory
> xFactory(pDocShell
->GetModel(), uno::UNO_QUERY
);
2774 uno::Reference
<beans::XPropertySet
> xPropertySet(xFactory
->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY
);
2775 xPropertySet
->setPropertyValue("EmbeddedDatabaseName", uno::Any(aStreamRelPath
));
2777 // Store it only after setting the above property, so that only one data source gets registered.
2778 SwDBManager::StoreEmbeddedDataSource(xStore
, xStorage
, aStreamRelPath
, aOwnURL
);
2781 xDBContext
->registerObject(sFind
, xNewInstance
);
2783 catch (const uno::Exception
&)
2791 OUString
SwDBManager::LoadAndRegisterDataSource(weld::Window
* pParent
, SwDocShell
* pDocShell
)
2793 sfx2::FileDialogHelper
aDlgHelper(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE
, FileDialogFlags::NONE
, pParent
);
2794 aDlgHelper
.SetContext(sfx2::FileDialogHelper::WriterRegisterDataSource
);
2795 uno::Reference
< ui::dialogs::XFilePicker3
> xFP
= aDlgHelper
.GetFilePicker();
2797 OUString
sFilterAll(SwResId(STR_FILTER_ALL
));
2798 OUString
sFilterAllData(SwResId(STR_FILTER_ALL_DATA
));
2800 const std::vector
<std::pair
<OUString
, OUString
>> filters
{
2801 { SwResId(STR_FILTER_SXB
), "*.odb" },
2802 { SwResId(STR_FILTER_SXC
), "*.ods;*.sxc" },
2803 { SwResId(STR_FILTER_SXW
), "*.odt;*.sxw" },
2804 { SwResId(STR_FILTER_DBF
), "*.dbf" },
2805 { SwResId(STR_FILTER_XLS
), "*.xls;*.xlsx" },
2806 { SwResId(STR_FILTER_DOC
), "*.doc;*.docx" },
2807 { SwResId(STR_FILTER_TXT
), "*.txt" },
2808 { SwResId(STR_FILTER_CSV
), "*.csv" },
2810 { SwResId(STR_FILTER_MDB
), "*.mdb;*.mde" },
2811 { SwResId(STR_FILTER_ACCDB
), "*.accdb;*.accde" },
2815 OUStringBuffer sAllDataFilter
;
2816 for (const auto& [name
, filter
] : filters
)
2819 if (!sAllDataFilter
.isEmpty())
2820 sAllDataFilter
.append(';');
2821 sAllDataFilter
.append(filter
);
2824 xFP
->appendFilter( sFilterAll
, "*" );
2825 xFP
->appendFilter( sFilterAllData
, sAllDataFilter
.makeStringAndClear());
2827 // Similar to sfx2::addExtension from sfx2/source/dialog/filtergrouping.cxx
2828 for (const auto& [name
, filter
] : filters
)
2829 xFP
->appendFilter(name
+ " (" + filter
+ ")", filter
);
2831 xFP
->setCurrentFilter( sFilterAll
) ;
2833 if( ERRCODE_NONE
== aDlgHelper
.Execute() )
2835 uno::Reference
< beans::XPropertySet
> aSettings
;
2836 const INetURLObject
aURL( xFP
->getSelectedFiles()[0] );
2837 const DBConnURIType type
= GetDBunoType( aURL
);
2839 if( DBConnURIType::FLAT
== type
)
2841 uno::Reference
<uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
2842 uno::Reference
< sdb::XTextConnectionSettings
> xSettingsDlg
= sdb::TextConnectionSettings::create(xContext
);
2843 if( xSettingsDlg
->execute() )
2844 aSettings
.set( uno::Reference
< beans::XPropertySet
>( xSettingsDlg
, uno::UNO_QUERY_THROW
) );
2846 sFind
= LoadAndRegisterDataSource_Impl( type
, DBConnURIType::FLAT
== type
? &aSettings
: nullptr, aURL
, nullptr, pDocShell
);
2848 s_aUncommittedRegistrations
.push_back(std::pair
<SwDocShell
*, OUString
>(pDocShell
, sFind
));
2853 void SwDBManager::StoreEmbeddedDataSource(const uno::Reference
<frame::XStorable
>& xStorable
,
2854 const uno::Reference
<embed::XStorage
>& xStorage
,
2855 const OUString
& rStreamRelPath
,
2856 const OUString
& rOwnURL
, bool bCopyTo
)
2858 // Construct vnd.sun.star.pkg:// URL for later loading, and TargetStorage/StreamRelPath for storing.
2859 OUString
const sTmpName
= ConstructVndSunStarPkgUrl(rOwnURL
, rStreamRelPath
);
2861 uno::Sequence
<beans::PropertyValue
> aSequence
= comphelper::InitPropertySequence(
2863 {"TargetStorage", uno::Any(xStorage
)},
2864 {"StreamRelPath", uno::Any(rStreamRelPath
)},
2865 {"BaseURI", uno::Any(rOwnURL
)}
2868 xStorable
->storeToURL(sTmpName
, aSequence
);
2870 xStorable
->storeAsURL(sTmpName
, aSequence
);
2873 OUString
SwDBManager::LoadAndRegisterDataSource(std::u16string_view rURI
, const OUString
*pDestDir
)
2875 return LoadAndRegisterDataSource_Impl( DBConnURIType::UNKNOWN
, nullptr, INetURLObject(rURI
), pDestDir
, nullptr );
2880 // tdf#117824 switch the embedded database away from using its current storage and point it to temporary storage
2881 // which allows the original storage to be deleted
2882 void switchEmbeddedDatabaseStorage(const uno::Reference
<sdb::XDatabaseContext
>& rDatabaseContext
, const OUString
& rName
)
2884 uno::Reference
<sdb::XDocumentDataSource
> xDS(rDatabaseContext
->getByName(rName
), uno::UNO_QUERY
);
2887 uno::Reference
<document::XStorageBasedDocument
> xStorageDoc(xDS
->getDatabaseDocument(), uno::UNO_QUERY
);
2890 xStorageDoc
->switchToStorage(comphelper::OStorageHelper::GetTemporaryStorage());
2894 void SwDBManager::RevokeDataSource(const OUString
& rName
)
2896 uno::Reference
<sdb::XDatabaseContext
> xDatabaseContext
= sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
2897 if (xDatabaseContext
->hasByName(rName
))
2899 switchEmbeddedDatabaseStorage(xDatabaseContext
, rName
);
2900 xDatabaseContext
->revokeObject(rName
);
2904 void SwDBManager::LoadAndRegisterEmbeddedDataSource(const SwDBData
& rData
, const SwDocShell
& rDocShell
)
2906 uno::Reference
<sdb::XDatabaseContext
> xDatabaseContext
= sdb::DatabaseContext::create(comphelper::getProcessComponentContext());
2908 OUString sDataSource
= rData
.sDataSource
;
2910 // Fallback, just in case the document would contain an embedded data source, but no DB fields.
2911 if (sDataSource
.isEmpty())
2912 sDataSource
= "EmbeddedDatabase";
2914 SwDBManager::RevokeDataSource( sDataSource
);
2916 // Encode the stream name and the real path into a single URL.
2917 const INetURLObject
& rURLObject
= rDocShell
.GetMedium()->GetURLObject();
2918 OUString
const aURL
= ConstructVndSunStarPkgUrl(
2919 rURLObject
.GetMainURL(INetURLObject::DecodeMechanism::NONE
),
2922 uno::Reference
<uno::XInterface
> xDataSource(xDatabaseContext
->getByName(aURL
), uno::UNO_QUERY
);
2923 xDatabaseContext
->registerObject( sDataSource
, xDataSource
);
2925 // temp file - don't remember connection
2926 if (rData
.sDataSource
.isEmpty())
2927 s_aUncommittedRegistrations
.push_back(std::pair
<SwDocShell
*, OUString
>(nullptr, sDataSource
));
2930 void SwDBManager::ExecuteFormLetter( SwWrtShell
& rSh
,
2931 const uno::Sequence
<beans::PropertyValue
>& rProperties
)
2933 //prevent second call
2934 if(m_pImpl
->pMergeDialog
)
2936 OUString sDataSource
, sDataTableOrQuery
;
2937 uno::Sequence
<uno::Any
> aSelection
;
2939 sal_Int32 nCmdType
= sdb::CommandType::TABLE
;
2940 uno::Reference
< sdbc::XConnection
> xConnection
;
2942 svx::ODataAccessDescriptor
aDescriptor(rProperties
);
2943 sDataSource
= aDescriptor
.getDataSource();
2944 OSL_VERIFY(aDescriptor
[svx::DataAccessDescriptorProperty::Command
] >>= sDataTableOrQuery
);
2945 OSL_VERIFY(aDescriptor
[svx::DataAccessDescriptorProperty::CommandType
] >>= nCmdType
);
2947 if ( aDescriptor
.has(svx::DataAccessDescriptorProperty::Selection
) )
2948 aDescriptor
[svx::DataAccessDescriptorProperty::Selection
] >>= aSelection
;
2949 if ( aDescriptor
.has(svx::DataAccessDescriptorProperty::Connection
) )
2950 aDescriptor
[svx::DataAccessDescriptorProperty::Connection
] >>= xConnection
;
2952 if(sDataSource
.isEmpty() || sDataTableOrQuery
.isEmpty())
2954 OSL_FAIL("PropertyValues missing or unset");
2958 //always create a connection for the dialog and dispose it after the dialog has been closed
2959 SwDSParam
* pFound
= nullptr;
2960 if(!xConnection
.is())
2962 xConnection
= SwDBManager::RegisterConnection(sDataSource
);
2963 pFound
= FindDSConnection(sDataSource
, true);
2965 SwAbstractDialogFactory
* pFact
= SwAbstractDialogFactory::Create();
2966 m_pImpl
->pMergeDialog
= pFact
->CreateMailMergeDlg(rSh
.GetView().GetViewFrame().GetFrameWeld(), rSh
,
2971 if(m_pImpl
->pMergeDialog
->Execute() == RET_OK
)
2973 aDescriptor
[svx::DataAccessDescriptorProperty::Selection
] <<= m_pImpl
->pMergeDialog
->GetSelection();
2975 uno::Reference
<sdbc::XResultSet
> xResSet
= m_pImpl
->pMergeDialog
->GetResultSet();
2977 aDescriptor
[svx::DataAccessDescriptorProperty::Cursor
] <<= xResSet
;
2979 // SfxObjectShellRef is ok, since there should be no control over the document lifetime here
2980 SfxObjectShellRef xDocShell
= rSh
.GetView().GetViewFrame().GetObjectShell();
2982 lcl_emitEvent(SfxEventHintId::SwMailMerge
, STR_SW_EVENT_MAIL_MERGE
, xDocShell
.get());
2984 // prepare mail merge descriptor
2985 SwMergeDescriptor
aMergeDesc( m_pImpl
->pMergeDialog
->GetMergeType(), rSh
, aDescriptor
);
2986 aMergeDesc
.sSaveToFilter
= m_pImpl
->pMergeDialog
->GetSaveFilter();
2987 aMergeDesc
.bCreateSingleFile
= m_pImpl
->pMergeDialog
->IsSaveSingleDoc();
2988 aMergeDesc
.bPrefixIsFilename
= aMergeDesc
.bCreateSingleFile
;
2989 aMergeDesc
.sPrefix
= m_pImpl
->pMergeDialog
->GetTargetURL();
2991 if(!aMergeDesc
.bCreateSingleFile
)
2993 if(m_pImpl
->pMergeDialog
->IsGenerateFromDataBase())
2994 aMergeDesc
.sDBcolumn
= m_pImpl
->pMergeDialog
->GetColumnName();
2996 if(m_pImpl
->pMergeDialog
->IsFileEncryptedFromDataBase())
2997 aMergeDesc
.sDBPasswordColumn
= m_pImpl
->pMergeDialog
->GetPasswordColumnName();
3000 Merge( aMergeDesc
);
3002 lcl_emitEvent(SfxEventHintId::SwMailMergeEnd
, STR_SW_EVENT_MAIL_MERGE_END
, xDocShell
.get());
3004 // reset the cursor inside
3006 aDescriptor
[svx::DataAccessDescriptorProperty::Cursor
] <<= xResSet
;
3010 for (const auto & pParam
: m_DataSourceParams
)
3012 if (pParam
.get() == pFound
)
3016 uno::Reference
<lang::XComponent
> xComp(pParam
->xConnection
, uno::UNO_QUERY
);
3020 catch(const uno::RuntimeException
&)
3022 //may be disposed already since multiple entries may have used the same connection
3026 //pFound doesn't need to be removed/deleted -
3027 //this has been done by the SwConnectionDisposedListener_Impl already
3030 m_pImpl
->pMergeDialog
.disposeAndClear();
3033 void SwDBManager::InsertText(SwWrtShell
& rSh
,
3034 const uno::Sequence
< beans::PropertyValue
>& rProperties
)
3036 OUString sDataSource
, sDataTableOrQuery
;
3037 uno::Reference
<sdbc::XResultSet
> xResSet
;
3038 uno::Sequence
<uno::Any
> aSelection
;
3039 sal_Int16 nCmdType
= sdb::CommandType::TABLE
;
3040 uno::Reference
< sdbc::XConnection
> xConnection
;
3041 for(const beans::PropertyValue
& rValue
: rProperties
)
3043 if ( rValue
.Name
== "DataSourceName" )
3044 rValue
.Value
>>= sDataSource
;
3045 else if ( rValue
.Name
== "Command" )
3046 rValue
.Value
>>= sDataTableOrQuery
;
3047 else if ( rValue
.Name
== "Cursor" )
3048 rValue
.Value
>>= xResSet
;
3049 else if ( rValue
.Name
== "Selection" )
3050 rValue
.Value
>>= aSelection
;
3051 else if ( rValue
.Name
== "CommandType" )
3052 rValue
.Value
>>= nCmdType
;
3053 else if ( rValue
.Name
== "ActiveConnection" )
3054 rValue
.Value
>>= xConnection
;
3056 if(sDataSource
.isEmpty() || sDataTableOrQuery
.isEmpty() || !xResSet
.is())
3058 OSL_FAIL("PropertyValues missing or unset");
3061 uno::Reference
< uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
3062 uno::Reference
<sdbc::XDataSource
> xSource
;
3063 uno::Reference
<container::XChild
> xChild(xConnection
, uno::UNO_QUERY
);
3065 xSource
.set(xChild
->getParent(), uno::UNO_QUERY
);
3067 xSource
= dbtools::getDataSource(sDataSource
, xContext
);
3068 uno::Reference
< sdbcx::XColumnsSupplier
> xColSupp( xResSet
, uno::UNO_QUERY
);
3070 aDBData
.sDataSource
= sDataSource
;
3071 aDBData
.sCommand
= sDataTableOrQuery
;
3072 aDBData
.nCommandType
= nCmdType
;
3074 SwAbstractDialogFactory
* pFact
= SwAbstractDialogFactory::Create();
3075 ScopedVclPtr
<AbstractSwInsertDBColAutoPilot
> pDlg(pFact
->CreateSwInsertDBColAutoPilot( rSh
.GetView(),
3079 if( RET_OK
!= pDlg
->Execute() )
3083 if(!xConnection
.is())
3084 xConnection
= xSource
->getConnection(sDummy
, sDummy
);
3087 pDlg
->DataToDoc( aSelection
, xSource
, xConnection
, xResSet
);
3089 catch (const uno::Exception
&)
3091 TOOLS_WARN_EXCEPTION("sw.mailmerge", "");
3095 uno::Reference
<sdbc::XDataSource
> SwDBManager::getDataSourceAsParent(const uno::Reference
< sdbc::XConnection
>& _xConnection
,const OUString
& _sDataSourceName
)
3097 uno::Reference
<sdbc::XDataSource
> xSource
;
3100 uno::Reference
<container::XChild
> xChild(_xConnection
, uno::UNO_QUERY
);
3102 xSource
.set(xChild
->getParent(), uno::UNO_QUERY
);
3103 if ( !xSource
.is() )
3104 xSource
= dbtools::getDataSource(_sDataSourceName
, ::comphelper::getProcessComponentContext());
3106 catch (const uno::Exception
&)
3108 TOOLS_WARN_EXCEPTION("sw.mailmerge", "getDataSourceAsParent()");
3113 uno::Reference
<sdbc::XResultSet
> SwDBManager::createCursor(const OUString
& _sDataSourceName
,
3114 const OUString
& _sCommand
,
3115 sal_Int32 _nCommandType
,
3116 const uno::Reference
<sdbc::XConnection
>& _xConnection
,
3117 const SwView
* pView
)
3119 uno::Reference
<sdbc::XResultSet
> xResultSet
;
3122 uno::Reference
< lang::XMultiServiceFactory
> xMgr( ::comphelper::getProcessServiceFactory() );
3125 uno::Reference
<uno::XInterface
> xInstance
= xMgr
->createInstance("com.sun.star.sdb.RowSet");
3126 uno::Reference
<beans::XPropertySet
> xRowSetPropSet(xInstance
, uno::UNO_QUERY
);
3127 if(xRowSetPropSet
.is())
3129 xRowSetPropSet
->setPropertyValue("DataSourceName", uno::Any(_sDataSourceName
));
3130 xRowSetPropSet
->setPropertyValue("ActiveConnection", uno::Any(_xConnection
));
3131 xRowSetPropSet
->setPropertyValue("Command", uno::Any(_sCommand
));
3132 xRowSetPropSet
->setPropertyValue("CommandType", uno::Any(_nCommandType
));
3134 uno::Reference
< sdb::XCompletedExecution
> xRowSet(xInstance
, uno::UNO_QUERY
);
3138 weld::Window
* pWindow
= pView
? pView
->GetFrameWeld() : nullptr;
3139 uno::Reference
< task::XInteractionHandler
> xHandler( task::InteractionHandler::createWithParent(comphelper::getComponentContext(xMgr
), pWindow
? pWindow
->GetXWindow() : nullptr), uno::UNO_QUERY_THROW
);
3140 xRowSet
->executeWithCompletion(xHandler
);
3142 xResultSet
.set(xRowSet
, uno::UNO_QUERY
);
3146 catch (const uno::Exception
&)
3148 TOOLS_WARN_EXCEPTION("sw.mailmerge", "Caught exception while creating a new RowSet");
3153 void SwDBManager::setEmbeddedName(const OUString
& rEmbeddedName
, SwDocShell
& rDocShell
)
3155 bool bLoad
= m_sEmbeddedName
!= rEmbeddedName
&& !rEmbeddedName
.isEmpty();
3156 bool bRegisterListener
= m_sEmbeddedName
.isEmpty() && !rEmbeddedName
.isEmpty();
3158 m_sEmbeddedName
= rEmbeddedName
;
3162 uno::Reference
<embed::XStorage
> xStorage
= rDocShell
.GetStorage();
3163 // It's OK that we don't have the named sub-storage yet, in case
3164 // we're in the process of creating it.
3165 if (xStorage
->hasByName(rEmbeddedName
))
3166 LoadAndRegisterEmbeddedDataSource(rDocShell
.GetDoc()->GetDBData(), rDocShell
);
3169 if (bRegisterListener
)
3170 // Register a remove listener, so we know when the embedded data source is removed.
3171 m_pImpl
->m_xDataSourceRemovedListener
= new SwDataSourceRemovedListener(*this);
3174 const OUString
& SwDBManager::getEmbeddedName() const
3176 return m_sEmbeddedName
;
3179 SwDoc
* SwDBManager::getDoc() const
3184 void SwDBManager::releaseRevokeListener()
3186 if (m_pImpl
->m_xDataSourceRemovedListener
.is())
3188 m_pImpl
->m_xDataSourceRemovedListener
->Dispose();
3189 m_pImpl
->m_xDataSourceRemovedListener
.clear();
3193 SwDBManager::ConnectionDisposedListener_Impl::ConnectionDisposedListener_Impl(SwDBManager
& rManager
)
3194 : m_pDBManager(&rManager
)
3198 void SwDBManager::ConnectionDisposedListener_Impl::disposing( const lang::EventObject
& rSource
)
3200 ::SolarMutexGuard aGuard
;
3202 if (!m_pDBManager
) return; // we're disposed too!
3204 uno::Reference
<sdbc::XConnection
> xSource(rSource
.Source
, uno::UNO_QUERY
);
3205 for (size_t nPos
= m_pDBManager
->m_DataSourceParams
.size(); nPos
; nPos
--)
3207 SwDSParam
* pParam
= m_pDBManager
->m_DataSourceParams
[nPos
- 1].get();
3208 if(pParam
->xConnection
.is() &&
3209 (xSource
== pParam
->xConnection
))
3211 m_pDBManager
->m_DataSourceParams
.erase(
3212 m_pDBManager
->m_DataSourceParams
.begin() + nPos
- 1);
3217 std::shared_ptr
<SwMailMergeConfigItem
> SwDBManager::PerformMailMerge(SwView
const * pView
)
3219 std::shared_ptr
<SwMailMergeConfigItem
> xConfigItem
= pView
->GetMailMergeConfigItem();
3223 svx::ODataAccessDescriptor aDescriptor
;
3224 aDescriptor
.setDataSource(xConfigItem
->GetCurrentDBData().sDataSource
);
3225 aDescriptor
[ svx::DataAccessDescriptorProperty::Connection
] <<= xConfigItem
->GetConnection().getTyped();
3226 aDescriptor
[ svx::DataAccessDescriptorProperty::Cursor
] <<= xConfigItem
->GetResultSet();
3227 aDescriptor
[ svx::DataAccessDescriptorProperty::Command
] <<= xConfigItem
->GetCurrentDBData().sCommand
;
3228 aDescriptor
[ svx::DataAccessDescriptorProperty::CommandType
] <<= xConfigItem
->GetCurrentDBData().nCommandType
;
3229 aDescriptor
[ svx::DataAccessDescriptorProperty::Selection
] <<= xConfigItem
->GetSelection();
3231 SwWrtShell
& rSh
= pView
->GetWrtShell();
3232 xConfigItem
->SetTargetView(nullptr);
3234 SwMergeDescriptor
aMergeDesc(DBMGR_MERGE_SHELL
, rSh
, aDescriptor
);
3235 aMergeDesc
.pMailMergeConfigItem
= xConfigItem
.get();
3236 aMergeDesc
.bCreateSingleFile
= true;
3237 rSh
.GetDBManager()->Merge(aMergeDesc
);
3242 void SwDBManager::RevokeLastRegistrations()
3244 if (s_aUncommittedRegistrations
.empty())
3247 SwView
* pView
= ( m_pDoc
&& m_pDoc
->GetDocShell() ) ? m_pDoc
->GetDocShell()->GetView() : nullptr;
3250 const std::shared_ptr
<SwMailMergeConfigItem
>& xConfigItem
= pView
->GetMailMergeConfigItem();
3253 xConfigItem
->DisposeResultSet();
3254 xConfigItem
->DocumentReloaded();
3258 for (auto it
= s_aUncommittedRegistrations
.begin(); it
!= s_aUncommittedRegistrations
.end();)
3260 if ((m_pDoc
&& it
->first
== m_pDoc
->GetDocShell()) || it
->first
== nullptr)
3262 RevokeDataSource(it
->second
);
3263 it
= s_aUncommittedRegistrations
.erase(it
);
3270 void SwDBManager::CommitLastRegistrations()
3272 for (auto aIt
= s_aUncommittedRegistrations
.begin(); aIt
!= s_aUncommittedRegistrations
.end();)
3274 if (aIt
->first
== m_pDoc
->GetDocShell() || aIt
->first
== nullptr)
3276 m_aNotUsedConnections
.push_back(aIt
->second
);
3277 aIt
= s_aUncommittedRegistrations
.erase(aIt
);
3284 void SwDBManager::SetAsUsed(const OUString
& rName
)
3286 auto aFound
= std::find(m_aNotUsedConnections
.begin(), m_aNotUsedConnections
.end(), rName
);
3287 if (aFound
!= m_aNotUsedConnections
.end())
3288 m_aNotUsedConnections
.erase(aFound
);
3291 void SwDBManager::RevokeNotUsedConnections()
3293 for (auto aIt
= m_aNotUsedConnections
.begin(); aIt
!= m_aNotUsedConnections
.end();)
3295 RevokeDataSource(*aIt
);
3296 aIt
= m_aNotUsedConnections
.erase(aIt
);
3300 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */