1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <comphelper/lok.hxx>
21 #include <officecfg/Office/Common.hxx>
22 #include <vcl/event.hxx>
23 #include <tools/debug.hxx>
24 #include <comphelper/diagnose_ex.hxx>
25 #include <strings.hrc>
29 #include "wizimpldata.hxx"
31 constexpr OUString HID_WIZARD_NEXT
= u
"SVT_HID_WIZARD_NEXT"_ustr
;
32 constexpr OUString HID_WIZARD_PREVIOUS
= u
"SVT_HID_WIZARD_PREVIOUS"_ustr
;
34 #define WIZARDDIALOG_BUTTON_OFFSET_Y 6
35 #define WIZARDDIALOG_BUTTON_DLGOFFSET_X 6
36 #define WIZARDDIALOG_VIEW_DLGOFFSET_X 6
37 #define WIZARDDIALOG_VIEW_DLGOFFSET_Y 6
41 //= WizardPageImplData
42 OWizardPage::OWizardPage(weld::Container
* pPage
, weld::DialogController
* pController
, const OUString
& rUIXMLDescription
, const OUString
& rID
)
43 : BuilderPage(pPage
, pController
, rUIXMLDescription
, rID
)
47 OWizardPage::~OWizardPage()
51 void OWizardPage::initializePage()
55 void OWizardPage::Activate()
57 BuilderPage::Activate();
58 updateDialogTravelUI();
61 void OWizardPage::updateDialogTravelUI()
63 auto pWizardMachine
= dynamic_cast<RoadmapWizardMachine
*>(m_pDialogController
);
65 pWizardMachine
->updateTravelUI();
68 bool OWizardPage::canAdvance() const
73 bool OWizardPage::commitPage( WizardTypes::CommitPageReason
)
78 void RoadmapWizard::SetLeftAlignedButtonCount( sal_Int16 _nCount
)
80 mnLeftAlignCount
= _nCount
;
83 void RoadmapWizard::ImplCalcSize( Size
& rSize
)
85 // calculate ButtonBar height and width
86 tools::Long nMaxHeight
= 0;
87 tools::Long nBarWidth
= WIZARDDIALOG_BUTTON_DLGOFFSET_X
* 2 + LogicalCoordinateToPixel(6);
88 ImplWizButtonData
* pBtnData
= mpFirstBtn
;
91 auto nBtnHeight
= pBtnData
->mpButton
->GetSizePixel().Height();
92 auto nBtnWidth
= pBtnData
->mpButton
->GetSizePixel().Width();
93 if (pBtnData
->mpButton
->IsVisible())
95 nBarWidth
+= nBtnWidth
;
96 nBarWidth
+= pBtnData
->mnOffset
;
98 if ( nBtnHeight
> nMaxHeight
)
99 nMaxHeight
= nBtnHeight
;
100 pBtnData
= pBtnData
->mpNext
;
103 nMaxHeight
+= WIZARDDIALOG_BUTTON_OFFSET_Y
*2;
104 rSize
.AdjustHeight(nMaxHeight
);
106 // add in the view window size
107 if ( mpViewWindow
&& mpViewWindow
->IsVisible() )
109 Size aViewSize
= mpViewWindow
->GetSizePixel();
111 rSize
.AdjustWidth(aViewSize
.Width() );
114 if (nBarWidth
> rSize
.Width())
115 rSize
.setWidth(nBarWidth
);
118 void RoadmapWizard::queue_resize(StateChangedType
/*eReason*/)
120 if (maWizardLayoutIdle
.IsActive())
124 maWizardLayoutIdle
.Start();
127 IMPL_LINK_NOARG(RoadmapWizard
, ImplHandleWizardLayoutTimerHdl
, Timer
*, void)
133 void RoadmapWizard::ImplPosCtrls()
135 Size aDlgSize
= GetOutputSizePixel();
136 tools::Long nBtnWidth
= 0;
137 tools::Long nMaxHeight
= 0;
138 tools::Long nOffY
= aDlgSize
.Height();
140 ImplWizButtonData
* pBtnData
= mpFirstBtn
;
144 if (j
>= mnLeftAlignCount
)
146 Size aBtnSize
= pBtnData
->mpButton
->GetSizePixel();
147 tools::Long nBtnHeight
= aBtnSize
.Height();
148 if ( nBtnHeight
> nMaxHeight
)
149 nMaxHeight
= nBtnHeight
;
150 nBtnWidth
+= aBtnSize
.Width();
151 nBtnWidth
+= pBtnData
->mnOffset
;
153 pBtnData
= pBtnData
->mpNext
;
159 tools::Long nOffX
= aDlgSize
.Width()-nBtnWidth
-WIZARDDIALOG_BUTTON_DLGOFFSET_X
;
160 tools::Long nOffLeftAlignX
= LogicalCoordinateToPixel(6);
161 nOffY
-= WIZARDDIALOG_BUTTON_OFFSET_Y
+nMaxHeight
;
163 pBtnData
= mpFirstBtn
;
167 Size aBtnSize
= pBtnData
->mpButton
->GetSizePixel();
168 if (i
>= mnLeftAlignCount
)
170 Point
aPos( nOffX
, nOffY
+((nMaxHeight
-aBtnSize
.Height())/2) );
171 pBtnData
->mpButton
->SetPosPixel( aPos
);
172 nOffX
+= aBtnSize
.Width();
173 nOffX
+= pBtnData
->mnOffset
;
177 Point
aPos( nOffLeftAlignX
, nOffY
+((nMaxHeight
-aBtnSize
.Height())/2) );
178 pBtnData
->mpButton
->SetPosPixel( aPos
);
179 nOffLeftAlignX
+= aBtnSize
.Width();
180 nOffLeftAlignX
+= pBtnData
->mnOffset
;
183 pBtnData
= pBtnData
->mpNext
;
187 nOffY
-= WIZARDDIALOG_BUTTON_OFFSET_Y
;
190 if ( !(mpViewWindow
&& mpViewWindow
->IsVisible()) )
193 tools::Long nViewOffX
= 0;
194 tools::Long nViewOffY
= 0;
195 tools::Long nViewWidth
= 0;
196 tools::Long nViewHeight
= 0;
197 tools::Long nDlgHeight
= nOffY
;
198 PosSizeFlags nViewPosFlags
= PosSizeFlags::Pos
;
201 if ( mbEmptyViewMargin
)
205 nViewHeight
= nDlgHeight
;
209 nViewOffX
= WIZARDDIALOG_VIEW_DLGOFFSET_X
;
210 nViewOffY
= WIZARDDIALOG_VIEW_DLGOFFSET_Y
;
211 nViewHeight
= nDlgHeight
-(WIZARDDIALOG_VIEW_DLGOFFSET_Y
*2);
213 nViewPosFlags
|= PosSizeFlags::Height
;
215 mpViewWindow
->setPosSizePixel( nViewOffX
, nViewOffY
,
216 nViewWidth
, nViewHeight
,
220 tools::Long
RoadmapWizard::LogicalCoordinateToPixel(int iCoordinate
) const
222 Size aLocSize
= LogicToPixel(Size(iCoordinate
, 0), MapMode(MapUnit::MapAppFont
));
223 int iPixelCoordinate
= aLocSize
.Width();
224 return iPixelCoordinate
;
227 void RoadmapWizard::ImplPosTabPage()
232 if ( !IsInInitShow() )
234 // #100199# - On Unix initial size is equal to screen size, on Windows
235 // it's 0,0. One cannot calculate the size unless dialog is visible.
236 if ( !IsReallyVisible() )
240 // calculate height of ButtonBar
241 tools::Long nMaxHeight
= 0;
242 ImplWizButtonData
* pBtnData
= mpFirstBtn
;
245 tools::Long nBtnHeight
= pBtnData
->mpButton
->GetSizePixel().Height();
246 if ( nBtnHeight
> nMaxHeight
)
247 nMaxHeight
= nBtnHeight
;
248 pBtnData
= pBtnData
->mpNext
;
251 nMaxHeight
+= WIZARDDIALOG_BUTTON_OFFSET_Y
*2;
254 Size aDlgSize
= GetOutputSizePixel();
255 aDlgSize
.AdjustHeight( -nMaxHeight
);
256 tools::Long nOffX
= 0;
257 tools::Long nOffY
= 0;
258 if ( mpViewWindow
&& mpViewWindow
->IsVisible() )
260 Size aViewSize
= mpViewWindow
->GetSizePixel();
262 tools::Long nViewOffset
= mbEmptyViewMargin
? 0 : WIZARDDIALOG_VIEW_DLGOFFSET_X
;
263 nOffX
+= aViewSize
.Width() + nViewOffset
;
264 aDlgSize
.AdjustWidth( -nOffX
);
266 Point
aPos( nOffX
, nOffY
);
267 mpCurTabPage
->SetPosSizePixel( aPos
, aDlgSize
);
270 void RoadmapWizard::ImplShowTabPage( TabPage
* pTabPage
)
272 if ( mpCurTabPage
== pTabPage
)
275 TabPage
* pOldTabPage
= mpCurTabPage
;
277 mpCurTabPage
= pTabPage
;
288 TabPage
* RoadmapWizard::ImplGetPage( sal_uInt16 nLevel
) const
290 sal_uInt16 nTempLevel
= 0;
291 ImplWizPageData
* pPageData
= mpFirstPage
;
294 if ( (nTempLevel
== nLevel
) || !pPageData
->mpNext
)
298 pPageData
= pPageData
->mpNext
;
302 return pPageData
->mpPage
;
306 void RoadmapWizard::AddButtonResponse( Button
* pButton
, int response
)
308 m_xRoadmapImpl
->maResponses
[pButton
] = response
;
311 void RoadmapWizard::implConstruct( const WizardButtonFlags _nButtonFlags
)
313 m_xWizardImpl
->sTitleBase
= GetText();
315 // create the buttons according to the wizard button flags
317 if (_nButtonFlags
& WizardButtonFlags::HELP
)
319 m_pHelp
= VclPtr
<HelpButton
>::Create(this, WB_TABSTOP
);
320 m_pHelp
->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont
)));
322 m_pHelp
->set_id(u
"help"_ustr
);
323 AddButtonResponse(m_pHelp
, RET_HELP
);
324 AddButton( m_pHelp
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
327 // the previous button
328 if (_nButtonFlags
& WizardButtonFlags::PREVIOUS
)
330 m_pPrevPage
= VclPtr
<PushButton
>::Create(this, WB_TABSTOP
);
331 m_pPrevPage
->SetHelpId( HID_WIZARD_PREVIOUS
);
332 m_pPrevPage
->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont
)));
333 m_pPrevPage
->SetText(VclResId(STR_WIZDLG_PREVIOUS
));
335 m_pPrevPage
->set_id(u
"previous"_ustr
);
337 if (_nButtonFlags
& WizardButtonFlags::NEXT
)
338 AddButton( m_pPrevPage
, ( WIZARDDIALOG_BUTTON_SMALLSTDOFFSET_X
) ); // half x-offset to the next button
340 AddButton( m_pPrevPage
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
341 mpPrevBtn
= m_pPrevPage
;
342 m_pPrevPage
->SetClickHdl( LINK( this, RoadmapWizard
, OnPrevPage
) );
346 if (_nButtonFlags
& WizardButtonFlags::NEXT
)
348 m_pNextPage
= VclPtr
<PushButton
>::Create(this, WB_TABSTOP
);
349 m_pNextPage
->SetHelpId( HID_WIZARD_NEXT
);
350 m_pNextPage
->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont
)));
351 m_pNextPage
->SetText(VclResId(STR_WIZDLG_NEXT
));
353 m_pNextPage
->set_id(u
"next"_ustr
);
355 AddButton( m_pNextPage
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
356 mpNextBtn
= m_pNextPage
;
357 m_pNextPage
->SetClickHdl( LINK( this, RoadmapWizard
, OnNextPage
) );
361 if (_nButtonFlags
& WizardButtonFlags::FINISH
)
363 m_pFinish
= VclPtr
<OKButton
>::Create(this, WB_TABSTOP
);
364 m_pFinish
->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont
)));
365 m_pFinish
->SetText(VclResId(STR_WIZDLG_FINISH
));
367 m_pFinish
->set_id(u
"finish"_ustr
);
369 AddButton( m_pFinish
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
370 m_pFinish
->SetClickHdl( LINK( this, RoadmapWizard
, OnFinish
) );
374 if (_nButtonFlags
& WizardButtonFlags::CANCEL
)
376 m_pCancel
= VclPtr
<CancelButton
>::Create(this, WB_TABSTOP
);
377 m_pCancel
->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont
)));
380 AddButton( m_pCancel
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
384 void RoadmapWizard::Resize()
386 if ( IsReallyShown() && !IsInInitShow() )
395 void RoadmapWizard::CalcAndSetSize()
397 Size aDlgSize
= GetPageSizePixel();
398 if ( !aDlgSize
.Width() || !aDlgSize
.Height() )
400 ImplWizPageData
* pPageData
= mpFirstPage
;
403 if ( pPageData
->mpPage
)
405 Size aPageSize
= pPageData
->mpPage
->GetSizePixel();
406 if ( aPageSize
.Width() > aDlgSize
.Width() )
407 aDlgSize
.setWidth( aPageSize
.Width() );
408 if ( aPageSize
.Height() > aDlgSize
.Height() )
409 aDlgSize
.setHeight( aPageSize
.Height() );
412 pPageData
= pPageData
->mpNext
;
415 ImplCalcSize( aDlgSize
);
416 SetMinOutputSizePixel( aDlgSize
);
417 SetOutputSizePixel( aDlgSize
);
420 void RoadmapWizard::StateChanged( StateChangedType nType
)
422 if ( nType
== StateChangedType::InitShow
)
424 if ( IsDefaultSize() )
431 ImplShowTabPage( ImplGetPage( mnCurLevel
) );
434 Dialog::StateChanged( nType
);
437 bool RoadmapWizard::EventNotify( NotifyEvent
& rNEvt
)
439 if ( (rNEvt
.GetType() == NotifyEventType::KEYINPUT
) && mpPrevBtn
&& mpNextBtn
)
441 const KeyEvent
* pKEvt
= rNEvt
.GetKeyEvent();
442 vcl::KeyCode aKeyCode
= pKEvt
->GetKeyCode();
443 sal_uInt16 nKeyCode
= aKeyCode
.GetCode();
445 if ( aKeyCode
.IsMod1() )
447 if ( aKeyCode
.IsShift() || (nKeyCode
== KEY_PAGEUP
) )
449 if ( (nKeyCode
== KEY_TAB
) || (nKeyCode
== KEY_PAGEUP
) )
451 if ( mpPrevBtn
->IsVisible() &&
452 mpPrevBtn
->IsEnabled() && mpPrevBtn
->IsInputEnabled() )
454 mpPrevBtn
->SetPressed( true );
455 mpPrevBtn
->SetPressed( false );
463 if ( (nKeyCode
== KEY_TAB
) || (nKeyCode
== KEY_PAGEDOWN
) )
465 if ( mpNextBtn
->IsVisible() &&
466 mpNextBtn
->IsEnabled() && mpNextBtn
->IsInputEnabled() )
468 mpNextBtn
->SetPressed( true );
469 mpNextBtn
->SetPressed( false );
478 return Dialog::EventNotify( rNEvt
);
481 void RoadmapWizard::GetOrCreatePage( const WizardTypes::WizardState i_nState
)
483 if ( nullptr != GetPage( i_nState
) )
486 VclPtr
<TabPage
> pNewPage
= createPage( i_nState
);
487 DBG_ASSERT( pNewPage
, "RoadmapWizard::GetOrCreatePage: invalid new page (NULL)!" );
489 // fill up the page sequence of our base class (with dummies)
490 while ( m_xWizardImpl
->nFirstUnknownPage
< i_nState
)
493 ++m_xWizardImpl
->nFirstUnknownPage
;
496 if ( m_xWizardImpl
->nFirstUnknownPage
== i_nState
)
498 // encountered this page number the first time
500 ++m_xWizardImpl
->nFirstUnknownPage
;
503 // already had this page - just change it
504 SetPage( i_nState
, pNewPage
);
507 void RoadmapWizard::ActivatePage()
509 WizardTypes::WizardState nCurrentLevel
= GetCurLevel();
510 GetOrCreatePage( nCurrentLevel
);
512 enterState( nCurrentLevel
);
515 bool RoadmapWizard::ShowPage( sal_uInt16 nLevel
)
519 ImplShowTabPage( ImplGetPage( mnCurLevel
) );
523 void RoadmapWizard::Finish( tools::Long nResult
)
526 EndDialog( nResult
);
527 else if ( GetStyle() & WB_CLOSEABLE
)
531 void RoadmapWizard::AddPage( TabPage
* pPage
)
533 ImplWizPageData
* pNewPageData
= new ImplWizPageData
;
534 pNewPageData
->mpNext
= nullptr;
535 pNewPageData
->mpPage
= pPage
;
538 mpFirstPage
= pNewPageData
;
542 ImplWizPageData
* pPageData
= mpFirstPage
;
543 while ( pPageData
->mpNext
)
544 pPageData
= pPageData
->mpNext
;
545 pPageData
->mpNext
= pNewPageData
;
549 void RoadmapWizard::RemovePage( TabPage
* pPage
)
551 ImplWizPageData
* pPrevPageData
= nullptr;
552 ImplWizPageData
* pPageData
= mpFirstPage
;
555 if ( pPageData
->mpPage
== pPage
)
558 pPrevPageData
->mpNext
= pPageData
->mpNext
;
560 mpFirstPage
= pPageData
->mpNext
;
561 if ( pPage
== mpCurTabPage
)
562 mpCurTabPage
= nullptr;
567 pPrevPageData
= pPageData
;
568 pPageData
= pPageData
->mpNext
;
571 OSL_FAIL( "RoadmapWizard::RemovePage() - Page not in list" );
574 void RoadmapWizard::SetPage( sal_uInt16 nLevel
, TabPage
* pPage
)
576 sal_uInt16 nTempLevel
= 0;
577 ImplWizPageData
* pPageData
= mpFirstPage
;
580 if ( (nTempLevel
== nLevel
) || !pPageData
->mpNext
)
584 pPageData
= pPageData
->mpNext
;
589 if ( pPageData
->mpPage
== mpCurTabPage
)
590 mpCurTabPage
= nullptr;
591 pPageData
->mpPage
= pPage
;
595 TabPage
* RoadmapWizard::GetPage( sal_uInt16 nLevel
) const
597 sal_uInt16 nTempLevel
= 0;
599 for (ImplWizPageData
* pPageData
= mpFirstPage
; pPageData
;
600 pPageData
= pPageData
->mpNext
)
602 if ( nTempLevel
== nLevel
)
603 return pPageData
->mpPage
;
610 void RoadmapWizard::AddButton( Button
* pButton
, tools::Long nOffset
)
612 ImplWizButtonData
* pNewBtnData
= new ImplWizButtonData
;
613 pNewBtnData
->mpNext
= nullptr;
614 pNewBtnData
->mpButton
= pButton
;
615 pNewBtnData
->mnOffset
= nOffset
;
618 mpFirstBtn
= pNewBtnData
;
621 ImplWizButtonData
* pBtnData
= mpFirstBtn
;
622 while ( pBtnData
->mpNext
)
623 pBtnData
= pBtnData
->mpNext
;
624 pBtnData
->mpNext
= pNewBtnData
;
628 void RoadmapWizard::RemoveButton( Button
* pButton
)
630 ImplWizButtonData
* pPrevBtnData
= nullptr;
631 ImplWizButtonData
* pBtnData
= mpFirstBtn
;
634 if ( pBtnData
->mpButton
== pButton
)
637 pPrevBtnData
->mpNext
= pBtnData
->mpNext
;
639 mpFirstBtn
= pBtnData
->mpNext
;
644 pPrevBtnData
= pBtnData
;
645 pBtnData
= pBtnData
->mpNext
;
648 OSL_FAIL( "RoadmapWizard::RemoveButton() - Button not in list" );
651 IMPL_LINK_NOARG(RoadmapWizard
, OnFinish
, Button
*, void)
653 if ( isTravelingSuspended() )
655 RoadmapWizardTravelSuspension
aTravelGuard( *this );
659 bool RoadmapWizard::skipBackwardUntil( WizardTypes::WizardState _nTargetState
)
661 // don't travel directly on m_xWizardImpl->aStateHistory, in case something goes wrong
662 std::stack
< WizardTypes::WizardState
> aTravelVirtually
= m_xWizardImpl
->aStateHistory
;
663 std::stack
< WizardTypes::WizardState
> aOldStateHistory
= m_xWizardImpl
->aStateHistory
;
665 WizardTypes::WizardState nCurrentRollbackState
= getCurrentState();
666 while ( nCurrentRollbackState
!= _nTargetState
)
668 DBG_ASSERT( !aTravelVirtually
.empty(), "RoadmapWizard::skipBackwardUntil: this target state does not exist in the history!" );
669 nCurrentRollbackState
= aTravelVirtually
.top();
670 aTravelVirtually
.pop();
672 m_xWizardImpl
->aStateHistory
= std::move(aTravelVirtually
);
673 if ( !ShowPage( _nTargetState
) )
675 m_xWizardImpl
->aStateHistory
= std::move(aOldStateHistory
);
681 bool RoadmapWizard::skipUntil( WizardTypes::WizardState _nTargetState
)
683 WizardTypes::WizardState nCurrentState
= getCurrentState();
685 // don't travel directly on m_xWizardImpl->aStateHistory, in case something goes wrong
686 std::stack
< WizardTypes::WizardState
> aTravelVirtually
= m_xWizardImpl
->aStateHistory
;
687 std::stack
< WizardTypes::WizardState
> aOldStateHistory
= m_xWizardImpl
->aStateHistory
;
688 while ( nCurrentState
!= _nTargetState
)
690 WizardTypes::WizardState nNextState
= determineNextState( nCurrentState
);
691 if ( WZS_INVALID_STATE
== nNextState
)
693 OSL_FAIL( "RoadmapWizard::skipUntil: the given target state does not exist!" );
697 // remember the skipped state in the history
698 aTravelVirtually
.push( nCurrentState
);
700 // get the next state
701 nCurrentState
= nNextState
;
703 m_xWizardImpl
->aStateHistory
= std::move(aTravelVirtually
);
704 // show the target page
705 if ( !ShowPage( nCurrentState
) )
707 // argh! prepareLeaveCurrentPage succeeded, determineNextState succeeded,
708 // but ShowPage doesn't? Somebody behaves very strange here...
709 OSL_FAIL( "RoadmapWizard::skipUntil: very unpolite..." );
710 m_xWizardImpl
->aStateHistory
= std::move(aOldStateHistory
);
716 void RoadmapWizard::travelNext()
718 // determine the next state to travel to
719 WizardTypes::WizardState nCurrentState
= getCurrentState();
720 WizardTypes::WizardState nNextState
= determineNextState(nCurrentState
);
721 if (WZS_INVALID_STATE
== nNextState
)
724 // the state history is used by the enterState method
726 m_xWizardImpl
->aStateHistory
.push(nCurrentState
);
727 if (!ShowPage(nNextState
))
729 m_xWizardImpl
->aStateHistory
.pop();
733 void RoadmapWizard::travelPrevious()
735 DBG_ASSERT(!m_xWizardImpl
->aStateHistory
.empty(), "RoadmapWizard::travelPrevious: have no previous page!");
737 // the next state to switch to
738 WizardTypes::WizardState nPreviousState
= m_xWizardImpl
->aStateHistory
.top();
740 // the state history is used by the enterState method
741 m_xWizardImpl
->aStateHistory
.pop();
743 if (!ShowPage(nPreviousState
))
745 m_xWizardImpl
->aStateHistory
.push(nPreviousState
);
751 void RoadmapWizard::removePageFromHistory( WizardTypes::WizardState nToRemove
)
754 std::stack
< WizardTypes::WizardState
> aTemp
;
755 while(!m_xWizardImpl
->aStateHistory
.empty())
757 WizardTypes::WizardState nPreviousState
= m_xWizardImpl
->aStateHistory
.top();
758 m_xWizardImpl
->aStateHistory
.pop();
759 if(nPreviousState
!= nToRemove
)
760 aTemp
.push( nPreviousState
);
764 while(!aTemp
.empty())
766 m_xWizardImpl
->aStateHistory
.push( aTemp
.top() );
771 IMPL_LINK_NOARG(RoadmapWizard
, OnPrevPage
, Button
*, void)
773 if ( isTravelingSuspended() )
775 RoadmapWizardTravelSuspension
aTravelGuard( *this );
779 IMPL_LINK_NOARG(RoadmapWizard
, OnNextPage
, Button
*, void)
781 if ( isTravelingSuspended() )
783 RoadmapWizardTravelSuspension
aTravelGuard( *this );
787 bool RoadmapWizard::isTravelingSuspended() const
789 return m_xWizardImpl
->m_bTravelingSuspended
;
792 void RoadmapWizard::suspendTraveling( AccessGuard
)
794 DBG_ASSERT( !m_xWizardImpl
->m_bTravelingSuspended
, "RoadmapWizard::suspendTraveling: already suspended!" );
795 m_xWizardImpl
->m_bTravelingSuspended
= true;
798 void RoadmapWizard::resumeTraveling( AccessGuard
)
800 DBG_ASSERT( m_xWizardImpl
->m_bTravelingSuspended
, "RoadmapWizard::resumeTraveling: nothing to resume!" );
801 m_xWizardImpl
->m_bTravelingSuspended
= false;
804 WizardMachine::WizardMachine(weld::Window
* pParent
, WizardButtonFlags nButtonFlags
)
805 : AssistantController(pParent
, u
"vcl/ui/wizard.ui"_ustr
, u
"Wizard"_ustr
)
806 , m_pCurTabPage(nullptr)
808 , m_pFirstPage(nullptr)
809 , m_xFinish(m_xAssistant
->weld_button_for_response(RET_OK
))
810 , m_xCancel(m_xAssistant
->weld_button_for_response(RET_CANCEL
))
811 , m_xNextPage(m_xAssistant
->weld_button_for_response(RET_YES
))
812 , m_xPrevPage(m_xAssistant
->weld_button_for_response(RET_NO
))
813 , m_xHelp(m_xAssistant
->weld_button_for_response(RET_HELP
))
814 , m_pImpl(new WizardMachineImplData
)
816 implConstruct(nButtonFlags
);
819 void WizardMachine::implConstruct(const WizardButtonFlags nButtonFlags
)
821 m_pImpl
->sTitleBase
= m_xAssistant
->get_title();
823 const bool bHideHelp
= comphelper::LibreOfficeKit::isActive() &&
824 officecfg::Office::Common::Help::HelpRootURL::get().isEmpty();
825 // create the buttons according to the wizard button flags
827 if (nButtonFlags
& WizardButtonFlags::HELP
&& !bHideHelp
)
832 // the previous button
833 if (nButtonFlags
& WizardButtonFlags::PREVIOUS
)
835 m_xPrevPage
->set_help_id( HID_WIZARD_PREVIOUS
);
838 m_xPrevPage
->connect_clicked( LINK( this, WizardMachine
, OnPrevPage
) );
844 if (nButtonFlags
& WizardButtonFlags::NEXT
)
846 m_xNextPage
->set_help_id( HID_WIZARD_NEXT
);
849 m_xNextPage
->connect_clicked( LINK( this, WizardMachine
, OnNextPage
) );
855 if (nButtonFlags
& WizardButtonFlags::FINISH
)
859 m_xFinish
->connect_clicked( LINK( this, WizardMachine
, OnFinish
) );
865 if (nButtonFlags
& WizardButtonFlags::CANCEL
)
868 m_xCancel
->connect_clicked( LINK( this, WizardMachine
, OnCancel
) );
874 WizardMachine::~WizardMachine()
879 RemovePage(m_pFirstPage
->mxPage
.get());
884 void WizardMachine::implUpdateTitle()
886 OUString
sCompleteTitle(m_pImpl
->sTitleBase
);
888 // append the page title
889 BuilderPage
* pCurrentPage
= GetPage(getCurrentState());
890 if ( pCurrentPage
&& !pCurrentPage
->GetPageTitle().isEmpty() )
892 sCompleteTitle
+= " - " + pCurrentPage
->GetPageTitle();
895 m_xAssistant
->set_title(sCompleteTitle
);
898 void WizardMachine::setTitleBase(const OUString
& _rTitleBase
)
900 m_pImpl
->sTitleBase
= _rTitleBase
;
904 OUString
WizardMachine::getPageIdentForState(WizardTypes::WizardState nState
) const
906 return OUString::number(nState
);
909 WizardTypes::WizardState
WizardMachine::getStateFromPageIdent(const OUString
& rIdent
) const
911 return rIdent
.toInt32();
914 BuilderPage
* WizardMachine::GetOrCreatePage( const WizardTypes::WizardState i_nState
)
916 if ( nullptr == GetPage( i_nState
) )
918 std::unique_ptr
<BuilderPage
> xNewPage
= createPage( i_nState
);
919 DBG_ASSERT( xNewPage
, "WizardMachine::GetOrCreatePage: invalid new page (NULL)!" );
921 // fill up the page sequence of our base class (with dummies)
922 while ( m_pImpl
->nFirstUnknownPage
< i_nState
)
925 ++m_pImpl
->nFirstUnknownPage
;
928 if ( m_pImpl
->nFirstUnknownPage
== i_nState
)
930 // encountered this page number the first time
931 AddPage(std::move(xNewPage
));
932 ++m_pImpl
->nFirstUnknownPage
;
935 // already had this page - just change it
936 SetPage(i_nState
, std::move(xNewPage
));
938 return GetPage( i_nState
);
941 void WizardMachine::ActivatePage()
943 WizardTypes::WizardState nCurrentLevel
= m_nCurState
;
944 GetOrCreatePage( nCurrentLevel
);
946 enterState( nCurrentLevel
);
949 bool WizardMachine::DeactivatePage()
951 WizardTypes::WizardState nCurrentState
= getCurrentState();
952 return leaveState(nCurrentState
);
955 void WizardMachine::defaultButton(WizardButtonFlags _nWizardButtonFlags
)
957 // the new default button
958 weld::Button
* pNewDefButton
= nullptr;
959 if (_nWizardButtonFlags
& WizardButtonFlags::FINISH
)
960 pNewDefButton
= m_xFinish
.get();
961 if (_nWizardButtonFlags
& WizardButtonFlags::NEXT
)
962 pNewDefButton
= m_xNextPage
.get();
963 if (_nWizardButtonFlags
& WizardButtonFlags::PREVIOUS
)
964 pNewDefButton
= m_xPrevPage
.get();
965 if (_nWizardButtonFlags
& WizardButtonFlags::HELP
)
966 pNewDefButton
= m_xHelp
.get();
967 if (_nWizardButtonFlags
& WizardButtonFlags::CANCEL
)
968 pNewDefButton
= m_xCancel
.get();
970 defaultButton(pNewDefButton
);
973 void WizardMachine::defaultButton(weld::Button
* _pNewDefButton
)
975 // loop through all (direct and indirect) descendants which participate in our tabbing order, and
976 // reset the WB_DEFBUTTON for every window which is a button and set _pNewDefButton as the new
978 m_xAssistant
->change_default_widget(nullptr, _pNewDefButton
);
981 void WizardMachine::enableButtons(WizardButtonFlags _nWizardButtonFlags
, bool _bEnable
)
983 if (_nWizardButtonFlags
& WizardButtonFlags::FINISH
)
984 m_xFinish
->set_sensitive(_bEnable
);
985 if (_nWizardButtonFlags
& WizardButtonFlags::NEXT
)
986 m_xNextPage
->set_sensitive(_bEnable
);
987 if (_nWizardButtonFlags
& WizardButtonFlags::PREVIOUS
)
988 m_xPrevPage
->set_sensitive(_bEnable
);
989 if (_nWizardButtonFlags
& WizardButtonFlags::HELP
)
990 m_xHelp
->set_sensitive(_bEnable
);
991 if (_nWizardButtonFlags
& WizardButtonFlags::CANCEL
)
992 m_xCancel
->set_sensitive(_bEnable
);
995 void WizardMachine::enterState(WizardTypes::WizardState _nState
)
998 IWizardPageController
* pController
= getPageController( GetPage( _nState
) );
999 OSL_ENSURE( pController
, "WizardMachine::enterState: no controller for the given page!" );
1001 pController
->initializePage();
1003 if ( isAutomaticNextButtonStateEnabled() )
1004 enableButtons( WizardButtonFlags::NEXT
, canAdvance() );
1006 enableButtons( WizardButtonFlags::PREVIOUS
, !m_pImpl
->aStateHistory
.empty() );
1008 // set the new title - it depends on the current page (i.e. state)
1012 bool WizardMachine::leaveState(WizardTypes::WizardState
)
1014 // no need to ask the page here.
1015 // If we reach this point, we already gave the current page the chance to commit it's data,
1016 // and it was allowed to commit it's data
1021 bool WizardMachine::onFinish()
1023 return Finish(RET_OK
);
1026 IMPL_LINK_NOARG(WizardMachine
, OnFinish
, weld::Button
&, void)
1028 if ( isTravelingSuspended() )
1031 // prevent WizardTravelSuspension from using this instance
1032 // after will be destructed due to onFinish and async response call
1034 WizardTravelSuspension
aTravelGuard( *this );
1035 if (!prepareLeaveCurrentState(WizardTypes::eFinish
))
1044 IMPL_LINK_NOARG(WizardMachine
, OnCancel
, weld::Button
&, void)
1046 m_xAssistant
->response(RET_CANCEL
);
1049 WizardTypes::WizardState
WizardMachine::determineNextState(WizardTypes::WizardState _nCurrentState
) const
1051 return _nCurrentState
+ 1;
1054 bool WizardMachine::prepareLeaveCurrentState( WizardTypes::CommitPageReason _eReason
)
1056 IWizardPageController
* pController
= getPageController( GetPage( getCurrentState() ) );
1057 ENSURE_OR_RETURN( pController
!= nullptr, "WizardMachine::prepareLeaveCurrentState: no controller for the current page!", true );
1058 return pController
->commitPage( _eReason
);
1061 bool WizardMachine::skipBackwardUntil(WizardTypes::WizardState _nTargetState
)
1063 // allowed to leave the current page?
1064 if ( !prepareLeaveCurrentState( WizardTypes::eTravelBackward
) )
1067 // don't travel directly on m_pImpl->aStateHistory, in case something goes wrong
1068 std::stack
< WizardTypes::WizardState
> aTravelVirtually
= m_pImpl
->aStateHistory
;
1069 std::stack
< WizardTypes::WizardState
> aOldStateHistory
= m_pImpl
->aStateHistory
;
1071 WizardTypes::WizardState nCurrentRollbackState
= getCurrentState();
1072 while ( nCurrentRollbackState
!= _nTargetState
)
1074 DBG_ASSERT( !aTravelVirtually
.empty(), "WizardMachine::skipBackwardUntil: this target state does not exist in the history!" );
1075 nCurrentRollbackState
= aTravelVirtually
.top();
1076 aTravelVirtually
.pop();
1078 m_pImpl
->aStateHistory
= std::move(aTravelVirtually
);
1079 if ( !ShowPage( _nTargetState
) )
1081 m_pImpl
->aStateHistory
= std::move(aOldStateHistory
);
1087 bool WizardMachine::skipUntil( WizardTypes::WizardState _nTargetState
)
1089 WizardTypes::WizardState nCurrentState
= getCurrentState();
1091 // allowed to leave the current page?
1092 if ( !prepareLeaveCurrentState( nCurrentState
< _nTargetState
? WizardTypes::eTravelForward
: WizardTypes::eTravelBackward
) )
1095 // don't travel directly on m_pImpl->aStateHistory, in case something goes wrong
1096 std::stack
< WizardTypes::WizardState
> aTravelVirtually
= m_pImpl
->aStateHistory
;
1097 std::stack
< WizardTypes::WizardState
> aOldStateHistory
= m_pImpl
->aStateHistory
;
1098 while ( nCurrentState
!= _nTargetState
)
1100 WizardTypes::WizardState nNextState
= determineNextState( nCurrentState
);
1101 if ( WZS_INVALID_STATE
== nNextState
)
1103 OSL_FAIL( "WizardMachine::skipUntil: the given target state does not exist!" );
1107 // remember the skipped state in the history
1108 aTravelVirtually
.push( nCurrentState
);
1110 // get the next state
1111 nCurrentState
= nNextState
;
1113 m_pImpl
->aStateHistory
= std::move(aTravelVirtually
);
1114 // show the target page
1115 if ( !ShowPage( nCurrentState
) )
1117 // argh! prepareLeaveCurrentPage succeeded, determineNextState succeeded,
1118 // but ShowPage doesn't? Somebody behaves very strange here...
1119 OSL_FAIL( "WizardMachine::skipUntil: very unpolite..." );
1120 m_pImpl
->aStateHistory
= std::move(aOldStateHistory
);
1126 void WizardMachine::skip()
1128 // allowed to leave the current page?
1129 if ( !prepareLeaveCurrentState( WizardTypes::eTravelForward
) )
1132 WizardTypes::WizardState nCurrentState
= getCurrentState();
1133 WizardTypes::WizardState nNextState
= determineNextState(nCurrentState
);
1135 if (WZS_INVALID_STATE
== nNextState
)
1138 // remember the skipped state in the history
1139 m_pImpl
->aStateHistory
.push(nCurrentState
);
1141 // get the next state
1142 nCurrentState
= nNextState
;
1144 // show the (n+1)th page
1145 if (!ShowPage(nCurrentState
))
1147 // TODO: this leaves us in a state where we have no current page and an inconsistent state history.
1148 // Perhaps we should rollback the skipping here...
1149 OSL_FAIL("RoadmapWizard::skip: very unpolite...");
1150 // if somebody does a skip and then does not allow to leave...
1151 // (can't be a commit error, as we've already committed the current page. So if ShowPage fails here,
1152 // somebody behaves really strange ...)
1159 bool WizardMachine::travelNext()
1161 // allowed to leave the current page?
1162 if ( !prepareLeaveCurrentState( WizardTypes::eTravelForward
) )
1165 // determine the next state to travel to
1166 WizardTypes::WizardState nCurrentState
= getCurrentState();
1167 WizardTypes::WizardState nNextState
= determineNextState(nCurrentState
);
1168 if (WZS_INVALID_STATE
== nNextState
)
1171 // the state history is used by the enterState method
1173 m_pImpl
->aStateHistory
.push(nCurrentState
);
1174 if (!ShowPage(nNextState
))
1176 m_pImpl
->aStateHistory
.pop();
1183 bool WizardMachine::ShowPage(WizardTypes::WizardState nState
)
1185 if (DeactivatePage())
1187 BuilderPage
* pOldTabPage
= m_pCurTabPage
;
1189 m_nCurState
= nState
;
1193 pOldTabPage
->Deactivate();
1195 m_xAssistant
->set_current_page(getPageIdentForState(nState
));
1197 m_pCurTabPage
= GetPage(m_nCurState
);
1198 m_pCurTabPage
->Activate();
1205 bool WizardMachine::ShowNextPage()
1207 return ShowPage(m_nCurState
+ 1);
1210 bool WizardMachine::ShowPrevPage()
1214 return ShowPage(m_nCurState
- 1);
1217 bool WizardMachine::travelPrevious()
1219 DBG_ASSERT(!m_pImpl
->aStateHistory
.empty(), "WizardMachine::travelPrevious: have no previous page!");
1221 // allowed to leave the current page?
1222 if ( !prepareLeaveCurrentState( WizardTypes::eTravelBackward
) )
1225 // the next state to switch to
1226 WizardTypes::WizardState nPreviousState
= m_pImpl
->aStateHistory
.top();
1228 // the state history is used by the enterState method
1229 m_pImpl
->aStateHistory
.pop();
1231 if (!ShowPage(nPreviousState
))
1233 m_pImpl
->aStateHistory
.push(nPreviousState
);
1242 void WizardMachine::removePageFromHistory( WizardTypes::WizardState nToRemove
)
1245 std::stack
< WizardTypes::WizardState
> aTemp
;
1246 while(!m_pImpl
->aStateHistory
.empty())
1248 WizardTypes::WizardState nPreviousState
= m_pImpl
->aStateHistory
.top();
1249 m_pImpl
->aStateHistory
.pop();
1250 if(nPreviousState
!= nToRemove
)
1251 aTemp
.push( nPreviousState
);
1255 while(!aTemp
.empty())
1257 m_pImpl
->aStateHistory
.push( aTemp
.top() );
1263 void WizardMachine::enableAutomaticNextButtonState()
1265 m_pImpl
->m_bAutoNextButtonState
= true;
1269 bool WizardMachine::isAutomaticNextButtonStateEnabled() const
1271 return m_pImpl
->m_bAutoNextButtonState
;
1274 IMPL_LINK_NOARG(WizardMachine
, OnPrevPage
, weld::Button
&, void)
1276 if ( isTravelingSuspended() )
1278 WizardTravelSuspension
aTravelGuard( *this );
1282 IMPL_LINK_NOARG(WizardMachine
, OnNextPage
, weld::Button
&, void)
1284 if ( isTravelingSuspended() )
1286 WizardTravelSuspension
aTravelGuard( *this );
1290 IWizardPageController
* WizardMachine::getPageController(BuilderPage
* pCurrentPage
) const
1292 IWizardPageController
* pController
= dynamic_cast<IWizardPageController
*>(pCurrentPage
);
1296 void WizardMachine::getStateHistory( std::vector
< WizardTypes::WizardState
>& _out_rHistory
)
1298 std::stack
< WizardTypes::WizardState
> aHistoryCopy( m_pImpl
->aStateHistory
);
1299 while ( !aHistoryCopy
.empty() )
1301 _out_rHistory
.push_back( aHistoryCopy
.top() );
1306 bool WizardMachine::canAdvance() const
1308 return WZS_INVALID_STATE
!= determineNextState( getCurrentState() );
1311 void WizardMachine::updateTravelUI()
1313 const IWizardPageController
* pController
= getPageController( GetPage( getCurrentState() ) );
1314 OSL_ENSURE( pController
!= nullptr, "RoadmapWizard::updateTravelUI: no controller for the current page!" );
1317 ( !pController
|| pController
->canAdvance() ) // the current page allows to advance
1318 && canAdvance(); // the dialog as a whole allows to advance
1319 enableButtons( WizardButtonFlags::NEXT
, bCanAdvance
);
1322 bool WizardMachine::isTravelingSuspended() const
1324 return m_pImpl
->m_bTravelingSuspended
;
1327 void WizardMachine::suspendTraveling( AccessGuard
)
1329 DBG_ASSERT( !m_pImpl
->m_bTravelingSuspended
, "WizardMachine::suspendTraveling: already suspended!" );
1330 m_pImpl
->m_bTravelingSuspended
= true;
1333 void WizardMachine::resumeTraveling( AccessGuard
)
1338 DBG_ASSERT( m_pImpl
->m_bTravelingSuspended
, "WizardMachine::resumeTraveling: nothing to resume!" );
1339 m_pImpl
->m_bTravelingSuspended
= false;
1342 bool WizardMachine::Finish(short nResult
)
1344 if ( DeactivatePage() )
1347 m_pCurTabPage
->Deactivate();
1349 m_xAssistant
->response(nResult
);
1356 void WizardMachine::AddPage(std::unique_ptr
<BuilderPage
> xPage
)
1358 WizPageData
* pNewPageData
= new WizPageData
;
1359 pNewPageData
->mpNext
= nullptr;
1360 pNewPageData
->mxPage
= std::move(xPage
);
1362 if ( !m_pFirstPage
)
1363 m_pFirstPage
= pNewPageData
;
1366 WizPageData
* pPageData
= m_pFirstPage
;
1367 while ( pPageData
->mpNext
)
1368 pPageData
= pPageData
->mpNext
;
1369 pPageData
->mpNext
= pNewPageData
;
1373 void WizardMachine::RemovePage(const BuilderPage
* pPage
)
1375 WizPageData
* pPrevPageData
= nullptr;
1376 WizPageData
* pPageData
= m_pFirstPage
;
1379 if (pPageData
->mxPage
.get() == pPage
)
1382 pPrevPageData
->mpNext
= pPageData
->mpNext
;
1384 m_pFirstPage
= pPageData
->mpNext
;
1385 if (pPage
== m_pCurTabPage
)
1386 m_pCurTabPage
= nullptr;
1391 pPrevPageData
= pPageData
;
1392 pPageData
= pPageData
->mpNext
;
1395 OSL_FAIL( "WizardMachine::RemovePage() - Page not in list" );
1398 void WizardMachine::SetPage(WizardTypes::WizardState nLevel
, std::unique_ptr
<BuilderPage
> xPage
)
1400 sal_uInt16 nTempLevel
= 0;
1401 WizPageData
* pPageData
= m_pFirstPage
;
1404 if ( (nTempLevel
== nLevel
) || !pPageData
->mpNext
)
1408 pPageData
= pPageData
->mpNext
;
1413 if (pPageData
->mxPage
.get() == m_pCurTabPage
)
1414 m_pCurTabPage
= nullptr;
1415 pPageData
->mxPage
= std::move(xPage
);
1419 BuilderPage
* WizardMachine::GetPage(WizardTypes::WizardState nLevel
) const
1421 sal_uInt16 nTempLevel
= 0;
1423 for (WizPageData
* pPageData
= m_pFirstPage
; pPageData
;
1424 pPageData
= pPageData
->mpNext
)
1426 if ( nTempLevel
== nLevel
)
1427 return pPageData
->mxPage
.get();
1436 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */