xref: /core/sw/source/ui/dbui/mmlayoutpage.cxx (revision 59953740b20ceb2bbc682625739f9bb6278ca7cb)
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 <swtypes.hxx>
21 #include "mmlayoutpage.hxx"
22 #include <mailmergewizard.hxx>
23 #include <mmconfigitem.hxx>
24 #include <mailmergehelper.hxx>
25 #include <unotools.hxx>
26 #include <comphelper/string.hxx>
27 #include <comphelper/propertyvalue.hxx>
28 #include <comphelper/servicehelper.hxx>
29 #include <i18nutil/unicode.hxx>
30 #include <unotools/tempfile.hxx>
31 #include <uitool.hxx>
32 #include <view.hxx>
33 #include <swundo.hxx>
34 #include <sfx2/docfilt.hxx>
35 #include <sfx2/fcontnr.hxx>
36 #include <svtools/unitconv.hxx>
37 #include <com/sun/star/view/XViewSettingsSupplier.hpp>
38 #include <com/sun/star/view/DocumentZoomType.hpp>
39 #include <fldmgr.hxx>
40 #include <fldbas.hxx>
41 #include <unotxdoc.hxx>
42 #include <docsh.hxx>
43 #include <doc.hxx>
44 #include <wrtsh.hxx>
45 #include <fmtsrnd.hxx>
46 #include <pagedesc.hxx>
47 #include <fmtanchr.hxx>
48 #include <fmtornt.hxx>
49 #include <fmtfsize.hxx>
50 #include <editeng/boxitem.hxx>
51 #include <osl/diagnose.h>
52 #include <osl/file.hxx>
53 #include <vcl/settings.hxx>
54 #include <unoprnms.hxx>
55 
56 #include <dbui.hrc>
57 
58 using namespace osl;
59 using namespace ::com::sun::star;
60 using namespace ::com::sun::star::uno;
61 using namespace ::com::sun::star::text;
62 using namespace ::com::sun::star::frame;
63 using namespace ::com::sun::star::view;
64 
65 constexpr tools::Long DEFAULT_LEFT_DISTANCE = o3tl::toTwips(25, o3tl::Length::mm); // 2,5 cm
66 constexpr tools::Long DEFAULT_TOP_DISTANCE  = o3tl::toTwips(55, o3tl::Length::mm); // 5,5 cm
67 constexpr tools::Long GREETING_TOP_DISTANCE = o3tl::toTwips(125, o3tl::Length::mm); //12,5 cm
68 constexpr tools::Long DEFAULT_ADDRESS_WIDTH = o3tl::toTwips(75, o3tl::Length::mm); // 7,5 cm
69 constexpr tools::Long DEFAULT_ADDRESS_HEIGHT = o3tl::toTwips(35, o3tl::Length::mm); // 3,5cm
70 
SwMailMergeLayoutPage(weld::Container * pPage,SwMailMergeWizard * pWizard)71 SwMailMergeLayoutPage::SwMailMergeLayoutPage(weld::Container* pPage, SwMailMergeWizard* pWizard)
72     : vcl::OWizardPage(pPage, pWizard, u"modules/swriter/ui/mmlayoutpage.ui"_ustr, u"MMLayoutPage"_ustr)
73     , m_pExampleWrtShell(nullptr)
74     , m_pAddressBlockFormat(nullptr)
75     , m_bIsGreetingInserted(false)
76     , m_pWizard(pWizard)
77     , m_xPosition(m_xBuilder->weld_container(u"addresspos"_ustr))
78     , m_xAlignToBodyCB(m_xBuilder->weld_check_button(u"align"_ustr))
79     , m_xLeftFT(m_xBuilder->weld_label(u"leftft"_ustr))
80     , m_xLeftMF(m_xBuilder->weld_metric_spin_button(u"left"_ustr, FieldUnit::CM))
81     , m_xTopMF(m_xBuilder->weld_metric_spin_button(u"top"_ustr, FieldUnit::CM))
82     , m_xGreetingLine(m_xBuilder->weld_container(u"greetingspos"_ustr))
83     , m_xUpPB(m_xBuilder->weld_button(u"up"_ustr))
84     , m_xDownPB(m_xBuilder->weld_button(u"down"_ustr))
85     , m_xZoomLB(m_xBuilder->weld_combo_box(u"zoom"_ustr))
86 {
87     std::shared_ptr<const SfxFilter> pSfxFlt =
88             SwDocShell::Factory().GetFilterContainer()->
89             GetFilter4FilterName(u"writer8"_ustr, SfxFilterFlags::EXPORT);
90 
91     //save the current document into a temporary file
92     {
93         //temp file needs its own block
94         //creating with extension is not supported by a static method :-(
95         OUString const sExt(
96             comphelper::string::stripStart(pSfxFlt->GetDefaultExtension(),'*'));
97         utl::TempFileNamed aTempFile( u"", true, sExt );
98         aTempFile.EnableKillingFile();
99         m_sExampleURL = aTempFile.GetURL();
100     }
101     SwView& rView = m_pWizard->GetSwView();
102     // Don't save embedded data set! It would steal it from current document.
103     uno::Sequence< beans::PropertyValue > aValues =
104     {
105         comphelper::makePropertyValue(u"FilterName"_ustr, pSfxFlt->GetFilterName()),
106         comphelper::makePropertyValue(u"NoEmbDataSet"_ustr, true)
107     };
108 
109     uno::Reference< frame::XStorable > xStore( rView.GetDocShell()->GetModel(), uno::UNO_QUERY);
110     xStore->storeToURL( m_sExampleURL, aValues   );
111 
112     Link<SwOneExampleFrame&,void> aLink(LINK(this, SwMailMergeLayoutPage, PreviewLoadedHdl_Impl));
113     m_xExampleFrame.reset(new SwOneExampleFrame(EX_SHOW_DEFAULT_PAGE, &aLink, &m_sExampleURL));
114     m_xExampleContainerWIN.reset(new weld::CustomWeld(*m_xBuilder, u"example"_ustr, *m_xExampleFrame));
115 
116     Size aSize = m_xExampleFrame->GetDrawingArea()->get_ref_device().LogicToPixel(
117             Size(124, 159), MapMode(MapUnit::MapAppFont));
118     m_xExampleFrame->set_size_request(aSize.Width(), aSize.Height());
119 
120     m_xExampleContainerWIN->hide();
121 
122     m_xLeftMF->set_value(m_xLeftMF->normalize(DEFAULT_LEFT_DISTANCE), FieldUnit::TWIP);
123     m_xTopMF->set_value(m_xTopMF->normalize(DEFAULT_TOP_DISTANCE), FieldUnit::TWIP);
124 
125     const LanguageTag& rLang = Application::GetSettings().GetUILanguageTag();
126     m_xZoomLB->append_text(unicode::formatPercent(50, rLang));
127     m_xZoomLB->append_text(unicode::formatPercent(75, rLang));
128     m_xZoomLB->append_text(unicode::formatPercent(100, rLang));
129     m_xZoomLB->set_active(0); //page size
130     m_xZoomLB->connect_changed(LINK(this, SwMailMergeLayoutPage, ZoomHdl_Impl));
131 
132     Link<weld::MetricSpinButton&,void> aFrameHdl = LINK(this, SwMailMergeLayoutPage, ChangeAddressHdl_Impl);
133     m_xLeftMF->connect_value_changed(aFrameHdl);
134     m_xTopMF->connect_value_changed(aFrameHdl);
135 
136     FieldUnit eFieldUnit = ::GetDfltMetric(false);
137     ::SetFieldUnit( *m_xLeftMF, eFieldUnit );
138     ::SetFieldUnit( *m_xTopMF, eFieldUnit );
139 
140     Link<weld::Button&,void> aUpDownHdl = LINK(this, SwMailMergeLayoutPage, GreetingsHdl_Impl );
141     m_xUpPB->connect_clicked(aUpDownHdl);
142     m_xDownPB->connect_clicked(aUpDownHdl);
143     m_xAlignToBodyCB->connect_toggled(LINK(this, SwMailMergeLayoutPage, AlignToTextHdl_Impl));
144     m_xAlignToBodyCB->set_active(true);
145 }
146 
~SwMailMergeLayoutPage()147 SwMailMergeLayoutPage::~SwMailMergeLayoutPage()
148 {
149     File::remove( m_sExampleURL );
150 }
151 
Activate()152 void SwMailMergeLayoutPage::Activate()
153 {
154     SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem();
155     bool bGreetingLine = rConfigItem.IsGreetingLine(false) && !rConfigItem.IsGreetingInserted();
156     bool bAddressBlock = rConfigItem.IsAddressBlock() && !rConfigItem.IsAddressInserted();
157 
158     m_xPosition->set_sensitive(bAddressBlock);
159     AlignToTextHdl_Impl(*m_xAlignToBodyCB);
160 
161     m_xGreetingLine->set_sensitive(bGreetingLine);
162 
163     //check if greeting and/or address frame have to be inserted/removed
164     if(!m_pExampleWrtShell) // initially there's nothing to check
165         return;
166 
167     if(!rConfigItem.IsGreetingInserted() &&
168             m_bIsGreetingInserted != bGreetingLine )
169     {
170         if( m_bIsGreetingInserted )
171         {
172             m_pExampleWrtShell->DelFullPara();
173             m_bIsGreetingInserted = false;
174         }
175         else
176         {
177             InsertGreeting(*m_pExampleWrtShell, m_pWizard->GetConfigItem(), true);
178             m_bIsGreetingInserted = true;
179         }
180     }
181     if(!rConfigItem.IsAddressInserted() &&
182             rConfigItem.IsAddressBlock() != ( nullptr != m_pAddressBlockFormat ))
183     {
184         if( m_pAddressBlockFormat )
185         {
186             m_pExampleWrtShell->Push();
187             m_pExampleWrtShell->GotoFly( m_pAddressBlockFormat->GetName() );
188             m_pExampleWrtShell->DelRight();
189             m_pAddressBlockFormat = nullptr;
190             m_pExampleWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent);
191         }
192         else
193         {
194             tools::Long nLeft = static_cast< tools::Long >(m_xLeftMF->denormalize(m_xLeftMF->get_value(FieldUnit::TWIP)));
195             tools::Long nTop  = static_cast< tools::Long >(m_xTopMF->denormalize(m_xTopMF->get_value(FieldUnit::TWIP)));
196             m_pAddressBlockFormat = InsertAddressFrame(
197                     *m_pExampleWrtShell, m_pWizard->GetConfigItem(),
198                     Point(nLeft, nTop),
199                     m_xAlignToBodyCB->get_active(), true);
200         }
201     }
202     m_xExampleFrame->Invalidate();
203 }
204 
commitPage(::vcl::WizardTypes::CommitPageReason eReason)205 bool SwMailMergeLayoutPage::commitPage(::vcl::WizardTypes::CommitPageReason eReason)
206 {
207     //now insert the frame and the greeting
208     SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem();
209     if (eReason == ::vcl::WizardTypes::eTravelForward || eReason == ::vcl::WizardTypes::eFinish)
210     {
211         tools::Long nLeft = static_cast< tools::Long >(m_xLeftMF->denormalize(m_xLeftMF->get_value(FieldUnit::TWIP)));
212         tools::Long nTop  = static_cast< tools::Long >(m_xTopMF->denormalize(m_xTopMF->get_value(FieldUnit::TWIP)));
213         InsertAddressAndGreeting(
214                     m_pWizard->GetSwView(),
215                     rConfigItem,
216                     Point(nLeft, nTop),
217                     m_xAlignToBodyCB->get_active());
218     }
219     return true;
220 }
221 
InsertAddressAndGreeting(SwView & rView,SwMailMergeConfigItem & rConfigItem,const Point & rAddressPosition,bool bAlignToBody)222 SwFrameFormat*  SwMailMergeLayoutPage::InsertAddressAndGreeting(SwView& rView,
223         SwMailMergeConfigItem& rConfigItem,
224         const Point& rAddressPosition,
225         bool bAlignToBody)
226 {
227     SwFrameFormat* pAddressBlockFormat = nullptr;
228     rView.GetWrtShell().StartUndo(SwUndoId::INSERT);
229     if(rConfigItem.IsAddressBlock() && !rConfigItem.IsAddressInserted())
230     {
231         //insert the frame
232         Point aAddressPosition(DEFAULT_LEFT_DISTANCE, DEFAULT_TOP_DISTANCE);
233         if(rAddressPosition.X() > 0 && rAddressPosition.Y() > 0)
234             aAddressPosition = rAddressPosition;
235         pAddressBlockFormat = InsertAddressFrame( rView.GetWrtShell(),
236                                         rConfigItem,
237                                         aAddressPosition, bAlignToBody, false);
238         rConfigItem.SetAddressInserted();
239     }
240     //now the greeting
241     if(rConfigItem.IsGreetingLine(false) && !rConfigItem.IsGreetingInserted())
242     {
243         InsertGreeting( rView.GetWrtShell(), rConfigItem, false);
244         rConfigItem.SetGreetingInserted();
245     }
246     rView.GetWrtShell().EndUndo(SwUndoId::INSERT);
247     return pAddressBlockFormat;
248 }
249 
InsertAddressFrame(SwWrtShell & rShell,SwMailMergeConfigItem const & rConfigItem,const Point & rDestination,bool bAlignLeft,bool bExample)250 SwFrameFormat* SwMailMergeLayoutPage::InsertAddressFrame(
251         SwWrtShell& rShell,
252         SwMailMergeConfigItem const & rConfigItem,
253         const Point& rDestination,
254         bool bAlignLeft,
255         bool bExample)
256 {
257     // insert the address block and the greeting line
258     SfxItemSetFixed<
259             RES_FRM_SIZE, RES_FRM_SIZE,
260             RES_SURROUND, RES_ANCHOR,
261             RES_BOX, RES_BOX>  aSet( rShell.GetAttrPool() );
262     aSet.Put(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, 1));
263     if(bAlignLeft)
264         aSet.Put(SwFormatHoriOrient( 0, text::HoriOrientation::NONE, text::RelOrientation::PAGE_PRINT_AREA ));
265     else
266         aSet.Put(SwFormatHoriOrient( rDestination.X(), text::HoriOrientation::NONE, text::RelOrientation::PAGE_FRAME ));
267     aSet.Put(SwFormatVertOrient( rDestination.Y(), text::VertOrientation::NONE, text::RelOrientation::PAGE_FRAME ));
268     aSet.Put(SwFormatFrameSize( SwFrameSize::Minimum, DEFAULT_ADDRESS_WIDTH, DEFAULT_ADDRESS_HEIGHT ));
269     // the example gets a border around the frame, the real document doesn't get one
270     if(!bExample)
271         aSet.Put(SvxBoxItem( RES_BOX ));
272     aSet.Put(SwFormatSurround( css::text::WrapTextMode_NONE ));
273 
274     rShell.NewFlyFrame(aSet, true );
275     SwFrameFormat* pRet = rShell.GetFlyFrameFormat();
276     OSL_ENSURE( pRet, "Fly not inserted" );
277 
278     rShell.UnSelectFrame();
279     const Sequence< OUString> aBlocks = rConfigItem.GetAddressBlocks();
280     if(bExample)
281     {
282         rShell.Insert(aBlocks[0]);
283     }
284     else
285     {
286         //the placeholders should be replaced by the appropriate fields
287         SwFieldMgr aFieldMgr(&rShell);
288         //create a database string source.command.commandtype.column
289         const SwDBData& rData = rConfigItem.GetCurrentDBData();
290         OUString sDBName(rData.sDataSource + OUStringChar(DB_DELIM)
291             + rData.sCommand + OUStringChar(DB_DELIM));
292         const OUString sDatabaseConditionPrefix(sDBName.replace(DB_DELIM, '.'));
293         sDBName += OUString::number(rData.nCommandType) + OUStringChar(DB_DELIM);
294 
295         // if only the country is in an address line the
296         // paragraph has to be hidden depending on the
297         // IsIncludeCountry()/GetExcludeCountry() settings
298 
299         bool bIncludeCountry = rConfigItem.IsIncludeCountry();
300         bool bHideEmptyParagraphs = rConfigItem.IsHideEmptyParagraphs();
301         const OUString& rExcludeCountry = rConfigItem.GetExcludeCountry();
302         bool bSpecialReplacementForCountry = (!bIncludeCountry || !rExcludeCountry.isEmpty());
303 
304         const std::vector<std::pair<OUString, int>>& rHeaders = rConfigItem.GetDefaultAddressHeaders();
305         Sequence< OUString> aAssignment =
306                         rConfigItem.GetColumnAssignment( rConfigItem.GetCurrentDBData() );
307         const OUString* pAssignment = aAssignment.getConstArray();
308         const OUString sCountryColumn(
309             (aAssignment.getLength() > MM_PART_COUNTRY && !aAssignment[MM_PART_COUNTRY].isEmpty())
310             ? aAssignment[MM_PART_COUNTRY]
311             : rHeaders[MM_PART_COUNTRY].first);
312 
313         OUString sHideParagraphsExpression;
314         SwAddressIterator aIter(aBlocks[0]);
315         while(aIter.HasMore())
316         {
317             SwMergeAddressItem aItem = aIter.Next();
318             if(aItem.bIsColumn)
319             {
320                 OUString sConvertedColumn = aItem.sText;
321                 auto nSize = std::min(static_cast<sal_uInt32>(rHeaders.size()),
322                                       static_cast<sal_uInt32>(aAssignment.getLength()));
323                 for(sal_uInt32 nColumn = 0; nColumn < nSize; ++nColumn)
324                 {
325                     if (rHeaders[nColumn].first == aItem.sText &&
326                         !pAssignment[nColumn].isEmpty())
327                     {
328                         sConvertedColumn = pAssignment[nColumn];
329                         break;
330                     }
331                 }
332                 const OUString sDB(sDBName + sConvertedColumn);
333 
334                 if(!sHideParagraphsExpression.isEmpty())
335                    sHideParagraphsExpression += " AND ";
336                 sHideParagraphsExpression += "![" + sDatabaseConditionPrefix + sConvertedColumn + "]";
337 
338                 if( bSpecialReplacementForCountry && sCountryColumn == sConvertedColumn )
339                 {
340                     // now insert a hidden paragraph field
341                     if( !rExcludeCountry.isEmpty() )
342                     {
343                         const OUString sExpression("[" + sDatabaseConditionPrefix + sCountryColumn + "]");
344                         SwInsertField_Data aData(SwFieldTypesEnum::ConditionalText, 0,
345                                                sExpression + " != \"" + rExcludeCountry + "\"",
346                                                sExpression,
347                                                0, &rShell );
348                         aFieldMgr.InsertField( aData );
349                     }
350                     else
351                     {
352                         SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, u""_ustr, u""_ustr, 0, &rShell );
353                         aFieldMgr.InsertField( aData );
354                     }
355                 }
356                 else
357                 {
358                     SwInsertField_Data aData(SwFieldTypesEnum::Database, 0, sDB, OUString(), 0, &rShell);
359                     aFieldMgr.InsertField( aData );
360                 }
361             }
362             else if(!aItem.bIsReturn)
363             {
364                 rShell.Insert(aItem.sText);
365             }
366             else
367             {
368                 if(bHideEmptyParagraphs)
369                 {
370                     SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, sHideParagraphsExpression, OUString(), 0, &rShell);
371                     aFieldMgr.InsertField( aData );
372                 }
373                 sHideParagraphsExpression.clear();
374                 //now add a new paragraph
375                 rShell.SplitNode();
376             }
377         }
378         if(bHideEmptyParagraphs && !sHideParagraphsExpression.isEmpty())
379         {
380             SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, sHideParagraphsExpression, OUString(), 0, &rShell);
381             aFieldMgr.InsertField( aData );
382         }
383     }
384     return pRet;
385 }
386 
InsertGreeting(SwWrtShell & rShell,SwMailMergeConfigItem const & rConfigItem,bool bExample)387 void SwMailMergeLayoutPage::InsertGreeting(SwWrtShell& rShell, SwMailMergeConfigItem const & rConfigItem, bool bExample)
388 {
389     //set the cursor to the desired position - if no text content is here then
390     //new paragraphs are inserted
391     const SwRect& rPageRect = rShell.GetAnyCurRect(CurRectType::Page);
392     const Point aGreetingPos( DEFAULT_LEFT_DISTANCE + rPageRect.Left(), GREETING_TOP_DISTANCE );
393 
394     const bool bRet = rShell.SetShadowCursorPos( aGreetingPos, SwFillMode::TabSpace );
395 
396     if(!bRet)
397     {
398         //there's already text at the desired position
399         //go to start of the doc, directly!
400         rShell.SttEndDoc(true);
401         //and go by paragraph until the position is reached
402         tools::Long nYPos = rShell.GetCharRect().Top();
403         while(nYPos < GREETING_TOP_DISTANCE)
404         {
405             if(!rShell.FwdPara())
406                 break;
407             nYPos = rShell.GetCharRect().Top();
408         }
409         //text needs to be appended
410         while(nYPos < GREETING_TOP_DISTANCE)
411         {
412             if(!rShell.AppendTextNode())
413                 break;
414             nYPos = rShell.GetCharRect().Top();
415         }
416     }
417     else
418     {
419         //we may end up inside of a paragraph if the left margin is not at DEFAULT_LEFT_DISTANCE
420         rShell.MovePara(GoCurrPara, fnParaStart);
421     }
422     bool bSplitNode = !rShell.IsEndPara();
423     SwNodeOffset nMoves(rConfigItem.GetGreetingMoves());
424     if( !bExample && SwNodeOffset(0) != nMoves )
425     {
426         if(nMoves < SwNodeOffset(0))
427         {
428             rShell.MoveParagraph( nMoves );
429         }
430         else
431             while(nMoves)
432             {
433                 bool bMoved = rShell.MoveParagraph();
434                 if(!bMoved)
435                 {
436                     //insert a new paragraph before the greeting line
437                     rShell.SplitNode();
438                 }
439                 --nMoves;
440             }
441     }
442     //now insert the greeting text - if we have any?
443     const bool bIndividual = rConfigItem.IsIndividualGreeting(false);
444     if(bIndividual)
445     {
446         //lock expression fields - prevents hiding of the paragraph to insert into
447         rShell.LockExpFields();
448         if(bExample)
449         {
450             for(sal_Int8 eGender = SwMailMergeConfigItem::FEMALE;
451                 eGender <= SwMailMergeConfigItem::NEUTRAL; ++eGender)
452             {
453                 Sequence< OUString > aEntries =
454                             rConfigItem.GetGreetings(static_cast<SwMailMergeConfigItem::Gender>(eGender));
455                 sal_Int32 nCurrent = rConfigItem.GetCurrentGreeting(static_cast<SwMailMergeConfigItem::Gender>(eGender));
456                 if( nCurrent >= 0 && nCurrent < aEntries.getLength())
457                 {
458                     // Greeting
459                     rShell.Insert(aEntries[nCurrent]);
460                     break;
461                 }
462             }
463         }
464         else
465         {
466             SwFieldMgr aFieldMgr(&rShell);
467             //three paragraphs, each with an appropriate hidden paragraph field
468             //are to be inserted
469 
470             //name of the gender column
471             const OUString sGenderColumn = rConfigItem.GetAssignedColumn(MM_PART_GENDER);
472             const OUString sNameColumn = rConfigItem.GetAssignedColumn(MM_PART_LASTNAME);
473 
474             const OUString& rFemaleGenderValue = rConfigItem.GetFemaleGenderValue();
475             bool bHideEmptyParagraphs = rConfigItem.IsHideEmptyParagraphs();
476             const SwDBData& rData = rConfigItem.GetCurrentDBData();
477             const OUString sCommonBase(rData.sDataSource + "." + rData.sCommand + ".");
478             const OUString sConditionBase("[" + sCommonBase + sGenderColumn + "]");
479             const OUString sNameColumnBase("[" + sCommonBase + sNameColumn + "]");
480 
481             const OUString sDBName(rData.sDataSource + OUStringChar(DB_DELIM)
482                 + rData.sCommand + OUStringChar(DB_DELIM)
483                 + OUString::number(rData.nCommandType) + OUStringChar(DB_DELIM));
484 
485 //          Female:  [database.sGenderColumn] != "rFemaleGenderValue" && [database.NameColumn]
486 //          Male:    [database.sGenderColumn] == "rFemaleGenderValue" && [database.rGenderColumn]
487 //          Neutral: [database.sNameColumn]
488             OSL_ENSURE(!sGenderColumn.isEmpty() && !rFemaleGenderValue.isEmpty(),
489                     "gender settings not available - how to form the condition?");
490             //column used as lastname
491             for(sal_Int8 eGender = SwMailMergeConfigItem::FEMALE;
492                 eGender <= SwMailMergeConfigItem::NEUTRAL; ++eGender)
493             {
494                 Sequence< OUString> aEntries = rConfigItem.GetGreetings(static_cast<SwMailMergeConfigItem::Gender>(eGender));
495                 sal_Int32 nCurrent = rConfigItem.GetCurrentGreeting(static_cast<SwMailMergeConfigItem::Gender>(eGender));
496                 if( nCurrent >= 0 && nCurrent < aEntries.getLength())
497                 {
498                     const OUString& sGreeting = aEntries[nCurrent];
499                     OUString sCondition;
500                     OUString sHideParagraphsExpression;
501                     switch(eGender)
502                     {
503                         case  SwMailMergeConfigItem::FEMALE:
504                             sCondition = sConditionBase + " != \"" + rFemaleGenderValue
505                                 + "\" OR NOT " + sNameColumnBase;
506                             sHideParagraphsExpression = "!" + sNameColumnBase;
507                         break;
508                         case  SwMailMergeConfigItem::MALE:
509                             sCondition = sConditionBase + " == \"" + rFemaleGenderValue
510                                 + "\" OR NOT " + sNameColumnBase;
511                         break;
512                         case  SwMailMergeConfigItem::NEUTRAL:
513                             sCondition = sNameColumnBase;
514                         break;
515                     }
516 
517                     if(bHideEmptyParagraphs && !sHideParagraphsExpression.isEmpty())
518                     {
519                         OUString sComplete = "(" + sCondition + ") OR (" + sHideParagraphsExpression + ")";
520                         SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, sComplete, OUString(), 0, &rShell);
521                         aFieldMgr.InsertField( aData );
522                     }
523                     else
524                     {
525                         SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, sCondition, OUString(), 0, &rShell);
526                         aFieldMgr.InsertField( aData );
527                     }
528                     //now the text has to be inserted
529                     const std::vector<std::pair<OUString, int>>& rHeaders = rConfigItem.GetDefaultAddressHeaders();
530                     Sequence< OUString> aAssignment =
531                                     rConfigItem.GetColumnAssignment( rConfigItem.GetCurrentDBData() );
532                     const OUString* pAssignment = aAssignment.getConstArray();
533                     SwAddressIterator aIter(sGreeting);
534                     while(aIter.HasMore())
535                     {
536                         SwMergeAddressItem aItem = aIter.Next();
537                         if(aItem.bIsColumn)
538                         {
539                             OUString sConvertedColumn = aItem.sText;
540                             auto nSize = std::min(static_cast<sal_uInt32>(rHeaders.size()),
541                                                   static_cast<sal_uInt32>(aAssignment.getLength()));
542                             for(sal_uInt32 nColumn = 0; nColumn < nSize; ++nColumn)
543                             {
544                                 if (rHeaders[nColumn].first == aItem.sText &&
545                                     !pAssignment[nColumn].isEmpty())
546                                 {
547                                     sConvertedColumn = pAssignment[nColumn];
548                                     break;
549                                 }
550                             }
551                             SwInsertField_Data aData(SwFieldTypesEnum::Database, 0,
552                                 sDBName + sConvertedColumn,
553                                 OUString(), 0, &rShell);
554                             aFieldMgr.InsertField( aData );
555                         }
556                         else
557                         {
558                             rShell.Insert(aItem.sText);
559                         }
560                     }
561                     //now add a new paragraph
562                     rShell.SplitNode();
563                 }
564             }
565 
566         }
567         rShell.UnlockExpFields();
568     }
569     else
570     {
571         Sequence< OUString> aEntries = rConfigItem.GetGreetings(SwMailMergeConfigItem::NEUTRAL);
572         sal_Int32 nCurrent = rConfigItem.GetCurrentGreeting(SwMailMergeConfigItem::NEUTRAL);
573         // Greeting
574         rShell.Insert(( nCurrent >= 0 && nCurrent < aEntries.getLength() )
575             ? aEntries[nCurrent] : OUString());
576     }
577     // now insert a new paragraph here if necessary
578     if(bSplitNode)
579     {
580         rShell.Push();
581         rShell.SplitNode();
582         rShell.Pop(SwCursorShell::PopMode::DeleteCurrent);
583     }
584     //put the cursor to the start of the paragraph
585     rShell.SttPara();
586 
587     OSL_ENSURE(nullptr == rShell.GetTableFormat(), "What to do with a table here?");
588 }
589 
IMPL_LINK_NOARG(SwMailMergeLayoutPage,PreviewLoadedHdl_Impl,SwOneExampleFrame &,void)590 IMPL_LINK_NOARG(SwMailMergeLayoutPage, PreviewLoadedHdl_Impl, SwOneExampleFrame&, void)
591 {
592     m_xExampleContainerWIN->show();
593 
594     rtl::Reference< SwXTextDocument > & xModel = m_xExampleFrame->GetModel();
595     //now the ViewOptions should be set properly
596     Reference< XViewSettingsSupplier >  xSettings(xModel->getCurrentController(), UNO_QUERY);
597     m_xViewProperties = xSettings->getViewSettings();
598     SwDocShell* pDocShell = xModel->GetDocShell();
599     m_pExampleWrtShell = pDocShell->GetWrtShell();
600     OSL_ENSURE(m_pExampleWrtShell, "No SwWrtShell found!");
601     if(!m_pExampleWrtShell)
602         return;
603 
604     SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem();
605     if(rConfigItem.IsAddressBlock())
606     {
607         m_pAddressBlockFormat = InsertAddressFrame(
608                 *m_pExampleWrtShell, rConfigItem,
609                 Point(DEFAULT_LEFT_DISTANCE, DEFAULT_TOP_DISTANCE),
610                 m_xAlignToBodyCB->get_active(), true);
611     }
612     if(rConfigItem.IsGreetingLine(false))
613     {
614         InsertGreeting(*m_pExampleWrtShell, rConfigItem, true);
615         m_bIsGreetingInserted = true;
616     }
617 
618     ZoomHdl_Impl(*m_xZoomLB);
619 
620     const SwFormatFrameSize& rPageSize = m_pExampleWrtShell->GetPageDesc(
621                                      m_pExampleWrtShell->GetCurPageDesc()).GetMaster().GetFrameSize();
622     m_xLeftMF->set_max(rPageSize.GetWidth() - DEFAULT_LEFT_DISTANCE, FieldUnit::NONE);
623     m_xTopMF->set_max(rPageSize.GetHeight() - DEFAULT_TOP_DISTANCE, FieldUnit::NONE);
624 }
625 
IMPL_LINK(SwMailMergeLayoutPage,ZoomHdl_Impl,weld::ComboBox &,rBox,void)626 IMPL_LINK(SwMailMergeLayoutPage, ZoomHdl_Impl, weld::ComboBox&, rBox, void)
627 {
628     if (!m_pExampleWrtShell)
629         return;
630 
631     sal_Int16 eType = DocumentZoomType::BY_VALUE;
632     short nZoom = 50;
633     switch (rBox.get_active())
634     {
635         case 0 : eType = DocumentZoomType::ENTIRE_PAGE; break;
636         case 1 : nZoom = 50; break;
637         case 2 : nZoom = 75; break;
638         case 3 : nZoom = 100; break;
639     }
640     Any aZoom;
641     aZoom <<= eType;
642     m_xViewProperties->setPropertyValue(UNO_NAME_ZOOM_TYPE, aZoom);
643     aZoom <<= nZoom;
644     m_xViewProperties->setPropertyValue(UNO_NAME_ZOOM_VALUE, aZoom);
645 
646     m_xExampleFrame->Invalidate();
647 }
648 
IMPL_LINK_NOARG(SwMailMergeLayoutPage,ChangeAddressHdl_Impl,weld::MetricSpinButton &,void)649 IMPL_LINK_NOARG(SwMailMergeLayoutPage, ChangeAddressHdl_Impl, weld::MetricSpinButton&, void)
650 {
651     if(!(m_pExampleWrtShell && m_pAddressBlockFormat))
652         return;
653 
654     tools::Long nLeft = static_cast< tools::Long >(m_xLeftMF->denormalize(m_xLeftMF->get_value(FieldUnit::TWIP)));
655     tools::Long nTop  = static_cast< tools::Long >(m_xTopMF->denormalize(m_xTopMF->get_value(FieldUnit::TWIP)));
656 
657     SfxItemSetFixed<RES_VERT_ORIENT, RES_ANCHOR> aSet(
658         m_pExampleWrtShell->GetAttrPool());
659     if (m_xAlignToBodyCB->get_active())
660         aSet.Put(SwFormatHoriOrient( 0, text::HoriOrientation::NONE, text::RelOrientation::PAGE_PRINT_AREA ));
661     else
662         aSet.Put(SwFormatHoriOrient( nLeft, text::HoriOrientation::NONE, text::RelOrientation::PAGE_FRAME ));
663     aSet.Put(SwFormatVertOrient( nTop, text::VertOrientation::NONE, text::RelOrientation::PAGE_FRAME ));
664     m_pExampleWrtShell->GetDoc()->SetFlyFrameAttr( *m_pAddressBlockFormat, aSet );
665     m_xExampleFrame->Invalidate();
666 }
667 
IMPL_LINK(SwMailMergeLayoutPage,GreetingsHdl_Impl,weld::Button &,rButton,void)668 IMPL_LINK(SwMailMergeLayoutPage, GreetingsHdl_Impl, weld::Button&, rButton, void)
669 {
670     bool bDown = &rButton == m_xDownPB.get();
671     bool bMoved = m_pExampleWrtShell->MoveParagraph( SwNodeOffset(bDown ? 1 : -1) );
672     if (bMoved || bDown)
673         m_pWizard->GetConfigItem().MoveGreeting(bDown ? 1 : -1 );
674     if(!bMoved && bDown)
675     {
676         //insert a new paragraph before the greeting line
677         m_pExampleWrtShell->SplitNode();
678     }
679     m_xExampleFrame->Invalidate();
680 }
681 
IMPL_LINK(SwMailMergeLayoutPage,AlignToTextHdl_Impl,weld::Toggleable &,rBox,void)682 IMPL_LINK(SwMailMergeLayoutPage, AlignToTextHdl_Impl, weld::Toggleable&, rBox, void)
683 {
684     bool bCheck = rBox.get_active() && rBox.get_sensitive();
685     m_xLeftFT->set_sensitive(!bCheck);
686     m_xLeftMF->set_sensitive(!bCheck);
687     ChangeAddressHdl_Impl( *m_xLeftMF );
688 }
689 
690 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
691