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 <tools/diagnose_ex.h>
25 #include <strings.hrc>
29 #include "wizimpldata.hxx"
31 #define HID_WIZARD_NEXT "SVT_HID_WIZARD_NEXT"
32 #define HID_WIZARD_PREVIOUS "SVT_HID_WIZARD_PREVIOUS"
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 OString
& 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
){
221 Size aLocSize
= LogicToPixel(Size(iCoordinate
, 0), MapMode(MapUnit::MapAppFont
));
222 int iPixelCoordinate
= aLocSize
.Width();
223 return iPixelCoordinate
;
226 void RoadmapWizard::ImplPosTabPage()
231 if ( !IsInInitShow() )
233 // #100199# - On Unix initial size is equal to screen size, on Windows
234 // it's 0,0. One cannot calculate the size unless dialog is visible.
235 if ( !IsReallyVisible() )
239 // calculate height of ButtonBar
240 tools::Long nMaxHeight
= 0;
241 ImplWizButtonData
* pBtnData
= mpFirstBtn
;
244 tools::Long nBtnHeight
= pBtnData
->mpButton
->GetSizePixel().Height();
245 if ( nBtnHeight
> nMaxHeight
)
246 nMaxHeight
= nBtnHeight
;
247 pBtnData
= pBtnData
->mpNext
;
250 nMaxHeight
+= WIZARDDIALOG_BUTTON_OFFSET_Y
*2;
253 Size aDlgSize
= GetOutputSizePixel();
254 aDlgSize
.AdjustHeight( -nMaxHeight
);
255 tools::Long nOffX
= 0;
256 tools::Long nOffY
= 0;
257 if ( mpViewWindow
&& mpViewWindow
->IsVisible() )
259 Size aViewSize
= mpViewWindow
->GetSizePixel();
261 tools::Long nViewOffset
= mbEmptyViewMargin
? 0 : WIZARDDIALOG_VIEW_DLGOFFSET_X
;
262 nOffX
+= aViewSize
.Width() + nViewOffset
;
263 aDlgSize
.AdjustWidth( -nOffX
);
265 Point
aPos( nOffX
, nOffY
);
266 mpCurTabPage
->SetPosSizePixel( aPos
, aDlgSize
);
269 void RoadmapWizard::ImplShowTabPage( TabPage
* pTabPage
)
271 if ( mpCurTabPage
== pTabPage
)
274 TabPage
* pOldTabPage
= mpCurTabPage
;
276 mpCurTabPage
= pTabPage
;
287 TabPage
* RoadmapWizard::ImplGetPage( sal_uInt16 nLevel
) const
289 sal_uInt16 nTempLevel
= 0;
290 ImplWizPageData
* pPageData
= mpFirstPage
;
293 if ( (nTempLevel
== nLevel
) || !pPageData
->mpNext
)
297 pPageData
= pPageData
->mpNext
;
301 return pPageData
->mpPage
;
305 void RoadmapWizard::implConstruct( const WizardButtonFlags _nButtonFlags
)
307 m_xWizardImpl
->sTitleBase
= GetText();
309 // create the buttons according to the wizard button flags
311 if (_nButtonFlags
& WizardButtonFlags::HELP
)
313 m_pHelp
= VclPtr
<HelpButton
>::Create(this, WB_TABSTOP
);
314 m_pHelp
->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont
)));
316 AddButton( m_pHelp
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
319 // the previous button
320 if (_nButtonFlags
& WizardButtonFlags::PREVIOUS
)
322 m_pPrevPage
= VclPtr
<PushButton
>::Create(this, WB_TABSTOP
);
323 m_pPrevPage
->SetHelpId( HID_WIZARD_PREVIOUS
);
324 m_pPrevPage
->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont
)));
325 m_pPrevPage
->SetText(VclResId(STR_WIZDLG_PREVIOUS
));
327 m_pPrevPage
->set_id("previous");
329 if (_nButtonFlags
& WizardButtonFlags::NEXT
)
330 AddButton( m_pPrevPage
, ( WIZARDDIALOG_BUTTON_SMALLSTDOFFSET_X
) ); // half x-offset to the next button
332 AddButton( m_pPrevPage
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
333 mpPrevBtn
= m_pPrevPage
;
334 m_pPrevPage
->SetClickHdl( LINK( this, RoadmapWizard
, OnPrevPage
) );
338 if (_nButtonFlags
& WizardButtonFlags::NEXT
)
340 m_pNextPage
= VclPtr
<PushButton
>::Create(this, WB_TABSTOP
);
341 m_pNextPage
->SetHelpId( HID_WIZARD_NEXT
);
342 m_pNextPage
->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont
)));
343 m_pNextPage
->SetText(VclResId(STR_WIZDLG_NEXT
));
345 m_pNextPage
->set_id("next");
347 AddButton( m_pNextPage
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
348 mpNextBtn
= m_pNextPage
;
349 m_pNextPage
->SetClickHdl( LINK( this, RoadmapWizard
, OnNextPage
) );
353 if (_nButtonFlags
& WizardButtonFlags::FINISH
)
355 m_pFinish
= VclPtr
<OKButton
>::Create(this, WB_TABSTOP
);
356 m_pFinish
->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont
)));
357 m_pFinish
->SetText(VclResId(STR_WIZDLG_FINISH
));
359 m_pFinish
->set_id("finish");
361 AddButton( m_pFinish
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
362 m_pFinish
->SetClickHdl( LINK( this, RoadmapWizard
, OnFinish
) );
366 if (_nButtonFlags
& WizardButtonFlags::CANCEL
)
368 m_pCancel
= VclPtr
<CancelButton
>::Create(this, WB_TABSTOP
);
369 m_pCancel
->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont
)));
372 AddButton( m_pCancel
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
376 void RoadmapWizard::Resize()
378 if ( IsReallyShown() && !IsInInitShow() )
387 void RoadmapWizard::implUpdateTitle()
389 OUString
sCompleteTitle(m_xWizardImpl
->sTitleBase
);
391 // append the page title
392 TabPage
* pCurrentPage
= GetPage(getCurrentState());
393 if ( pCurrentPage
&& !pCurrentPage
->GetText().isEmpty() )
395 sCompleteTitle
+= " - " + pCurrentPage
->GetText();
398 SetText(sCompleteTitle
);
401 void RoadmapWizard::CalcAndSetSize()
403 Size aDlgSize
= GetPageSizePixel();
404 if ( !aDlgSize
.Width() || !aDlgSize
.Height() )
406 ImplWizPageData
* pPageData
= mpFirstPage
;
409 if ( pPageData
->mpPage
)
411 Size aPageSize
= pPageData
->mpPage
->GetSizePixel();
412 if ( aPageSize
.Width() > aDlgSize
.Width() )
413 aDlgSize
.setWidth( aPageSize
.Width() );
414 if ( aPageSize
.Height() > aDlgSize
.Height() )
415 aDlgSize
.setHeight( aPageSize
.Height() );
418 pPageData
= pPageData
->mpNext
;
421 ImplCalcSize( aDlgSize
);
422 SetMinOutputSizePixel( aDlgSize
);
423 SetOutputSizePixel( aDlgSize
);
426 void RoadmapWizard::StateChanged( StateChangedType nType
)
428 if ( nType
== StateChangedType::InitShow
)
430 if ( IsDefaultSize() )
437 ImplShowTabPage( ImplGetPage( mnCurLevel
) );
440 Dialog::StateChanged( nType
);
443 bool RoadmapWizard::EventNotify( NotifyEvent
& rNEvt
)
445 if ( (rNEvt
.GetType() == MouseNotifyEvent::KEYINPUT
) && mpPrevBtn
&& mpNextBtn
)
447 const KeyEvent
* pKEvt
= rNEvt
.GetKeyEvent();
448 vcl::KeyCode aKeyCode
= pKEvt
->GetKeyCode();
449 sal_uInt16 nKeyCode
= aKeyCode
.GetCode();
451 if ( aKeyCode
.IsMod1() )
453 if ( aKeyCode
.IsShift() || (nKeyCode
== KEY_PAGEUP
) )
455 if ( (nKeyCode
== KEY_TAB
) || (nKeyCode
== KEY_PAGEUP
) )
457 if ( mpPrevBtn
->IsVisible() &&
458 mpPrevBtn
->IsEnabled() && mpPrevBtn
->IsInputEnabled() )
460 mpPrevBtn
->SetPressed( true );
461 mpPrevBtn
->SetPressed( false );
469 if ( (nKeyCode
== KEY_TAB
) || (nKeyCode
== KEY_PAGEDOWN
) )
471 if ( mpNextBtn
->IsVisible() &&
472 mpNextBtn
->IsEnabled() && mpNextBtn
->IsInputEnabled() )
474 mpNextBtn
->SetPressed( true );
475 mpNextBtn
->SetPressed( false );
484 return Dialog::EventNotify( rNEvt
);
487 TabPage
* RoadmapWizard::GetOrCreatePage( const WizardTypes::WizardState i_nState
)
489 if ( nullptr == GetPage( i_nState
) )
491 VclPtr
<TabPage
> pNewPage
= createPage( i_nState
);
492 DBG_ASSERT( pNewPage
, "RoadmapWizard::GetOrCreatePage: invalid new page (NULL)!" );
494 // fill up the page sequence of our base class (with dummies)
495 while ( m_xWizardImpl
->nFirstUnknownPage
< i_nState
)
498 ++m_xWizardImpl
->nFirstUnknownPage
;
501 if ( m_xWizardImpl
->nFirstUnknownPage
== i_nState
)
503 // encountered this page number the first time
505 ++m_xWizardImpl
->nFirstUnknownPage
;
508 // already had this page - just change it
509 SetPage( i_nState
, pNewPage
);
511 return GetPage( i_nState
);
514 void RoadmapWizard::ActivatePage()
516 WizardTypes::WizardState nCurrentLevel
= GetCurLevel();
517 GetOrCreatePage( nCurrentLevel
);
519 enterState( nCurrentLevel
);
522 bool RoadmapWizard::ShowPage( sal_uInt16 nLevel
)
526 ImplShowTabPage( ImplGetPage( mnCurLevel
) );
530 bool RoadmapWizard::Finish( tools::Long nResult
)
533 EndDialog( nResult
);
534 else if ( GetStyle() & WB_CLOSEABLE
)
539 void RoadmapWizard::AddPage( TabPage
* pPage
)
541 ImplWizPageData
* pNewPageData
= new ImplWizPageData
;
542 pNewPageData
->mpNext
= nullptr;
543 pNewPageData
->mpPage
= pPage
;
546 mpFirstPage
= pNewPageData
;
549 ImplWizPageData
* pPageData
= mpFirstPage
;
550 while ( pPageData
->mpNext
)
551 pPageData
= pPageData
->mpNext
;
552 pPageData
->mpNext
= pNewPageData
;
556 void RoadmapWizard::RemovePage( TabPage
* pPage
)
558 ImplWizPageData
* pPrevPageData
= nullptr;
559 ImplWizPageData
* pPageData
= mpFirstPage
;
562 if ( pPageData
->mpPage
== pPage
)
565 pPrevPageData
->mpNext
= pPageData
->mpNext
;
567 mpFirstPage
= pPageData
->mpNext
;
568 if ( pPage
== mpCurTabPage
)
569 mpCurTabPage
= nullptr;
574 pPrevPageData
= pPageData
;
575 pPageData
= pPageData
->mpNext
;
578 OSL_FAIL( "RoadmapWizard::RemovePage() - Page not in list" );
581 void RoadmapWizard::SetPage( sal_uInt16 nLevel
, TabPage
* pPage
)
583 sal_uInt16 nTempLevel
= 0;
584 ImplWizPageData
* pPageData
= mpFirstPage
;
587 if ( (nTempLevel
== nLevel
) || !pPageData
->mpNext
)
591 pPageData
= pPageData
->mpNext
;
596 if ( pPageData
->mpPage
== mpCurTabPage
)
597 mpCurTabPage
= nullptr;
598 pPageData
->mpPage
= pPage
;
602 TabPage
* RoadmapWizard::GetPage( sal_uInt16 nLevel
) const
604 sal_uInt16 nTempLevel
= 0;
606 for (ImplWizPageData
* pPageData
= mpFirstPage
; pPageData
;
607 pPageData
= pPageData
->mpNext
)
609 if ( nTempLevel
== nLevel
)
610 return pPageData
->mpPage
;
617 void RoadmapWizard::AddButton( Button
* pButton
, tools::Long nOffset
)
619 ImplWizButtonData
* pNewBtnData
= new ImplWizButtonData
;
620 pNewBtnData
->mpNext
= nullptr;
621 pNewBtnData
->mpButton
= pButton
;
622 pNewBtnData
->mnOffset
= nOffset
;
625 mpFirstBtn
= pNewBtnData
;
628 ImplWizButtonData
* pBtnData
= mpFirstBtn
;
629 while ( pBtnData
->mpNext
)
630 pBtnData
= pBtnData
->mpNext
;
631 pBtnData
->mpNext
= pNewBtnData
;
635 void RoadmapWizard::RemoveButton( Button
* pButton
)
637 ImplWizButtonData
* pPrevBtnData
= nullptr;
638 ImplWizButtonData
* pBtnData
= mpFirstBtn
;
641 if ( pBtnData
->mpButton
== pButton
)
644 pPrevBtnData
->mpNext
= pBtnData
->mpNext
;
646 mpFirstBtn
= pBtnData
->mpNext
;
651 pPrevBtnData
= pBtnData
;
652 pBtnData
= pBtnData
->mpNext
;
655 OSL_FAIL( "RoadmapWizard::RemoveButton() - Button not in list" );
658 void RoadmapWizard::enableButtons(WizardButtonFlags _nWizardButtonFlags
, bool _bEnable
)
660 if (m_pFinish
&& (_nWizardButtonFlags
& WizardButtonFlags::FINISH
))
661 m_pFinish
->Enable(_bEnable
);
662 if (m_pNextPage
&& (_nWizardButtonFlags
& WizardButtonFlags::NEXT
))
663 m_pNextPage
->Enable(_bEnable
);
664 if (m_pPrevPage
&& (_nWizardButtonFlags
& WizardButtonFlags::PREVIOUS
))
665 m_pPrevPage
->Enable(_bEnable
);
666 if (m_pHelp
&& (_nWizardButtonFlags
& WizardButtonFlags::HELP
))
667 m_pHelp
->Enable(_bEnable
);
668 if (m_pCancel
&& (_nWizardButtonFlags
& WizardButtonFlags::CANCEL
))
669 m_pCancel
->Enable(_bEnable
);
672 IMPL_LINK_NOARG(RoadmapWizard
, OnFinish
, Button
*, void)
674 if ( isTravelingSuspended() )
676 RoadmapWizardTravelSuspension
aTravelGuard( *this );
677 if (!prepareLeaveCurrentState(WizardTypes::eFinish
))
684 bool RoadmapWizard::prepareLeaveCurrentState( WizardTypes::CommitPageReason _eReason
)
686 IWizardPageController
* pController
= getPageController( GetPage( getCurrentState() ) );
687 ENSURE_OR_RETURN( pController
!= nullptr, "RoadmapWizard::prepareLeaveCurrentState: no controller for the current page!", true );
688 return pController
->commitPage( _eReason
);
691 bool RoadmapWizard::skipBackwardUntil( WizardTypes::WizardState _nTargetState
)
693 // allowed to leave the current page?
694 if (!prepareLeaveCurrentState(WizardTypes::eTravelBackward
))
697 // don't travel directly on m_xWizardImpl->aStateHistory, in case something goes wrong
698 std::stack
< WizardTypes::WizardState
> aTravelVirtually
= m_xWizardImpl
->aStateHistory
;
699 std::stack
< WizardTypes::WizardState
> aOldStateHistory
= m_xWizardImpl
->aStateHistory
;
701 WizardTypes::WizardState nCurrentRollbackState
= getCurrentState();
702 while ( nCurrentRollbackState
!= _nTargetState
)
704 DBG_ASSERT( !aTravelVirtually
.empty(), "RoadmapWizard::skipBackwardUntil: this target state does not exist in the history!" );
705 nCurrentRollbackState
= aTravelVirtually
.top();
706 aTravelVirtually
.pop();
708 m_xWizardImpl
->aStateHistory
= aTravelVirtually
;
709 if ( !ShowPage( _nTargetState
) )
711 m_xWizardImpl
->aStateHistory
= aOldStateHistory
;
717 bool RoadmapWizard::skipUntil( WizardTypes::WizardState _nTargetState
)
719 WizardTypes::WizardState nCurrentState
= getCurrentState();
721 // allowed to leave the current page?
722 if ( !prepareLeaveCurrentState( nCurrentState
< _nTargetState
? WizardTypes::eTravelForward
: WizardTypes::eTravelBackward
) )
725 // don't travel directly on m_xWizardImpl->aStateHistory, in case something goes wrong
726 std::stack
< WizardTypes::WizardState
> aTravelVirtually
= m_xWizardImpl
->aStateHistory
;
727 std::stack
< WizardTypes::WizardState
> aOldStateHistory
= m_xWizardImpl
->aStateHistory
;
728 while ( nCurrentState
!= _nTargetState
)
730 WizardTypes::WizardState nNextState
= determineNextState( nCurrentState
);
731 if ( WZS_INVALID_STATE
== nNextState
)
733 OSL_FAIL( "RoadmapWizard::skipUntil: the given target state does not exist!" );
737 // remember the skipped state in the history
738 aTravelVirtually
.push( nCurrentState
);
740 // get the next state
741 nCurrentState
= nNextState
;
743 m_xWizardImpl
->aStateHistory
= aTravelVirtually
;
744 // show the target page
745 if ( !ShowPage( nCurrentState
) )
747 // argh! prepareLeaveCurrentPage succeeded, determineNextState succeeded,
748 // but ShowPage doesn't? Somebody behaves very strange here...
749 OSL_FAIL( "RoadmapWizard::skipUntil: very unpolite..." );
750 m_xWizardImpl
->aStateHistory
= aOldStateHistory
;
756 bool RoadmapWizard::travelNext()
758 // allowed to leave the current page?
759 if ( !prepareLeaveCurrentState( WizardTypes::eTravelForward
) )
762 // determine the next state to travel to
763 WizardTypes::WizardState nCurrentState
= getCurrentState();
764 WizardTypes::WizardState nNextState
= determineNextState(nCurrentState
);
765 if (WZS_INVALID_STATE
== nNextState
)
768 // the state history is used by the enterState method
770 m_xWizardImpl
->aStateHistory
.push(nCurrentState
);
771 if (!ShowPage(nNextState
))
773 m_xWizardImpl
->aStateHistory
.pop();
780 bool RoadmapWizard::travelPrevious()
782 DBG_ASSERT(!m_xWizardImpl
->aStateHistory
.empty(), "RoadmapWizard::travelPrevious: have no previous page!");
784 // allowed to leave the current page?
785 if ( !prepareLeaveCurrentState( WizardTypes::eTravelBackward
) )
788 // the next state to switch to
789 WizardTypes::WizardState nPreviousState
= m_xWizardImpl
->aStateHistory
.top();
791 // the state history is used by the enterState method
792 m_xWizardImpl
->aStateHistory
.pop();
794 if (!ShowPage(nPreviousState
))
796 m_xWizardImpl
->aStateHistory
.push(nPreviousState
);
804 void RoadmapWizard::removePageFromHistory( WizardTypes::WizardState nToRemove
)
807 std::stack
< WizardTypes::WizardState
> aTemp
;
808 while(!m_xWizardImpl
->aStateHistory
.empty())
810 WizardTypes::WizardState nPreviousState
= m_xWizardImpl
->aStateHistory
.top();
811 m_xWizardImpl
->aStateHistory
.pop();
812 if(nPreviousState
!= nToRemove
)
813 aTemp
.push( nPreviousState
);
817 while(!aTemp
.empty())
819 m_xWizardImpl
->aStateHistory
.push( aTemp
.top() );
824 bool RoadmapWizard::isAutomaticNextButtonStateEnabled() const
826 return m_xWizardImpl
->m_bAutoNextButtonState
;
829 IMPL_LINK_NOARG(RoadmapWizard
, OnPrevPage
, Button
*, void)
831 if ( isTravelingSuspended() )
833 RoadmapWizardTravelSuspension
aTravelGuard( *this );
837 IMPL_LINK_NOARG(RoadmapWizard
, OnNextPage
, Button
*, void)
839 if ( isTravelingSuspended() )
841 RoadmapWizardTravelSuspension
aTravelGuard( *this );
845 IWizardPageController
* RoadmapWizard::getPageController( TabPage
* _pCurrentPage
)
847 IWizardPageController
* pController
= dynamic_cast< IWizardPageController
* >( _pCurrentPage
);
851 bool RoadmapWizard::isTravelingSuspended() const
853 return m_xWizardImpl
->m_bTravelingSuspended
;
856 void RoadmapWizard::suspendTraveling( AccessGuard
)
858 DBG_ASSERT( !m_xWizardImpl
->m_bTravelingSuspended
, "RoadmapWizard::suspendTraveling: already suspended!" );
859 m_xWizardImpl
->m_bTravelingSuspended
= true;
862 void RoadmapWizard::resumeTraveling( AccessGuard
)
864 DBG_ASSERT( m_xWizardImpl
->m_bTravelingSuspended
, "RoadmapWizard::resumeTraveling: nothing to resume!" );
865 m_xWizardImpl
->m_bTravelingSuspended
= false;
868 WizardMachine::WizardMachine(weld::Window
* pParent
, WizardButtonFlags nButtonFlags
)
869 : AssistantController(pParent
, "vcl/ui/wizard.ui", "Wizard")
870 , m_pCurTabPage(nullptr)
872 , m_pFirstPage(nullptr)
873 , m_xFinish(m_xAssistant
->weld_widget_for_response(RET_OK
))
874 , m_xCancel(m_xAssistant
->weld_widget_for_response(RET_CANCEL
))
875 , m_xNextPage(m_xAssistant
->weld_widget_for_response(RET_YES
))
876 , m_xPrevPage(m_xAssistant
->weld_widget_for_response(RET_NO
))
877 , m_xHelp(m_xAssistant
->weld_widget_for_response(RET_HELP
))
878 , m_pImpl(new WizardMachineImplData
)
880 implConstruct(nButtonFlags
);
883 void WizardMachine::implConstruct(const WizardButtonFlags nButtonFlags
)
885 m_pImpl
->sTitleBase
= m_xAssistant
->get_title();
887 const bool bHideHelp
= comphelper::LibreOfficeKit::isActive() &&
888 officecfg::Office::Common::Help::HelpRootURL::get().isEmpty();
889 // create the buttons according to the wizard button flags
891 if (nButtonFlags
& WizardButtonFlags::HELP
&& !bHideHelp
)
896 // the previous button
897 if (nButtonFlags
& WizardButtonFlags::PREVIOUS
)
899 m_xPrevPage
->set_help_id( HID_WIZARD_PREVIOUS
);
902 m_xPrevPage
->connect_clicked( LINK( this, WizardMachine
, OnPrevPage
) );
908 if (nButtonFlags
& WizardButtonFlags::NEXT
)
910 m_xNextPage
->set_help_id( HID_WIZARD_NEXT
);
913 m_xNextPage
->connect_clicked( LINK( this, WizardMachine
, OnNextPage
) );
919 if (nButtonFlags
& WizardButtonFlags::FINISH
)
923 m_xFinish
->connect_clicked( LINK( this, WizardMachine
, OnFinish
) );
929 if (nButtonFlags
& WizardButtonFlags::CANCEL
)
932 m_xCancel
->connect_clicked( LINK( this, WizardMachine
, OnCancel
) );
938 WizardMachine::~WizardMachine()
943 RemovePage(m_pFirstPage
->mxPage
.get());
948 void WizardMachine::implUpdateTitle()
950 OUString
sCompleteTitle(m_pImpl
->sTitleBase
);
952 // append the page title
953 BuilderPage
* pCurrentPage
= GetPage(getCurrentState());
954 if ( pCurrentPage
&& !pCurrentPage
->GetPageTitle().isEmpty() )
956 sCompleteTitle
+= " - " + pCurrentPage
->GetPageTitle();
959 m_xAssistant
->set_title(sCompleteTitle
);
962 void WizardMachine::setTitleBase(const OUString
& _rTitleBase
)
964 m_pImpl
->sTitleBase
= _rTitleBase
;
968 BuilderPage
* WizardMachine::GetOrCreatePage( const WizardTypes::WizardState i_nState
)
970 if ( nullptr == GetPage( i_nState
) )
972 std::unique_ptr
<BuilderPage
> xNewPage
= createPage( i_nState
);
973 DBG_ASSERT( xNewPage
, "WizardMachine::GetOrCreatePage: invalid new page (NULL)!" );
975 // fill up the page sequence of our base class (with dummies)
976 while ( m_pImpl
->nFirstUnknownPage
< i_nState
)
979 ++m_pImpl
->nFirstUnknownPage
;
982 if ( m_pImpl
->nFirstUnknownPage
== i_nState
)
984 // encountered this page number the first time
985 AddPage(std::move(xNewPage
));
986 ++m_pImpl
->nFirstUnknownPage
;
989 // already had this page - just change it
990 SetPage(i_nState
, std::move(xNewPage
));
992 return GetPage( i_nState
);
995 void WizardMachine::ActivatePage()
997 WizardTypes::WizardState nCurrentLevel
= m_nCurState
;
998 GetOrCreatePage( nCurrentLevel
);
1000 enterState( nCurrentLevel
);
1003 bool WizardMachine::DeactivatePage()
1005 WizardTypes::WizardState nCurrentState
= getCurrentState();
1006 return leaveState(nCurrentState
);
1009 void WizardMachine::defaultButton(WizardButtonFlags _nWizardButtonFlags
)
1011 // the new default button
1012 weld::Button
* pNewDefButton
= nullptr;
1013 if (_nWizardButtonFlags
& WizardButtonFlags::FINISH
)
1014 pNewDefButton
= m_xFinish
.get();
1015 if (_nWizardButtonFlags
& WizardButtonFlags::NEXT
)
1016 pNewDefButton
= m_xNextPage
.get();
1017 if (_nWizardButtonFlags
& WizardButtonFlags::PREVIOUS
)
1018 pNewDefButton
= m_xPrevPage
.get();
1019 if (_nWizardButtonFlags
& WizardButtonFlags::HELP
)
1020 pNewDefButton
= m_xHelp
.get();
1021 if (_nWizardButtonFlags
& WizardButtonFlags::CANCEL
)
1022 pNewDefButton
= m_xCancel
.get();
1024 if ( pNewDefButton
)
1025 defaultButton( pNewDefButton
);
1027 m_xAssistant
->recursively_unset_default_buttons();
1030 void WizardMachine::defaultButton(weld::Button
* _pNewDefButton
)
1032 // loop through all (direct and indirect) descendants which participate in our tabbing order, and
1033 // reset the WB_DEFBUTTON for every window which is a button
1034 m_xAssistant
->recursively_unset_default_buttons();
1036 // set its new style
1038 _pNewDefButton
->set_has_default(true);
1041 void WizardMachine::enableButtons(WizardButtonFlags _nWizardButtonFlags
, bool _bEnable
)
1043 if (_nWizardButtonFlags
& WizardButtonFlags::FINISH
)
1044 m_xFinish
->set_sensitive(_bEnable
);
1045 if (_nWizardButtonFlags
& WizardButtonFlags::NEXT
)
1046 m_xNextPage
->set_sensitive(_bEnable
);
1047 if (_nWizardButtonFlags
& WizardButtonFlags::PREVIOUS
)
1048 m_xPrevPage
->set_sensitive(_bEnable
);
1049 if (_nWizardButtonFlags
& WizardButtonFlags::HELP
)
1050 m_xHelp
->set_sensitive(_bEnable
);
1051 if (_nWizardButtonFlags
& WizardButtonFlags::CANCEL
)
1052 m_xCancel
->set_sensitive(_bEnable
);
1055 void WizardMachine::enterState(WizardTypes::WizardState _nState
)
1058 IWizardPageController
* pController
= getPageController( GetPage( _nState
) );
1059 OSL_ENSURE( pController
, "WizardMachine::enterState: no controller for the given page!" );
1061 pController
->initializePage();
1063 if ( isAutomaticNextButtonStateEnabled() )
1064 enableButtons( WizardButtonFlags::NEXT
, canAdvance() );
1066 enableButtons( WizardButtonFlags::PREVIOUS
, !m_pImpl
->aStateHistory
.empty() );
1068 // set the new title - it depends on the current page (i.e. state)
1072 bool WizardMachine::leaveState(WizardTypes::WizardState
)
1074 // no need to ask the page here.
1075 // If we reach this point, we already gave the current page the chance to commit it's data,
1076 // and it was allowed to commit it's data
1081 bool WizardMachine::onFinish()
1083 return Finish(RET_OK
);
1086 IMPL_LINK_NOARG(WizardMachine
, OnFinish
, weld::Button
&, void)
1088 if ( isTravelingSuspended() )
1091 // prevent WizardTravelSuspension from using this instance
1092 // after will be destructed due to onFinish and async response call
1094 WizardTravelSuspension
aTravelGuard( *this );
1095 if (!prepareLeaveCurrentState(WizardTypes::eFinish
))
1104 IMPL_LINK_NOARG(WizardMachine
, OnCancel
, weld::Button
&, void)
1106 m_xAssistant
->response(RET_CANCEL
);
1109 WizardTypes::WizardState
WizardMachine::determineNextState(WizardTypes::WizardState _nCurrentState
) const
1111 return _nCurrentState
+ 1;
1114 bool WizardMachine::prepareLeaveCurrentState( WizardTypes::CommitPageReason _eReason
)
1116 IWizardPageController
* pController
= getPageController( GetPage( getCurrentState() ) );
1117 ENSURE_OR_RETURN( pController
!= nullptr, "WizardMachine::prepareLeaveCurrentState: no controller for the current page!", true );
1118 return pController
->commitPage( _eReason
);
1121 bool WizardMachine::skipBackwardUntil(WizardTypes::WizardState _nTargetState
)
1123 // allowed to leave the current page?
1124 if ( !prepareLeaveCurrentState( WizardTypes::eTravelBackward
) )
1127 // don't travel directly on m_pImpl->aStateHistory, in case something goes wrong
1128 std::stack
< WizardTypes::WizardState
> aTravelVirtually
= m_pImpl
->aStateHistory
;
1129 std::stack
< WizardTypes::WizardState
> aOldStateHistory
= m_pImpl
->aStateHistory
;
1131 WizardTypes::WizardState nCurrentRollbackState
= getCurrentState();
1132 while ( nCurrentRollbackState
!= _nTargetState
)
1134 DBG_ASSERT( !aTravelVirtually
.empty(), "WizardMachine::skipBackwardUntil: this target state does not exist in the history!" );
1135 nCurrentRollbackState
= aTravelVirtually
.top();
1136 aTravelVirtually
.pop();
1138 m_pImpl
->aStateHistory
= aTravelVirtually
;
1139 if ( !ShowPage( _nTargetState
) )
1141 m_pImpl
->aStateHistory
= aOldStateHistory
;
1147 bool WizardMachine::skipUntil( WizardTypes::WizardState _nTargetState
)
1149 WizardTypes::WizardState nCurrentState
= getCurrentState();
1151 // allowed to leave the current page?
1152 if ( !prepareLeaveCurrentState( nCurrentState
< _nTargetState
? WizardTypes::eTravelForward
: WizardTypes::eTravelBackward
) )
1155 // don't travel directly on m_pImpl->aStateHistory, in case something goes wrong
1156 std::stack
< WizardTypes::WizardState
> aTravelVirtually
= m_pImpl
->aStateHistory
;
1157 std::stack
< WizardTypes::WizardState
> aOldStateHistory
= m_pImpl
->aStateHistory
;
1158 while ( nCurrentState
!= _nTargetState
)
1160 WizardTypes::WizardState nNextState
= determineNextState( nCurrentState
);
1161 if ( WZS_INVALID_STATE
== nNextState
)
1163 OSL_FAIL( "WizardMachine::skipUntil: the given target state does not exist!" );
1167 // remember the skipped state in the history
1168 aTravelVirtually
.push( nCurrentState
);
1170 // get the next state
1171 nCurrentState
= nNextState
;
1173 m_pImpl
->aStateHistory
= aTravelVirtually
;
1174 // show the target page
1175 if ( !ShowPage( nCurrentState
) )
1177 // argh! prepareLeaveCurrentPage succeeded, determineNextState succeeded,
1178 // but ShowPage doesn't? Somebody behaves very strange here...
1179 OSL_FAIL( "WizardMachine::skipUntil: very unpolite..." );
1180 m_pImpl
->aStateHistory
= aOldStateHistory
;
1186 void WizardMachine::skip()
1188 // allowed to leave the current page?
1189 if ( !prepareLeaveCurrentState( WizardTypes::eTravelForward
) )
1192 WizardTypes::WizardState nCurrentState
= getCurrentState();
1193 WizardTypes::WizardState nNextState
= determineNextState(nCurrentState
);
1195 if (WZS_INVALID_STATE
== nNextState
)
1198 // remember the skipped state in the history
1199 m_pImpl
->aStateHistory
.push(nCurrentState
);
1201 // get the next state
1202 nCurrentState
= nNextState
;
1204 // show the (n+1)th page
1205 if (!ShowPage(nCurrentState
))
1207 // TODO: this leaves us in a state where we have no current page and an inconsistent state history.
1208 // Perhaps we should rollback the skipping here...
1209 OSL_FAIL("RoadmapWizard::skip: very unpolite...");
1210 // if somebody does a skip and then does not allow to leave...
1211 // (can't be a commit error, as we've already committed the current page. So if ShowPage fails here,
1212 // somebody behaves really strange ...)
1219 bool WizardMachine::travelNext()
1221 // allowed to leave the current page?
1222 if ( !prepareLeaveCurrentState( WizardTypes::eTravelForward
) )
1225 // determine the next state to travel to
1226 WizardTypes::WizardState nCurrentState
= getCurrentState();
1227 WizardTypes::WizardState nNextState
= determineNextState(nCurrentState
);
1228 if (WZS_INVALID_STATE
== nNextState
)
1231 // the state history is used by the enterState method
1233 m_pImpl
->aStateHistory
.push(nCurrentState
);
1234 if (!ShowPage(nNextState
))
1236 m_pImpl
->aStateHistory
.pop();
1243 bool WizardMachine::ShowPage(WizardTypes::WizardState nState
)
1245 if (DeactivatePage())
1247 BuilderPage
* pOldTabPage
= m_pCurTabPage
;
1249 m_nCurState
= nState
;
1253 pOldTabPage
->Deactivate();
1255 m_xAssistant
->set_current_page(OString::number(nState
));
1257 m_pCurTabPage
= GetPage(m_nCurState
);
1258 m_pCurTabPage
->Activate();
1265 bool WizardMachine::ShowNextPage()
1267 return ShowPage(m_nCurState
+ 1);
1270 bool WizardMachine::ShowPrevPage()
1274 return ShowPage(m_nCurState
- 1);
1277 bool WizardMachine::travelPrevious()
1279 DBG_ASSERT(!m_pImpl
->aStateHistory
.empty(), "WizardMachine::travelPrevious: have no previous page!");
1281 // allowed to leave the current page?
1282 if ( !prepareLeaveCurrentState( WizardTypes::eTravelBackward
) )
1285 // the next state to switch to
1286 WizardTypes::WizardState nPreviousState
= m_pImpl
->aStateHistory
.top();
1288 // the state history is used by the enterState method
1289 m_pImpl
->aStateHistory
.pop();
1291 if (!ShowPage(nPreviousState
))
1293 m_pImpl
->aStateHistory
.push(nPreviousState
);
1302 void WizardMachine::removePageFromHistory( WizardTypes::WizardState nToRemove
)
1305 std::stack
< WizardTypes::WizardState
> aTemp
;
1306 while(!m_pImpl
->aStateHistory
.empty())
1308 WizardTypes::WizardState nPreviousState
= m_pImpl
->aStateHistory
.top();
1309 m_pImpl
->aStateHistory
.pop();
1310 if(nPreviousState
!= nToRemove
)
1311 aTemp
.push( nPreviousState
);
1315 while(!aTemp
.empty())
1317 m_pImpl
->aStateHistory
.push( aTemp
.top() );
1323 void WizardMachine::enableAutomaticNextButtonState()
1325 m_pImpl
->m_bAutoNextButtonState
= true;
1329 bool WizardMachine::isAutomaticNextButtonStateEnabled() const
1331 return m_pImpl
->m_bAutoNextButtonState
;
1334 IMPL_LINK_NOARG(WizardMachine
, OnPrevPage
, weld::Button
&, void)
1336 if ( isTravelingSuspended() )
1338 WizardTravelSuspension
aTravelGuard( *this );
1342 IMPL_LINK_NOARG(WizardMachine
, OnNextPage
, weld::Button
&, void)
1344 if ( isTravelingSuspended() )
1346 WizardTravelSuspension
aTravelGuard( *this );
1350 IWizardPageController
* WizardMachine::getPageController(BuilderPage
* pCurrentPage
) const
1352 IWizardPageController
* pController
= dynamic_cast<IWizardPageController
*>(pCurrentPage
);
1356 void WizardMachine::getStateHistory( std::vector
< WizardTypes::WizardState
>& _out_rHistory
)
1358 std::stack
< WizardTypes::WizardState
> aHistoryCopy( m_pImpl
->aStateHistory
);
1359 while ( !aHistoryCopy
.empty() )
1361 _out_rHistory
.push_back( aHistoryCopy
.top() );
1366 bool WizardMachine::canAdvance() const
1368 return WZS_INVALID_STATE
!= determineNextState( getCurrentState() );
1371 void WizardMachine::updateTravelUI()
1373 const IWizardPageController
* pController
= getPageController( GetPage( getCurrentState() ) );
1374 OSL_ENSURE( pController
!= nullptr, "RoadmapWizard::updateTravelUI: no controller for the current page!" );
1377 ( !pController
|| pController
->canAdvance() ) // the current page allows to advance
1378 && canAdvance(); // the dialog as a whole allows to advance
1379 enableButtons( WizardButtonFlags::NEXT
, bCanAdvance
);
1382 bool WizardMachine::isTravelingSuspended() const
1384 return m_pImpl
->m_bTravelingSuspended
;
1387 void WizardMachine::suspendTraveling( AccessGuard
)
1389 DBG_ASSERT( !m_pImpl
->m_bTravelingSuspended
, "WizardMachine::suspendTraveling: already suspended!" );
1390 m_pImpl
->m_bTravelingSuspended
= true;
1393 void WizardMachine::resumeTraveling( AccessGuard
)
1398 DBG_ASSERT( m_pImpl
->m_bTravelingSuspended
, "WizardMachine::resumeTraveling: nothing to resume!" );
1399 m_pImpl
->m_bTravelingSuspended
= false;
1402 bool WizardMachine::Finish(short nResult
)
1404 if ( DeactivatePage() )
1407 m_pCurTabPage
->Deactivate();
1409 m_xAssistant
->response(nResult
);
1416 void WizardMachine::AddPage(std::unique_ptr
<BuilderPage
> xPage
)
1418 WizPageData
* pNewPageData
= new WizPageData
;
1419 pNewPageData
->mpNext
= nullptr;
1420 pNewPageData
->mxPage
= std::move(xPage
);
1422 if ( !m_pFirstPage
)
1423 m_pFirstPage
= pNewPageData
;
1426 WizPageData
* pPageData
= m_pFirstPage
;
1427 while ( pPageData
->mpNext
)
1428 pPageData
= pPageData
->mpNext
;
1429 pPageData
->mpNext
= pNewPageData
;
1433 void WizardMachine::RemovePage(const BuilderPage
* pPage
)
1435 WizPageData
* pPrevPageData
= nullptr;
1436 WizPageData
* pPageData
= m_pFirstPage
;
1439 if (pPageData
->mxPage
.get() == pPage
)
1442 pPrevPageData
->mpNext
= pPageData
->mpNext
;
1444 m_pFirstPage
= pPageData
->mpNext
;
1445 if (pPage
== m_pCurTabPage
)
1446 m_pCurTabPage
= nullptr;
1451 pPrevPageData
= pPageData
;
1452 pPageData
= pPageData
->mpNext
;
1455 OSL_FAIL( "WizardMachine::RemovePage() - Page not in list" );
1458 void WizardMachine::SetPage(WizardTypes::WizardState nLevel
, std::unique_ptr
<BuilderPage
> xPage
)
1460 sal_uInt16 nTempLevel
= 0;
1461 WizPageData
* pPageData
= m_pFirstPage
;
1464 if ( (nTempLevel
== nLevel
) || !pPageData
->mpNext
)
1468 pPageData
= pPageData
->mpNext
;
1473 if (pPageData
->mxPage
.get() == m_pCurTabPage
)
1474 m_pCurTabPage
= nullptr;
1475 pPageData
->mxPage
= std::move(xPage
);
1479 BuilderPage
* WizardMachine::GetPage(WizardTypes::WizardState nLevel
) const
1481 sal_uInt16 nTempLevel
= 0;
1483 for (WizPageData
* pPageData
= m_pFirstPage
; pPageData
;
1484 pPageData
= pPageData
->mpNext
)
1486 if ( nTempLevel
== nLevel
)
1487 return pPageData
->mxPage
.get();
1496 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */