Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <cstdarg>
21 :
22 : #include <stdio.h>
23 : #include <unotxdoc.hxx>
24 : #include <com/sun/star/text/NotePrintMode.hpp>
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/frame/XComponentLoader.hpp>
29 : #include <com/sun/star/lang/DisposedException.hpp>
30 : #include <com/sun/star/lang/XEventListener.hpp>
31 : #include <com/sun/star/util/NumberFormatter.hpp>
32 : #include <com/sun/star/sdb/DatabaseContext.hpp>
33 : #include <com/sun/star/sdb/TextConnectionSettings.hpp>
34 : #include <com/sun/star/sdb/XCompletedConnection.hpp>
35 : #include <com/sun/star/sdb/XCompletedExecution.hpp>
36 : #include <com/sun/star/container/XChild.hpp>
37 : #include <com/sun/star/text/MailMergeEvent.hpp>
38 : #include <com/sun/star/frame/XStorable.hpp>
39 : #include <com/sun/star/task/InteractionHandler.hpp>
40 : #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
41 : #include <com/sun/star/ui/dialogs/XFilePicker.hpp>
42 : #include <com/sun/star/ui/dialogs/XFilterManager.hpp>
43 : #include <com/sun/star/uno/XNamingService.hpp>
44 : #include <com/sun/star/util/XCloseable.hpp>
45 : #include <com/sun/star/beans/XPropertySet.hpp>
46 : #include <sfx2/fcontnr.hxx>
47 : #include <sfx2/filedlghelper.hxx>
48 : #include <sfx2/viewfrm.hxx>
49 : #include <dbconfig.hxx>
50 : #include <swdbtoolsclient.hxx>
51 : #include <pagedesc.hxx>
52 : #include <vcl/lstbox.hxx>
53 : #include <unotools/tempfile.hxx>
54 : #include <unotools/pathoptions.hxx>
55 : #include <svl/urihelper.hxx>
56 : #include <svl/zforlist.hxx>
57 : #include <svl/zformat.hxx>
58 : #include <svl/stritem.hxx>
59 : #include <svl/eitem.hxx>
60 : #include <vcl/oldprintadaptor.hxx>
61 : #include <sfx2/docfile.hxx>
62 : #include <sfx2/progress.hxx>
63 : #include <sfx2/dispatch.hxx>
64 : #include <svl/mailenum.hxx>
65 : #include <cmdid.h>
66 : #include <swmodule.hxx>
67 : #include <view.hxx>
68 : #include <docsh.hxx>
69 : #include <edtwin.hxx>
70 : #include <wrtsh.hxx>
71 : #include <fldbas.hxx>
72 : #include <initui.hxx>
73 : #include <swundo.hxx>
74 : #include <flddat.hxx>
75 : #include <modcfg.hxx>
76 : #include <shellio.hxx>
77 : #include <dbui.hxx>
78 : #include <dbmgr.hxx>
79 : #include <doc.hxx>
80 : #include <swwait.hxx>
81 : #include <swunohelper.hxx>
82 : #include <dbui.hrc>
83 : #include <globals.hrc>
84 : #include <statstr.hrc>
85 : #include <mmconfigitem.hxx>
86 : #include <sfx2/request.hxx>
87 : #include <hintids.hxx>
88 : #include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
89 : #include <com/sun/star/sdbc/XRowSet.hpp>
90 : #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
91 : #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
92 : #include <com/sun/star/sdb/XQueriesSupplier.hpp>
93 : #include <com/sun/star/sdb/XColumn.hpp>
94 : #include <com/sun/star/sdbc/DataType.hpp>
95 : #include <com/sun/star/sdbc/ResultSetType.hpp>
96 : #include <com/sun/star/mail/MailAttachment.hpp>
97 : #include <comphelper/processfactory.hxx>
98 : #include <comphelper/property.hxx>
99 : #include <comphelper/string.hxx>
100 : #include <comphelper/types.hxx>
101 : #include <mailmergehelper.hxx>
102 : #include <maildispatcher.hxx>
103 : #include <svtools/htmlcfg.hxx>
104 : #include <i18nlangtag/languagetag.hxx>
105 : #include <com/sun/star/util/XNumberFormatTypes.hpp>
106 : #include <editeng/langitem.hxx>
107 : #include <svl/numuno.hxx>
108 :
109 : #include <unomailmerge.hxx>
110 : #include <sfx2/event.hxx>
111 : #include <vcl/msgbox.hxx>
112 : #include <svx/dataaccessdescriptor.hxx>
113 : #include <osl/mutex.hxx>
114 : #include <rtl/textenc.h>
115 : #include <ndindex.hxx>
116 : #include <pam.hxx>
117 : #include <swcrsr.hxx>
118 : #include <swevent.hxx>
119 : #include <osl/file.hxx>
120 : #include <swabstdlg.hxx>
121 : #include <fmthdft.hxx>
122 : #include <envelp.hrc>
123 : #include <vector>
124 : #include <unomid.h>
125 : #include <section.hxx>
126 : #include <rootfrm.hxx>
127 :
128 : #include <boost/scoped_ptr.hpp>
129 :
130 : using namespace ::osl;
131 : using namespace ::svx;
132 : using namespace ::com::sun::star;
133 : using namespace ::com::sun::star::text;
134 : using namespace ::com::sun::star::uno;
135 : using namespace ::com::sun::star::container;
136 : using namespace ::com::sun::star::frame;
137 : using namespace ::com::sun::star::lang;
138 : using namespace ::com::sun::star::sdb;
139 : using namespace ::com::sun::star::sdbc;
140 : using namespace ::com::sun::star::sdbcx;
141 : using namespace ::com::sun::star::beans;
142 : using namespace ::com::sun::star::util;
143 : using namespace ::com::sun::star::task;
144 : using namespace ::com::sun::star::ui::dialogs;
145 :
146 : #define DB_SEP_SPACE 0
147 : #define DB_SEP_TAB 1
148 : #define DB_SEP_RETURN 2
149 : #define DB_SEP_NEWLINE 3
150 :
151 : const sal_Char cCursor[] = "Cursor";
152 : const sal_Char cCommand[] = "Command";
153 : const sal_Char cCommandType[] = "CommandType";
154 : const sal_Char cDataSourceName[] = "DataSourceName";
155 : const sal_Char cSelection[] = "Selection";
156 : const sal_Char cActiveConnection[] = "ActiveConnection";
157 :
158 : // Use nameless namespace to avoid to rubbish the global namespace
159 :
160 : namespace
161 : {
162 :
163 0 : bool lcl_getCountFromResultSet( sal_Int32& rCount, const uno::Reference<XResultSet>& xResultSet )
164 : {
165 0 : uno::Reference<XPropertySet> xPrSet(xResultSet, UNO_QUERY);
166 0 : if(xPrSet.is())
167 : {
168 : try
169 : {
170 0 : sal_Bool bFinal = sal_False;
171 0 : Any aFinal = xPrSet->getPropertyValue("IsRowCountFinal");
172 0 : aFinal >>= bFinal;
173 0 : if(!bFinal)
174 : {
175 0 : xResultSet->last();
176 0 : xResultSet->first();
177 : }
178 0 : Any aCount = xPrSet->getPropertyValue("RowCount");
179 0 : if( aCount >>= rCount )
180 0 : return true;
181 : }
182 0 : catch(const Exception&)
183 : {
184 : }
185 : }
186 0 : return false;
187 : }
188 : // copy compatibility options
189 0 : void lcl_CopyCompatibilityOptions( SwWrtShell& rSourceShell, SwWrtShell& rTargetShell)
190 : {
191 0 : IDocumentSettingAccess* pIDsa = rSourceShell.getIDocumentSettingAccess();
192 :
193 0 : rTargetShell.SetParaSpaceMax( pIDsa->get(IDocumentSettingAccess::PARA_SPACE_MAX));
194 0 : rTargetShell.SetParaSpaceMaxAtPages(pIDsa->get(IDocumentSettingAccess::PARA_SPACE_MAX_AT_PAGES));
195 0 : rTargetShell.SetTabCompat( pIDsa->get(IDocumentSettingAccess::TAB_COMPAT));
196 0 : rTargetShell.SetAddExtLeading( pIDsa->get(IDocumentSettingAccess::ADD_EXT_LEADING));
197 0 : rTargetShell.SetUseVirDev( pIDsa->get(IDocumentSettingAccess::USE_VIRTUAL_DEVICE));
198 0 : rTargetShell.SetAddParaSpacingToTableCells( pIDsa->get(IDocumentSettingAccess::ADD_PARA_SPACING_TO_TABLE_CELLS));
199 0 : rTargetShell.SetUseFormerLineSpacing( pIDsa->get(IDocumentSettingAccess::OLD_LINE_SPACING));
200 0 : rTargetShell.SetUseFormerObjectPositioning( pIDsa->get(IDocumentSettingAccess::USE_FORMER_OBJECT_POS));
201 0 : rTargetShell.SetConsiderWrapOnObjPos( pIDsa->get(IDocumentSettingAccess::CONSIDER_WRAP_ON_OBJECT_POSITION));
202 0 : rTargetShell.SetUseFormerTextWrapping( pIDsa->get(IDocumentSettingAccess::USE_FORMER_TEXT_WRAPPING));
203 0 : }
204 : }
205 :
206 : class SwConnectionDisposedListener_Impl : public cppu::WeakImplHelper1
207 : < lang::XEventListener >
208 : {
209 : SwNewDBMgr& rDBMgr;
210 :
211 : virtual void SAL_CALL disposing( const EventObject& Source ) throw (RuntimeException, std::exception) SAL_OVERRIDE;
212 : public:
213 : SwConnectionDisposedListener_Impl(SwNewDBMgr& rMgr);
214 : virtual ~SwConnectionDisposedListener_Impl();
215 :
216 : };
217 :
218 0 : struct SwNewDBMgr_Impl
219 : {
220 : SwDSParam* pMergeData;
221 : AbstractMailMergeDlg* pMergeDialog;
222 : uno::Reference<lang::XEventListener> xDisposeListener;
223 :
224 0 : SwNewDBMgr_Impl(SwNewDBMgr& rDBMgr)
225 : :pMergeData(0)
226 : ,pMergeDialog(0)
227 0 : ,xDisposeListener(new SwConnectionDisposedListener_Impl(rDBMgr))
228 0 : {}
229 : };
230 :
231 0 : static void lcl_InitNumberFormatter(SwDSParam& rParam, uno::Reference<XDataSource> xSource)
232 : {
233 0 : uno::Reference<XComponentContext> xContext = ::comphelper::getProcessComponentContext();
234 0 : rParam.xFormatter = uno::Reference<util::XNumberFormatter>(util::NumberFormatter::create(xContext), UNO_QUERY);
235 0 : if(!xSource.is())
236 0 : xSource = SwNewDBMgr::getDataSourceAsParent(rParam.xConnection, rParam.sDataSource);
237 :
238 0 : uno::Reference<XPropertySet> xSourceProps(xSource, UNO_QUERY);
239 0 : if(xSourceProps.is())
240 : {
241 0 : Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier");
242 0 : if(aFormats.hasValue())
243 : {
244 0 : uno::Reference<XNumberFormatsSupplier> xSuppl;
245 0 : aFormats >>= xSuppl;
246 0 : if(xSuppl.is())
247 : {
248 0 : uno::Reference< XPropertySet > xSettings = xSuppl->getNumberFormatSettings();
249 0 : Any aNull = xSettings->getPropertyValue("NullDate");
250 0 : aNull >>= rParam.aNullDate;
251 0 : if(rParam.xFormatter.is())
252 0 : rParam.xFormatter->attachNumberFormatsSupplier(xSuppl);
253 0 : }
254 0 : }
255 0 : }
256 0 : }
257 :
258 0 : static sal_Bool lcl_MoveAbsolute(SwDSParam* pParam, long nAbsPos)
259 : {
260 0 : sal_Bool bRet = sal_False;
261 : try
262 : {
263 0 : if(pParam->bScrollable)
264 : {
265 0 : bRet = pParam->xResultSet->absolute( nAbsPos );
266 : }
267 : else
268 : {
269 : OSL_FAIL("no absolute positioning available");
270 : }
271 : }
272 0 : catch(const Exception&)
273 : {
274 : }
275 0 : return bRet;
276 : }
277 :
278 0 : static sal_Bool lcl_GetColumnCnt(SwDSParam* pParam,
279 : const OUString& rColumnName, long nLanguage, OUString& rResult, double* pNumber)
280 : {
281 0 : uno::Reference< XColumnsSupplier > xColsSupp( pParam->xResultSet, UNO_QUERY );
282 0 : uno::Reference<XNameAccess> xCols;
283 : try
284 : {
285 0 : xCols = xColsSupp->getColumns();
286 : }
287 0 : catch(const lang::DisposedException&)
288 : {
289 : }
290 0 : if(!xCols.is() || !xCols->hasByName(rColumnName))
291 0 : return sal_False;
292 0 : Any aCol = xCols->getByName(rColumnName);
293 0 : uno::Reference< XPropertySet > xColumnProps;
294 0 : aCol >>= xColumnProps;
295 :
296 0 : SwDBFormatData aFormatData;
297 0 : if(!pParam->xFormatter.is())
298 : {
299 : uno::Reference<XDataSource> xSource = SwNewDBMgr::getDataSourceAsParent(
300 0 : pParam->xConnection,pParam->sDataSource);
301 0 : lcl_InitNumberFormatter(*pParam, xSource );
302 : }
303 0 : aFormatData.aNullDate = pParam->aNullDate;
304 0 : aFormatData.xFormatter = pParam->xFormatter;
305 :
306 0 : aFormatData.aLocale = LanguageTag( (LanguageType)nLanguage ).getLocale();
307 :
308 0 : rResult = SwNewDBMgr::GetDBField( xColumnProps, aFormatData, pNumber);
309 0 : return sal_True;
310 : };
311 :
312 : /*--------------------------------------------------------------------
313 : Description: import data
314 : --------------------------------------------------------------------*/
315 0 : sal_Bool SwNewDBMgr::MergeNew(const SwMergeDescriptor& rMergeDesc )
316 : {
317 : OSL_ENSURE(!bInMerge && !pImpl->pMergeData, "merge already activated!");
318 :
319 0 : SwDBData aData;
320 0 : aData.nCommandType = CommandType::TABLE;
321 0 : uno::Reference<XResultSet> xResSet;
322 0 : Sequence<Any> aSelection;
323 0 : uno::Reference< XConnection> xConnection;
324 :
325 0 : aData.sDataSource = rMergeDesc.rDescriptor.getDataSource();
326 0 : rMergeDesc.rDescriptor[daCommand] >>= aData.sCommand;
327 0 : rMergeDesc.rDescriptor[daCommandType] >>= aData.nCommandType;
328 :
329 0 : if ( rMergeDesc.rDescriptor.has(daCursor) )
330 0 : rMergeDesc.rDescriptor[daCursor] >>= xResSet;
331 0 : if ( rMergeDesc.rDescriptor.has(daSelection) )
332 0 : rMergeDesc.rDescriptor[daSelection] >>= aSelection;
333 0 : if ( rMergeDesc.rDescriptor.has(daConnection) )
334 0 : rMergeDesc.rDescriptor[daConnection] >>= xConnection;
335 :
336 0 : if(aData.sDataSource.isEmpty() || aData.sCommand.isEmpty() || !xResSet.is())
337 : {
338 0 : return sal_False;
339 : }
340 :
341 0 : pImpl->pMergeData = new SwDSParam(aData, xResSet, aSelection);
342 0 : SwDSParam* pTemp = FindDSData(aData, sal_False);
343 0 : if(pTemp)
344 0 : *pTemp = *pImpl->pMergeData;
345 : else
346 : {
347 : // calls from the calculator may have added a connection with an invalid commandtype
348 : //"real" data base connections added here have to re-use the already available
349 : //DSData and set the correct CommandType
350 0 : SwDBData aTempData(aData);
351 0 : aData.nCommandType = -1;
352 0 : pTemp = FindDSData(aData, sal_False);
353 0 : if(pTemp)
354 0 : *pTemp = *pImpl->pMergeData;
355 : else
356 : {
357 0 : SwDSParam* pInsert = new SwDSParam(*pImpl->pMergeData);
358 0 : aDataSourceParams.push_back(pInsert);
359 : try
360 : {
361 0 : uno::Reference<XComponent> xComponent(pInsert->xConnection, UNO_QUERY);
362 0 : if(xComponent.is())
363 0 : xComponent->addEventListener(pImpl->xDisposeListener);
364 : }
365 0 : catch(const Exception&)
366 : {
367 : }
368 0 : }
369 : }
370 0 : if(!pImpl->pMergeData->xConnection.is())
371 0 : pImpl->pMergeData->xConnection = xConnection;
372 : // add an XEventListener
373 :
374 : try{
375 : //set to start position
376 0 : if(pImpl->pMergeData->aSelection.getLength())
377 : {
378 0 : sal_Int32 nPos = 0;
379 0 : pImpl->pMergeData->aSelection.getConstArray()[ pImpl->pMergeData->nSelectionIndex++ ] >>= nPos;
380 0 : pImpl->pMergeData->bEndOfDB = !pImpl->pMergeData->xResultSet->absolute( nPos );
381 0 : pImpl->pMergeData->CheckEndOfDB();
382 0 : if(pImpl->pMergeData->nSelectionIndex >= pImpl->pMergeData->aSelection.getLength())
383 0 : pImpl->pMergeData->bEndOfDB = sal_True;
384 : }
385 : else
386 : {
387 0 : pImpl->pMergeData->bEndOfDB = !pImpl->pMergeData->xResultSet->first();
388 0 : pImpl->pMergeData->CheckEndOfDB();
389 : }
390 : }
391 0 : catch(const Exception&)
392 : {
393 0 : pImpl->pMergeData->bEndOfDB = sal_True;
394 0 : pImpl->pMergeData->CheckEndOfDB();
395 : OSL_FAIL("exception in MergeNew()");
396 : }
397 :
398 0 : uno::Reference<XDataSource> xSource = SwNewDBMgr::getDataSourceAsParent(xConnection,aData.sDataSource);
399 :
400 0 : lcl_InitNumberFormatter(*pImpl->pMergeData, xSource);
401 :
402 0 : rMergeDesc.rSh.ChgDBData(aData);
403 0 : bInMerge = sal_True;
404 :
405 0 : if (IsInitDBFields())
406 : {
407 : // with database fields without DB-Name, use DB-Name from Doc
408 0 : std::vector<OUString> aDBNames;
409 0 : aDBNames.push_back(OUString());
410 0 : SwDBData aInsertData = rMergeDesc.rSh.GetDBData();
411 0 : OUString sDBName = aInsertData.sDataSource;
412 0 : sDBName += OUString(DB_DELIM);
413 0 : sDBName += aInsertData.sCommand;
414 0 : sDBName += OUString(DB_DELIM);
415 0 : sDBName += OUString::number(aInsertData.nCommandType);
416 0 : rMergeDesc.rSh.ChangeDBFields( aDBNames, sDBName);
417 0 : SetInitDBFields(sal_False);
418 : }
419 :
420 0 : sal_Bool bRet = sal_True;
421 0 : switch(rMergeDesc.nMergeType)
422 : {
423 : case DBMGR_MERGE:
424 0 : bRet = Merge(&rMergeDesc.rSh);
425 0 : break;
426 :
427 : case DBMGR_MERGE_MAILMERGE: // printing merge from 'old' merge dialog or from UNO-component
428 : case DBMGR_MERGE_MAILING:
429 : case DBMGR_MERGE_MAILFILES:
430 : case DBMGR_MERGE_SINGLE_FILE:
431 : // save files and send them as e-Mail if required
432 : bRet = MergeMailFiles(&rMergeDesc.rSh,
433 0 : rMergeDesc);
434 0 : break;
435 :
436 : default:
437 : // insert selected entries
438 : // (was: InsertRecord)
439 0 : ImportFromConnection(&rMergeDesc.rSh);
440 0 : break;
441 : }
442 :
443 0 : EndMerge();
444 0 : return bRet;
445 : }
446 :
447 : /*--------------------------------------------------------------------
448 : Description: import data
449 : --------------------------------------------------------------------*/
450 0 : sal_Bool SwNewDBMgr::Merge(SwWrtShell* pSh)
451 : {
452 0 : pSh->StartAllAction();
453 :
454 0 : pSh->SwViewShell::UpdateFlds(sal_True);
455 0 : pSh->SetModified();
456 :
457 0 : pSh->EndAllAction();
458 :
459 0 : return sal_True;
460 : }
461 :
462 0 : void SwNewDBMgr::ImportFromConnection( SwWrtShell* pSh )
463 : {
464 0 : if(pImpl->pMergeData && !pImpl->pMergeData->bEndOfDB)
465 : {
466 : {
467 0 : pSh->StartAllAction();
468 0 : pSh->StartUndo(UNDO_EMPTY);
469 0 : sal_Bool bGroupUndo(pSh->DoesGroupUndo());
470 0 : pSh->DoGroupUndo(sal_False);
471 :
472 0 : if( pSh->HasSelection() )
473 0 : pSh->DelRight();
474 :
475 0 : boost::scoped_ptr<SwWait> pWait;
476 :
477 : {
478 0 : sal_uLong i = 0;
479 0 : do {
480 :
481 0 : ImportDBEntry(pSh);
482 0 : if( 10 == ++i )
483 0 : pWait.reset(new SwWait( *pSh->GetView().GetDocShell(), true));
484 :
485 0 : } while(ToNextMergeRecord());
486 : }
487 :
488 0 : pSh->DoGroupUndo(bGroupUndo);
489 0 : pSh->EndUndo(UNDO_EMPTY);
490 0 : pSh->EndAllAction();
491 : }
492 : }
493 0 : }
494 :
495 0 : static OUString lcl_FindColumn(const OUString& sFormatStr,sal_uInt16 &nUsedPos, sal_uInt8 &nSeparator)
496 : {
497 0 : OUString sReturn;
498 0 : sal_uInt16 nLen = sFormatStr.getLength();
499 0 : nSeparator = 0xff;
500 0 : while(nUsedPos < nLen && nSeparator == 0xff)
501 : {
502 0 : sal_Unicode cAkt = sFormatStr[nUsedPos];
503 0 : switch(cAkt)
504 : {
505 : case ',':
506 0 : nSeparator = DB_SEP_SPACE;
507 0 : break;
508 : case ';':
509 0 : nSeparator = DB_SEP_RETURN;
510 0 : break;
511 : case ':':
512 0 : nSeparator = DB_SEP_TAB;
513 0 : break;
514 : case '#':
515 0 : nSeparator = DB_SEP_NEWLINE;
516 0 : break;
517 : default:
518 0 : sReturn += OUString(cAkt);
519 : }
520 0 : nUsedPos++;
521 :
522 : }
523 0 : return sReturn;
524 : }
525 :
526 0 : void SwNewDBMgr::ImportDBEntry(SwWrtShell* pSh)
527 : {
528 0 : if(pImpl->pMergeData && !pImpl->pMergeData->bEndOfDB)
529 : {
530 0 : uno::Reference< XColumnsSupplier > xColsSupp( pImpl->pMergeData->xResultSet, UNO_QUERY );
531 0 : uno::Reference<XNameAccess> xCols = xColsSupp->getColumns();
532 0 : OUString sFormatStr;
533 0 : sal_uInt16 nFmtLen = sFormatStr.getLength();
534 0 : if( nFmtLen )
535 : {
536 0 : const char cSpace = ' ';
537 0 : const char cTab = '\t';
538 0 : sal_uInt16 nUsedPos = 0;
539 : sal_uInt8 nSeparator;
540 0 : OUString sColumn = lcl_FindColumn(sFormatStr, nUsedPos, nSeparator);
541 0 : while( !sColumn.isEmpty() )
542 : {
543 0 : if(!xCols->hasByName(sColumn))
544 0 : return;
545 0 : Any aCol = xCols->getByName(sColumn);
546 0 : uno::Reference< XPropertySet > xColumnProp;
547 0 : aCol >>= xColumnProp;
548 0 : if(xColumnProp.is())
549 : {
550 0 : SwDBFormatData aDBFormat;
551 0 : OUString sInsert = GetDBField( xColumnProp, aDBFormat);
552 0 : if( DB_SEP_SPACE == nSeparator )
553 0 : sInsert += OUString(cSpace);
554 0 : else if( DB_SEP_TAB == nSeparator)
555 0 : sInsert += OUString(cTab);
556 0 : pSh->Insert(sInsert);
557 0 : if( DB_SEP_RETURN == nSeparator)
558 0 : pSh->SplitNode();
559 0 : else if(DB_SEP_NEWLINE == nSeparator)
560 0 : pSh->InsertLineBreak();
561 : }
562 : else
563 : {
564 : // column not found -> show error
565 0 : OUStringBuffer sInsert;
566 0 : sInsert.append('?').append(sColumn).append('?');
567 0 : pSh->Insert(sInsert.makeStringAndClear());
568 : }
569 0 : sColumn = lcl_FindColumn(sFormatStr, nUsedPos, nSeparator);
570 0 : }
571 0 : pSh->SplitNode();
572 : }
573 : else
574 : {
575 0 : OUString sStr;
576 0 : Sequence<OUString> aColNames = xCols->getElementNames();
577 0 : const OUString* pColNames = aColNames.getConstArray();
578 0 : long nLength = aColNames.getLength();
579 0 : for(long i = 0; i < nLength; i++)
580 : {
581 0 : Any aCol = xCols->getByName(pColNames[i]);
582 0 : uno::Reference< XPropertySet > xColumnProp;
583 0 : aCol >>= xColumnProp;
584 0 : SwDBFormatData aDBFormat;
585 0 : sStr += GetDBField( xColumnProp, aDBFormat);
586 0 : if (i < nLength - 1)
587 0 : sStr += "\t";
588 0 : }
589 0 : pSh->SwEditShell::Insert2(sStr);
590 0 : pSh->SwFEShell::SplitNode(); // line feed
591 0 : }
592 : }
593 : }
594 :
595 : /*--------------------------------------------------------------------
596 : Description: fill Listbox with tablelist
597 : --------------------------------------------------------------------*/
598 0 : sal_Bool SwNewDBMgr::GetTableNames(ListBox* pListBox, const OUString& rDBName)
599 : {
600 0 : sal_Bool bRet = sal_False;
601 0 : OUString sOldTableName(pListBox->GetSelectEntry());
602 0 : pListBox->Clear();
603 0 : SwDSParam* pParam = FindDSConnection(rDBName, sal_False);
604 0 : uno::Reference< XConnection> xConnection;
605 0 : if(pParam && pParam->xConnection.is())
606 0 : xConnection = pParam->xConnection;
607 : else
608 : {
609 0 : OUString sDBName(rDBName);
610 0 : if ( !sDBName.isEmpty() )
611 0 : xConnection = RegisterConnection( sDBName );
612 : }
613 0 : if(xConnection.is())
614 : {
615 0 : uno::Reference<XTablesSupplier> xTSupplier = uno::Reference<XTablesSupplier>(xConnection, UNO_QUERY);
616 0 : if(xTSupplier.is())
617 : {
618 0 : uno::Reference<XNameAccess> xTbls = xTSupplier->getTables();
619 0 : Sequence<OUString> aTbls = xTbls->getElementNames();
620 0 : const OUString* pTbls = aTbls.getConstArray();
621 0 : for(long i = 0; i < aTbls.getLength(); i++)
622 : {
623 0 : sal_uInt16 nEntry = pListBox->InsertEntry(pTbls[i]);
624 0 : pListBox->SetEntryData(nEntry, (void*)0);
625 0 : }
626 : }
627 0 : uno::Reference<XQueriesSupplier> xQSupplier = uno::Reference<XQueriesSupplier>(xConnection, UNO_QUERY);
628 0 : if(xQSupplier.is())
629 : {
630 0 : uno::Reference<XNameAccess> xQueries = xQSupplier->getQueries();
631 0 : Sequence<OUString> aQueries = xQueries->getElementNames();
632 0 : const OUString* pQueries = aQueries.getConstArray();
633 0 : for(long i = 0; i < aQueries.getLength(); i++)
634 : {
635 0 : sal_uInt16 nEntry = pListBox->InsertEntry(pQueries[i]);
636 0 : pListBox->SetEntryData(nEntry, (void*)1);
637 0 : }
638 : }
639 0 : if (!sOldTableName.isEmpty())
640 0 : pListBox->SelectEntry(sOldTableName);
641 0 : bRet = sal_True;
642 : }
643 0 : return bRet;
644 : }
645 :
646 : /*--------------------------------------------------------------------
647 : Description: fill Listbox with column names of a database
648 : --------------------------------------------------------------------*/
649 0 : sal_Bool SwNewDBMgr::GetColumnNames(ListBox* pListBox,
650 : const OUString& rDBName, const OUString& rTableName, sal_Bool bAppend)
651 : {
652 0 : if (!bAppend)
653 0 : pListBox->Clear();
654 0 : SwDBData aData;
655 0 : aData.sDataSource = rDBName;
656 0 : aData.sCommand = rTableName;
657 0 : aData.nCommandType = -1;
658 0 : SwDSParam* pParam = FindDSData(aData, sal_False);
659 0 : uno::Reference< XConnection> xConnection;
660 0 : if(pParam && pParam->xConnection.is())
661 0 : xConnection = pParam->xConnection;
662 : else
663 : {
664 0 : OUString sDBName(rDBName);
665 0 : xConnection = RegisterConnection( sDBName );
666 : }
667 0 : uno::Reference< XColumnsSupplier> xColsSupp = SwNewDBMgr::GetColumnSupplier(xConnection, rTableName);
668 0 : if(xColsSupp.is())
669 : {
670 0 : uno::Reference<XNameAccess> xCols = xColsSupp->getColumns();
671 0 : const Sequence<OUString> aColNames = xCols->getElementNames();
672 0 : const OUString* pColNames = aColNames.getConstArray();
673 0 : for(int nCol = 0; nCol < aColNames.getLength(); nCol++)
674 : {
675 0 : pListBox->InsertEntry(pColNames[nCol]);
676 : }
677 0 : ::comphelper::disposeComponent( xColsSupp );
678 : }
679 0 : return(sal_True);
680 : }
681 :
682 0 : sal_Bool SwNewDBMgr::GetColumnNames(ListBox* pListBox,
683 : uno::Reference< XConnection> xConnection,
684 : const OUString& rTableName, sal_Bool bAppend)
685 : {
686 0 : if (!bAppend)
687 0 : pListBox->Clear();
688 0 : uno::Reference< XColumnsSupplier> xColsSupp = SwNewDBMgr::GetColumnSupplier(xConnection, rTableName);
689 0 : if(xColsSupp.is())
690 : {
691 0 : uno::Reference<XNameAccess> xCols = xColsSupp->getColumns();
692 0 : const Sequence<OUString> aColNames = xCols->getElementNames();
693 0 : const OUString* pColNames = aColNames.getConstArray();
694 0 : for(int nCol = 0; nCol < aColNames.getLength(); nCol++)
695 : {
696 0 : pListBox->InsertEntry(pColNames[nCol]);
697 : }
698 0 : ::comphelper::disposeComponent( xColsSupp );
699 : }
700 0 : return(sal_True);
701 : }
702 :
703 : /*--------------------------------------------------------------------
704 : Description: CTOR
705 : --------------------------------------------------------------------*/
706 0 : SwNewDBMgr::SwNewDBMgr() :
707 : bInitDBFields(sal_False),
708 : bInMerge(sal_False),
709 : bMergeSilent(sal_False),
710 : bMergeLock(sal_False),
711 0 : pImpl(new SwNewDBMgr_Impl(*this)),
712 0 : pMergeEvtSrc(NULL)
713 : {
714 0 : }
715 :
716 0 : SwNewDBMgr::~SwNewDBMgr()
717 : {
718 0 : for(sal_uInt16 nPos = 0; nPos < aDataSourceParams.size(); nPos++)
719 : {
720 0 : SwDSParam* pParam = &aDataSourceParams[nPos];
721 0 : if(pParam->xConnection.is())
722 : {
723 : try
724 : {
725 0 : uno::Reference<XComponent> xComp(pParam->xConnection, UNO_QUERY);
726 0 : if(xComp.is())
727 0 : xComp->dispose();
728 : }
729 0 : catch(const RuntimeException&)
730 : {
731 : //may be disposed already since multiple entries may have used the same connection
732 : }
733 : }
734 : }
735 0 : delete pImpl;
736 0 : }
737 :
738 : /*--------------------------------------------------------------------
739 : Description: save bulk letters as single documents
740 : --------------------------------------------------------------------*/
741 0 : static OUString lcl_FindUniqueName(SwWrtShell* pTargetShell, const OUString& rStartingPageDesc, sal_uLong nDocNo )
742 : {
743 : do
744 : {
745 0 : OUString sTest = rStartingPageDesc;
746 0 : sTest += OUString::number( nDocNo );
747 0 : if( !pTargetShell->FindPageDescByName( sTest ) )
748 0 : return sTest;
749 0 : ++nDocNo;
750 0 : }while(true);
751 : }
752 :
753 0 : static void lcl_CopyDynamicDefaults( const SwDoc& rSource, SwDoc& rTarget )
754 : {
755 : sal_uInt16 aRangeOfDefaults[] = {
756 : RES_FRMATR_BEGIN, RES_FRMATR_END-1,
757 : RES_CHRATR_BEGIN, RES_CHRATR_END-1,
758 : RES_PARATR_BEGIN, RES_PARATR_END-1,
759 : RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END-1,
760 : RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1,
761 : 0
762 0 : };
763 :
764 0 : SfxItemSet aNewDefaults( rTarget.GetAttrPool(), aRangeOfDefaults );
765 :
766 : sal_uInt16 nWhich;
767 0 : sal_uInt16 nRange = 0;
768 0 : while( aRangeOfDefaults[nRange] != 0)
769 : {
770 0 : for( nWhich = aRangeOfDefaults[nRange]; nWhich < aRangeOfDefaults[nRange + 1]; ++nWhich )
771 : {
772 0 : const SfxPoolItem& rSourceAttr = rSource.GetDefault( nWhich );
773 0 : if( rSourceAttr != rTarget.GetDefault( nWhich ) )
774 0 : aNewDefaults.Put( rSourceAttr );
775 : }
776 0 : nRange += 2;
777 : }
778 0 : if( aNewDefaults.Count() )
779 0 : rTarget.SetDefault( aNewDefaults );
780 0 : }
781 :
782 0 : static void lcl_CopyFollowPageDesc(
783 : SwWrtShell& rTargetShell,
784 : const SwPageDesc& rSourcePageDesc,
785 : const SwPageDesc& rTargetPageDesc,
786 : const sal_uLong nDocNo )
787 : {
788 : //now copy the follow page desc, too
789 0 : const SwPageDesc* pFollowPageDesc = rSourcePageDesc.GetFollow();
790 0 : OUString sFollowPageDesc = pFollowPageDesc->GetName();
791 0 : if( sFollowPageDesc != rSourcePageDesc.GetName() )
792 : {
793 0 : SwDoc* pTargetDoc = rTargetShell.GetDoc();
794 0 : OUString sNewFollowPageDesc = lcl_FindUniqueName(&rTargetShell, sFollowPageDesc, nDocNo );
795 0 : sal_uInt16 nNewDesc = pTargetDoc->MakePageDesc( sNewFollowPageDesc );
796 0 : SwPageDesc& rTargetFollowPageDesc = pTargetDoc->GetPageDesc( nNewDesc );
797 :
798 0 : pTargetDoc->CopyPageDesc( *pFollowPageDesc, rTargetFollowPageDesc, false );
799 0 : SwPageDesc aDesc( rTargetPageDesc );
800 0 : aDesc.SetFollow( &rTargetFollowPageDesc );
801 0 : pTargetDoc->ChgPageDesc( rTargetPageDesc.GetName(), aDesc );
802 0 : }
803 0 : }
804 :
805 0 : static void lcl_RemoveSectionLinks( SwWrtShell& rWorkShell )
806 : {
807 : //reset all links of the sections of synchronized labels
808 0 : sal_uInt16 nSections = rWorkShell.GetSectionFmtCount();
809 0 : for( sal_uInt16 nSection = 0; nSection < nSections; ++nSection )
810 : {
811 0 : SwSectionData aSectionData( *rWorkShell.GetSectionFmt( nSection ).GetSection() );
812 0 : if( aSectionData.GetType() == FILE_LINK_SECTION )
813 : {
814 0 : aSectionData.SetType( CONTENT_SECTION );
815 0 : aSectionData.SetLinkFileName( OUString() );
816 0 : rWorkShell.UpdateSection( nSection, aSectionData );
817 : }
818 0 : }
819 0 : rWorkShell.SetLabelDoc( sal_False );
820 0 : }
821 :
822 0 : sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
823 : const SwMergeDescriptor& rMergeDescriptor)
824 : {
825 : //check if the doc is synchronized and contains at least one linked section
826 0 : bool bSynchronizedDoc = pSourceShell->IsLabelDoc() && pSourceShell->GetSectionFmtCount() > 1;
827 0 : sal_Bool bNoError = sal_True;
828 0 : bool bEMail = rMergeDescriptor.nMergeType == DBMGR_MERGE_MAILING;
829 0 : const bool bAsSingleFile = rMergeDescriptor.nMergeType == DBMGR_MERGE_SINGLE_FILE;
830 :
831 0 : ::rtl::Reference< MailDispatcher > xMailDispatcher;
832 0 : OUString sBodyMimeType;
833 0 : rtl_TextEncoding eEncoding = ::osl_getThreadTextEncoding();
834 :
835 0 : if(bEMail)
836 : {
837 0 : xMailDispatcher.set( new MailDispatcher(rMergeDescriptor.xSmtpServer));
838 0 : if(!rMergeDescriptor.bSendAsAttachment && rMergeDescriptor.bSendAsHTML)
839 : {
840 0 : sBodyMimeType = "text/html; charset=";
841 0 : sBodyMimeType += OUString::createFromAscii(
842 0 : rtl_getBestMimeCharsetFromTextEncoding( eEncoding ));
843 0 : SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get();
844 0 : eEncoding = rHtmlOptions.GetTextEncoding();
845 : }
846 : else
847 0 : sBodyMimeType =
848 0 : OUString("text/plain; charset=UTF-8; format=flowed");
849 : }
850 :
851 0 : uno::Reference< XPropertySet > xColumnProp;
852 : {
853 0 : bool bColumnName = !sEMailAddrFld.isEmpty();
854 :
855 0 : if (bColumnName)
856 : {
857 0 : uno::Reference< XColumnsSupplier > xColsSupp( pImpl->pMergeData->xResultSet, UNO_QUERY );
858 0 : uno::Reference<XNameAccess> xCols = xColsSupp->getColumns();
859 0 : if(!xCols->hasByName(sEMailAddrFld))
860 0 : return sal_False;
861 0 : Any aCol = xCols->getByName(sEMailAddrFld);
862 0 : aCol >>= xColumnProp;
863 : }
864 :
865 : // Try saving the source document
866 0 : SfxDispatcher* pSfxDispatcher = pSourceShell->GetView().GetViewFrame()->GetDispatcher();
867 0 : SwDocShell* pSourceDocSh = pSourceShell->GetView().GetDocShell();
868 0 : pSfxDispatcher->Execute( pSourceDocSh->HasName() ? SID_SAVEDOC : SID_SAVEASDOC, SFX_CALLMODE_SYNCHRON|SFX_CALLMODE_RECORD);
869 0 : if( !pSourceDocSh->IsModified() )
870 : {
871 : const SfxFilter* pStoreToFilter = SwIoSystem::GetFileFilter(
872 0 : pSourceDocSh->GetMedium()->GetURLObject().GetMainURL( INetURLObject::NO_DECODE ), ::aEmptyOUStr );
873 0 : SfxFilterContainer* pFilterContainer = SwDocShell::Factory().GetFilterContainer();
874 0 : const OUString* pStoreToFilterOptions = 0;
875 :
876 : // if a save_to filter is set then use it - otherwise use the default
877 0 : if( bEMail && !rMergeDescriptor.bSendAsAttachment )
878 : {
879 0 : OUString sExtension = rMergeDescriptor.bSendAsHTML ? OUString("html") : OUString("txt");
880 0 : pStoreToFilter = pFilterContainer->GetFilter4Extension(sExtension, SFX_FILTER_EXPORT);
881 : }
882 0 : else if( !rMergeDescriptor.sSaveToFilter.isEmpty())
883 : {
884 : const SfxFilter* pFilter =
885 0 : pFilterContainer->GetFilter4FilterName( rMergeDescriptor.sSaveToFilter );
886 0 : if(pFilter)
887 : {
888 0 : pStoreToFilter = pFilter;
889 0 : if(!rMergeDescriptor.sSaveToFilterOptions.isEmpty())
890 0 : pStoreToFilterOptions = &rMergeDescriptor.sSaveToFilterOptions;
891 : }
892 : }
893 0 : bCancel = sal_False;
894 :
895 : // in case of creating a single resulting file this has to be created here
896 0 : SwWrtShell* pTargetShell = 0;
897 :
898 : // the shell will be explicitly closed at the end of the method, but it is
899 : // still more safe to use SfxObjectShellLock here
900 0 : SfxObjectShellLock xTargetDocShell;
901 :
902 0 : SwView* pTargetView = 0;
903 0 : boost::scoped_ptr< utl::TempFile > aTempFile;
904 0 : OUString sModifiedStartingPageDesc;
905 0 : OUString sStartingPageDesc;
906 0 : sal_uInt16 nStartingPageNo = 0;
907 0 : bool bPageStylesWithHeaderFooter = false;
908 0 : if(bAsSingleFile || rMergeDescriptor.bCreateSingleFile)
909 : {
910 : // create a target docshell to put the merged document into
911 0 : xTargetDocShell = new SwDocShell( SFX_CREATE_MODE_STANDARD );
912 0 : xTargetDocShell->DoInitNew( 0 );
913 0 : SfxViewFrame* pTargetFrame = SfxViewFrame::LoadHiddenDocument( *xTargetDocShell, 0 );
914 :
915 0 : pTargetView = static_cast<SwView*>( pTargetFrame->GetViewShell() );
916 :
917 : //initiate SelectShell() to create sub shells
918 0 : pTargetView->AttrChangedNotify( &pTargetView->GetWrtShell() );
919 0 : pTargetShell = pTargetView->GetWrtShellPtr();
920 : //copy the styles from the source to the target document
921 0 : pTargetView->GetDocShell()->_LoadStyles( *pSourceDocSh, sal_True );
922 : //determine the page style and number used at the start of the source document
923 0 : pSourceShell->SttEndDoc(sal_True);
924 0 : nStartingPageNo = pSourceShell->GetVirtPageNum();
925 0 : sStartingPageDesc = sModifiedStartingPageDesc = pSourceShell->GetPageDesc(
926 0 : pSourceShell->GetCurPageDesc()).GetName();
927 : // copy compatibility options
928 0 : lcl_CopyCompatibilityOptions( *pSourceShell, *pTargetShell);
929 : // #72821# copy dynamic defaults
930 0 : lcl_CopyDynamicDefaults( *pSourceShell->GetDoc(), *pTargetShell->GetDoc() );
931 : // #i72517#
932 0 : const SwPageDesc* pSourcePageDesc = pSourceShell->FindPageDescByName( sStartingPageDesc );
933 0 : const SwFrmFmt& rMaster = pSourcePageDesc->GetMaster();
934 0 : bPageStylesWithHeaderFooter = rMaster.GetHeader().IsActive() ||
935 0 : rMaster.GetFooter().IsActive();
936 :
937 : }
938 :
939 0 : PrintMonitor aPrtMonDlg(&pSourceShell->GetView().GetEditWin(), PrintMonitor::MONITOR_TYPE_PRINT);
940 0 : aPrtMonDlg.m_pDocName->SetText(pSourceShell->GetView().GetDocShell()->GetTitle(22));
941 :
942 0 : aPrtMonDlg.m_pCancel->SetClickHdl(LINK(this, SwNewDBMgr, PrtCancelHdl));
943 0 : if (!IsMergeSilent())
944 0 : aPrtMonDlg.Show();
945 :
946 : // Progress, to prohibit KeyInputs
947 0 : SfxProgress aProgress(pSourceDocSh, ::aEmptyOUStr, 1);
948 :
949 : // lock all dispatchers
950 0 : SfxViewFrame* pViewFrm = SfxViewFrame::GetFirst(pSourceDocSh);
951 0 : while (pViewFrm)
952 : {
953 0 : pViewFrm->GetDispatcher()->Lock(true);
954 0 : pViewFrm = SfxViewFrame::GetNext(*pViewFrm, pSourceDocSh);
955 : }
956 0 : sal_uLong nDocNo = 1;
957 :
958 : long nStartRow, nEndRow;
959 0 : bool bFreezedLayouts = false;
960 : // collect temporary files
961 0 : ::std::vector< OUString> aFilesToRemove;
962 0 : do
963 : {
964 0 : nStartRow = pImpl->pMergeData ? pImpl->pMergeData->xResultSet->getRow() : 0;
965 : {
966 0 : OUString sPath(sSubject);
967 :
968 0 : OUString sAddress;
969 0 : if( !bEMail && bColumnName )
970 : {
971 0 : SwDBFormatData aDBFormat;
972 0 : aDBFormat.xFormatter = pImpl->pMergeData->xFormatter;
973 0 : aDBFormat.aNullDate = pImpl->pMergeData->aNullDate;
974 0 : sAddress = GetDBField( xColumnProp, aDBFormat);
975 0 : if (sAddress.isEmpty())
976 0 : sAddress = "_";
977 0 : sPath += sAddress;
978 : }
979 :
980 : // create a new temporary file name - only done once in case of bCreateSingleFile
981 0 : if( 1 == nDocNo || (!rMergeDescriptor.bCreateSingleFile && !bAsSingleFile) )
982 : {
983 0 : INetURLObject aEntry(sPath);
984 0 : OUString sLeading;
985 : //#i97667# if the name is from a database field then it will be used _as is_
986 0 : if( !sAddress.isEmpty() )
987 0 : sLeading = sAddress;
988 : else
989 0 : sLeading = aEntry.GetBase();
990 0 : aEntry.removeSegment();
991 0 : sPath = aEntry.GetMainURL( INetURLObject::NO_DECODE );
992 0 : OUString sExt(comphelper::string::stripStart(pStoreToFilter->GetDefaultExtension(), '*'));
993 : aTempFile.reset(
994 0 : new utl::TempFile(sLeading,&sExt,&sPath ));
995 0 : if( bAsSingleFile )
996 0 : aTempFile->EnableKillingFile();
997 : }
998 :
999 0 : if( !aTempFile->IsValid() )
1000 : {
1001 0 : ErrorHandler::HandleError( ERRCODE_IO_NOTSUPPORTED );
1002 0 : bNoError = sal_False;
1003 0 : bCancel = sal_True;
1004 : }
1005 : else
1006 : {
1007 0 : INetURLObject aTempFileURL(aTempFile->GetURL());
1008 0 : aPrtMonDlg.m_pPrinter->SetText( aTempFileURL.GetBase() );
1009 0 : OUString sStat(SW_RES(STR_STATSTR_LETTER)); // Brief
1010 0 : sStat += " ";
1011 0 : sStat += OUString::number( nDocNo );
1012 0 : aPrtMonDlg.m_pPrintInfo->SetText(sStat);
1013 :
1014 : // computation time for Save-Monitor:
1015 0 : for (sal_uInt16 i = 0; i < 25; i++)
1016 0 : Application::Reschedule();
1017 :
1018 : // The SfxObjectShell will be closed explicitly later but it is more safe to use SfxObjectShellLock here
1019 0 : SfxObjectShellLock xWorkDocSh;
1020 : // copy the source document
1021 0 : if( 1 == nDocNo && (bAsSingleFile || rMergeDescriptor.bCreateSingleFile) )
1022 : {
1023 0 : uno::Reference< util::XCloneable > xClone( pSourceDocSh->GetModel(), uno::UNO_QUERY);
1024 0 : uno::Reference< lang::XUnoTunnel > xWorkDocShell( xClone->createClone(), uno::UNO_QUERY);
1025 0 : SwXTextDocument* pWorkModel = reinterpret_cast<SwXTextDocument*>(xWorkDocShell->getSomething(SwXTextDocument::getUnoTunnelId()));
1026 0 : xWorkDocSh = pWorkModel->GetDocShell();
1027 : }
1028 : else
1029 0 : xWorkDocSh = pSourceDocSh->GetDoc()->CreateCopy( true );
1030 :
1031 : {
1032 : //create a view frame for the document
1033 0 : SfxViewFrame* pWorkFrame = SfxViewFrame::LoadHiddenDocument( *xWorkDocSh, 0 );
1034 : //request the layout calculation
1035 : SwWrtShell& rWorkShell =
1036 0 : static_cast< SwView* >(pWorkFrame->GetViewShell())->GetWrtShell();
1037 0 : rWorkShell.CalcLayout();
1038 0 : SwDoc* pWorkDoc = ((SwDocShell*)(&xWorkDocSh))->GetDoc();
1039 0 : SwNewDBMgr* pOldDBMgr = pWorkDoc->GetNewDBMgr();
1040 0 : pWorkDoc->SetNewDBMgr( this );
1041 0 : SFX_APP()->NotifyEvent(SfxEventHint(SW_EVENT_FIELD_MERGE, SwDocShell::GetEventName(STR_SW_EVENT_FIELD_MERGE), xWorkDocSh));
1042 0 : pWorkDoc->UpdateFlds(NULL, false);
1043 0 : SFX_APP()->NotifyEvent(SfxEventHint(SW_EVENT_FIELD_MERGE_FINISHED, SwDocShell::GetEventName(STR_SW_EVENT_FIELD_MERGE_FINISHED), xWorkDocSh));
1044 :
1045 0 : pWorkDoc->RemoveInvisibleContent();
1046 :
1047 : // launch MailMergeEvent if required
1048 0 : const SwXMailMerge *pEvtSrc = GetMailMergeEvtSrc();
1049 0 : if(pEvtSrc)
1050 : {
1051 0 : uno::Reference< XInterface > xRef( (XMailMergeBroadcaster *) pEvtSrc );
1052 0 : text::MailMergeEvent aEvt( xRef, xWorkDocSh->GetModel() );
1053 0 : pEvtSrc->LaunchMailMergeEvent( aEvt );
1054 : }
1055 :
1056 0 : if(rMergeDescriptor.bCreateSingleFile || bAsSingleFile )
1057 : {
1058 : OSL_ENSURE( pTargetShell, "no target shell available!" );
1059 : // copy created file into the target document
1060 0 : rWorkShell.ConvertFieldsToText();
1061 0 : rWorkShell.SetNumberingRestart();
1062 0 : if( bSynchronizedDoc )
1063 : {
1064 0 : lcl_RemoveSectionLinks( rWorkShell );
1065 : }
1066 :
1067 : // insert the document into the target document
1068 0 : rWorkShell.SttEndDoc(sal_False);
1069 0 : rWorkShell.SttEndDoc(sal_True);
1070 0 : rWorkShell.SelAll();
1071 0 : pTargetShell->SwCrsrShell::SttEndDoc( sal_False );
1072 : //#i72517# the headers and footers are still those from the source - update in case of fields inside header/footer
1073 0 : if( !nDocNo && bPageStylesWithHeaderFooter )
1074 0 : pTargetShell->GetView().GetDocShell()->_LoadStyles( *rWorkShell.GetView().GetDocShell(), sal_True );
1075 : //#i72517# put the styles to the target document
1076 : //if the source uses headers or footers each new copy need to copy a new page styles
1077 0 : if(bPageStylesWithHeaderFooter)
1078 : {
1079 : //create a new pagestyle
1080 : //copy the pagedesc from the current document to the new document and change the name of the to-be-applied style
1081 :
1082 0 : SwDoc* pTargetDoc = pTargetShell->GetDoc();
1083 0 : SwPageDesc* pSourcePageDesc = rWorkShell.FindPageDescByName( sStartingPageDesc );
1084 0 : OUString sNewPageDescName = lcl_FindUniqueName(pTargetShell, sStartingPageDesc, nDocNo );
1085 0 : pTargetDoc->MakePageDesc( sNewPageDescName );
1086 0 : SwPageDesc* pTargetPageDesc = pTargetShell->FindPageDescByName( sNewPageDescName );
1087 0 : if(pSourcePageDesc && pTargetPageDesc)
1088 : {
1089 0 : pTargetDoc->CopyPageDesc( *pSourcePageDesc, *pTargetPageDesc, false );
1090 0 : sModifiedStartingPageDesc = sNewPageDescName;
1091 0 : lcl_CopyFollowPageDesc( *pTargetShell, *pSourcePageDesc, *pTargetPageDesc, nDocNo );
1092 0 : }
1093 : }
1094 :
1095 0 : if(nDocNo > 1)
1096 0 : pTargetShell->InsertPageBreak( &sModifiedStartingPageDesc, nStartingPageNo );
1097 : else
1098 0 : pTargetShell->SetPageStyle(sModifiedStartingPageDesc);
1099 : OSL_ENSURE(!pTargetShell->GetTableFmt(),"target document ends with a table - paragraph should be appended");
1100 : //#i51359# add a second paragraph in case there's only one
1101 : {
1102 0 : SwNodeIndex aIdx( pWorkDoc->GetNodes().GetEndOfExtras(), 2 );
1103 0 : SwPosition aTestPos( aIdx );
1104 0 : SwCursor aTestCrsr(aTestPos,0,false);
1105 0 : if(!aTestCrsr.MovePara(fnParaNext, fnParaStart))
1106 : {
1107 : //append a paragraph
1108 0 : pWorkDoc->AppendTxtNode( aTestPos );
1109 0 : }
1110 : }
1111 0 : pTargetShell->Paste( rWorkShell.GetDoc(), sal_True );
1112 :
1113 : //convert fields in page styles (header/footer - has to be done after the first document has been pasted
1114 0 : if(1 == nDocNo)
1115 : {
1116 0 : pTargetShell->CalcLayout();
1117 0 : pTargetShell->ConvertFieldsToText();
1118 0 : }
1119 : }
1120 : else
1121 : {
1122 0 : OUString sFileURL = aTempFileURL.GetMainURL( INetURLObject::NO_DECODE );
1123 : SfxMedium* pDstMed = new SfxMedium(
1124 : sFileURL,
1125 0 : STREAM_STD_READWRITE );
1126 0 : pDstMed->SetFilter( pStoreToFilter );
1127 0 : if(pDstMed->GetItemSet())
1128 : {
1129 0 : if(pStoreToFilterOptions )
1130 0 : pDstMed->GetItemSet()->Put(SfxStringItem(SID_FILE_FILTEROPTIONS, *pStoreToFilterOptions));
1131 0 : if(rMergeDescriptor.aSaveToFilterData.getLength())
1132 0 : pDstMed->GetItemSet()->Put(SfxUsrAnyItem(SID_FILTER_DATA, makeAny(rMergeDescriptor.aSaveToFilterData)));
1133 : }
1134 :
1135 : //convert fields to text if we are exporting to PDF
1136 : //this prevents a second merge while updating the fields in SwXTextDocument::getRendererCount()
1137 0 : if( pStoreToFilter && pStoreToFilter->GetFilterName().equalsAscii("writer_pdf_Export"))
1138 0 : rWorkShell.ConvertFieldsToText();
1139 0 : xWorkDocSh->DoSaveAs(*pDstMed);
1140 0 : xWorkDocSh->DoSaveCompleted(pDstMed);
1141 0 : if( xWorkDocSh->GetError() )
1142 : {
1143 : // error message ??
1144 0 : ErrorHandler::HandleError( xWorkDocSh->GetError() );
1145 0 : bCancel = sal_True;
1146 0 : bNoError = sal_False;
1147 : }
1148 0 : if( bEMail )
1149 : {
1150 0 : SwDBFormatData aDBFormat;
1151 0 : aDBFormat.xFormatter = pImpl->pMergeData->xFormatter;
1152 0 : aDBFormat.aNullDate = pImpl->pMergeData->aNullDate;
1153 0 : OUString sMailAddress = GetDBField( xColumnProp, aDBFormat);
1154 0 : if(!SwMailMergeHelper::CheckMailAddress( sMailAddress ))
1155 : {
1156 : OSL_FAIL("invalid e-Mail address in database column");
1157 : }
1158 : else
1159 : {
1160 0 : SwMailMessage* pMessage = new SwMailMessage;
1161 0 : uno::Reference< mail::XMailMessage > xMessage = pMessage;
1162 0 : if(rMergeDescriptor.pMailMergeConfigItem->IsMailReplyTo())
1163 0 : pMessage->setReplyToAddress(rMergeDescriptor.pMailMergeConfigItem->GetMailReplyTo());
1164 0 : pMessage->addRecipient( sMailAddress );
1165 0 : pMessage->SetSenderAddress( rMergeDescriptor.pMailMergeConfigItem->GetMailAddress() );
1166 0 : OUString sBody;
1167 0 : if(rMergeDescriptor.bSendAsAttachment)
1168 : {
1169 0 : sBody = rMergeDescriptor.sMailBody;
1170 0 : mail::MailAttachment aAttach;
1171 0 : aAttach.Data = new SwMailTransferable(
1172 : sFileURL,
1173 : rMergeDescriptor.sAttachmentName,
1174 0 : pStoreToFilter->GetMimeType());
1175 0 : aAttach.ReadableName = rMergeDescriptor.sAttachmentName;
1176 0 : pMessage->addAttachment( aAttach );
1177 : }
1178 : else
1179 : {
1180 : {
1181 : //read in the temporary file and use it as mail body
1182 0 : SfxMedium aMedium( sFileURL, STREAM_READ);
1183 0 : SvStream* pInStream = aMedium.GetInStream();
1184 : OSL_ENSURE(pInStream, "no output file created?");
1185 0 : if(pInStream)
1186 : {
1187 0 : pInStream->SetStreamCharSet( eEncoding );
1188 0 : OString sLine;
1189 0 : sal_Bool bDone = pInStream->ReadLine( sLine );
1190 0 : while ( bDone )
1191 : {
1192 0 : sBody += OStringToOUString(sLine, eEncoding);
1193 0 : sBody += "\n";
1194 0 : bDone = pInStream->ReadLine( sLine );
1195 0 : }
1196 0 : }
1197 : }
1198 : }
1199 0 : pMessage->setSubject( rMergeDescriptor.sSubject );
1200 : uno::Reference< datatransfer::XTransferable> xBody =
1201 : new SwMailTransferable(
1202 : sBody,
1203 0 : sBodyMimeType);
1204 0 : pMessage->setBody( xBody );
1205 :
1206 0 : if(rMergeDescriptor.aCopiesTo.getLength())
1207 : {
1208 0 : const OUString* pCopies = rMergeDescriptor.aCopiesTo.getConstArray();
1209 0 : for( sal_Int32 nToken = 0; nToken < rMergeDescriptor.aCopiesTo.getLength(); ++nToken)
1210 0 : pMessage->addCcRecipient( pCopies[nToken] );
1211 : }
1212 0 : if(rMergeDescriptor.aBlindCopiesTo.getLength())
1213 : {
1214 0 : const OUString* pCopies = rMergeDescriptor.aBlindCopiesTo.getConstArray();
1215 0 : for( sal_Int32 nToken = 0; nToken < rMergeDescriptor.aBlindCopiesTo.getLength(); ++nToken)
1216 0 : pMessage->addBccRecipient( pCopies[nToken] );
1217 : }
1218 0 : xMailDispatcher->enqueueMailMessage( xMessage );
1219 0 : if(!xMailDispatcher->isStarted())
1220 0 : xMailDispatcher->start();
1221 : //schedule for removal
1222 0 : aFilesToRemove.push_back(sFileURL);
1223 0 : }
1224 0 : }
1225 : }
1226 0 : pWorkDoc->SetNewDBMgr( pOldDBMgr );
1227 : }
1228 0 : xWorkDocSh->DoClose();
1229 0 : }
1230 : }
1231 0 : nDocNo++;
1232 0 : nEndRow = pImpl->pMergeData ? pImpl->pMergeData->xResultSet->getRow() : 0;
1233 :
1234 : // Freeze the layouts of the target document after the first inserted
1235 : // sub-document, to get the correct PageDesc.
1236 0 : if(!bFreezedLayouts && (rMergeDescriptor.bCreateSingleFile || bAsSingleFile))
1237 : {
1238 0 : std::set<SwRootFrm*> aAllLayouts = pTargetShell->GetDoc()->GetAllLayouts();
1239 : std::for_each( aAllLayouts.begin(), aAllLayouts.end(),
1240 0 : ::std::bind2nd(::std::mem_fun(&SwRootFrm::FreezeLayout), true));
1241 0 : bFreezedLayouts = true;
1242 : }
1243 0 : } while( !bCancel &&
1244 0 : (bSynchronizedDoc && (nStartRow != nEndRow)? ExistsNextRecord() : ToNextMergeRecord()));
1245 :
1246 : // Unfreeze target document layouts and correct all PageDescs.
1247 0 : if(rMergeDescriptor.bCreateSingleFile || bAsSingleFile)
1248 : {
1249 0 : std::set<SwRootFrm*> aAllLayouts = pTargetShell->GetDoc()->GetAllLayouts();
1250 : std::for_each( aAllLayouts.begin(), aAllLayouts.end(),
1251 0 : ::std::bind2nd(::std::mem_fun(&SwRootFrm::FreezeLayout), false));
1252 0 : std::for_each( aAllLayouts.begin(), aAllLayouts.end(),std::mem_fun(&SwRootFrm::AllCheckPageDescs));
1253 : }
1254 :
1255 0 : aPrtMonDlg.Show( false );
1256 :
1257 : // save the single output document
1258 0 : if(rMergeDescriptor.bCreateSingleFile || bAsSingleFile)
1259 : {
1260 0 : if( rMergeDescriptor.nMergeType != DBMGR_MERGE_MAILMERGE )
1261 : {
1262 : OSL_ENSURE( aTempFile.get(), "Temporary file not available" );
1263 0 : OUString sSub(sSubject);
1264 0 : INetURLObject aTempFileURL(bAsSingleFile ? sSub : aTempFile->GetURL());
1265 : SfxMedium* pDstMed = new SfxMedium(
1266 : aTempFileURL.GetMainURL( INetURLObject::NO_DECODE ),
1267 0 : STREAM_STD_READWRITE );
1268 0 : pDstMed->SetFilter( pStoreToFilter );
1269 0 : if(pDstMed->GetItemSet())
1270 : {
1271 0 : if(pStoreToFilterOptions )
1272 0 : pDstMed->GetItemSet()->Put(SfxStringItem(SID_FILE_FILTEROPTIONS, *pStoreToFilterOptions));
1273 0 : if(rMergeDescriptor.aSaveToFilterData.getLength())
1274 0 : pDstMed->GetItemSet()->Put(SfxUsrAnyItem(SID_FILTER_DATA, makeAny(rMergeDescriptor.aSaveToFilterData)));
1275 : }
1276 :
1277 0 : xTargetDocShell->DoSaveAs(*pDstMed);
1278 0 : xTargetDocShell->DoSaveCompleted(pDstMed);
1279 0 : if( xTargetDocShell->GetError() )
1280 : {
1281 : // error message ??
1282 0 : ErrorHandler::HandleError( xTargetDocShell->GetError() );
1283 0 : bNoError = sal_False;
1284 0 : }
1285 : }
1286 0 : else if( pTargetView ) // must be available!
1287 : {
1288 : //print the target document
1289 : #if OSL_DEBUG_LEVEL > 1
1290 : sal_Bool _bVal;
1291 : sal_Int16 _nVal;
1292 : OUString _sVal;
1293 : const beans::PropertyValue* pDbgPrintOptions = rMergeDescriptor.aPrintOptions.getConstArray();
1294 : for( sal_Int32 nOption = 0; nOption < rMergeDescriptor.aPrintOptions.getLength(); ++nOption)
1295 : {
1296 : OUString aName( pDbgPrintOptions[nOption].Name );
1297 : uno::Any aVal( pDbgPrintOptions[nOption].Value );
1298 : aVal >>= _bVal;
1299 : aVal >>= _nVal;
1300 : aVal >>= _sVal;
1301 : }
1302 : #endif
1303 : // printing should be done synchronously otherwise the document
1304 : // might already become invalid during the process
1305 0 : uno::Sequence< beans::PropertyValue > aOptions( rMergeDescriptor.aPrintOptions );
1306 :
1307 0 : aOptions.realloc( 1 );
1308 0 : aOptions[ 0 ].Name = "Wait";
1309 0 : aOptions[ 0 ].Value <<= sal_True ;
1310 : // move print options
1311 0 : const beans::PropertyValue* pPrintOptions = rMergeDescriptor.aPrintOptions.getConstArray();
1312 0 : for( sal_Int32 nOption = 0, nIndex = 1 ; nOption < rMergeDescriptor.aPrintOptions.getLength(); ++nOption)
1313 : {
1314 0 : if( pPrintOptions[nOption].Name == "CopyCount" || pPrintOptions[nOption].Name == "FileName"
1315 0 : || pPrintOptions[nOption].Name == "Collate" || pPrintOptions[nOption].Name == "Pages"
1316 0 : || pPrintOptions[nOption].Name == "Wait" || pPrintOptions[nOption].Name == "PrinterName" )
1317 : {
1318 : // add an option
1319 0 : aOptions.realloc( nIndex + 1 );
1320 0 : aOptions[ nIndex ].Name = pPrintOptions[nOption].Name;
1321 0 : aOptions[ nIndex++ ].Value = pPrintOptions[nOption].Value ;
1322 : }
1323 : }
1324 :
1325 0 : pTargetView->ExecPrint( aOptions, IsMergeSilent(), rMergeDescriptor.bPrintAsync );
1326 : }
1327 0 : xTargetDocShell->DoClose();
1328 : }
1329 :
1330 : //remove the temporary files
1331 0 : ::std::vector<OUString>::iterator aFileIter;
1332 0 : for(aFileIter = aFilesToRemove.begin();
1333 0 : aFileIter != aFilesToRemove.end(); ++aFileIter)
1334 0 : SWUnoHelper::UCB_DeleteFile( *aFileIter );
1335 :
1336 : // unlock all dispatchers
1337 0 : pViewFrm = SfxViewFrame::GetFirst(pSourceDocSh);
1338 0 : while (pViewFrm)
1339 : {
1340 0 : pViewFrm->GetDispatcher()->Lock(false);
1341 0 : pViewFrm = SfxViewFrame::GetNext(*pViewFrm, pSourceDocSh);
1342 : }
1343 :
1344 0 : SW_MOD()->SetView(&pSourceShell->GetView());
1345 : }
1346 : }
1347 :
1348 0 : if(bEMail)
1349 : {
1350 0 : xMailDispatcher->stop();
1351 0 : xMailDispatcher->shutdown();
1352 : }
1353 :
1354 0 : return bNoError;
1355 : }
1356 :
1357 0 : void SwNewDBMgr::MergeCancel()
1358 : {
1359 0 : bCancel = sal_True;
1360 0 : }
1361 :
1362 0 : IMPL_LINK_INLINE_START( SwNewDBMgr, PrtCancelHdl, Button *, pButton )
1363 : {
1364 0 : pButton->GetParent()->Hide();
1365 0 : MergeCancel();
1366 0 : return 0;
1367 : }
1368 0 : IMPL_LINK_INLINE_END( SwNewDBMgr, PrtCancelHdl, Button *, pButton )
1369 :
1370 : /*--------------------------------------------------------------------
1371 : Description: determine the column's Numberformat and transfer
1372 : to the forwarded Formatter, if applicable.
1373 : --------------------------------------------------------------------*/
1374 0 : sal_uLong SwNewDBMgr::GetColumnFmt( const OUString& rDBName,
1375 : const OUString& rTableName,
1376 : const OUString& rColNm,
1377 : SvNumberFormatter* pNFmtr,
1378 : long nLanguage )
1379 : {
1380 0 : sal_uLong nRet = 0;
1381 0 : if(pNFmtr)
1382 : {
1383 0 : uno::Reference< XDataSource> xSource;
1384 0 : uno::Reference< XConnection> xConnection;
1385 0 : bool bUseMergeData = false;
1386 0 : uno::Reference< XColumnsSupplier> xColsSupp;
1387 0 : bool bDisposeConnection = false;
1388 0 : if(pImpl->pMergeData &&
1389 0 : pImpl->pMergeData->sDataSource.equals(rDBName) && pImpl->pMergeData->sCommand.equals(rTableName))
1390 : {
1391 0 : xConnection = pImpl->pMergeData->xConnection;
1392 0 : xSource = SwNewDBMgr::getDataSourceAsParent(xConnection,rDBName);
1393 0 : bUseMergeData = true;
1394 0 : xColsSupp = xColsSupp.query( pImpl->pMergeData->xResultSet );
1395 : }
1396 0 : if(!xConnection.is())
1397 : {
1398 0 : SwDBData aData;
1399 0 : aData.sDataSource = rDBName;
1400 0 : aData.sCommand = rTableName;
1401 0 : aData.nCommandType = -1;
1402 0 : SwDSParam* pParam = FindDSData(aData, sal_False);
1403 0 : if(pParam && pParam->xConnection.is())
1404 : {
1405 0 : xConnection = pParam->xConnection;
1406 0 : xColsSupp = xColsSupp.query( pParam->xResultSet );
1407 : }
1408 : else
1409 : {
1410 0 : OUString sDBName(rDBName);
1411 0 : xConnection = RegisterConnection( sDBName );
1412 0 : bDisposeConnection = true;
1413 : }
1414 0 : if(bUseMergeData)
1415 0 : pImpl->pMergeData->xConnection = xConnection;
1416 : }
1417 0 : bool bDispose = !xColsSupp.is();
1418 0 : if(bDispose)
1419 : {
1420 0 : xColsSupp = SwNewDBMgr::GetColumnSupplier(xConnection, rTableName);
1421 : }
1422 0 : if(xColsSupp.is())
1423 : {
1424 0 : uno::Reference<XNameAccess> xCols;
1425 : try
1426 : {
1427 0 : xCols = xColsSupp->getColumns();
1428 : }
1429 0 : catch(const Exception&)
1430 : {
1431 : OSL_FAIL("Exception in getColumns()");
1432 : }
1433 0 : if(!xCols.is() || !xCols->hasByName(rColNm))
1434 0 : return nRet;
1435 0 : Any aCol = xCols->getByName(rColNm);
1436 0 : uno::Reference< XPropertySet > xColumn;
1437 0 : aCol >>= xColumn;
1438 0 : nRet = GetColumnFmt(xSource, xConnection, xColumn, pNFmtr, nLanguage);
1439 0 : if(bDispose)
1440 : {
1441 0 : ::comphelper::disposeComponent( xColsSupp );
1442 : }
1443 0 : if(bDisposeConnection)
1444 : {
1445 0 : ::comphelper::disposeComponent( xConnection );
1446 0 : }
1447 : }
1448 : else
1449 0 : nRet = pNFmtr->GetFormatIndex( NF_NUMBER_STANDARD, LANGUAGE_SYSTEM );
1450 : }
1451 0 : return nRet;
1452 : }
1453 :
1454 0 : sal_uLong SwNewDBMgr::GetColumnFmt( uno::Reference< XDataSource> xSource,
1455 : uno::Reference< XConnection> xConnection,
1456 : uno::Reference< XPropertySet> xColumn,
1457 : SvNumberFormatter* pNFmtr,
1458 : long nLanguage )
1459 : {
1460 : // set the NumberFormat in the doc if applicable
1461 0 : sal_uLong nRet = 0;
1462 :
1463 0 : if(!xSource.is())
1464 : {
1465 0 : uno::Reference<XChild> xChild(xConnection, UNO_QUERY);
1466 0 : if ( xChild.is() )
1467 0 : xSource = uno::Reference<XDataSource>(xChild->getParent(), UNO_QUERY);
1468 : }
1469 0 : if(xSource.is() && xConnection.is() && xColumn.is() && pNFmtr)
1470 : {
1471 0 : SvNumberFormatsSupplierObj* pNumFmt = new SvNumberFormatsSupplierObj( pNFmtr );
1472 0 : uno::Reference< util::XNumberFormatsSupplier > xDocNumFmtsSupplier = pNumFmt;
1473 0 : uno::Reference< XNumberFormats > xDocNumberFormats = xDocNumFmtsSupplier->getNumberFormats();
1474 0 : uno::Reference< XNumberFormatTypes > xDocNumberFormatTypes(xDocNumberFormats, UNO_QUERY);
1475 :
1476 0 : com::sun::star::lang::Locale aLocale( LanguageTag( (LanguageType)nLanguage ).getLocale());
1477 :
1478 : //get the number formatter of the data source
1479 0 : uno::Reference<XPropertySet> xSourceProps(xSource, UNO_QUERY);
1480 0 : uno::Reference< XNumberFormats > xNumberFormats;
1481 0 : if(xSourceProps.is())
1482 : {
1483 0 : Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier");
1484 0 : if(aFormats.hasValue())
1485 : {
1486 0 : uno::Reference<XNumberFormatsSupplier> xSuppl;
1487 0 : aFormats >>= xSuppl;
1488 0 : if(xSuppl.is())
1489 : {
1490 0 : xNumberFormats = xSuppl->getNumberFormats();
1491 0 : }
1492 0 : }
1493 : }
1494 0 : bool bUseDefault = true;
1495 : try
1496 : {
1497 0 : Any aFormatKey = xColumn->getPropertyValue("FormatKey");
1498 0 : if(aFormatKey.hasValue())
1499 : {
1500 0 : sal_Int32 nFmt = 0;
1501 0 : aFormatKey >>= nFmt;
1502 0 : if(xNumberFormats.is())
1503 : {
1504 : try
1505 : {
1506 0 : uno::Reference<XPropertySet> xNumProps = xNumberFormats->getByKey( nFmt );
1507 0 : Any aFormatString = xNumProps->getPropertyValue("FormatString");
1508 0 : Any aLocaleVal = xNumProps->getPropertyValue("Locale");
1509 0 : OUString sFormat;
1510 0 : aFormatString >>= sFormat;
1511 0 : lang::Locale aLoc;
1512 0 : aLocaleVal >>= aLoc;
1513 0 : nFmt = xDocNumberFormats->queryKey( sFormat, aLoc, sal_False );
1514 0 : if(NUMBERFORMAT_ENTRY_NOT_FOUND == sal::static_int_cast< sal_uInt32, sal_Int32>(nFmt))
1515 0 : nFmt = xDocNumberFormats->addNew( sFormat, aLoc );
1516 0 : nRet = nFmt;
1517 0 : bUseDefault = false;
1518 : }
1519 0 : catch(const Exception&)
1520 : {
1521 : OSL_FAIL("illegal number format key");
1522 : }
1523 : }
1524 0 : }
1525 : }
1526 0 : catch(const Exception&)
1527 : {
1528 : OSL_FAIL("no FormatKey property found");
1529 : }
1530 0 : if(bUseDefault)
1531 0 : nRet = SwNewDBMgr::GetDbtoolsClient().getDefaultNumberFormat(xColumn, xDocNumberFormatTypes, aLocale);
1532 : }
1533 0 : return nRet;
1534 : }
1535 :
1536 0 : sal_Int32 SwNewDBMgr::GetColumnType( const OUString& rDBName,
1537 : const OUString& rTableName,
1538 : const OUString& rColNm )
1539 : {
1540 0 : sal_Int32 nRet = DataType::SQLNULL;
1541 0 : SwDBData aData;
1542 0 : aData.sDataSource = rDBName;
1543 0 : aData.sCommand = rTableName;
1544 0 : aData.nCommandType = -1;
1545 0 : SwDSParam* pParam = FindDSData(aData, sal_False);
1546 0 : uno::Reference< XConnection> xConnection;
1547 0 : uno::Reference< XColumnsSupplier > xColsSupp;
1548 0 : bool bDispose = false;
1549 0 : if(pParam && pParam->xConnection.is())
1550 : {
1551 0 : xConnection = pParam->xConnection;
1552 0 : xColsSupp = uno::Reference< XColumnsSupplier >( pParam->xResultSet, UNO_QUERY );
1553 : }
1554 : else
1555 : {
1556 0 : OUString sDBName(rDBName);
1557 0 : xConnection = RegisterConnection( sDBName );
1558 : }
1559 0 : if( !xColsSupp.is() )
1560 : {
1561 0 : xColsSupp = SwNewDBMgr::GetColumnSupplier(xConnection, rTableName);
1562 0 : bDispose = true;
1563 : }
1564 0 : if(xColsSupp.is())
1565 : {
1566 0 : uno::Reference<XNameAccess> xCols = xColsSupp->getColumns();
1567 0 : if(xCols->hasByName(rColNm))
1568 : {
1569 0 : Any aCol = xCols->getByName(rColNm);
1570 0 : uno::Reference<XPropertySet> xCol;
1571 0 : aCol >>= xCol;
1572 0 : Any aType = xCol->getPropertyValue("Type");
1573 0 : aType >>= nRet;
1574 : }
1575 0 : if(bDispose)
1576 0 : ::comphelper::disposeComponent( xColsSupp );
1577 : }
1578 0 : return nRet;
1579 : }
1580 :
1581 0 : uno::Reference< sdbc::XConnection> SwNewDBMgr::GetConnection(const OUString& rDataSource,
1582 : uno::Reference<XDataSource>& rxSource)
1583 : {
1584 0 : Reference< sdbc::XConnection> xConnection;
1585 0 : Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
1586 : try
1587 : {
1588 0 : Reference<XCompletedConnection> xComplConnection(SwNewDBMgr::GetDbtoolsClient().getDataSource(rDataSource, xContext),UNO_QUERY);
1589 0 : if ( xComplConnection.is() )
1590 : {
1591 0 : rxSource.set(xComplConnection,UNO_QUERY);
1592 0 : Reference< XInteractionHandler > xHandler( InteractionHandler::createWithParent(xContext, 0), UNO_QUERY_THROW );
1593 0 : xConnection = xComplConnection->connectWithCompletion( xHandler );
1594 0 : }
1595 : }
1596 0 : catch(const Exception&)
1597 : {
1598 : }
1599 :
1600 0 : return xConnection;
1601 : }
1602 :
1603 0 : uno::Reference< sdbcx::XColumnsSupplier> SwNewDBMgr::GetColumnSupplier(uno::Reference<sdbc::XConnection> xConnection,
1604 : const OUString& rTableOrQuery,
1605 : sal_uInt8 eTableOrQuery)
1606 : {
1607 0 : Reference< sdbcx::XColumnsSupplier> xRet;
1608 : try
1609 : {
1610 0 : if(eTableOrQuery == SW_DB_SELECT_UNKNOWN)
1611 : {
1612 : //search for a table with the given command name
1613 0 : Reference<XTablesSupplier> xTSupplier = Reference<XTablesSupplier>(xConnection, UNO_QUERY);
1614 0 : if(xTSupplier.is())
1615 : {
1616 0 : Reference<XNameAccess> xTbls = xTSupplier->getTables();
1617 0 : eTableOrQuery = xTbls->hasByName(rTableOrQuery) ?
1618 0 : SW_DB_SELECT_TABLE : SW_DB_SELECT_QUERY;
1619 0 : }
1620 : }
1621 : sal_Int32 nCommandType = SW_DB_SELECT_TABLE == eTableOrQuery ?
1622 0 : CommandType::TABLE : CommandType::QUERY;
1623 0 : Reference< XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() );
1624 0 : Reference<XRowSet> xRowSet(xMgr->createInstance("com.sun.star.sdb.RowSet"), UNO_QUERY);
1625 :
1626 0 : OUString sDataSource;
1627 0 : Reference<XDataSource> xSource = SwNewDBMgr::getDataSourceAsParent(xConnection, sDataSource);
1628 0 : Reference<XPropertySet> xSourceProperties(xSource, UNO_QUERY);
1629 0 : if(xSourceProperties.is())
1630 : {
1631 0 : xSourceProperties->getPropertyValue("Name") >>= sDataSource;
1632 : }
1633 :
1634 0 : Reference<XPropertySet> xRowProperties(xRowSet, UNO_QUERY);
1635 0 : xRowProperties->setPropertyValue("DataSourceName", makeAny(sDataSource));
1636 0 : xRowProperties->setPropertyValue("Command", makeAny(OUString(rTableOrQuery)));
1637 0 : xRowProperties->setPropertyValue("CommandType", makeAny(nCommandType));
1638 0 : xRowProperties->setPropertyValue("FetchSize", makeAny((sal_Int32)10));
1639 0 : xRowProperties->setPropertyValue("ActiveConnection", makeAny(xConnection));
1640 0 : xRowSet->execute();
1641 0 : xRet = Reference<XColumnsSupplier>( xRowSet, UNO_QUERY );
1642 : }
1643 0 : catch(const uno::Exception&)
1644 : {
1645 : OSL_FAIL("Exception in SwDBMgr::GetColumnSupplier");
1646 : }
1647 :
1648 0 : return xRet;
1649 : }
1650 :
1651 0 : OUString SwNewDBMgr::GetDBField(uno::Reference<XPropertySet> xColumnProps,
1652 : const SwDBFormatData& rDBFormatData,
1653 : double* pNumber)
1654 : {
1655 0 : uno::Reference< XColumn > xColumn(xColumnProps, UNO_QUERY);
1656 0 : OUString sRet;
1657 : OSL_ENSURE(xColumn.is(), "SwNewDBMgr::::ImportDBField: illegal arguments");
1658 0 : if(!xColumn.is())
1659 0 : return sRet;
1660 :
1661 0 : Any aType = xColumnProps->getPropertyValue("Type");
1662 0 : sal_Int32 eDataType = 0;
1663 0 : aType >>= eDataType;
1664 0 : switch(eDataType)
1665 : {
1666 : case DataType::CHAR:
1667 : case DataType::VARCHAR:
1668 : case DataType::LONGVARCHAR:
1669 : try
1670 : {
1671 0 : sRet = xColumn->getString();
1672 : }
1673 0 : catch(const SQLException&)
1674 : {
1675 : }
1676 0 : break;
1677 : case DataType::BIT:
1678 : case DataType::BOOLEAN:
1679 : case DataType::TINYINT:
1680 : case DataType::SMALLINT:
1681 : case DataType::INTEGER:
1682 : case DataType::BIGINT:
1683 : case DataType::FLOAT:
1684 : case DataType::REAL:
1685 : case DataType::DOUBLE:
1686 : case DataType::NUMERIC:
1687 : case DataType::DECIMAL:
1688 : case DataType::DATE:
1689 : case DataType::TIME:
1690 : case DataType::TIMESTAMP:
1691 : {
1692 :
1693 : try
1694 : {
1695 0 : SwDbtoolsClient& aClient = SwNewDBMgr::GetDbtoolsClient();
1696 0 : sRet = aClient.getFormattedValue(
1697 : xColumnProps,
1698 : rDBFormatData.xFormatter,
1699 : rDBFormatData.aLocale,
1700 0 : rDBFormatData.aNullDate);
1701 0 : if (pNumber)
1702 : {
1703 0 : double fVal = xColumn->getDouble();
1704 0 : if(!xColumn->wasNull())
1705 : {
1706 0 : *pNumber = fVal;
1707 : }
1708 : }
1709 : }
1710 0 : catch(const Exception&)
1711 : {
1712 : OSL_FAIL("exception caught");
1713 : }
1714 :
1715 : }
1716 0 : break;
1717 : }
1718 :
1719 0 : return sRet;
1720 : }
1721 :
1722 : // releases the merge data source table or query after merge is completed
1723 0 : void SwNewDBMgr::EndMerge()
1724 : {
1725 : OSL_ENSURE(bInMerge, "merge is not active");
1726 0 : bInMerge = sal_False;
1727 0 : delete pImpl->pMergeData;
1728 0 : pImpl->pMergeData = 0;
1729 0 : }
1730 :
1731 : // checks if a desired data source table or query is open
1732 0 : sal_Bool SwNewDBMgr::IsDataSourceOpen(const OUString& rDataSource,
1733 : const OUString& rTableOrQuery, sal_Bool bMergeOnly)
1734 : {
1735 0 : if(pImpl->pMergeData)
1736 : {
1737 0 : return !bMergeLock &&
1738 0 : ((rDataSource == pImpl->pMergeData->sDataSource &&
1739 0 : rTableOrQuery == pImpl->pMergeData->sCommand)
1740 0 : ||(rDataSource.isEmpty() && rTableOrQuery.isEmpty()))
1741 0 : &&
1742 0 : pImpl->pMergeData->xResultSet.is();
1743 : }
1744 0 : else if(!bMergeOnly)
1745 : {
1746 0 : SwDBData aData;
1747 0 : aData.sDataSource = rDataSource;
1748 0 : aData.sCommand = rTableOrQuery;
1749 0 : aData.nCommandType = -1;
1750 0 : SwDSParam* pFound = FindDSData(aData, sal_False);
1751 0 : return (pFound && pFound->xResultSet.is());
1752 : }
1753 0 : return sal_False;
1754 : }
1755 :
1756 : // read column data at a specified position
1757 0 : sal_Bool SwNewDBMgr::GetColumnCnt(const OUString& rSourceName, const OUString& rTableName,
1758 : const OUString& rColumnName, sal_uInt32 nAbsRecordId,
1759 : long nLanguage,
1760 : OUString& rResult, double* pNumber)
1761 : {
1762 0 : sal_Bool bRet = sal_False;
1763 0 : SwDSParam* pFound = 0;
1764 : //check if it's the merge data source
1765 0 : if(pImpl->pMergeData &&
1766 0 : rSourceName == pImpl->pMergeData->sDataSource &&
1767 0 : rTableName == pImpl->pMergeData->sCommand)
1768 : {
1769 0 : pFound = pImpl->pMergeData;
1770 : }
1771 : else
1772 : {
1773 0 : SwDBData aData;
1774 0 : aData.sDataSource = rSourceName;
1775 0 : aData.sCommand = rTableName;
1776 0 : aData.nCommandType = -1;
1777 0 : pFound = FindDSData(aData, sal_False);
1778 : }
1779 0 : if (!pFound)
1780 0 : return sal_False;
1781 : //check validity of supplied record Id
1782 0 : if(pFound->aSelection.getLength())
1783 : {
1784 : //the destination has to be an element of the selection
1785 0 : const Any* pSelection = pFound->aSelection.getConstArray();
1786 0 : bool bFound = false;
1787 0 : for(sal_Int32 nPos = 0; !bFound && nPos < pFound->aSelection.getLength(); nPos++)
1788 : {
1789 0 : sal_Int32 nSelection = 0;
1790 0 : pSelection[nPos] >>= nSelection;
1791 0 : if(nSelection == static_cast<sal_Int32>(nAbsRecordId))
1792 0 : bFound = true;
1793 : }
1794 0 : if(!bFound)
1795 0 : return sal_False;
1796 : }
1797 0 : if(pFound->xResultSet.is() && !pFound->bAfterSelection)
1798 : {
1799 0 : sal_Int32 nOldRow = 0;
1800 : try
1801 : {
1802 0 : nOldRow = pFound->xResultSet->getRow();
1803 : }
1804 0 : catch(const Exception&)
1805 : {
1806 0 : return sal_False;
1807 : }
1808 : //position to the desired index
1809 0 : sal_Bool bMove = sal_True;
1810 0 : if ( nOldRow != static_cast<sal_Int32>(nAbsRecordId) )
1811 0 : bMove = lcl_MoveAbsolute(pFound, nAbsRecordId);
1812 0 : if(bMove)
1813 : {
1814 0 : bRet = lcl_GetColumnCnt(pFound, rColumnName, nLanguage, rResult, pNumber);
1815 : }
1816 0 : if ( nOldRow != static_cast<sal_Int32>(nAbsRecordId) )
1817 0 : bMove = lcl_MoveAbsolute(pFound, nOldRow);
1818 : }
1819 0 : return bRet;
1820 : }
1821 :
1822 : // reads the column data at the current position
1823 0 : sal_Bool SwNewDBMgr::GetMergeColumnCnt(const OUString& rColumnName, sal_uInt16 nLanguage,
1824 : OUString &rResult, double *pNumber, sal_uInt32 * /*pFormat*/)
1825 : {
1826 0 : if(!pImpl->pMergeData || !pImpl->pMergeData->xResultSet.is() || pImpl->pMergeData->bAfterSelection )
1827 : {
1828 0 : rResult = "";
1829 0 : return sal_False;
1830 : }
1831 :
1832 0 : sal_Bool bRet = lcl_GetColumnCnt(pImpl->pMergeData, rColumnName, nLanguage, rResult, pNumber);
1833 0 : return bRet;
1834 : }
1835 :
1836 0 : sal_Bool SwNewDBMgr::ToNextMergeRecord()
1837 : {
1838 : OSL_ENSURE(pImpl->pMergeData && pImpl->pMergeData->xResultSet.is(), "no data source in merge");
1839 0 : return ToNextRecord(pImpl->pMergeData);
1840 : }
1841 :
1842 0 : sal_Bool SwNewDBMgr::ToNextRecord(
1843 : const OUString& rDataSource, const OUString& rCommand, sal_Int32 /*nCommandType*/)
1844 : {
1845 0 : SwDSParam* pFound = 0;
1846 0 : if(pImpl->pMergeData &&
1847 0 : rDataSource == pImpl->pMergeData->sDataSource &&
1848 0 : rCommand == pImpl->pMergeData->sCommand)
1849 0 : pFound = pImpl->pMergeData;
1850 : else
1851 : {
1852 0 : SwDBData aData;
1853 0 : aData.sDataSource = rDataSource;
1854 0 : aData.sCommand = rCommand;
1855 0 : aData.nCommandType = -1;
1856 0 : pFound = FindDSData(aData, sal_False);
1857 : }
1858 0 : return ToNextRecord(pFound);
1859 : }
1860 :
1861 0 : sal_Bool SwNewDBMgr::ToNextRecord(SwDSParam* pParam)
1862 : {
1863 0 : sal_Bool bRet = sal_True;
1864 0 : if(!pParam || !pParam->xResultSet.is() || pParam->bEndOfDB ||
1865 0 : (pParam->aSelection.getLength() && pParam->aSelection.getLength() <= pParam->nSelectionIndex))
1866 : {
1867 0 : if(pParam)
1868 0 : pParam->CheckEndOfDB();
1869 0 : return sal_False;
1870 : }
1871 : try
1872 : {
1873 0 : if(pParam->aSelection.getLength())
1874 : {
1875 0 : sal_Int32 nPos = 0;
1876 0 : pParam->aSelection.getConstArray()[ pParam->nSelectionIndex++ ] >>= nPos;
1877 0 : pParam->bEndOfDB = !pParam->xResultSet->absolute( nPos );
1878 0 : pParam->CheckEndOfDB();
1879 0 : bRet = !pParam->bEndOfDB;
1880 0 : if(pParam->nSelectionIndex >= pParam->aSelection.getLength())
1881 0 : pParam->bEndOfDB = sal_True;
1882 : }
1883 : else
1884 : {
1885 0 : sal_Int32 nBefore = pParam->xResultSet->getRow();
1886 0 : pParam->bEndOfDB = !pParam->xResultSet->next();
1887 0 : if( !pParam->bEndOfDB && nBefore == pParam->xResultSet->getRow())
1888 : {
1889 : //next returned true but it didn't move
1890 0 : pParam->bEndOfDB = sal_True;
1891 : }
1892 :
1893 0 : pParam->CheckEndOfDB();
1894 0 : bRet = !pParam->bEndOfDB;
1895 0 : ++pParam->nSelectionIndex;
1896 : }
1897 : }
1898 0 : catch(const Exception&)
1899 : {
1900 : }
1901 0 : return bRet;
1902 : }
1903 :
1904 : /* ------------------------------------------------------------------------
1905 : synchronized labels contain a next record field at their end
1906 : to assure that the next page can be created in mail merge
1907 : the cursor position must be validated
1908 : ---------------------------------------------------------------------------*/
1909 0 : sal_Bool SwNewDBMgr::ExistsNextRecord() const
1910 : {
1911 0 : return pImpl->pMergeData && !pImpl->pMergeData->bEndOfDB;
1912 : }
1913 :
1914 0 : sal_uInt32 SwNewDBMgr::GetSelectedRecordId()
1915 : {
1916 0 : sal_uInt32 nRet = 0;
1917 : OSL_ENSURE(pImpl->pMergeData && pImpl->pMergeData->xResultSet.is(), "no data source in merge");
1918 0 : if(!pImpl->pMergeData || !pImpl->pMergeData->xResultSet.is())
1919 0 : return sal_False;
1920 : try
1921 : {
1922 0 : nRet = pImpl->pMergeData->xResultSet->getRow();
1923 : }
1924 0 : catch(const Exception&)
1925 : {
1926 : }
1927 0 : return nRet;
1928 : }
1929 :
1930 0 : sal_Bool SwNewDBMgr::ToRecordId(sal_Int32 nSet)
1931 : {
1932 : OSL_ENSURE(pImpl->pMergeData && pImpl->pMergeData->xResultSet.is(), "no data source in merge");
1933 0 : if(!pImpl->pMergeData || !pImpl->pMergeData->xResultSet.is()|| nSet < 0)
1934 0 : return sal_False;
1935 0 : sal_Bool bRet = sal_False;
1936 0 : sal_Int32 nAbsPos = nSet;
1937 :
1938 0 : if(nAbsPos >= 0)
1939 : {
1940 0 : bRet = lcl_MoveAbsolute(pImpl->pMergeData, nAbsPos);
1941 0 : pImpl->pMergeData->bEndOfDB = !bRet;
1942 0 : pImpl->pMergeData->CheckEndOfDB();
1943 : }
1944 0 : return bRet;
1945 : }
1946 :
1947 0 : sal_Bool SwNewDBMgr::OpenDataSource(const OUString& rDataSource, const OUString& rTableOrQuery,
1948 : sal_Int32 nCommandType, bool bCreate)
1949 : {
1950 0 : SwDBData aData;
1951 0 : aData.sDataSource = rDataSource;
1952 0 : aData.sCommand = rTableOrQuery;
1953 0 : aData.nCommandType = nCommandType;
1954 :
1955 0 : SwDSParam* pFound = FindDSData(aData, sal_True);
1956 0 : uno::Reference< XDataSource> xSource;
1957 0 : if(pFound->xResultSet.is())
1958 0 : return sal_True;
1959 0 : SwDSParam* pParam = FindDSConnection(rDataSource, sal_False);
1960 0 : uno::Reference< XConnection> xConnection;
1961 0 : if(pParam && pParam->xConnection.is())
1962 0 : pFound->xConnection = pParam->xConnection;
1963 0 : else if(bCreate)
1964 : {
1965 0 : OUString sDataSource(rDataSource);
1966 0 : pFound->xConnection = RegisterConnection( sDataSource );
1967 : }
1968 0 : if(pFound->xConnection.is())
1969 : {
1970 : try
1971 : {
1972 0 : uno::Reference< sdbc::XDatabaseMetaData > xMetaData = pFound->xConnection->getMetaData();
1973 : try
1974 : {
1975 : pFound->bScrollable = xMetaData
1976 0 : ->supportsResultSetType((sal_Int32)ResultSetType::SCROLL_INSENSITIVE);
1977 : }
1978 0 : catch(const Exception&)
1979 : {
1980 : // DB driver may not be ODBC 3.0 compliant
1981 0 : pFound->bScrollable = sal_True;
1982 : }
1983 0 : pFound->xStatement = pFound->xConnection->createStatement();
1984 0 : OUString aQuoteChar = xMetaData->getIdentifierQuoteString();
1985 0 : OUString sStatement("SELECT * FROM ");
1986 0 : sStatement = "SELECT * FROM ";
1987 0 : sStatement += aQuoteChar;
1988 0 : sStatement += rTableOrQuery;
1989 0 : sStatement += aQuoteChar;
1990 0 : pFound->xResultSet = pFound->xStatement->executeQuery( sStatement );
1991 :
1992 : //after executeQuery the cursor must be positioned
1993 0 : pFound->bEndOfDB = !pFound->xResultSet->next();
1994 0 : pFound->bAfterSelection = sal_False;
1995 0 : pFound->CheckEndOfDB();
1996 0 : ++pFound->nSelectionIndex;
1997 : }
1998 0 : catch (const Exception&)
1999 : {
2000 0 : pFound->xResultSet = 0;
2001 0 : pFound->xStatement = 0;
2002 0 : pFound->xConnection = 0;
2003 : }
2004 : }
2005 0 : return pFound->xResultSet.is();
2006 : }
2007 :
2008 0 : uno::Reference< XConnection> SwNewDBMgr::RegisterConnection(OUString& rDataSource)
2009 : {
2010 0 : SwDSParam* pFound = SwNewDBMgr::FindDSConnection(rDataSource, sal_True);
2011 0 : uno::Reference< XDataSource> xSource;
2012 0 : if(!pFound->xConnection.is())
2013 : {
2014 0 : pFound->xConnection = SwNewDBMgr::GetConnection(rDataSource, xSource );
2015 : try
2016 : {
2017 0 : uno::Reference<XComponent> xComponent(pFound->xConnection, UNO_QUERY);
2018 0 : if(xComponent.is())
2019 0 : xComponent->addEventListener(pImpl->xDisposeListener);
2020 : }
2021 0 : catch(const Exception&)
2022 : {
2023 : }
2024 : }
2025 0 : return pFound->xConnection;
2026 : }
2027 :
2028 0 : sal_uInt32 SwNewDBMgr::GetSelectedRecordId(
2029 : const OUString& rDataSource, const OUString& rTableOrQuery, sal_Int32 nCommandType)
2030 : {
2031 0 : sal_uInt32 nRet = 0xffffffff;
2032 : //check for merge data source first
2033 0 : if(pImpl->pMergeData && rDataSource == pImpl->pMergeData->sDataSource &&
2034 0 : rTableOrQuery == pImpl->pMergeData->sCommand &&
2035 0 : (nCommandType == -1 || nCommandType == pImpl->pMergeData->nCommandType) &&
2036 0 : pImpl->pMergeData->xResultSet.is())
2037 0 : nRet = GetSelectedRecordId();
2038 : else
2039 : {
2040 0 : SwDBData aData;
2041 0 : aData.sDataSource = rDataSource;
2042 0 : aData.sCommand = rTableOrQuery;
2043 0 : aData.nCommandType = nCommandType;
2044 0 : SwDSParam* pFound = FindDSData(aData, sal_False);
2045 0 : if(pFound && pFound->xResultSet.is())
2046 : {
2047 : try
2048 : { //if a selection array is set the current row at the result set may not be set yet
2049 0 : if(pFound->aSelection.getLength())
2050 : {
2051 0 : sal_Int32 nSelIndex = pFound->nSelectionIndex;
2052 0 : if(nSelIndex >= pFound->aSelection.getLength())
2053 0 : nSelIndex = pFound->aSelection.getLength() -1;
2054 0 : pFound->aSelection.getConstArray()[nSelIndex] >>= nRet;
2055 :
2056 : }
2057 : else
2058 0 : nRet = pFound->xResultSet->getRow();
2059 : }
2060 0 : catch(const Exception&)
2061 : {
2062 : }
2063 0 : }
2064 : }
2065 0 : return nRet;
2066 : }
2067 :
2068 : // close all data sources - after fields were updated
2069 0 : void SwNewDBMgr::CloseAll(sal_Bool bIncludingMerge)
2070 : {
2071 : //the only thing done here is to reset the selection index
2072 : //all connections stay open
2073 0 : for(sal_uInt16 nPos = 0; nPos < aDataSourceParams.size(); nPos++)
2074 : {
2075 0 : SwDSParam* pParam = &aDataSourceParams[nPos];
2076 0 : if(bIncludingMerge || pParam != pImpl->pMergeData)
2077 : {
2078 0 : pParam->nSelectionIndex = 0;
2079 0 : pParam->bAfterSelection = sal_False;
2080 0 : pParam->bEndOfDB = sal_False;
2081 : try
2082 : {
2083 0 : if(!bInMerge && pParam->xResultSet.is())
2084 0 : pParam->xResultSet->first();
2085 : }
2086 0 : catch(const Exception&)
2087 : {}
2088 : }
2089 : }
2090 0 : }
2091 :
2092 0 : SwDSParam* SwNewDBMgr::FindDSData(const SwDBData& rData, sal_Bool bCreate)
2093 : {
2094 : //prefer merge data if available
2095 0 : if(pImpl->pMergeData && rData.sDataSource == pImpl->pMergeData->sDataSource &&
2096 0 : rData.sCommand == pImpl->pMergeData->sCommand &&
2097 0 : (rData.nCommandType == -1 || rData.nCommandType == pImpl->pMergeData->nCommandType ||
2098 0 : (bCreate && pImpl->pMergeData->nCommandType == -1)))
2099 : {
2100 0 : return pImpl->pMergeData;
2101 : }
2102 :
2103 0 : SwDSParam* pFound = 0;
2104 0 : for(sal_uInt16 nPos = aDataSourceParams.size(); nPos; nPos--)
2105 : {
2106 0 : SwDSParam* pParam = &aDataSourceParams[nPos - 1];
2107 0 : if(rData.sDataSource == pParam->sDataSource &&
2108 0 : rData.sCommand == pParam->sCommand &&
2109 0 : (rData.nCommandType == -1 || rData.nCommandType == pParam->nCommandType ||
2110 0 : (bCreate && pParam->nCommandType == -1)))
2111 : {
2112 : // calls from the calculator may add a connection with an invalid commandtype
2113 : //later added "real" data base connections have to re-use the already available
2114 : //DSData and set the correct CommandType
2115 0 : if(bCreate && pParam->nCommandType == -1)
2116 0 : pParam->nCommandType = rData.nCommandType;
2117 0 : pFound = pParam;
2118 0 : break;
2119 : }
2120 : }
2121 0 : if(bCreate)
2122 : {
2123 0 : if(!pFound)
2124 : {
2125 0 : pFound = new SwDSParam(rData);
2126 0 : aDataSourceParams.push_back(pFound);
2127 : try
2128 : {
2129 0 : uno::Reference<XComponent> xComponent(pFound->xConnection, UNO_QUERY);
2130 0 : if(xComponent.is())
2131 0 : xComponent->addEventListener(pImpl->xDisposeListener);
2132 : }
2133 0 : catch(const Exception&)
2134 : {
2135 : }
2136 : }
2137 : }
2138 0 : return pFound;
2139 : }
2140 :
2141 0 : SwDSParam* SwNewDBMgr::FindDSConnection(const OUString& rDataSource, sal_Bool bCreate)
2142 : {
2143 : //prefer merge data if available
2144 0 : if(pImpl->pMergeData && rDataSource == pImpl->pMergeData->sDataSource )
2145 : {
2146 0 : return pImpl->pMergeData;
2147 : }
2148 0 : SwDSParam* pFound = 0;
2149 0 : for(sal_uInt16 nPos = 0; nPos < aDataSourceParams.size(); nPos++)
2150 : {
2151 0 : SwDSParam* pParam = &aDataSourceParams[nPos];
2152 0 : if(rDataSource == pParam->sDataSource)
2153 : {
2154 0 : pFound = pParam;
2155 0 : break;
2156 : }
2157 : }
2158 0 : if(bCreate && !pFound)
2159 : {
2160 0 : SwDBData aData;
2161 0 : aData.sDataSource = rDataSource;
2162 0 : pFound = new SwDSParam(aData);
2163 0 : aDataSourceParams.push_back(pFound);
2164 : try
2165 : {
2166 0 : uno::Reference<XComponent> xComponent(pFound->xConnection, UNO_QUERY);
2167 0 : if(xComponent.is())
2168 0 : xComponent->addEventListener(pImpl->xDisposeListener);
2169 : }
2170 0 : catch(const Exception&)
2171 : {
2172 0 : }
2173 : }
2174 0 : return pFound;
2175 : }
2176 :
2177 0 : const SwDBData& SwNewDBMgr::GetAddressDBName()
2178 : {
2179 0 : return SW_MOD()->GetDBConfig()->GetAddressSource();
2180 : }
2181 :
2182 0 : Sequence<OUString> SwNewDBMgr::GetExistingDatabaseNames()
2183 : {
2184 0 : Reference<XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
2185 0 : Reference<XDatabaseContext> xDBContext = DatabaseContext::create(xContext);
2186 0 : return xDBContext->getElementNames();
2187 : }
2188 :
2189 0 : OUString SwNewDBMgr::LoadAndRegisterDataSource()
2190 : {
2191 0 : sfx2::FileDialogHelper aDlgHelper( TemplateDescription::FILEOPEN_SIMPLE, 0 );
2192 0 : Reference < XFilePicker > xFP = aDlgHelper.GetFilePicker();
2193 :
2194 0 : OUString sHomePath(SvtPathOptions().GetWorkPath());
2195 0 : aDlgHelper.SetDisplayDirectory( sHomePath );
2196 :
2197 0 : Reference<XFilterManager> xFltMgr(xFP, UNO_QUERY);
2198 :
2199 0 : OUString sFilterAll(SW_RES(STR_FILTER_ALL));
2200 0 : OUString sFilterAllData(SW_RES(STR_FILTER_ALL_DATA));
2201 0 : OUString sFilterSXB(SW_RES(STR_FILTER_SXB));
2202 0 : OUString sFilterSXC(SW_RES(STR_FILTER_SXC));
2203 0 : OUString sFilterDBF(SW_RES(STR_FILTER_DBF));
2204 0 : OUString sFilterXLS(SW_RES(STR_FILTER_XLS));
2205 0 : OUString sFilterTXT(SW_RES(STR_FILTER_TXT));
2206 0 : OUString sFilterCSV(SW_RES(STR_FILTER_CSV));
2207 : #ifdef WNT
2208 : OUString sFilterMDB(SW_RES(STR_FILTER_MDB));
2209 : OUString sFilterACCDB(SW_RES(STR_FILTER_ACCDB));
2210 : #endif
2211 0 : xFltMgr->appendFilter( sFilterAll, "*" );
2212 0 : xFltMgr->appendFilter( sFilterAllData, "*.ods;*.sxc;*.dbf;*.xls;*.txt;*.csv");
2213 :
2214 0 : xFltMgr->appendFilter( sFilterSXB, "*.odb" );
2215 0 : xFltMgr->appendFilter( sFilterSXC, "*.ods;*.sxc" );
2216 0 : xFltMgr->appendFilter( sFilterDBF, "*.dbf" );
2217 0 : xFltMgr->appendFilter( sFilterXLS, "*.xls" );
2218 0 : xFltMgr->appendFilter( sFilterTXT, "*.txt" );
2219 0 : xFltMgr->appendFilter( sFilterCSV, "*.csv" );
2220 : #ifdef WNT
2221 : xFltMgr->appendFilter( sFilterMDB, "*.mdb" );
2222 : xFltMgr->appendFilter( sFilterACCDB, "*.accdb" );
2223 : #endif
2224 :
2225 0 : xFltMgr->setCurrentFilter( sFilterAll ) ;
2226 0 : OUString sFind;
2227 0 : bool bTextConnection = false;
2228 0 : if( ERRCODE_NONE == aDlgHelper.Execute() )
2229 : {
2230 0 : OUString sURL = xFP->getFiles().getConstArray()[0];
2231 : //data sources have to be registered depending on their extensions
2232 0 : INetURLObject aURL( sURL );
2233 0 : OUString sExt( aURL.GetExtension() );
2234 0 : Any aURLAny;
2235 0 : Any aTableFilterAny;
2236 0 : Any aSuppressVersionsAny;
2237 0 : Any aInfoAny;
2238 0 : INetURLObject aTempURL(aURL);
2239 0 : bool bStore = true;
2240 0 : if(sExt == "odb")
2241 : {
2242 0 : bStore = false;
2243 : }
2244 0 : else if(sExt.equalsIgnoreAsciiCase("sxc")
2245 0 : || sExt.equalsIgnoreAsciiCase("ods")
2246 0 : || sExt.equalsIgnoreAsciiCase("xls"))
2247 : {
2248 0 : OUString sDBURL("sdbc:calc:");
2249 0 : sDBURL += aTempURL.GetMainURL(INetURLObject::NO_DECODE);
2250 0 : aURLAny <<= sDBURL;
2251 : }
2252 0 : else if(sExt.equalsIgnoreAsciiCase("dbf"))
2253 : {
2254 0 : aTempURL.removeSegment();
2255 0 : aTempURL.removeFinalSlash();
2256 0 : OUString sDBURL("sdbc:dbase:");
2257 0 : sDBURL += aTempURL.GetMainURL(INetURLObject::NO_DECODE);
2258 0 : aURLAny <<= sDBURL;
2259 : //set the filter to the file name without extension
2260 0 : Sequence<OUString> aFilters(1);
2261 0 : aFilters[0] = aURL.getBase();
2262 0 : aTableFilterAny <<= aFilters;
2263 : }
2264 0 : else if(sExt.equalsIgnoreAsciiCase("csv") || sExt.equalsIgnoreAsciiCase("txt"))
2265 : {
2266 0 : aTempURL.removeSegment();
2267 0 : aTempURL.removeFinalSlash();
2268 0 : OUString sDBURL("sdbc:flat:");
2269 : //only the 'path' has to be added
2270 0 : sDBURL += aTempURL.GetMainURL(INetURLObject::NO_DECODE);
2271 0 : aURLAny <<= sDBURL;
2272 :
2273 0 : bTextConnection = true;
2274 : //set the filter to the file name without extension
2275 0 : Sequence<OUString> aFilters(1);
2276 0 : aFilters[0] = aURL.getBase();
2277 0 : aTableFilterAny <<= aFilters;
2278 : }
2279 : #ifdef WNT
2280 : else if(sExt.equalsIgnoreAsciiCase("mdb"))
2281 : {
2282 : OUString sDBURL("sdbc:ado:access:PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=");
2283 : sDBURL += aTempURL.PathToFileName();
2284 : aURLAny <<= sDBURL;
2285 : aSuppressVersionsAny <<= makeAny(true);
2286 : }
2287 : else if(sExt.equalsIgnoreAsciiCase("accdb"))
2288 : {
2289 : OUString sDBURL("sdbc:ado:PROVIDER=Microsoft.ACE.OLEDB.12.0;DATA SOURCE=");
2290 : sDBURL += aTempURL.PathToFileName();
2291 : aURLAny <<= sDBURL;
2292 : aSuppressVersionsAny <<= makeAny(true);
2293 : }
2294 : #endif
2295 : try
2296 : {
2297 0 : Reference<XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
2298 0 : Reference<XDatabaseContext> xDBContext = DatabaseContext::create(xContext);
2299 :
2300 : OUString sNewName = INetURLObject::decode( aURL.getName(),
2301 : '%',
2302 : INetURLObject::DECODE_UNAMBIGUOUS,
2303 0 : RTL_TEXTENCODING_UTF8 );
2304 0 : sal_Int32 nExtLen = aURL.GetExtension().getLength();
2305 0 : sNewName = sNewName.replaceAt( sNewName.getLength() - nExtLen - 1, nExtLen + 1, "" );
2306 :
2307 : //find a unique name if sNewName already exists
2308 0 : sFind = sNewName;
2309 0 : sal_Int32 nIndex = 0;
2310 0 : while(xDBContext->hasByName(sFind))
2311 : {
2312 0 : sFind = sNewName;
2313 0 : sFind += OUString::number(++nIndex);
2314 : }
2315 :
2316 0 : Reference<XInterface> xNewInstance;
2317 0 : if(!bStore)
2318 : {
2319 : //odb-file
2320 0 : Any aDataSource = xDBContext->getByName(aTempURL.GetMainURL(INetURLObject::NO_DECODE));
2321 0 : aDataSource >>= xNewInstance;
2322 : }
2323 : else
2324 : {
2325 0 : xNewInstance = xDBContext->createInstance();
2326 0 : Reference<XPropertySet> xDataProperties(xNewInstance, UNO_QUERY);
2327 :
2328 0 : if(aURLAny.hasValue())
2329 0 : xDataProperties->setPropertyValue("URL", aURLAny);
2330 0 : if(aTableFilterAny.hasValue())
2331 0 : xDataProperties->setPropertyValue("TableFilter", aTableFilterAny);
2332 0 : if(aSuppressVersionsAny.hasValue())
2333 0 : xDataProperties->setPropertyValue("SuppressVersionColumns", aSuppressVersionsAny);
2334 0 : if(aInfoAny.hasValue())
2335 0 : xDataProperties->setPropertyValue("Info", aInfoAny);
2336 :
2337 0 : if( bTextConnection )
2338 : {
2339 0 : uno::Reference < sdb::XTextConnectionSettings > xSettingsDlg = sdb::TextConnectionSettings::create(xContext);
2340 0 : if( xSettingsDlg->execute() )
2341 : {
2342 0 : uno::Any aSettings = xDataProperties->getPropertyValue( "Settings" );
2343 0 : uno::Reference < beans::XPropertySet > xDSSettings;
2344 0 : aSettings >>= xDSSettings;
2345 : ::comphelper::copyProperties(
2346 : uno::Reference < beans::XPropertySet >( xSettingsDlg, uno::UNO_QUERY_THROW ),
2347 0 : xDSSettings );
2348 0 : xDSSettings->setPropertyValue( "Extension", uno::makeAny( sExt ));
2349 0 : }
2350 : }
2351 :
2352 0 : Reference<XDocumentDataSource> xDS(xNewInstance, UNO_QUERY_THROW);
2353 0 : Reference<XStorable> xStore(xDS->getDatabaseDocument(), UNO_QUERY_THROW);
2354 0 : OUString sOutputExt = ".odb";
2355 0 : OUString sTmpName;
2356 : {
2357 0 : utl::TempFile aTempFile(sNewName , &sOutputExt, &sHomePath);
2358 0 : aTempFile.EnableKillingFile(true);
2359 0 : sTmpName = aTempFile.GetURL();
2360 : }
2361 0 : xStore->storeAsURL(sTmpName, Sequence< PropertyValue >());
2362 : }
2363 0 : xDBContext->registerObject( sFind, xNewInstance );
2364 :
2365 : }
2366 0 : catch(const Exception&)
2367 : {
2368 0 : }
2369 : }
2370 0 : return sFind;
2371 :
2372 : }
2373 :
2374 0 : void SwNewDBMgr::ExecuteFormLetter( SwWrtShell& rSh,
2375 : const Sequence<PropertyValue>& rProperties,
2376 : sal_Bool bWithDataSourceBrowser)
2377 : {
2378 : //prevent second call
2379 0 : if(pImpl->pMergeDialog)
2380 0 : return ;
2381 0 : OUString sDataSource, sDataTableOrQuery;
2382 0 : Sequence<Any> aSelection;
2383 :
2384 0 : sal_Int32 nCmdType = CommandType::TABLE;
2385 0 : uno::Reference< XConnection> xConnection;
2386 :
2387 0 : ODataAccessDescriptor aDescriptor(rProperties);
2388 0 : sDataSource = aDescriptor.getDataSource();
2389 0 : OSL_VERIFY(aDescriptor[daCommand] >>= sDataTableOrQuery);
2390 0 : OSL_VERIFY(aDescriptor[daCommandType] >>= nCmdType);
2391 :
2392 0 : if ( aDescriptor.has(daSelection) )
2393 0 : aDescriptor[daSelection] >>= aSelection;
2394 0 : if ( aDescriptor.has(daConnection) )
2395 0 : aDescriptor[daConnection] >>= xConnection;
2396 :
2397 0 : if(sDataSource.isEmpty() || sDataTableOrQuery.isEmpty())
2398 : {
2399 : OSL_FAIL("PropertyValues missing or unset");
2400 0 : return;
2401 : }
2402 :
2403 : //always create a connection for the dialog and dispose it after the dialog has been closed
2404 0 : SwDSParam* pFound = 0;
2405 0 : if(!xConnection.is())
2406 : {
2407 0 : xConnection = SwNewDBMgr::RegisterConnection(sDataSource);
2408 0 : pFound = FindDSConnection(sDataSource, sal_True);
2409 : }
2410 0 : SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
2411 : OSL_ENSURE(pFact, "Dialogdiet fail!");
2412 : pImpl->pMergeDialog = pFact->CreateMailMergeDlg( DLG_MAILMERGE,
2413 0 : &rSh.GetView().GetViewFrame()->GetWindow(), rSh,
2414 : sDataSource,
2415 : sDataTableOrQuery,
2416 : nCmdType,
2417 : xConnection,
2418 0 : bWithDataSourceBrowser ? 0 : &aSelection);
2419 : OSL_ENSURE(pImpl->pMergeDialog, "Dialogdiet fail!");
2420 0 : if(pImpl->pMergeDialog->Execute() == RET_OK)
2421 : {
2422 0 : aDescriptor[daSelection] <<= pImpl->pMergeDialog->GetSelection();
2423 :
2424 0 : uno::Reference<XResultSet> xResSet = pImpl->pMergeDialog->GetResultSet();
2425 0 : if(xResSet.is())
2426 0 : aDescriptor[daCursor] <<= xResSet;
2427 :
2428 : // SfxObjectShellRef is ok, since there should be no control over the document lifetime here
2429 0 : SfxObjectShellRef xDocShell = rSh.GetView().GetViewFrame()->GetObjectShell();
2430 0 : SFX_APP()->NotifyEvent(SfxEventHint(SW_EVENT_MAIL_MERGE, SwDocShell::GetEventName(STR_SW_EVENT_MAIL_MERGE), xDocShell));
2431 : {
2432 : //copy rSh to aTempFile
2433 0 : OUString sTempURL;
2434 : const SfxFilter *pSfxFlt = SwIoSystem::GetFilterOfFormat(
2435 : OUString(FILTER_XML),
2436 0 : SwDocShell::Factory().GetFilterContainer() );
2437 : try
2438 : {
2439 :
2440 0 : uno::Sequence< beans::PropertyValue > aValues(1);
2441 0 : beans::PropertyValue* pValues = aValues.getArray();
2442 0 : pValues[0].Name = "FilterName";
2443 0 : pValues[0].Value <<= OUString(pSfxFlt->GetFilterName());
2444 0 : uno::Reference< XStorable > xStore( xDocShell->GetModel(), uno::UNO_QUERY);
2445 0 : sTempURL = URIHelper::SmartRel2Abs( INetURLObject(), utl::TempFile::CreateTempName() );
2446 0 : xStore->storeToURL( sTempURL, aValues );
2447 : }
2448 0 : catch(const uno::Exception&)
2449 : {
2450 : }
2451 0 : if( xDocShell->GetError() )
2452 : {
2453 : // error message ??
2454 0 : ErrorHandler::HandleError( xDocShell->GetError() );
2455 : }
2456 : else
2457 : {
2458 : // the shell will be explicitly closed, but it is more safe to use SfxObjectShellLock here
2459 : // especially for the case that the loading has failed
2460 0 : SfxObjectShellLock xWorkDocSh( new SwDocShell( SFX_CREATE_MODE_INTERNAL ));
2461 0 : SfxMedium* pWorkMed = new SfxMedium( sTempURL, STREAM_STD_READ );
2462 0 : pWorkMed->SetFilter( pSfxFlt );
2463 0 : if( xWorkDocSh->DoLoad(pWorkMed) )
2464 : {
2465 0 : SfxViewFrame *pFrame = SfxViewFrame::LoadHiddenDocument( *xWorkDocSh, 0 );
2466 0 : SwView *pView = (SwView*) pFrame->GetViewShell();
2467 0 : pView->AttrChangedNotify( &pView->GetWrtShell() );// in order for SelectShell to be called
2468 : //set the current DBMgr
2469 0 : SwDoc* pWorkDoc = pView->GetWrtShell().GetDoc();
2470 0 : SwNewDBMgr* pWorkDBMgr = pWorkDoc->GetNewDBMgr();
2471 0 : pWorkDoc->SetNewDBMgr( this );
2472 :
2473 0 : SwMergeDescriptor aMergeDesc( pImpl->pMergeDialog->GetMergeType(), pView->GetWrtShell(), aDescriptor );
2474 0 : aMergeDesc.sSaveToFilter = pImpl->pMergeDialog->GetSaveFilter();
2475 0 : aMergeDesc.bCreateSingleFile = !pImpl->pMergeDialog->IsSaveIndividualDocs();
2476 0 : if( !aMergeDesc.bCreateSingleFile && pImpl->pMergeDialog->IsGenerateFromDataBase() )
2477 : {
2478 0 : aMergeDesc.sAddressFromColumn = pImpl->pMergeDialog->GetColumnName();
2479 0 : aMergeDesc.sSubject = pImpl->pMergeDialog->GetPath();
2480 : }
2481 :
2482 0 : MergeNew(aMergeDesc);
2483 :
2484 0 : pWorkDoc->SetNewDBMgr( pWorkDBMgr );
2485 : //close the temporary file
2486 0 : uno::Reference< util::XCloseable > xClose( xWorkDocSh->GetModel(), uno::UNO_QUERY );
2487 0 : if (xClose.is())
2488 : {
2489 : try
2490 : {
2491 : //! 'sal_True' -> transfer ownership to vetoing object if vetoed!
2492 : //! I.e. now that object is responsible for closing the model and doc shell.
2493 0 : xClose->close( sal_True );
2494 : }
2495 0 : catch (const uno::Exception&)
2496 : {
2497 : }
2498 0 : }
2499 0 : }
2500 : }
2501 : //remove the temporary file
2502 0 : SWUnoHelper::UCB_DeleteFile( sTempURL );
2503 : }
2504 0 : SFX_APP()->NotifyEvent(SfxEventHint(SW_EVENT_MAIL_MERGE_END, SwDocShell::GetEventName(STR_SW_EVENT_MAIL_MERGE_END), rSh.GetView().GetViewFrame()->GetObjectShell()));
2505 :
2506 : // reset the cursor inside
2507 0 : xResSet = NULL;
2508 0 : aDescriptor[daCursor] <<= xResSet;
2509 : }
2510 0 : if(pFound)
2511 : {
2512 0 : for(sal_uInt16 nPos = 0; nPos < aDataSourceParams.size(); nPos++)
2513 : {
2514 0 : SwDSParam* pParam = &aDataSourceParams[nPos];
2515 0 : if(pParam == pFound)
2516 : {
2517 : try
2518 : {
2519 0 : uno::Reference<XComponent> xComp(pParam->xConnection, UNO_QUERY);
2520 0 : if(xComp.is())
2521 0 : xComp->dispose();
2522 : }
2523 0 : catch(const RuntimeException&)
2524 : {
2525 : //may be disposed already since multiple entries may have used the same connection
2526 : }
2527 0 : break;
2528 : }
2529 : //pFound doesn't need to be removed/deleted -
2530 : //this has been done by the SwConnectionDisposedListener_Impl already
2531 : }
2532 : }
2533 0 : DELETEZ(pImpl->pMergeDialog);
2534 : }
2535 :
2536 0 : void SwNewDBMgr::InsertText(SwWrtShell& rSh,
2537 : const Sequence< PropertyValue>& rProperties)
2538 : {
2539 0 : OUString sDataSource, sDataTableOrQuery;
2540 0 : uno::Reference<XResultSet> xResSet;
2541 0 : Sequence<Any> aSelection;
2542 0 : sal_Int16 nCmdType = CommandType::TABLE;
2543 0 : const PropertyValue* pValues = rProperties.getConstArray();
2544 0 : uno::Reference< XConnection> xConnection;
2545 0 : for(sal_Int32 nPos = 0; nPos < rProperties.getLength(); nPos++)
2546 : {
2547 0 : if ( pValues[nPos].Name == cDataSourceName )
2548 0 : pValues[nPos].Value >>= sDataSource;
2549 0 : else if ( pValues[nPos].Name == cCommand )
2550 0 : pValues[nPos].Value >>= sDataTableOrQuery;
2551 0 : else if ( pValues[nPos].Name == cCursor )
2552 0 : pValues[nPos].Value >>= xResSet;
2553 0 : else if ( pValues[nPos].Name == cSelection )
2554 0 : pValues[nPos].Value >>= aSelection;
2555 0 : else if ( pValues[nPos].Name == cCommandType )
2556 0 : pValues[nPos].Value >>= nCmdType;
2557 0 : else if ( pValues[nPos].Name == cActiveConnection )
2558 0 : pValues[nPos].Value >>= xConnection;
2559 : }
2560 0 : if(sDataSource.isEmpty() || sDataTableOrQuery.isEmpty() || !xResSet.is())
2561 : {
2562 : OSL_FAIL("PropertyValues missing or unset");
2563 0 : return;
2564 : }
2565 0 : Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
2566 0 : uno::Reference<XDataSource> xSource;
2567 0 : uno::Reference<XChild> xChild(xConnection, UNO_QUERY);
2568 0 : if(xChild.is())
2569 0 : xSource = uno::Reference<XDataSource>(xChild->getParent(), UNO_QUERY);
2570 0 : if(!xSource.is())
2571 0 : xSource = SwNewDBMgr::GetDbtoolsClient().getDataSource(sDataSource, xContext);
2572 0 : uno::Reference< XColumnsSupplier > xColSupp( xResSet, UNO_QUERY );
2573 0 : SwDBData aDBData;
2574 0 : aDBData.sDataSource = sDataSource;
2575 0 : aDBData.sCommand = sDataTableOrQuery;
2576 0 : aDBData.nCommandType = nCmdType;
2577 :
2578 0 : SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
2579 : OSL_ENSURE(pFact, "SwAbstractDialogFactory fail!");
2580 :
2581 0 : boost::scoped_ptr<AbstractSwInsertDBColAutoPilot> pDlg(pFact->CreateSwInsertDBColAutoPilot( rSh.GetView(),
2582 : xSource,
2583 : xColSupp,
2584 0 : aDBData ));
2585 : OSL_ENSURE(pDlg, "Dialogdiet fail!");
2586 0 : if( RET_OK == pDlg->Execute() )
2587 : {
2588 0 : OUString sDummy;
2589 0 : if(!xConnection.is())
2590 0 : xConnection = xSource->getConnection(sDummy, sDummy);
2591 : try
2592 : {
2593 0 : pDlg->DataToDoc( aSelection , xSource, xConnection, xResSet);
2594 : }
2595 0 : catch(const Exception&)
2596 : {
2597 : OSL_FAIL("exception caught");
2598 0 : }
2599 0 : }
2600 : }
2601 :
2602 : SwDbtoolsClient* SwNewDBMgr::pDbtoolsClient = NULL;
2603 :
2604 0 : SwDbtoolsClient& SwNewDBMgr::GetDbtoolsClient()
2605 : {
2606 0 : if ( !pDbtoolsClient )
2607 0 : pDbtoolsClient = new SwDbtoolsClient;
2608 0 : return *pDbtoolsClient;
2609 : }
2610 :
2611 0 : void SwNewDBMgr::RemoveDbtoolsClient()
2612 : {
2613 0 : delete pDbtoolsClient;
2614 0 : pDbtoolsClient = 0;
2615 0 : }
2616 :
2617 0 : uno::Reference<XDataSource> SwNewDBMgr::getDataSourceAsParent(const uno::Reference< XConnection>& _xConnection,const OUString& _sDataSourceName)
2618 : {
2619 0 : uno::Reference<XDataSource> xSource;
2620 : try
2621 : {
2622 0 : uno::Reference<XChild> xChild(_xConnection, UNO_QUERY);
2623 0 : if ( xChild.is() )
2624 0 : xSource = uno::Reference<XDataSource>(xChild->getParent(), UNO_QUERY);
2625 0 : if ( !xSource.is() )
2626 0 : xSource = SwNewDBMgr::GetDbtoolsClient().getDataSource(_sDataSourceName, ::comphelper::getProcessComponentContext());
2627 : }
2628 0 : catch(const Exception&)
2629 : {
2630 : OSL_FAIL("exception in getDataSourceAsParent caught");
2631 : }
2632 0 : return xSource;
2633 : }
2634 :
2635 0 : uno::Reference<XResultSet> SwNewDBMgr::createCursor(const OUString& _sDataSourceName,
2636 : const OUString& _sCommand,
2637 : sal_Int32 _nCommandType,
2638 : const uno::Reference<XConnection>& _xConnection
2639 : )
2640 : {
2641 0 : uno::Reference<XResultSet> xResultSet;
2642 : try
2643 : {
2644 0 : uno::Reference< XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() );
2645 0 : if( xMgr.is() )
2646 : {
2647 0 : uno::Reference<XInterface> xInstance = xMgr->createInstance("com.sun.star.sdb.RowSet");
2648 0 : uno::Reference<XPropertySet> xRowSetPropSet(xInstance, UNO_QUERY);
2649 0 : if(xRowSetPropSet.is())
2650 : {
2651 0 : xRowSetPropSet->setPropertyValue("DataSourceName", makeAny(_sDataSourceName));
2652 0 : xRowSetPropSet->setPropertyValue("ActiveConnection", makeAny(_xConnection));
2653 0 : xRowSetPropSet->setPropertyValue("Command", makeAny(_sCommand));
2654 0 : xRowSetPropSet->setPropertyValue("CommandType", makeAny(_nCommandType));
2655 :
2656 0 : uno::Reference< XCompletedExecution > xRowSet(xInstance, UNO_QUERY);
2657 :
2658 0 : if ( xRowSet.is() )
2659 : {
2660 0 : uno::Reference< XInteractionHandler > xHandler( InteractionHandler::createWithParent(comphelper::getComponentContext(xMgr), 0), UNO_QUERY_THROW );
2661 0 : xRowSet->executeWithCompletion(xHandler);
2662 : }
2663 0 : xResultSet = uno::Reference<XResultSet>(xRowSet, UNO_QUERY);
2664 0 : }
2665 0 : }
2666 : }
2667 0 : catch(const Exception&)
2668 : {
2669 : OSL_FAIL("Caught exception while creating a new RowSet!");
2670 : }
2671 0 : return xResultSet;
2672 : }
2673 :
2674 : // merge all data into one resulting document and return the number of merged documents
2675 0 : sal_Int32 SwNewDBMgr::MergeDocuments( SwMailMergeConfigItem& rMMConfig,
2676 : SwView& rSourceView )
2677 : {
2678 : // check the availability of all data in the config item
2679 0 : uno::Reference< XResultSet> xResultSet = rMMConfig.GetResultSet();
2680 0 : if(!xResultSet.is())
2681 0 : return 0;
2682 0 : bInMerge = sal_True;
2683 0 : sal_Int32 nRet = 0;
2684 : pImpl->pMergeData = new SwDSParam(
2685 0 : rMMConfig.GetCurrentDBData(), xResultSet, rMMConfig.GetSelection());
2686 :
2687 : try{
2688 : //set to start position
2689 0 : if(pImpl->pMergeData->aSelection.getLength())
2690 : {
2691 0 : sal_Int32 nPos = 0;
2692 0 : pImpl->pMergeData->aSelection.getConstArray()[ pImpl->pMergeData->nSelectionIndex++ ] >>= nPos;
2693 0 : pImpl->pMergeData->bEndOfDB = !pImpl->pMergeData->xResultSet->absolute( nPos );
2694 0 : pImpl->pMergeData->CheckEndOfDB();
2695 0 : if(pImpl->pMergeData->nSelectionIndex >= pImpl->pMergeData->aSelection.getLength())
2696 0 : pImpl->pMergeData->bEndOfDB = sal_True;
2697 : }
2698 : else
2699 : {
2700 0 : pImpl->pMergeData->bEndOfDB = !pImpl->pMergeData->xResultSet->first();
2701 0 : pImpl->pMergeData->CheckEndOfDB();
2702 : }
2703 : }
2704 0 : catch(const Exception&)
2705 : {
2706 0 : pImpl->pMergeData->bEndOfDB = sal_True;
2707 0 : pImpl->pMergeData->CheckEndOfDB();
2708 : OSL_FAIL("exception in MergeNew()");
2709 : }
2710 :
2711 : //bCancel is set from the PrintMonitor
2712 0 : bCancel = sal_False;
2713 :
2714 0 : CreateMonitor aMonitorDlg(&rSourceView.GetEditWin());
2715 0 : aMonitorDlg.SetCancelHdl(LINK(this, SwNewDBMgr, PrtCancelHdl));
2716 0 : if (!IsMergeSilent())
2717 : {
2718 0 : aMonitorDlg.Show();
2719 0 : aMonitorDlg.Invalidate();
2720 0 : aMonitorDlg.Update();
2721 : // the print monitor needs some time to act
2722 0 : for( sal_uInt16 i = 0; i < 25; i++)
2723 0 : Application::Reschedule();
2724 : }
2725 :
2726 0 : SwWrtShell& rSourceShell = rSourceView.GetWrtShell();
2727 0 : bool bSynchronizedDoc = rSourceShell.IsLabelDoc() && rSourceShell.GetSectionFmtCount() > 1;
2728 : //save the settings of the first
2729 0 : rSourceShell.SttEndDoc(sal_True);
2730 0 : sal_uInt16 nStartingPageNo = rSourceShell.GetVirtPageNum();
2731 0 : OUString sModifiedStartingPageDesc;
2732 0 : OUString sStartingPageDesc = sModifiedStartingPageDesc = rSourceShell.GetPageDesc(
2733 0 : rSourceShell.GetCurPageDesc()).GetName();
2734 :
2735 : try
2736 : {
2737 : // create a target docshell to put the merged document into
2738 0 : SfxObjectShellRef xTargetDocShell( new SwDocShell( SFX_CREATE_MODE_STANDARD ) );
2739 0 : xTargetDocShell->DoInitNew( 0 );
2740 0 : SfxViewFrame* pTargetFrame = SfxViewFrame::LoadHiddenDocument( *xTargetDocShell, 0 );
2741 :
2742 : //the created window has to be located at the same position as the source window
2743 0 : Window& rTargetWindow = pTargetFrame->GetFrame().GetWindow();
2744 0 : Window& rSourceWindow = rSourceView.GetViewFrame()->GetFrame().GetWindow();
2745 0 : rTargetWindow.SetPosPixel(rSourceWindow.GetPosPixel());
2746 :
2747 0 : SwView* pTargetView = static_cast<SwView*>( pTargetFrame->GetViewShell() );
2748 0 : rMMConfig.SetTargetView(pTargetView);
2749 : //initiate SelectShell() to create sub shells
2750 0 : pTargetView->AttrChangedNotify( &pTargetView->GetWrtShell() );
2751 0 : SwWrtShell* pTargetShell = pTargetView->GetWrtShellPtr();
2752 : // #i63806#
2753 0 : const SwPageDesc* pSourcePageDesc = rSourceShell.FindPageDescByName( sStartingPageDesc );
2754 0 : const SwFrmFmt& rMaster = pSourcePageDesc->GetMaster();
2755 0 : bool bPageStylesWithHeaderFooter = rMaster.GetHeader().IsActive() ||
2756 0 : rMaster.GetFooter().IsActive();
2757 :
2758 : // copy compatibility options
2759 0 : lcl_CopyCompatibilityOptions( rSourceShell, *pTargetShell);
2760 : // #72821# copy dynamic defaults
2761 0 : lcl_CopyDynamicDefaults( *rSourceShell.GetDoc(), *pTargetShell->GetDoc() );
2762 :
2763 : long nStartRow, nEndRow;
2764 0 : sal_uLong nDocNo = 1;
2765 0 : sal_Int32 nDocCount = 0;
2766 0 : if( !IsMergeSilent() && lcl_getCountFromResultSet( nDocCount, pImpl->pMergeData->xResultSet ) )
2767 0 : aMonitorDlg.SetTotalCount( nDocCount );
2768 :
2769 0 : do
2770 : {
2771 0 : nStartRow = pImpl->pMergeData->xResultSet->getRow();
2772 0 : if (!IsMergeSilent())
2773 : {
2774 0 : aMonitorDlg.SetCurrentPosition( nDocNo );
2775 0 : aMonitorDlg.Invalidate();
2776 0 : aMonitorDlg.Update();
2777 : // the print monitor needs some time to act
2778 0 : for( sal_uInt16 i = 0; i < 25; i++)
2779 0 : Application::Reschedule();
2780 : }
2781 :
2782 : // copy the source document
2783 : // the copy will be closed later, but it is more safe to use SfxObjectShellLock here
2784 0 : SfxObjectShellLock xWorkDocSh;
2785 0 : if(nDocNo == 1 )
2786 : {
2787 0 : uno::Reference< util::XCloneable > xClone( rSourceView.GetDocShell()->GetModel(), uno::UNO_QUERY);
2788 0 : uno::Reference< lang::XUnoTunnel > xWorkDocShell( xClone->createClone(), uno::UNO_QUERY);
2789 0 : SwXTextDocument* pWorkModel = reinterpret_cast<SwXTextDocument*>(xWorkDocShell->getSomething(SwXTextDocument::getUnoTunnelId()));
2790 0 : xWorkDocSh = pWorkModel->GetDocShell();
2791 : }
2792 : else
2793 : {
2794 0 : xWorkDocSh = rSourceView.GetDocShell()->GetDoc()->CreateCopy(true);
2795 : }
2796 : //create a ViewFrame
2797 0 : SwView* pWorkView = static_cast< SwView* >( SfxViewFrame::LoadHiddenDocument( *xWorkDocSh, 0 )->GetViewShell() );
2798 0 : SwWrtShell& rWorkShell = pWorkView->GetWrtShell();
2799 0 : pWorkView->AttrChangedNotify( &rWorkShell );// in order for SelectShell to be called
2800 :
2801 : // merge the data
2802 0 : SwDoc* pWorkDoc = rWorkShell.GetDoc();
2803 0 : SwNewDBMgr* pWorkDBMgr = pWorkDoc->GetNewDBMgr();
2804 0 : pWorkDoc->SetNewDBMgr( this );
2805 0 : pWorkDoc->EmbedAllLinks();
2806 0 : SwUndoId nLastUndoId(UNDO_EMPTY);
2807 0 : if (rWorkShell.GetLastUndoInfo(0, & nLastUndoId))
2808 : {
2809 0 : if (UNDO_UI_DELETE_INVISIBLECNTNT == nLastUndoId)
2810 : {
2811 0 : rWorkShell.Undo();
2812 : }
2813 : }
2814 : // #i69485# lock fields to prevent access to the result set while calculating layout
2815 0 : rWorkShell.LockExpFlds();
2816 : // create a layout
2817 0 : rWorkShell.CalcLayout();
2818 0 : rWorkShell.UnlockExpFlds();
2819 0 : SFX_APP()->NotifyEvent(SfxEventHint(SW_EVENT_FIELD_MERGE, SwDocShell::GetEventName(STR_SW_EVENT_FIELD_MERGE), rWorkShell.GetView().GetViewFrame()->GetObjectShell()));
2820 0 : rWorkShell.SwViewShell::UpdateFlds();
2821 0 : SFX_APP()->NotifyEvent(SfxEventHint(SW_EVENT_FIELD_MERGE_FINISHED, SwDocShell::GetEventName(STR_SW_EVENT_FIELD_MERGE_FINISHED), rWorkShell.GetView().GetViewFrame()->GetObjectShell()));
2822 :
2823 : // strip invisible content and convert fields to text
2824 0 : rWorkShell.RemoveInvisibleContent();
2825 0 : rWorkShell.ConvertFieldsToText();
2826 0 : rWorkShell.SetNumberingRestart();
2827 0 : if( bSynchronizedDoc )
2828 : {
2829 0 : lcl_RemoveSectionLinks( rWorkShell );
2830 : }
2831 :
2832 : // insert the document into the target document
2833 0 : rWorkShell.SttEndDoc(sal_False);
2834 0 : rWorkShell.SttEndDoc(sal_True);
2835 0 : rWorkShell.SelAll();
2836 0 : pTargetShell->SttEndDoc(sal_False);
2837 :
2838 : //#i63806# put the styles to the target document
2839 : //if the source uses headers or footers each new copy need to copy a new page styles
2840 0 : if(bPageStylesWithHeaderFooter)
2841 : {
2842 : //create a new pagestyle
2843 : //copy the pagedesc from the current document to the new document and change the name of the to-be-applied style
2844 :
2845 0 : SwDoc* pTargetDoc = pTargetShell->GetDoc();
2846 0 : OUString sNewPageDescName = lcl_FindUniqueName(pTargetShell, sStartingPageDesc, nDocNo );
2847 0 : pTargetShell->GetDoc()->MakePageDesc( sNewPageDescName );
2848 0 : SwPageDesc* pTargetPageDesc = pTargetShell->FindPageDescByName( sNewPageDescName );
2849 0 : const SwPageDesc* pWorkPageDesc = rWorkShell.FindPageDescByName( sStartingPageDesc );
2850 :
2851 0 : if(pWorkPageDesc && pTargetPageDesc)
2852 : {
2853 0 : pTargetDoc->CopyPageDesc( *pWorkPageDesc, *pTargetPageDesc, false );
2854 0 : sModifiedStartingPageDesc = sNewPageDescName;
2855 0 : lcl_CopyFollowPageDesc( *pTargetShell, *pWorkPageDesc, *pTargetPageDesc, nDocNo );
2856 0 : }
2857 : }
2858 0 : if(nDocNo == 1 || bPageStylesWithHeaderFooter)
2859 : {
2860 0 : pTargetView->GetDocShell()->_LoadStyles( *rSourceView.GetDocShell(), sal_True );
2861 : }
2862 0 : if(nDocNo > 1)
2863 : {
2864 0 : pTargetShell->InsertPageBreak( &sModifiedStartingPageDesc, nStartingPageNo );
2865 : }
2866 : else
2867 : {
2868 0 : pTargetShell->SetPageStyle(sModifiedStartingPageDesc);
2869 : }
2870 0 : sal_uInt16 nPageCountBefore = pTargetShell->GetPageCnt();
2871 : OSL_ENSURE(!pTargetShell->GetTableFmt(),"target document ends with a table - paragraph should be appended");
2872 : //#i51359# add a second paragraph in case there's only one
2873 : {
2874 0 : SwNodeIndex aIdx( pWorkDoc->GetNodes().GetEndOfExtras(), 2 );
2875 0 : SwPosition aTestPos( aIdx );
2876 0 : SwCursor aTestCrsr(aTestPos,0,false);
2877 0 : if(!aTestCrsr.MovePara(fnParaNext, fnParaStart))
2878 : {
2879 : //append a paragraph
2880 0 : pWorkDoc->AppendTxtNode( aTestPos );
2881 0 : }
2882 : }
2883 0 : pTargetShell->Paste( rWorkShell.GetDoc(), sal_True );
2884 : //convert fields in page styles (header/footer - has to be done after the first document has been pasted
2885 0 : if(1 == nDocNo)
2886 : {
2887 0 : pTargetShell->CalcLayout();
2888 0 : pTargetShell->ConvertFieldsToText();
2889 : }
2890 : //add the document info to the config item
2891 : SwDocMergeInfo aMergeInfo;
2892 0 : aMergeInfo.nStartPageInTarget = nPageCountBefore;
2893 : //#i72820# calculate layout to be able to find the correct page index
2894 0 : pTargetShell->CalcLayout();
2895 0 : aMergeInfo.nEndPageInTarget = pTargetShell->GetPageCnt();
2896 0 : aMergeInfo.nDBRow = nStartRow;
2897 0 : rMMConfig.AddMergedDocument( aMergeInfo );
2898 0 : ++nRet;
2899 :
2900 : // the print monitor needs some time to act
2901 0 : for( sal_uInt16 i = 0; i < 25; i++)
2902 0 : Application::Reschedule();
2903 :
2904 : //restore the ole DBMgr
2905 0 : pWorkDoc->SetNewDBMgr( pWorkDBMgr );
2906 : //now the temporary document should be closed
2907 0 : SfxObjectShellRef xDocSh(pWorkView->GetDocShell());
2908 0 : xDocSh->DoClose();
2909 0 : nEndRow = pImpl->pMergeData->xResultSet->getRow();
2910 0 : ++nDocNo;
2911 0 : } while( !bCancel &&
2912 0 : (bSynchronizedDoc && (nStartRow != nEndRow)? ExistsNextRecord() : ToNextMergeRecord()));
2913 :
2914 : //deselect all, go out of the frame and go to the beginning of the document
2915 0 : Point aPt(LONG_MIN, LONG_MIN);
2916 0 : pTargetShell->SelectObj(aPt, SW_LEAVE_FRAME);
2917 0 : if (pTargetShell->IsSelFrmMode())
2918 : {
2919 0 : pTargetShell->UnSelectFrm();
2920 0 : pTargetShell->LeaveSelFrmMode();
2921 : }
2922 0 : pTargetShell->EnterStdMode();
2923 0 : pTargetShell->SttDoc();
2924 :
2925 : }
2926 0 : catch(const Exception&)
2927 : {
2928 : OSL_FAIL("exception caught in SwNewDBMgr::MergeDocuments");
2929 : }
2930 0 : DELETEZ(pImpl->pMergeData);
2931 0 : bInMerge = sal_False;
2932 0 : return nRet;
2933 : }
2934 :
2935 0 : SwConnectionDisposedListener_Impl::SwConnectionDisposedListener_Impl(SwNewDBMgr& rMgr) :
2936 0 : rDBMgr(rMgr)
2937 : {
2938 0 : };
2939 :
2940 0 : SwConnectionDisposedListener_Impl::~SwConnectionDisposedListener_Impl()
2941 : {
2942 0 : };
2943 :
2944 0 : void SwConnectionDisposedListener_Impl::disposing( const EventObject& rSource )
2945 : throw (RuntimeException, std::exception)
2946 : {
2947 0 : ::SolarMutexGuard aGuard;
2948 0 : uno::Reference<XConnection> xSource(rSource.Source, UNO_QUERY);
2949 0 : for(sal_uInt16 nPos = rDBMgr.aDataSourceParams.size(); nPos; nPos--)
2950 : {
2951 0 : SwDSParam* pParam = &rDBMgr.aDataSourceParams[nPos - 1];
2952 0 : if(pParam->xConnection.is() &&
2953 0 : (xSource == pParam->xConnection))
2954 : {
2955 0 : rDBMgr.aDataSourceParams.erase(rDBMgr.aDataSourceParams.begin() + nPos - 1);
2956 : }
2957 0 : }
2958 0 : }
2959 :
2960 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|