xref: /core/sfx2/source/dialog/tabdlg.cxx (revision 2953db95)
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 
21 #include <limits.h>
22 #include <stdlib.h>
23 #include <algorithm>
24 
25 #include <appdata.hxx>
26 #include <sfxtypes.hxx>
27 #include <sfx2/tabdlg.hxx>
28 #include <sfx2/viewfrm.hxx>
29 #include <sfx2/app.hxx>
30 #include <sfx2/sfxresid.hxx>
31 #include <sfx2/sfxhelp.hxx>
32 #include <sfx2/ctrlitem.hxx>
33 #include <sfx2/bindings.hxx>
34 #include <sfx2/sfxdlg.hxx>
35 #include <sfx2/viewsh.hxx>
36 #include <unotools/viewoptions.hxx>
37 #include <vcl/IDialogRenderable.hxx>
38 #include <vcl/virdev.hxx>
39 #include <sal/log.hxx>
40 #include <osl/diagnose.h>
41 
42 #include <sfx2/strings.hrc>
43 #include <helpids.h>
44 
45 using namespace ::com::sun::star::uno;
46 
47 #define USERITEM_NAME           "UserItem"
48 
49 
50 struct TabPageImpl
51 {
52     bool                        mbStandard;
53     SfxOkDialogController*      mpSfxDialogController;
54     css::uno::Reference< css::frame::XFrame > mxFrame;
55 
56     TabPageImpl() : mbStandard(false), mpSfxDialogController(nullptr) {}
57 };
58 
59 struct Data_Impl
60 {
61     OString const sId;                  // The ID
62     CreateTabPage fnCreatePage;   // Pointer to Factory
63     GetTabPageRanges fnGetRanges; // Pointer to Ranges-Function
64     std::unique_ptr<SfxTabPage> xTabPage;         // The TabPage itself
65     bool bRefresh;                // Flag: Page must be re-initialized
66 
67     // Constructor
68     Data_Impl( const OString& rId, CreateTabPage fnPage,
69                GetTabPageRanges fnRanges ) :
70 
71         sId         ( rId ),
72         fnCreatePage( fnPage ),
73         fnGetRanges ( fnRanges ),
74         bRefresh    ( false )
75     {
76     }
77 };
78 
79 SfxTabDialogItem::SfxTabDialogItem( const SfxTabDialogItem& rAttr, SfxItemPool* pItemPool )
80     : SfxSetItem( rAttr, pItemPool )
81 {
82 }
83 
84 SfxTabDialogItem::SfxTabDialogItem( sal_uInt16 nId, const SfxItemSet& rItemSet )
85     : SfxSetItem( nId, rItemSet )
86 {
87 }
88 
89 SfxPoolItem* SfxTabDialogItem::Clone(SfxItemPool* pToPool) const
90 {
91     return new SfxTabDialogItem( *this, pToPool );
92 }
93 
94 typedef std::vector<Data_Impl*> SfxTabDlgData_Impl;
95 
96 struct TabDlg_Impl
97 {
98     bool                bHideResetBtn : 1;
99     bool                bStarted : 1;
100     SfxTabDlgData_Impl  aData;
101 
102     explicit TabDlg_Impl(sal_uInt8 nCnt)
103         : bHideResetBtn(false)
104         , bStarted(false)
105     {
106         aData.reserve( nCnt );
107     }
108 };
109 
110 static Data_Impl* Find( const SfxTabDlgData_Impl& rArr, const OString& rId, sal_uInt16* pPos = nullptr)
111 {
112     const sal_uInt16 nCount = rArr.size();
113 
114     for ( sal_uInt16 i = 0; i < nCount; ++i )
115     {
116         Data_Impl* pObj = rArr[i];
117 
118         if ( pObj->sId == rId )
119         {
120             if ( pPos )
121                 *pPos = i;
122             return pObj;
123         }
124     }
125     return nullptr;
126 }
127 
128 void SfxTabPage::SetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame)
129 {
130     if (pImpl)
131         pImpl->mxFrame = xFrame;
132 }
133 
134 css::uno::Reference< css::frame::XFrame > SfxTabPage::GetFrame() const
135 {
136     if (pImpl)
137         return pImpl->mxFrame;
138     return css::uno::Reference< css::frame::XFrame >();
139 }
140 
141 SfxTabPage::SfxTabPage(weld::Container* pPage, weld::DialogController* pController, const OUString& rUIXMLDescription, const OString& rID, const SfxItemSet *rAttrSet)
142     : BuilderPage(pPage, pController, rUIXMLDescription, rID)
143     , pSet                ( rAttrSet )
144     , bHasExchangeSupport ( false )
145     , pImpl               ( new TabPageImpl )
146 {
147     pImpl->mpSfxDialogController = dynamic_cast<SfxOkDialogController*>(m_pDialogController);
148 }
149 
150 SfxTabPage::~SfxTabPage()
151 {
152     if (m_xContainer)
153     {
154         std::unique_ptr<weld::Container> xParent(m_xContainer->weld_parent());
155         if (xParent)
156             xParent->move(m_xContainer.get(), nullptr);
157     }
158     m_xContainer.reset();
159     pImpl.reset();
160     m_xBuilder.reset();
161 }
162 
163 bool SfxTabPage::FillItemSet( SfxItemSet* )
164 {
165     return false;
166 }
167 
168 void SfxTabPage::Reset( const SfxItemSet* )
169 {
170 }
171 
172 void SfxTabPage::ActivatePage( const SfxItemSet& )
173 /*  [Description]
174 
175     Default implementation of the virtual ActivatePage method. This method is
176     called when a page of dialogue supports the exchange of data between pages.
177     <SfxTabPage::DeactivatePage(SfxItemSet *)>
178 */
179 {
180 }
181 
182 DeactivateRC SfxTabPage::DeactivatePage( SfxItemSet* )
183 
184 /*  [Description]
185 
186     Default implementation of the virtual DeactivatePage method. This method is
187     called by Sfx when leaving a page; the application can, through the return
188     value, control whether to leave the page. If the page is displayed through
189     bHasExchangeSupport which supports data exchange between pages, then a
190     pointer to the exchange set is passed as parameter. This takes on data for
191     the exchange, then the set is available as a parameter in
192     <SfxTabPage::ActivatePage(const SfxItemSet &)>.
193 
194     [Return value]
195 
196     DeactivateRC::LeavePage; Allow leaving the page
197 */
198 
199 {
200     return DeactivateRC::LeavePage;
201 }
202 
203 
204 void SfxTabPage::FillUserData()
205 
206 /*  [Description]
207 
208     Virtual method is called by the base class in the destructor to save
209     specific information of the TabPage in the ini-file. When overriding a
210     string must be compiled, which is then flushed with the <SetUserData()>.
211 */
212 
213 {
214 }
215 
216 
217 bool SfxTabPage::IsReadOnly() const
218 {
219     return false;
220 }
221 
222 
223 const SfxPoolItem* SfxTabPage::GetItem( const SfxItemSet& rSet, sal_uInt16 nSlot, bool bDeep )
224 
225 /*  [Description]
226 
227     static Method: hereby are the implementations of the TabPage code
228     being simplified.
229 */
230 
231 {
232     const SfxItemPool* pPool = rSet.GetPool();
233     sal_uInt16 nWh = pPool->GetWhich( nSlot, bDeep );
234     const SfxPoolItem* pItem = nullptr;
235     rSet.GetItemState( nWh, true, &pItem );
236 
237     if ( !pItem && nWh != nSlot )
238         pItem = &pPool->GetDefaultItem( nWh );
239     return pItem;
240 }
241 
242 
243 const SfxPoolItem* SfxTabPage::GetOldItem( const SfxItemSet& rSet,
244                                            sal_uInt16 nSlot, bool bDeep )
245 
246 /*  [Description]
247 
248     This method returns an attribute for comparison of the old value.
249 */
250 
251 {
252     const SfxItemSet& rOldSet = GetItemSet();
253     sal_uInt16 nWh = GetWhich( nSlot, bDeep );
254     const SfxPoolItem* pItem = nullptr;
255 
256     if ( pImpl->mbStandard && rOldSet.GetParent() )
257         pItem = GetItem( *rOldSet.GetParent(), nSlot );
258     else if ( rSet.GetParent() &&
259               SfxItemState::DONTCARE == rSet.GetItemState( nWh ) )
260         pItem = GetItem( *rSet.GetParent(), nSlot );
261     else
262         pItem = GetItem( rOldSet, nSlot );
263     return pItem;
264 }
265 
266 void SfxTabPage::PageCreated( const SfxAllItemSet& /*aSet*/ )
267 {
268     SAL_WARN( "sfx.dialog", "SfxTabPage::PageCreated should not be called");
269 }
270 
271 void SfxTabPage::ChangesApplied()
272 {
273 }
274 
275 void SfxTabPage::SetDialogController(SfxOkDialogController* pDialog)
276 {
277     pImpl->mpSfxDialogController = pDialog;
278     m_pDialogController = pImpl->mpSfxDialogController;
279 }
280 
281 SfxOkDialogController* SfxTabPage::GetDialogController() const
282 {
283     return pImpl->mpSfxDialogController;
284 }
285 
286 OString SfxTabPage::GetHelpId() const
287 {
288     if (m_xContainer)
289         return m_xContainer->get_help_id();
290     return OString();
291 }
292 
293 weld::Window* SfxTabPage::GetFrameWeld() const
294 {
295     if (m_pDialogController)
296         return m_pDialogController->getDialog();
297     return nullptr;
298 }
299 
300 const SfxItemSet* SfxTabPage::GetDialogExampleSet() const
301 {
302     if (pImpl->mpSfxDialogController)
303         return pImpl->mpSfxDialogController->GetExampleSet();
304     return nullptr;
305 }
306 
307 SfxTabDialogController::SfxTabDialogController
308 (
309     weld::Window* pParent,              // Parent Window
310     const OUString& rUIXMLDescription, const OString& rID, // Dialog .ui path, Dialog Name
311     const SfxItemSet* pItemSet,   // Itemset with the data;
312                                   // can be NULL, when Pages are onDemand
313     bool bEditFmt                 // when yes -> additional Button for standard
314 )
315     : SfxOkDialogController(pParent, rUIXMLDescription, rID)
316     , m_xTabCtrl(m_xBuilder->weld_notebook("tabcontrol"))
317     , m_xOKBtn(m_xBuilder->weld_button("ok"))
318     , m_xApplyBtn(m_xBuilder->weld_button("apply"))
319     , m_xUserBtn(m_xBuilder->weld_button("user"))
320     , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
321     , m_xResetBtn(m_xBuilder->weld_button("reset"))
322     , m_xBaseFmtBtn(m_xBuilder->weld_button("standard"))
323     , m_pSet(pItemSet ? new SfxItemSet(*pItemSet) : nullptr)
324     , m_bStandardPushed(false)
325 {
326     m_pImpl.reset(new TabDlg_Impl(m_xTabCtrl->get_n_pages()));
327     m_pImpl->bHideResetBtn = !m_xResetBtn->get_visible();
328     m_xOKBtn->connect_clicked(LINK(this, SfxTabDialogController, OkHdl));
329     m_xCancelBtn->connect_clicked(LINK(this, SfxTabDialogController, CancelHdl));
330     m_xResetBtn->connect_clicked(LINK(this, SfxTabDialogController, ResetHdl));
331     m_xResetBtn->set_label(SfxResId(STR_RESET));
332     m_xTabCtrl->connect_enter_page(LINK(this, SfxTabDialogController, ActivatePageHdl));
333     m_xTabCtrl->connect_leave_page(LINK(this, SfxTabDialogController, DeactivatePageHdl));
334     m_xResetBtn->set_help_id(HID_TABDLG_RESET_BTN);
335 
336     if (bEditFmt)
337     {
338         m_xBaseFmtBtn->set_label(SfxResId(STR_STANDARD_SHORTCUT));
339         m_xBaseFmtBtn->connect_clicked(LINK(this, SfxTabDialogController, BaseFmtHdl));
340         m_xBaseFmtBtn->set_help_id(HID_TABDLG_STANDARD_BTN);
341         m_xBaseFmtBtn->show();
342     }
343 
344     if (m_xUserBtn)
345         m_xUserBtn->connect_clicked(LINK(this, SfxTabDialogController, UserHdl));
346 
347     if (m_pSet)
348     {
349         m_xExampleSet.reset(new SfxItemSet(*m_pSet));
350         m_pOutSet.reset(new SfxItemSet(*m_pSet->GetPool(), m_pSet->GetRanges()));
351     }
352 }
353 
354 IMPL_LINK_NOARG(SfxTabDialogController, OkHdl, weld::Button&, void)
355 
356 /*  [Description]
357 
358     Handler of the Ok-Buttons
359     This calls the current page <SfxTabPage::DeactivatePage(SfxItemSet *)>.
360     Returns <DeactivateRC::LeavePage>, <SfxTabDialog::Ok()> is called
361     and the Dialog is ended.
362 */
363 
364 {
365     if (PrepareLeaveCurrentPage())
366         m_xDialog->response(Ok());
367 }
368 
369 IMPL_LINK_NOARG(SfxTabDialogController, UserHdl, weld::Button&, void)
370 
371 /*  [Description]
372 
373     Handler of the User-Buttons
374     This calls the current page <SfxTabPage::DeactivatePage(SfxItemSet *)>.
375     returns this <DeactivateRC::LeavePage> and  <SfxTabDialog::Ok()> is called.
376     Then the Dialog is ended with the Return value <SfxTabDialog::Ok()>
377 */
378 
379 {
380     if (PrepareLeaveCurrentPage())
381     {
382         short nRet = Ok();
383         if (RET_OK == nRet)
384             nRet = RET_USER;
385         else
386             nRet = RET_CANCEL;
387         m_xDialog->response(nRet);
388     }
389 }
390 
391 IMPL_LINK_NOARG(SfxTabDialogController, CancelHdl, weld::Button&, void)
392 {
393     m_xDialog->response(RET_CANCEL);
394 }
395 
396 IMPL_LINK_NOARG(SfxTabDialogController, ResetHdl, weld::Button&, void)
397 
398 /*  [Description]
399 
400     Handler behind the reset button.
401     The Current Page is new initialized with their initial data, all the
402     settings that the user has made on this page are repealed.
403 */
404 
405 {
406     Data_Impl* pDataObject = Find(m_pImpl->aData, m_xTabCtrl->get_current_page_ident());
407     assert(pDataObject && "Id not known");
408 
409     pDataObject->xTabPage->Reset(m_pSet.get());
410     // Also reset relevant items of ExampleSet and OutSet to initial state
411     if (!pDataObject->fnGetRanges)
412         return;
413 
414     if (!m_xExampleSet)
415         m_xExampleSet.reset(new SfxItemSet(*m_pSet));
416 
417     const SfxItemPool* pPool = m_pSet->GetPool();
418     const sal_uInt16* pTmpRanges = (pDataObject->fnGetRanges)();
419 
420     while (*pTmpRanges)
421     {
422         const sal_uInt16* pU = pTmpRanges + 1;
423 
424         // Correct Range with multiple values
425         sal_uInt16 nTmp = *pTmpRanges, nTmpEnd = *pU;
426         DBG_ASSERT(nTmp <= nTmpEnd, "Range is sorted the wrong way");
427 
428         if (nTmp > nTmpEnd)
429         {
430             // If really sorted wrongly, then set new
431             std::swap(nTmp, nTmpEnd);
432         }
433 
434         while (nTmp && nTmp <= nTmpEnd)
435         {
436             // Iterate over the Range and set the Items
437             sal_uInt16 nWh = pPool->GetWhich(nTmp);
438             const SfxPoolItem* pItem;
439             if (SfxItemState::SET == m_pSet->GetItemState(nWh, false, &pItem))
440             {
441                 m_xExampleSet->Put(*pItem);
442                 m_pOutSet->Put(*pItem);
443             }
444             else
445             {
446                 m_xExampleSet->ClearItem(nWh);
447                 m_pOutSet->ClearItem(nWh);
448             }
449             nTmp++;
450         }
451         // Go to the next pair
452         pTmpRanges += 2;
453     }
454 }
455 
456 /*  [Description]
457 
458     Handler behind the Standard-Button.
459     This button is available when editing style sheets. All the set attributes
460     in the edited stylesheet are deleted.
461 */
462 IMPL_LINK_NOARG(SfxTabDialogController, BaseFmtHdl, weld::Button&, void)
463 {
464     m_bStandardPushed = true;
465 
466     Data_Impl* pDataObject = Find(m_pImpl->aData, m_xTabCtrl->get_current_page_ident());
467     assert(pDataObject && "Id not known");
468 
469     if (!pDataObject->fnGetRanges)
470         return;
471 
472     if (!m_xExampleSet)
473         m_xExampleSet.reset(new SfxItemSet(*m_pSet));
474 
475     const SfxItemPool* pPool = m_pSet->GetPool();
476     const sal_uInt16* pTmpRanges = (pDataObject->fnGetRanges)();
477     SfxItemSet aTmpSet(*m_xExampleSet);
478 
479     while (*pTmpRanges)
480     {
481         const sal_uInt16* pU = pTmpRanges + 1;
482 
483         // Correct Range with multiple values
484         sal_uInt16 nTmp = *pTmpRanges, nTmpEnd = *pU;
485         DBG_ASSERT( nTmp <= nTmpEnd, "Range is sorted the wrong way" );
486 
487         if ( nTmp > nTmpEnd )
488         {
489             // If really sorted wrongly, then set new
490             std::swap(nTmp, nTmpEnd);
491         }
492 
493         while ( nTmp && nTmp <= nTmpEnd ) // guard against overflow
494         {
495             // Iterate over the Range and set the Items
496             sal_uInt16 nWh = pPool->GetWhich(nTmp);
497             m_xExampleSet->ClearItem(nWh);
498             aTmpSet.ClearItem(nWh);
499             // At the Outset of InvalidateItem,
500             // so that the change takes effect
501             m_pOutSet->InvalidateItem(nWh);
502             nTmp++;
503         }
504         // Go to the next pair
505         pTmpRanges += 2;
506     }
507     // Set all Items as new  -> the call the current Page Reset()
508     assert(pDataObject->xTabPage && "the Page is gone");
509     pDataObject->xTabPage->Reset( &aTmpSet );
510     pDataObject->xTabPage->pImpl->mbStandard = true;
511 }
512 
513 IMPL_LINK(SfxTabDialogController, ActivatePageHdl, const OString&, rPage, void)
514 
515 /*  [Description]
516 
517     Handler that is called by StarView for switching to a different page.
518     If possible the <SfxTabPage::Reset(const SfxItemSet &)> or
519     <SfxTabPage::ActivatePage(const SfxItemSet &)> is called on the new page
520 */
521 
522 {
523     assert(!m_pImpl->aData.empty() && "no Pages registered");
524     Data_Impl* pDataObject = Find(m_pImpl->aData, rPage);
525     if (!pDataObject)
526     {
527         SAL_WARN("sfx.dialog", "Tab Page ID not known, this is pretty serious and needs investigation");
528         return;
529     }
530 
531     SfxTabPage* pTabPage = pDataObject->xTabPage.get();
532     if (!pTabPage)
533         return;
534 
535     if (pDataObject->bRefresh)
536         pTabPage->Reset(m_pSet.get());
537     pDataObject->bRefresh = false;
538 
539     if (m_xExampleSet)
540         pTabPage->ActivatePage(*m_xExampleSet);
541 
542     if (pTabPage->IsReadOnly() || m_pImpl->bHideResetBtn)
543         m_xResetBtn->hide();
544     else
545         m_xResetBtn->show();
546 }
547 
548 IMPL_LINK(SfxTabDialogController, DeactivatePageHdl, const OString&, rPage, bool)
549 
550 /*  [Description]
551 
552     Handler that is called by StarView before leaving a page.
553 
554     [Cross-reference]
555 
556     <SfxTabPage::DeactivatePage(SfxItemSet *)>
557 */
558 
559 {
560     assert(!m_pImpl->aData.empty() && "no Pages registered");
561     Data_Impl* pDataObject = Find(m_pImpl->aData, rPage);
562     if (!pDataObject)
563     {
564         SAL_WARN("sfx.dialog", "Tab Page ID not known, this is pretty serious and needs investigation");
565         return false;
566     }
567 
568     SfxTabPage* pPage = pDataObject->xTabPage.get();
569     if (!pPage)
570         return true;
571 
572     DeactivateRC nRet = DeactivateRC::LeavePage;
573 
574     if (!m_xExampleSet && pPage->HasExchangeSupport() && m_pSet)
575         m_xExampleSet.reset(new SfxItemSet(*m_pSet->GetPool(), m_pSet->GetRanges()));
576 
577     if (m_pSet)
578     {
579         SfxItemSet aTmp( *m_pSet->GetPool(), m_pSet->GetRanges() );
580 
581         if (pPage->HasExchangeSupport())
582             nRet = pPage->DeactivatePage(&aTmp);
583         else
584             nRet = pPage->DeactivatePage(nullptr);
585         if ( ( DeactivateRC::LeavePage & nRet ) == DeactivateRC::LeavePage &&
586              aTmp.Count() && m_xExampleSet)
587         {
588             m_xExampleSet->Put( aTmp );
589             m_pOutSet->Put( aTmp );
590         }
591     }
592     else
593     {
594         if ( pPage->HasExchangeSupport() ) //!!!
595         {
596             if (!m_xExampleSet)
597             {
598                 SfxItemPool* pPool = pPage->GetItemSet().GetPool();
599                 m_xExampleSet.reset(new SfxItemSet(*pPool, GetInputRanges(*pPool)));
600             }
601             nRet = pPage->DeactivatePage(m_xExampleSet.get());
602         }
603         else
604             nRet = pPage->DeactivatePage( nullptr );
605     }
606 
607     if ( nRet & DeactivateRC::RefreshSet )
608     {
609         RefreshInputSet();
610         // Flag all Pages as to be initialized as new
611 
612         for (auto const& elem : m_pImpl->aData)
613         {
614             elem->bRefresh = ( elem->xTabPage.get() != pPage ); // Do not refresh own Page anymore
615         }
616     }
617     return static_cast<bool>(nRet & DeactivateRC::LeavePage);
618 }
619 
620 bool SfxTabDialogController::PrepareLeaveCurrentPage()
621 {
622     const OString sId = m_xTabCtrl->get_current_page_ident();
623     Data_Impl* pDataObject = Find(m_pImpl->aData, sId);
624     DBG_ASSERT( pDataObject, "Id not known" );
625     SfxTabPage* pPage = pDataObject ? pDataObject->xTabPage.get() : nullptr;
626 
627     bool bEnd = !pPage;
628 
629     if ( pPage )
630     {
631         DeactivateRC nRet = DeactivateRC::LeavePage;
632         if ( m_pSet )
633         {
634             SfxItemSet aTmp( *m_pSet->GetPool(), m_pSet->GetRanges() );
635 
636             if ( pPage->HasExchangeSupport() )
637                 nRet = pPage->DeactivatePage( &aTmp );
638             else
639                 nRet = pPage->DeactivatePage( nullptr );
640 
641             if ( ( DeactivateRC::LeavePage & nRet ) == DeactivateRC::LeavePage
642                  && aTmp.Count() )
643             {
644                 m_xExampleSet->Put( aTmp );
645                 m_pOutSet->Put( aTmp );
646             }
647         }
648         else
649             nRet = pPage->DeactivatePage( nullptr );
650         bEnd = nRet != DeactivateRC::KeepPage;
651     }
652 
653     return bEnd;
654 }
655 
656 const sal_uInt16* SfxTabDialogController::GetInputRanges(const SfxItemPool& rPool)
657 
658 /*  [Description]
659 
660     Makes the set over the range of all pages of the dialogue. Pages have the
661     static method for querying their range in AddTabPage, ie deliver their
662     sets onDemand.
663 
664     [Return value]
665 
666     Pointer to a null-terminated array of sal_uInt16. This array belongs to the
667     dialog and is deleted when the dialogue is destroy.
668 
669     [Cross-reference]
670 
671     <SfxTabDialog::AddTabPage(sal_uInt16, CreateTabPage, GetTabPageRanges, bool)>
672     <SfxTabDialog::AddTabPage(sal_uInt16, const String &, CreateTabPage, GetTabPageRanges, bool, sal_uInt16)>
673     <SfxTabDialog::AddTabPage(sal_uInt16, const Bitmap &, CreateTabPage, GetTabPageRanges, bool, sal_uInt16)>
674 */
675 
676 {
677     if ( m_pSet )
678     {
679         SAL_WARN( "sfx.dialog", "Set already exists!" );
680         return m_pSet->GetRanges();
681     }
682 
683     if ( m_pRanges )
684         return m_pRanges.get();
685     std::vector<sal_uInt16> aUS;
686 
687     for (auto const& elem : m_pImpl->aData)
688     {
689 
690         if ( elem->fnGetRanges )
691         {
692             const sal_uInt16* pTmpRanges = (elem->fnGetRanges)();
693             const sal_uInt16* pIter = pTmpRanges;
694 
695             sal_uInt16 nLen;
696             for( nLen = 0; *pIter; ++nLen, ++pIter )
697                 ;
698             aUS.insert( aUS.end(), pTmpRanges, pTmpRanges + nLen );
699         }
700     }
701 
702     //! Remove duplicated Ids?
703     {
704         for (auto & elem : aUS)
705             elem = rPool.GetWhich(elem);
706     }
707 
708     // sort
709     if ( aUS.size() > 1 )
710     {
711         std::sort( aUS.begin(), aUS.end() );
712     }
713 
714     m_pRanges.reset(new sal_uInt16[aUS.size() + 1]);
715     std::copy( aUS.begin(), aUS.end(), m_pRanges.get() );
716     m_pRanges[aUS.size()] = 0;
717     return m_pRanges.get();
718 }
719 
720 SfxTabDialogController::~SfxTabDialogController()
721 {
722     SavePosAndId();
723 
724     for (auto & elem : m_pImpl->aData)
725     {
726         if ( elem->xTabPage )
727         {
728             // save settings of all pages (user data)
729             elem->xTabPage->FillUserData();
730             OUString aPageData( elem->xTabPage->GetUserData() );
731             if ( !aPageData.isEmpty() )
732             {
733                 // save settings of all pages (user data)
734                 OUString sConfigId = OStringToOUString(elem->xTabPage->GetConfigId(),
735                     RTL_TEXTENCODING_UTF8);
736                 SvtViewOptions aPageOpt(EViewType::TabPage, sConfigId);
737                 aPageOpt.SetUserItem( USERITEM_NAME, makeAny( aPageData ) );
738             }
739 
740             elem->xTabPage.reset();
741         }
742         delete elem;
743         elem = nullptr;
744     }
745 }
746 
747 short SfxTabDialogController::Ok()
748 
749 /*  [Description]
750 
751     Ok handler for the Dialogue.
752 
753     Dialog's current location and current page are saved for the next time
754     the dialog is shown.
755 
756     The OutputSet is created and for each page this or the special OutputSet
757     is set by calling the method <SfxTabPage::FillItemSet(SfxItemSet &)>, to
758     insert the entered data by the user into the set.
759 
760     [Return value]
761 
762     RET_OK:       if at least one page has returned from FillItemSet,
763                   otherwise RET_CANCEL.
764 */
765 {
766     SavePosAndId(); //See fdo#38828 "Apply" resetting window position
767 
768     if ( !m_pOutSet )
769     {
770         if ( m_xExampleSet )
771             m_pOutSet.reset(new SfxItemSet( *m_xExampleSet ));
772         else if ( m_pSet )
773             m_pOutSet = m_pSet->Clone( false );  // without Items
774     }
775     bool bModified = false;
776 
777     for (auto const& elem : m_pImpl->aData)
778     {
779         SfxTabPage* pTabPage = elem->xTabPage.get();
780 
781         if ( pTabPage )
782         {
783             if ( m_pSet && !pTabPage->HasExchangeSupport() )
784             {
785                 SfxItemSet aTmp( *m_pSet->GetPool(), m_pSet->GetRanges() );
786 
787                 if ( pTabPage->FillItemSet( &aTmp ) )
788                 {
789                     bModified = true;
790                     if (m_xExampleSet)
791                         m_xExampleSet->Put( aTmp );
792                     m_pOutSet->Put( aTmp );
793                 }
794             }
795         }
796     }
797 
798     if (m_pOutSet && m_pOutSet->Count() > 0)
799         bModified = true;
800 
801     if (m_bStandardPushed)
802         bModified = true;
803 
804     return bModified ? RET_OK : RET_CANCEL;
805 }
806 
807 void SfxTabDialogController::RefreshInputSet()
808 
809 /*  [Description]
810 
811     Default implementation of the virtual Method.
812     This is called, when <SfxTabPage::DeactivatePage(SfxItemSet *)>
813     returns <DeactivateRC::RefreshSet>.
814 */
815 
816 {
817     SAL_INFO ( "sfx.dialog", "RefreshInputSet not implemented" );
818 }
819 
820 void SfxTabDialogController::PageCreated
821 
822 /*  [Description]
823 
824     Default implementation of the virtual method. This is called immediately
825     after creating a page. Here the dialogue can call the TabPage Method
826     directly.
827 */
828 
829 (
830     const OString&, // Id of the created page
831     SfxTabPage&     // Reference to the created page
832 )
833 {
834 }
835 
836 void SfxTabDialogController::SavePosAndId()
837 {
838     // save settings (screen position and current page)
839     SvtViewOptions aDlgOpt(EViewType::TabDialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8));
840     aDlgOpt.SetPageID(m_xTabCtrl->get_current_page_ident());
841 }
842 
843 /*
844     Adds a page to the dialog. The Name must correspond to an entry in the
845     TabControl in the dialog .ui
846 */
847 void SfxTabDialogController::AddTabPage(const OString &rName /* Page ID */,
848                                         CreateTabPage pCreateFunc  /* Pointer to the Factory Method */,
849                                         GetTabPageRanges pRangesFunc /* Pointer to the Method for querying Ranges onDemand */)
850 {
851     m_pImpl->aData.push_back(new Data_Impl(rName, pCreateFunc, pRangesFunc));
852 }
853 
854 void SfxTabDialogController::AddTabPage(const OString &rName /* Page ID */,
855                                         sal_uInt16 nPageCreateId /* Identifier of the Factory Method to create the page */)
856 {
857     SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
858     CreateTabPage pCreateFunc = pFact->GetTabPageCreatorFunc(nPageCreateId);
859     GetTabPageRanges pRangesFunc = pFact->GetTabPageRangesFunc(nPageCreateId);
860     AddTabPage(rName, pCreateFunc, pRangesFunc);
861 }
862 
863 /*  [Description]
864 
865     Add a page to the dialog. The Rider text is passed on, the page has no
866     counterpart in the TabControl in the resource of the dialogue.
867 */
868 
869 void SfxTabDialogController::AddTabPage(const OString &rName, /* Page ID */
870                                         const OUString& rRiderText,
871                                         CreateTabPage pCreateFunc  /* Pointer to the Factory Method */)
872 {
873     assert(!m_xTabCtrl->get_page(rName) && "Double Page-Ids in the Tabpage");
874     m_xTabCtrl->append_page(rName, rRiderText);
875     AddTabPage(rName, pCreateFunc, nullptr);
876 }
877 
878 void SfxTabDialogController::AddTabPage(const OString &rName, const OUString& rRiderText,
879                                         sal_uInt16 nPageCreateId /* Identifier of the Factory Method to create the page */)
880 {
881     assert(!m_xTabCtrl->get_page(rName) && "Double Page-Ids in the Tabpage");
882     m_xTabCtrl->append_page(rName, rRiderText);
883     AddTabPage(rName, nPageCreateId);
884 }
885 
886 /*  [Description]
887 
888     Default implementation of the virtual Method.
889     This is called when pages create their sets onDemand.
890 */
891 SfxItemSet* SfxTabDialogController::CreateInputItemSet(const OString&)
892 {
893     SAL_WARN( "sfx.dialog", "CreateInputItemSet not implemented" );
894     return new SfxAllItemSet(SfxGetpApp()->GetPool());
895 }
896 
897 void SfxTabDialogController::CreatePages()
898 {
899     for (auto pDataObject : m_pImpl->aData)
900     {
901         if (pDataObject->xTabPage)
902            continue;
903         weld::Container* pPage = m_xTabCtrl->get_page(pDataObject->sId);
904         if (m_pSet)
905             pDataObject->xTabPage = (pDataObject->fnCreatePage)(pPage, this, m_pSet.get());
906         else
907             pDataObject->xTabPage = (pDataObject->fnCreatePage)(pPage, this, CreateInputItemSet(pDataObject->sId));
908         pDataObject->xTabPage->SetDialogController(this);
909         OUString sConfigId = OStringToOUString(pDataObject->xTabPage->GetConfigId(), RTL_TEXTENCODING_UTF8);
910         SvtViewOptions aPageOpt(EViewType::TabPage, sConfigId);
911         OUString sUserData;
912         Any aUserItem = aPageOpt.GetUserItem(USERITEM_NAME);
913         OUString aTemp;
914         if ( aUserItem >>= aTemp )
915             sUserData = aTemp;
916         pDataObject->xTabPage->SetUserData(sUserData);
917 
918         PageCreated(pDataObject->sId, *pDataObject->xTabPage);
919         pDataObject->xTabPage->Reset(m_pSet.get());
920     }
921 }
922 
923 void SfxTabDialogController::setPreviewsToSamePlace()
924 {
925     //where tab pages have the same basic layout with a preview on the right,
926     //get both of their non-preview areas to request the same size so that the
927     //preview appears in the same place in each one so flipping between tabs
928     //isn't distracting as it jumps around
929     std::vector<std::unique_ptr<weld::Widget>> aGrids;
930     for (auto pDataObject : m_pImpl->aData)
931     {
932         if (!pDataObject->xTabPage)
933             continue;
934         if (!pDataObject->xTabPage->m_xBuilder)
935             continue;
936         std::unique_ptr<weld::Widget> pGrid = pDataObject->xTabPage->m_xBuilder->weld_widget("maingrid");
937         if (!pGrid)
938             continue;
939         aGrids.emplace_back(std::move(pGrid));
940     }
941 
942     m_xSizeGroup.reset();
943 
944     if (aGrids.size() <= 1)
945         return;
946 
947     m_xSizeGroup = m_xBuilder->create_size_group();
948     for (auto& rGrid : aGrids)
949         m_xSizeGroup->add_widget(rGrid.get());
950 }
951 
952 void SfxTabDialogController::RemoveTabPage(const OString& rId)
953 
954 /*  [Description]
955 
956     Delete the TabPage with ID nId
957 */
958 
959 {
960     sal_uInt16 nPos = 0;
961     m_xTabCtrl->remove_page(rId);
962     Data_Impl* pDataObject = Find( m_pImpl->aData, rId, &nPos );
963 
964     if ( pDataObject )
965     {
966         if ( pDataObject->xTabPage )
967         {
968             pDataObject->xTabPage->FillUserData();
969             OUString aPageData( pDataObject->xTabPage->GetUserData() );
970             if ( !aPageData.isEmpty() )
971             {
972                 // save settings of this page (user data)
973                 OUString sConfigId = OStringToOUString(pDataObject->xTabPage->GetConfigId(),
974                     RTL_TEXTENCODING_UTF8);
975                 SvtViewOptions aPageOpt(EViewType::TabPage, sConfigId);
976                 aPageOpt.SetUserItem( USERITEM_NAME, makeAny( aPageData ) );
977             }
978 
979             pDataObject->xTabPage.reset();
980         }
981 
982         delete pDataObject;
983         m_pImpl->aData.erase( m_pImpl->aData.begin() + nPos );
984     }
985     else
986     {
987         SAL_INFO( "sfx.dialog", "TabPage-Id not known" );
988     }
989 }
990 
991 void SfxTabDialogController::Start_Impl()
992 {
993     CreatePages();
994 
995     setPreviewsToSamePlace();
996 
997     assert(m_pImpl->aData.size() == static_cast<size_t>(m_xTabCtrl->get_n_pages())
998             && "not all pages registered");
999 
1000     // load old settings, when exists, setting SetCurPageId will override the settings,
1001     // something that the sort dialog in calc depends on
1002     if (m_sAppPageId.isEmpty())
1003     {
1004         SvtViewOptions aDlgOpt(EViewType::TabDialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8));
1005         if (aDlgOpt.Exists())
1006             m_xTabCtrl->set_current_page(aDlgOpt.GetPageID());
1007     }
1008 
1009     ActivatePageHdl(m_xTabCtrl->get_current_page_ident());
1010 
1011     m_pImpl->bStarted = true;
1012 }
1013 
1014 void SfxTabDialogController::SetCurPageId(const OString& rIdent)
1015 {
1016     m_sAppPageId = rIdent;
1017     m_xTabCtrl->set_current_page(m_sAppPageId);
1018 }
1019 
1020 /*  [Description]
1021 
1022     The TabPage is activated with the specified Id.
1023 */
1024 void SfxTabDialogController::ShowPage(const OString& rIdent)
1025 {
1026     SetCurPageId(rIdent);
1027     ActivatePageHdl(rIdent);
1028 }
1029 
1030 OString SfxTabDialogController::GetCurPageId() const
1031 {
1032     return m_xTabCtrl->get_current_page_ident();
1033 }
1034 
1035 short SfxTabDialogController::run()
1036 {
1037     Start_Impl();
1038     return SfxDialogController::run();
1039 }
1040 
1041 bool SfxTabDialogController::runAsync(const std::shared_ptr<SfxTabDialogController>& rController,
1042                                       const std::function<void(sal_Int32)>& rFunc)
1043 {
1044     rController->Start_Impl();
1045     return weld::DialogController::runAsync(rController, rFunc);
1046 }
1047 
1048 void SfxTabDialogController::SetInputSet( const SfxItemSet* pInSet )
1049 
1050 /*  [Description]
1051 
1052     With this method the Input-Set can subsequently be set initially or re-set.
1053 */
1054 
1055 {
1056     bool bSet = ( m_pSet != nullptr );
1057     m_pSet.reset(pInSet ? new SfxItemSet(*pInSet) : nullptr);
1058 
1059     if (!bSet && !m_xExampleSet && !m_pOutSet && m_pSet)
1060     {
1061         m_xExampleSet.reset(new SfxItemSet(*m_pSet));
1062         m_pOutSet.reset(new SfxItemSet( *m_pSet->GetPool(), m_pSet->GetRanges() ));
1063     }
1064 }
1065 
1066 SfxItemSet* SfxTabDialogController::GetInputSetImpl()
1067 
1068 /*  [Description]
1069 
1070     Derived classes may create new storage for the InputSet. This has to be
1071     released in the Destructor. To do this, this method must be called.
1072 */
1073 
1074 {
1075     return m_pSet.get();
1076 }
1077 
1078 void SfxTabDialogController::RemoveResetButton()
1079 {
1080     m_xResetBtn->hide();
1081     m_pImpl->bHideResetBtn = true;
1082 }
1083 
1084 void SfxTabDialogController::RemoveStandardButton()
1085 {
1086     m_xBaseFmtBtn->hide();
1087 }
1088 
1089 SfxTabPage* SfxTabDialogController::GetTabPage(const OString& rPageId) const
1090 
1091 /*  [Description]
1092 
1093     Return TabPage with the specified Id.
1094 */
1095 
1096 {
1097     Data_Impl* pDataObject = Find(m_pImpl->aData, rPageId);
1098     if (pDataObject)
1099         return pDataObject->xTabPage.get();
1100     return nullptr;
1101 }
1102 
1103 void SfxTabDialogController::SetApplyHandler(const Link<weld::Button&, void>& _rHdl)
1104 {
1105     DBG_ASSERT( m_xApplyBtn, "SfxTabDialog::GetApplyHandler: no apply button enabled!" );
1106     if (m_xApplyBtn)
1107         m_xApplyBtn->connect_clicked(_rHdl);
1108 }
1109 
1110 bool SfxTabDialogController::Apply()
1111 {
1112     bool bApplied = false;
1113     if (PrepareLeaveCurrentPage())
1114     {
1115         bApplied = (Ok() == RET_OK);
1116         //let the pages update their saved values
1117         GetInputSetImpl()->Put(*GetOutputItemSet());
1118         for (auto pDataObject : m_pImpl->aData)
1119         {
1120             if (!pDataObject->xTabPage)
1121                 continue;
1122             pDataObject->xTabPage->ChangesApplied();
1123         }
1124     }
1125     return bApplied;
1126 }
1127 
1128 std::vector<OString> SfxTabDialogController::getAllPageUIXMLDescriptions() const
1129 {
1130     int nPages = m_xTabCtrl->get_n_pages();
1131     std::vector<OString> aRet;
1132     aRet.reserve(nPages);
1133     for (int i = 0; i < nPages; ++i)
1134         aRet.push_back(m_xTabCtrl->get_page_ident(i));
1135     return aRet;
1136 }
1137 
1138 bool SfxTabDialogController::selectPageByUIXMLDescription(const OString& rUIXMLDescription)
1139 {
1140     ShowPage(rUIXMLDescription);
1141     return m_xTabCtrl->get_current_page_ident() == rUIXMLDescription;
1142 }
1143 
1144 BitmapEx SfxTabDialogController::createScreenshot() const
1145 {
1146     // if we haven't run Start_Impl yet, do so now to create the initial pages
1147     if (!m_pImpl->bStarted)
1148     {
1149         const_cast<SfxTabDialogController*>(this)->Start_Impl();
1150     }
1151 
1152     VclPtr<VirtualDevice> xDialogSurface(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT));
1153     m_xDialog->draw(*xDialogSurface);
1154     return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel());
1155 }
1156 
1157 OString SfxTabDialogController::GetScreenshotId() const
1158 {
1159     const OString sId = m_xTabCtrl->get_current_page_ident();
1160     Data_Impl* pDataObject = Find(m_pImpl->aData, sId);
1161     SfxTabPage* pPage = pDataObject ? pDataObject->xTabPage.get() : nullptr;
1162     if (pPage)
1163     {
1164         OString sHelpId(pPage->GetHelpId());
1165         if (!sHelpId.isEmpty())
1166             return sHelpId;
1167     }
1168     return m_xDialog->get_help_id();
1169 }
1170 
1171 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1172