bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / control / wizardmachine.cxx
blobf52069404564d9a17bb0f60b5e23b74fdd11e2c7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
26 #include <svdata.hxx>
27 #include <wizdlg.hxx>
28 #include <stack>
29 #include "wizimpldata.hxx"
31 constexpr OUStringLiteral HID_WIZARD_NEXT = u"SVT_HID_WIZARD_NEXT";
32 constexpr OUStringLiteral HID_WIZARD_PREVIOUS = u"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
39 namespace vcl
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);
64 if (pWizardMachine)
65 pWizardMachine->updateTravelUI();
68 bool OWizardPage::canAdvance() const
70 return true;
73 bool OWizardPage::commitPage( WizardTypes::CommitPageReason )
75 return true;
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;
89 while (pBtnData)
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;
102 if ( nMaxHeight )
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();
110 // align left
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())
121 return;
122 if (IsInClose())
123 return;
124 maWizardLayoutIdle.Start();
127 IMPL_LINK_NOARG(RoadmapWizard, ImplHandleWizardLayoutTimerHdl, Timer*, void)
129 ImplPosCtrls();
130 ImplPosTabPage();
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;
141 int j = 0;
142 while ( pBtnData )
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;
154 j++;
157 if ( nMaxHeight )
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;
164 int i = 0;
165 while ( pBtnData )
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;
175 else
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;
184 i++;
187 nOffY -= WIZARDDIALOG_BUTTON_OFFSET_Y;
190 if ( !(mpViewWindow && mpViewWindow->IsVisible()) )
191 return;
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;
199 // align left
201 if ( mbEmptyViewMargin )
203 nViewOffX = 0;
204 nViewOffY = 0;
205 nViewHeight = nDlgHeight;
207 else
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,
217 nViewPosFlags );
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()
229 if ( !mpCurTabPage )
230 return;
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() )
237 return;
240 // calculate height of ButtonBar
241 tools::Long nMaxHeight = 0;
242 ImplWizButtonData* pBtnData = mpFirstBtn;
243 while ( pBtnData )
245 tools::Long nBtnHeight = pBtnData->mpButton->GetSizePixel().Height();
246 if ( nBtnHeight > nMaxHeight )
247 nMaxHeight = nBtnHeight;
248 pBtnData = pBtnData->mpNext;
250 if ( nMaxHeight )
251 nMaxHeight += WIZARDDIALOG_BUTTON_OFFSET_Y*2;
253 // position TabPage
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();
261 // align left
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 )
273 return;
275 TabPage* pOldTabPage = mpCurTabPage;
277 mpCurTabPage = pTabPage;
278 if ( pTabPage )
280 ImplPosTabPage();
281 pTabPage->Show();
284 if ( pOldTabPage )
285 pOldTabPage->Hide();
288 TabPage* RoadmapWizard::ImplGetPage( sal_uInt16 nLevel ) const
290 sal_uInt16 nTempLevel = 0;
291 ImplWizPageData* pPageData = mpFirstPage;
292 while ( pPageData )
294 if ( (nTempLevel == nLevel) || !pPageData->mpNext )
295 break;
297 nTempLevel++;
298 pPageData = pPageData->mpNext;
301 if ( pPageData )
302 return pPageData->mpPage;
303 return nullptr;
306 void RoadmapWizard::implConstruct( const WizardButtonFlags _nButtonFlags )
308 m_xWizardImpl->sTitleBase = GetText();
310 // create the buttons according to the wizard button flags
311 // the help button
312 if (_nButtonFlags & WizardButtonFlags::HELP)
314 m_pHelp= VclPtr<HelpButton>::Create(this, WB_TABSTOP);
315 m_pHelp->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont)));
316 m_pHelp->Show();
317 AddButton( m_pHelp, WIZARDDIALOG_BUTTON_STDOFFSET_X);
320 // the previous button
321 if (_nButtonFlags & WizardButtonFlags::PREVIOUS)
323 m_pPrevPage = VclPtr<PushButton>::Create(this, WB_TABSTOP);
324 m_pPrevPage->SetHelpId( HID_WIZARD_PREVIOUS );
325 m_pPrevPage->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont)));
326 m_pPrevPage->SetText(VclResId(STR_WIZDLG_PREVIOUS));
327 m_pPrevPage->Show();
328 m_pPrevPage->set_id("previous");
330 if (_nButtonFlags & WizardButtonFlags::NEXT)
331 AddButton( m_pPrevPage, ( WIZARDDIALOG_BUTTON_SMALLSTDOFFSET_X) ); // half x-offset to the next button
332 else
333 AddButton( m_pPrevPage, WIZARDDIALOG_BUTTON_STDOFFSET_X );
334 mpPrevBtn = m_pPrevPage;
335 m_pPrevPage->SetClickHdl( LINK( this, RoadmapWizard, OnPrevPage ) );
338 // the next button
339 if (_nButtonFlags & WizardButtonFlags::NEXT)
341 m_pNextPage = VclPtr<PushButton>::Create(this, WB_TABSTOP);
342 m_pNextPage->SetHelpId( HID_WIZARD_NEXT );
343 m_pNextPage->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont)));
344 m_pNextPage->SetText(VclResId(STR_WIZDLG_NEXT));
345 m_pNextPage->Show();
346 m_pNextPage->set_id("next");
348 AddButton( m_pNextPage, WIZARDDIALOG_BUTTON_STDOFFSET_X );
349 mpNextBtn = m_pNextPage;
350 m_pNextPage->SetClickHdl( LINK( this, RoadmapWizard, OnNextPage ) );
353 // the finish button
354 if (_nButtonFlags & WizardButtonFlags::FINISH)
356 m_pFinish = VclPtr<OKButton>::Create(this, WB_TABSTOP);
357 m_pFinish->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont)));
358 m_pFinish->SetText(VclResId(STR_WIZDLG_FINISH));
359 m_pFinish->Show();
360 m_pFinish->set_id("finish");
362 AddButton( m_pFinish, WIZARDDIALOG_BUTTON_STDOFFSET_X );
363 m_pFinish->SetClickHdl( LINK( this, RoadmapWizard, OnFinish ) );
366 // the cancel button
367 if (_nButtonFlags & WizardButtonFlags::CANCEL)
369 m_pCancel = VclPtr<CancelButton>::Create(this, WB_TABSTOP);
370 m_pCancel->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont)));
371 m_pCancel->Show();
373 AddButton( m_pCancel, WIZARDDIALOG_BUTTON_STDOFFSET_X );
377 void RoadmapWizard::Resize()
379 if ( IsReallyShown() && !IsInInitShow() )
381 ImplPosCtrls();
382 ImplPosTabPage();
385 Dialog::Resize();
388 void RoadmapWizard::CalcAndSetSize()
390 Size aDlgSize = GetPageSizePixel();
391 if ( !aDlgSize.Width() || !aDlgSize.Height() )
393 ImplWizPageData* pPageData = mpFirstPage;
394 while ( pPageData )
396 if ( pPageData->mpPage )
398 Size aPageSize = pPageData->mpPage->GetSizePixel();
399 if ( aPageSize.Width() > aDlgSize.Width() )
400 aDlgSize.setWidth( aPageSize.Width() );
401 if ( aPageSize.Height() > aDlgSize.Height() )
402 aDlgSize.setHeight( aPageSize.Height() );
405 pPageData = pPageData->mpNext;
408 ImplCalcSize( aDlgSize );
409 SetMinOutputSizePixel( aDlgSize );
410 SetOutputSizePixel( aDlgSize );
413 void RoadmapWizard::StateChanged( StateChangedType nType )
415 if ( nType == StateChangedType::InitShow )
417 if ( IsDefaultSize() )
419 CalcAndSetSize();
422 ImplPosCtrls();
423 ImplPosTabPage();
424 ImplShowTabPage( ImplGetPage( mnCurLevel ) );
427 Dialog::StateChanged( nType );
430 bool RoadmapWizard::EventNotify( NotifyEvent& rNEvt )
432 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && mpPrevBtn && mpNextBtn )
434 const KeyEvent* pKEvt = rNEvt.GetKeyEvent();
435 vcl::KeyCode aKeyCode = pKEvt->GetKeyCode();
436 sal_uInt16 nKeyCode = aKeyCode.GetCode();
438 if ( aKeyCode.IsMod1() )
440 if ( aKeyCode.IsShift() || (nKeyCode == KEY_PAGEUP) )
442 if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEUP) )
444 if ( mpPrevBtn->IsVisible() &&
445 mpPrevBtn->IsEnabled() && mpPrevBtn->IsInputEnabled() )
447 mpPrevBtn->SetPressed( true );
448 mpPrevBtn->SetPressed( false );
449 mpPrevBtn->Click();
451 return true;
454 else
456 if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEDOWN) )
458 if ( mpNextBtn->IsVisible() &&
459 mpNextBtn->IsEnabled() && mpNextBtn->IsInputEnabled() )
461 mpNextBtn->SetPressed( true );
462 mpNextBtn->SetPressed( false );
463 mpNextBtn->Click();
465 return true;
471 return Dialog::EventNotify( rNEvt );
474 void RoadmapWizard::GetOrCreatePage( const WizardTypes::WizardState i_nState )
476 if ( nullptr != GetPage( i_nState ) )
477 return;
479 VclPtr<TabPage> pNewPage = createPage( i_nState );
480 DBG_ASSERT( pNewPage, "RoadmapWizard::GetOrCreatePage: invalid new page (NULL)!" );
482 // fill up the page sequence of our base class (with dummies)
483 while ( m_xWizardImpl->nFirstUnknownPage < i_nState )
485 AddPage( nullptr );
486 ++m_xWizardImpl->nFirstUnknownPage;
489 if ( m_xWizardImpl->nFirstUnknownPage == i_nState )
491 // encountered this page number the first time
492 AddPage( pNewPage );
493 ++m_xWizardImpl->nFirstUnknownPage;
495 else
496 // already had this page - just change it
497 SetPage( i_nState, pNewPage );
500 void RoadmapWizard::ActivatePage()
502 WizardTypes::WizardState nCurrentLevel = GetCurLevel();
503 GetOrCreatePage( nCurrentLevel );
505 enterState( nCurrentLevel );
508 bool RoadmapWizard::ShowPage( sal_uInt16 nLevel )
510 mnCurLevel = nLevel;
511 ActivatePage();
512 ImplShowTabPage( ImplGetPage( mnCurLevel ) );
513 return true;
516 void RoadmapWizard::Finish( tools::Long nResult )
518 if ( IsInExecute() )
519 EndDialog( nResult );
520 else if ( GetStyle() & WB_CLOSEABLE )
521 Close();
524 void RoadmapWizard::AddPage( TabPage* pPage )
526 ImplWizPageData* pNewPageData = new ImplWizPageData;
527 pNewPageData->mpNext = nullptr;
528 pNewPageData->mpPage = pPage;
530 if ( !mpFirstPage )
531 mpFirstPage = pNewPageData;
532 else
534 pPage->Hide();
535 ImplWizPageData* pPageData = mpFirstPage;
536 while ( pPageData->mpNext )
537 pPageData = pPageData->mpNext;
538 pPageData->mpNext = pNewPageData;
542 void RoadmapWizard::RemovePage( TabPage* pPage )
544 ImplWizPageData* pPrevPageData = nullptr;
545 ImplWizPageData* pPageData = mpFirstPage;
546 while ( pPageData )
548 if ( pPageData->mpPage == pPage )
550 if ( pPrevPageData )
551 pPrevPageData->mpNext = pPageData->mpNext;
552 else
553 mpFirstPage = pPageData->mpNext;
554 if ( pPage == mpCurTabPage )
555 mpCurTabPage = nullptr;
556 delete pPageData;
557 return;
560 pPrevPageData = pPageData;
561 pPageData = pPageData->mpNext;
564 OSL_FAIL( "RoadmapWizard::RemovePage() - Page not in list" );
567 void RoadmapWizard::SetPage( sal_uInt16 nLevel, TabPage* pPage )
569 sal_uInt16 nTempLevel = 0;
570 ImplWizPageData* pPageData = mpFirstPage;
571 while ( pPageData )
573 if ( (nTempLevel == nLevel) || !pPageData->mpNext )
574 break;
576 nTempLevel++;
577 pPageData = pPageData->mpNext;
580 if ( pPageData )
582 if ( pPageData->mpPage == mpCurTabPage )
583 mpCurTabPage = nullptr;
584 pPageData->mpPage = pPage;
588 TabPage* RoadmapWizard::GetPage( sal_uInt16 nLevel ) const
590 sal_uInt16 nTempLevel = 0;
592 for (ImplWizPageData* pPageData = mpFirstPage; pPageData;
593 pPageData = pPageData->mpNext)
595 if ( nTempLevel == nLevel )
596 return pPageData->mpPage;
597 nTempLevel++;
600 return nullptr;
603 void RoadmapWizard::AddButton( Button* pButton, tools::Long nOffset )
605 ImplWizButtonData* pNewBtnData = new ImplWizButtonData;
606 pNewBtnData->mpNext = nullptr;
607 pNewBtnData->mpButton = pButton;
608 pNewBtnData->mnOffset = nOffset;
610 if ( !mpFirstBtn )
611 mpFirstBtn = pNewBtnData;
612 else
614 ImplWizButtonData* pBtnData = mpFirstBtn;
615 while ( pBtnData->mpNext )
616 pBtnData = pBtnData->mpNext;
617 pBtnData->mpNext = pNewBtnData;
621 void RoadmapWizard::RemoveButton( Button* pButton )
623 ImplWizButtonData* pPrevBtnData = nullptr;
624 ImplWizButtonData* pBtnData = mpFirstBtn;
625 while ( pBtnData )
627 if ( pBtnData->mpButton == pButton )
629 if ( pPrevBtnData )
630 pPrevBtnData->mpNext = pBtnData->mpNext;
631 else
632 mpFirstBtn = pBtnData->mpNext;
633 delete pBtnData;
634 return;
637 pPrevBtnData = pBtnData;
638 pBtnData = pBtnData->mpNext;
641 OSL_FAIL( "RoadmapWizard::RemoveButton() - Button not in list" );
644 IMPL_LINK_NOARG(RoadmapWizard, OnFinish, Button*, void)
646 if ( isTravelingSuspended() )
647 return;
648 RoadmapWizardTravelSuspension aTravelGuard( *this );
649 Finish( RET_OK );
652 bool RoadmapWizard::skipBackwardUntil( WizardTypes::WizardState _nTargetState )
654 // don't travel directly on m_xWizardImpl->aStateHistory, in case something goes wrong
655 std::stack< WizardTypes::WizardState > aTravelVirtually = m_xWizardImpl->aStateHistory;
656 std::stack< WizardTypes::WizardState > aOldStateHistory = m_xWizardImpl->aStateHistory;
658 WizardTypes::WizardState nCurrentRollbackState = getCurrentState();
659 while ( nCurrentRollbackState != _nTargetState )
661 DBG_ASSERT( !aTravelVirtually.empty(), "RoadmapWizard::skipBackwardUntil: this target state does not exist in the history!" );
662 nCurrentRollbackState = aTravelVirtually.top();
663 aTravelVirtually.pop();
665 m_xWizardImpl->aStateHistory = aTravelVirtually;
666 if ( !ShowPage( _nTargetState ) )
668 m_xWizardImpl->aStateHistory = aOldStateHistory;
669 return false;
671 return true;
674 bool RoadmapWizard::skipUntil( WizardTypes::WizardState _nTargetState )
676 WizardTypes::WizardState nCurrentState = getCurrentState();
678 // don't travel directly on m_xWizardImpl->aStateHistory, in case something goes wrong
679 std::stack< WizardTypes::WizardState > aTravelVirtually = m_xWizardImpl->aStateHistory;
680 std::stack< WizardTypes::WizardState > aOldStateHistory = m_xWizardImpl->aStateHistory;
681 while ( nCurrentState != _nTargetState )
683 WizardTypes::WizardState nNextState = determineNextState( nCurrentState );
684 if ( WZS_INVALID_STATE == nNextState )
686 OSL_FAIL( "RoadmapWizard::skipUntil: the given target state does not exist!" );
687 return false;
690 // remember the skipped state in the history
691 aTravelVirtually.push( nCurrentState );
693 // get the next state
694 nCurrentState = nNextState;
696 m_xWizardImpl->aStateHistory = aTravelVirtually;
697 // show the target page
698 if ( !ShowPage( nCurrentState ) )
700 // argh! prepareLeaveCurrentPage succeeded, determineNextState succeeded,
701 // but ShowPage doesn't? Somebody behaves very strange here...
702 OSL_FAIL( "RoadmapWizard::skipUntil: very unpolite..." );
703 m_xWizardImpl->aStateHistory = aOldStateHistory;
704 return false;
706 return true;
709 void RoadmapWizard::travelNext()
711 // determine the next state to travel to
712 WizardTypes::WizardState nCurrentState = getCurrentState();
713 WizardTypes::WizardState nNextState = determineNextState(nCurrentState);
714 if (WZS_INVALID_STATE == nNextState)
715 return;
717 // the state history is used by the enterState method
718 // all fine
719 m_xWizardImpl->aStateHistory.push(nCurrentState);
720 if (!ShowPage(nNextState))
722 m_xWizardImpl->aStateHistory.pop();
726 void RoadmapWizard::travelPrevious()
728 DBG_ASSERT(!m_xWizardImpl->aStateHistory.empty(), "RoadmapWizard::travelPrevious: have no previous page!");
730 // the next state to switch to
731 WizardTypes::WizardState nPreviousState = m_xWizardImpl->aStateHistory.top();
733 // the state history is used by the enterState method
734 m_xWizardImpl->aStateHistory.pop();
735 // show this page
736 if (!ShowPage(nPreviousState))
738 m_xWizardImpl->aStateHistory.push(nPreviousState);
741 // all fine
744 void RoadmapWizard::removePageFromHistory( WizardTypes::WizardState nToRemove )
747 std::stack< WizardTypes::WizardState > aTemp;
748 while(!m_xWizardImpl->aStateHistory.empty())
750 WizardTypes::WizardState nPreviousState = m_xWizardImpl->aStateHistory.top();
751 m_xWizardImpl->aStateHistory.pop();
752 if(nPreviousState != nToRemove)
753 aTemp.push( nPreviousState );
754 else
755 break;
757 while(!aTemp.empty())
759 m_xWizardImpl->aStateHistory.push( aTemp.top() );
760 aTemp.pop();
764 IMPL_LINK_NOARG(RoadmapWizard, OnPrevPage, Button*, void)
766 if ( isTravelingSuspended() )
767 return;
768 RoadmapWizardTravelSuspension aTravelGuard( *this );
769 travelPrevious();
772 IMPL_LINK_NOARG(RoadmapWizard, OnNextPage, Button*, void)
774 if ( isTravelingSuspended() )
775 return;
776 RoadmapWizardTravelSuspension aTravelGuard( *this );
777 travelNext();
780 bool RoadmapWizard::isTravelingSuspended() const
782 return m_xWizardImpl->m_bTravelingSuspended;
785 void RoadmapWizard::suspendTraveling( AccessGuard )
787 DBG_ASSERT( !m_xWizardImpl->m_bTravelingSuspended, "RoadmapWizard::suspendTraveling: already suspended!" );
788 m_xWizardImpl->m_bTravelingSuspended = true;
791 void RoadmapWizard::resumeTraveling( AccessGuard )
793 DBG_ASSERT( m_xWizardImpl->m_bTravelingSuspended, "RoadmapWizard::resumeTraveling: nothing to resume!" );
794 m_xWizardImpl->m_bTravelingSuspended = false;
797 WizardMachine::WizardMachine(weld::Window* pParent, WizardButtonFlags nButtonFlags)
798 : AssistantController(pParent, "vcl/ui/wizard.ui", "Wizard")
799 , m_pCurTabPage(nullptr)
800 , m_nCurState(0)
801 , m_pFirstPage(nullptr)
802 , m_xFinish(m_xAssistant->weld_widget_for_response(RET_OK))
803 , m_xCancel(m_xAssistant->weld_widget_for_response(RET_CANCEL))
804 , m_xNextPage(m_xAssistant->weld_widget_for_response(RET_YES))
805 , m_xPrevPage(m_xAssistant->weld_widget_for_response(RET_NO))
806 , m_xHelp(m_xAssistant->weld_widget_for_response(RET_HELP))
807 , m_pImpl(new WizardMachineImplData)
809 implConstruct(nButtonFlags);
812 void WizardMachine::implConstruct(const WizardButtonFlags nButtonFlags)
814 m_pImpl->sTitleBase = m_xAssistant->get_title();
816 const bool bHideHelp = comphelper::LibreOfficeKit::isActive() &&
817 officecfg::Office::Common::Help::HelpRootURL::get().isEmpty();
818 // create the buttons according to the wizard button flags
819 // the help button
820 if (nButtonFlags & WizardButtonFlags::HELP && !bHideHelp)
821 m_xHelp->show();
822 else
823 m_xHelp->hide();
825 // the previous button
826 if (nButtonFlags & WizardButtonFlags::PREVIOUS)
828 m_xPrevPage->set_help_id( HID_WIZARD_PREVIOUS );
829 m_xPrevPage->show();
831 m_xPrevPage->connect_clicked( LINK( this, WizardMachine, OnPrevPage ) );
833 else
834 m_xPrevPage->hide();
836 // the next button
837 if (nButtonFlags & WizardButtonFlags::NEXT)
839 m_xNextPage->set_help_id( HID_WIZARD_NEXT );
840 m_xNextPage->show();
842 m_xNextPage->connect_clicked( LINK( this, WizardMachine, OnNextPage ) );
844 else
845 m_xNextPage->hide();
847 // the finish button
848 if (nButtonFlags & WizardButtonFlags::FINISH)
850 m_xFinish->show();
852 m_xFinish->connect_clicked( LINK( this, WizardMachine, OnFinish ) );
854 else
855 m_xFinish->hide();
857 // the cancel button
858 if (nButtonFlags & WizardButtonFlags::CANCEL)
860 m_xCancel->show();
861 m_xCancel->connect_clicked( LINK( this, WizardMachine, OnCancel ) );
863 else
864 m_xCancel->hide();
867 WizardMachine::~WizardMachine()
869 if (m_pImpl)
871 while (m_pFirstPage)
872 RemovePage(m_pFirstPage->mxPage.get());
873 m_pImpl.reset();
877 void WizardMachine::implUpdateTitle()
879 OUString sCompleteTitle(m_pImpl->sTitleBase);
881 // append the page title
882 BuilderPage* pCurrentPage = GetPage(getCurrentState());
883 if ( pCurrentPage && !pCurrentPage->GetPageTitle().isEmpty() )
885 sCompleteTitle += " - " + pCurrentPage->GetPageTitle();
888 m_xAssistant->set_title(sCompleteTitle);
891 void WizardMachine::setTitleBase(const OUString& _rTitleBase)
893 m_pImpl->sTitleBase = _rTitleBase;
894 implUpdateTitle();
897 OUString WizardMachine::getPageIdentForState(WizardTypes::WizardState nState) const
899 return OUString::number(nState);
902 WizardTypes::WizardState WizardMachine::getStateFromPageIdent(const OUString& rIdent) const
904 return rIdent.toInt32();
907 BuilderPage* WizardMachine::GetOrCreatePage( const WizardTypes::WizardState i_nState )
909 if ( nullptr == GetPage( i_nState ) )
911 std::unique_ptr<BuilderPage> xNewPage = createPage( i_nState );
912 DBG_ASSERT( xNewPage, "WizardMachine::GetOrCreatePage: invalid new page (NULL)!" );
914 // fill up the page sequence of our base class (with dummies)
915 while ( m_pImpl->nFirstUnknownPage < i_nState )
917 AddPage( nullptr );
918 ++m_pImpl->nFirstUnknownPage;
921 if ( m_pImpl->nFirstUnknownPage == i_nState )
923 // encountered this page number the first time
924 AddPage(std::move(xNewPage));
925 ++m_pImpl->nFirstUnknownPage;
927 else
928 // already had this page - just change it
929 SetPage(i_nState, std::move(xNewPage));
931 return GetPage( i_nState );
934 void WizardMachine::ActivatePage()
936 WizardTypes::WizardState nCurrentLevel = m_nCurState;
937 GetOrCreatePage( nCurrentLevel );
939 enterState( nCurrentLevel );
942 bool WizardMachine::DeactivatePage()
944 WizardTypes::WizardState nCurrentState = getCurrentState();
945 return leaveState(nCurrentState);
948 void WizardMachine::defaultButton(WizardButtonFlags _nWizardButtonFlags)
950 // the new default button
951 weld::Button* pNewDefButton = nullptr;
952 if (_nWizardButtonFlags & WizardButtonFlags::FINISH)
953 pNewDefButton = m_xFinish.get();
954 if (_nWizardButtonFlags & WizardButtonFlags::NEXT)
955 pNewDefButton = m_xNextPage.get();
956 if (_nWizardButtonFlags & WizardButtonFlags::PREVIOUS)
957 pNewDefButton = m_xPrevPage.get();
958 if (_nWizardButtonFlags & WizardButtonFlags::HELP)
959 pNewDefButton = m_xHelp.get();
960 if (_nWizardButtonFlags & WizardButtonFlags::CANCEL)
961 pNewDefButton = m_xCancel.get();
963 defaultButton(pNewDefButton);
966 void WizardMachine::defaultButton(weld::Button* _pNewDefButton)
968 // loop through all (direct and indirect) descendants which participate in our tabbing order, and
969 // reset the WB_DEFBUTTON for every window which is a button and set _pNewDefButton as the new
970 // WB_DEFBUTTON
971 m_xAssistant->change_default_widget(nullptr, _pNewDefButton);
974 void WizardMachine::enableButtons(WizardButtonFlags _nWizardButtonFlags, bool _bEnable)
976 if (_nWizardButtonFlags & WizardButtonFlags::FINISH)
977 m_xFinish->set_sensitive(_bEnable);
978 if (_nWizardButtonFlags & WizardButtonFlags::NEXT)
979 m_xNextPage->set_sensitive(_bEnable);
980 if (_nWizardButtonFlags & WizardButtonFlags::PREVIOUS)
981 m_xPrevPage->set_sensitive(_bEnable);
982 if (_nWizardButtonFlags & WizardButtonFlags::HELP)
983 m_xHelp->set_sensitive(_bEnable);
984 if (_nWizardButtonFlags & WizardButtonFlags::CANCEL)
985 m_xCancel->set_sensitive(_bEnable);
988 void WizardMachine::enterState(WizardTypes::WizardState _nState)
990 // tell the page
991 IWizardPageController* pController = getPageController( GetPage( _nState ) );
992 OSL_ENSURE( pController, "WizardMachine::enterState: no controller for the given page!" );
993 if ( pController )
994 pController->initializePage();
996 if ( isAutomaticNextButtonStateEnabled() )
997 enableButtons( WizardButtonFlags::NEXT, canAdvance() );
999 enableButtons( WizardButtonFlags::PREVIOUS, !m_pImpl->aStateHistory.empty() );
1001 // set the new title - it depends on the current page (i.e. state)
1002 implUpdateTitle();
1005 bool WizardMachine::leaveState(WizardTypes::WizardState)
1007 // no need to ask the page here.
1008 // If we reach this point, we already gave the current page the chance to commit it's data,
1009 // and it was allowed to commit it's data
1011 return true;
1014 bool WizardMachine::onFinish()
1016 return Finish(RET_OK);
1019 IMPL_LINK_NOARG(WizardMachine, OnFinish, weld::Button&, void)
1021 if ( isTravelingSuspended() )
1022 return;
1024 // prevent WizardTravelSuspension from using this instance
1025 // after will be destructed due to onFinish and async response call
1027 WizardTravelSuspension aTravelGuard( *this );
1028 if (!prepareLeaveCurrentState(WizardTypes::eFinish))
1030 return;
1034 onFinish();
1037 IMPL_LINK_NOARG(WizardMachine, OnCancel, weld::Button&, void)
1039 m_xAssistant->response(RET_CANCEL);
1042 WizardTypes::WizardState WizardMachine::determineNextState(WizardTypes::WizardState _nCurrentState ) const
1044 return _nCurrentState + 1;
1047 bool WizardMachine::prepareLeaveCurrentState( WizardTypes::CommitPageReason _eReason )
1049 IWizardPageController* pController = getPageController( GetPage( getCurrentState() ) );
1050 ENSURE_OR_RETURN( pController != nullptr, "WizardMachine::prepareLeaveCurrentState: no controller for the current page!", true );
1051 return pController->commitPage( _eReason );
1054 bool WizardMachine::skipBackwardUntil(WizardTypes::WizardState _nTargetState)
1056 // allowed to leave the current page?
1057 if ( !prepareLeaveCurrentState( WizardTypes::eTravelBackward ) )
1058 return false;
1060 // don't travel directly on m_pImpl->aStateHistory, in case something goes wrong
1061 std::stack< WizardTypes::WizardState > aTravelVirtually = m_pImpl->aStateHistory;
1062 std::stack< WizardTypes::WizardState > aOldStateHistory = m_pImpl->aStateHistory;
1064 WizardTypes::WizardState nCurrentRollbackState = getCurrentState();
1065 while ( nCurrentRollbackState != _nTargetState )
1067 DBG_ASSERT( !aTravelVirtually.empty(), "WizardMachine::skipBackwardUntil: this target state does not exist in the history!" );
1068 nCurrentRollbackState = aTravelVirtually.top();
1069 aTravelVirtually.pop();
1071 m_pImpl->aStateHistory = aTravelVirtually;
1072 if ( !ShowPage( _nTargetState ) )
1074 m_pImpl->aStateHistory = aOldStateHistory;
1075 return false;
1077 return true;
1080 bool WizardMachine::skipUntil( WizardTypes::WizardState _nTargetState )
1082 WizardTypes::WizardState nCurrentState = getCurrentState();
1084 // allowed to leave the current page?
1085 if ( !prepareLeaveCurrentState( nCurrentState < _nTargetState ? WizardTypes::eTravelForward : WizardTypes::eTravelBackward ) )
1086 return false;
1088 // don't travel directly on m_pImpl->aStateHistory, in case something goes wrong
1089 std::stack< WizardTypes::WizardState > aTravelVirtually = m_pImpl->aStateHistory;
1090 std::stack< WizardTypes::WizardState > aOldStateHistory = m_pImpl->aStateHistory;
1091 while ( nCurrentState != _nTargetState )
1093 WizardTypes::WizardState nNextState = determineNextState( nCurrentState );
1094 if ( WZS_INVALID_STATE == nNextState )
1096 OSL_FAIL( "WizardMachine::skipUntil: the given target state does not exist!" );
1097 return false;
1100 // remember the skipped state in the history
1101 aTravelVirtually.push( nCurrentState );
1103 // get the next state
1104 nCurrentState = nNextState;
1106 m_pImpl->aStateHistory = aTravelVirtually;
1107 // show the target page
1108 if ( !ShowPage( nCurrentState ) )
1110 // argh! prepareLeaveCurrentPage succeeded, determineNextState succeeded,
1111 // but ShowPage doesn't? Somebody behaves very strange here...
1112 OSL_FAIL( "WizardMachine::skipUntil: very unpolite..." );
1113 m_pImpl->aStateHistory = aOldStateHistory;
1114 return false;
1116 return true;
1119 void WizardMachine::skip()
1121 // allowed to leave the current page?
1122 if ( !prepareLeaveCurrentState( WizardTypes::eTravelForward ) )
1123 return;
1125 WizardTypes::WizardState nCurrentState = getCurrentState();
1126 WizardTypes::WizardState nNextState = determineNextState(nCurrentState);
1128 if (WZS_INVALID_STATE == nNextState)
1129 return;
1131 // remember the skipped state in the history
1132 m_pImpl->aStateHistory.push(nCurrentState);
1134 // get the next state
1135 nCurrentState = nNextState;
1137 // show the (n+1)th page
1138 if (!ShowPage(nCurrentState))
1140 // TODO: this leaves us in a state where we have no current page and an inconsistent state history.
1141 // Perhaps we should rollback the skipping here...
1142 OSL_FAIL("RoadmapWizard::skip: very unpolite...");
1143 // if somebody does a skip and then does not allow to leave...
1144 // (can't be a commit error, as we've already committed the current page. So if ShowPage fails here,
1145 // somebody behaves really strange ...)
1146 return;
1149 // all fine
1152 bool WizardMachine::travelNext()
1154 // allowed to leave the current page?
1155 if ( !prepareLeaveCurrentState( WizardTypes::eTravelForward ) )
1156 return false;
1158 // determine the next state to travel to
1159 WizardTypes::WizardState nCurrentState = getCurrentState();
1160 WizardTypes::WizardState nNextState = determineNextState(nCurrentState);
1161 if (WZS_INVALID_STATE == nNextState)
1162 return false;
1164 // the state history is used by the enterState method
1165 // all fine
1166 m_pImpl->aStateHistory.push(nCurrentState);
1167 if (!ShowPage(nNextState))
1169 m_pImpl->aStateHistory.pop();
1170 return false;
1173 return true;
1176 bool WizardMachine::ShowPage(WizardTypes::WizardState nState)
1178 if (DeactivatePage())
1180 BuilderPage* pOldTabPage = m_pCurTabPage;
1182 m_nCurState = nState;
1183 ActivatePage();
1185 if (pOldTabPage)
1186 pOldTabPage->Deactivate();
1188 m_xAssistant->set_current_page(getPageIdentForState(nState));
1190 m_pCurTabPage = GetPage(m_nCurState);
1191 m_pCurTabPage->Activate();
1193 return true;
1195 return false;
1198 bool WizardMachine::ShowNextPage()
1200 return ShowPage(m_nCurState + 1);
1203 bool WizardMachine::ShowPrevPage()
1205 if (!m_nCurState)
1206 return false;
1207 return ShowPage(m_nCurState - 1);
1210 bool WizardMachine::travelPrevious()
1212 DBG_ASSERT(!m_pImpl->aStateHistory.empty(), "WizardMachine::travelPrevious: have no previous page!");
1214 // allowed to leave the current page?
1215 if ( !prepareLeaveCurrentState( WizardTypes::eTravelBackward ) )
1216 return false;
1218 // the next state to switch to
1219 WizardTypes::WizardState nPreviousState = m_pImpl->aStateHistory.top();
1221 // the state history is used by the enterState method
1222 m_pImpl->aStateHistory.pop();
1223 // show this page
1224 if (!ShowPage(nPreviousState))
1226 m_pImpl->aStateHistory.push(nPreviousState);
1227 return false;
1230 // all fine
1231 return true;
1235 void WizardMachine::removePageFromHistory( WizardTypes::WizardState nToRemove )
1238 std::stack< WizardTypes::WizardState > aTemp;
1239 while(!m_pImpl->aStateHistory.empty())
1241 WizardTypes::WizardState nPreviousState = m_pImpl->aStateHistory.top();
1242 m_pImpl->aStateHistory.pop();
1243 if(nPreviousState != nToRemove)
1244 aTemp.push( nPreviousState );
1245 else
1246 break;
1248 while(!aTemp.empty())
1250 m_pImpl->aStateHistory.push( aTemp.top() );
1251 aTemp.pop();
1256 void WizardMachine::enableAutomaticNextButtonState()
1258 m_pImpl->m_bAutoNextButtonState = true;
1262 bool WizardMachine::isAutomaticNextButtonStateEnabled() const
1264 return m_pImpl->m_bAutoNextButtonState;
1267 IMPL_LINK_NOARG(WizardMachine, OnPrevPage, weld::Button&, void)
1269 if ( isTravelingSuspended() )
1270 return;
1271 WizardTravelSuspension aTravelGuard( *this );
1272 travelPrevious();
1275 IMPL_LINK_NOARG(WizardMachine, OnNextPage, weld::Button&, void)
1277 if ( isTravelingSuspended() )
1278 return;
1279 WizardTravelSuspension aTravelGuard( *this );
1280 travelNext();
1283 IWizardPageController* WizardMachine::getPageController(BuilderPage* pCurrentPage) const
1285 IWizardPageController* pController = dynamic_cast<IWizardPageController*>(pCurrentPage);
1286 return pController;
1289 void WizardMachine::getStateHistory( std::vector< WizardTypes::WizardState >& _out_rHistory )
1291 std::stack< WizardTypes::WizardState > aHistoryCopy( m_pImpl->aStateHistory );
1292 while ( !aHistoryCopy.empty() )
1294 _out_rHistory.push_back( aHistoryCopy.top() );
1295 aHistoryCopy.pop();
1299 bool WizardMachine::canAdvance() const
1301 return WZS_INVALID_STATE != determineNextState( getCurrentState() );
1304 void WizardMachine::updateTravelUI()
1306 const IWizardPageController* pController = getPageController( GetPage( getCurrentState() ) );
1307 OSL_ENSURE( pController != nullptr, "RoadmapWizard::updateTravelUI: no controller for the current page!" );
1309 bool bCanAdvance =
1310 ( !pController || pController->canAdvance() ) // the current page allows to advance
1311 && canAdvance(); // the dialog as a whole allows to advance
1312 enableButtons( WizardButtonFlags::NEXT, bCanAdvance );
1315 bool WizardMachine::isTravelingSuspended() const
1317 return m_pImpl->m_bTravelingSuspended;
1320 void WizardMachine::suspendTraveling( AccessGuard )
1322 DBG_ASSERT( !m_pImpl->m_bTravelingSuspended, "WizardMachine::suspendTraveling: already suspended!" );
1323 m_pImpl->m_bTravelingSuspended = true;
1326 void WizardMachine::resumeTraveling( AccessGuard )
1328 if (!m_pImpl)
1329 return;
1331 DBG_ASSERT( m_pImpl->m_bTravelingSuspended, "WizardMachine::resumeTraveling: nothing to resume!" );
1332 m_pImpl->m_bTravelingSuspended = false;
1335 bool WizardMachine::Finish(short nResult)
1337 if ( DeactivatePage() )
1339 if (m_pCurTabPage)
1340 m_pCurTabPage->Deactivate();
1342 m_xAssistant->response(nResult);
1343 return true;
1345 else
1346 return false;
1349 void WizardMachine::AddPage(std::unique_ptr<BuilderPage> xPage)
1351 WizPageData* pNewPageData = new WizPageData;
1352 pNewPageData->mpNext = nullptr;
1353 pNewPageData->mxPage = std::move(xPage);
1355 if ( !m_pFirstPage )
1356 m_pFirstPage = pNewPageData;
1357 else
1359 WizPageData* pPageData = m_pFirstPage;
1360 while ( pPageData->mpNext )
1361 pPageData = pPageData->mpNext;
1362 pPageData->mpNext = pNewPageData;
1366 void WizardMachine::RemovePage(const BuilderPage* pPage)
1368 WizPageData* pPrevPageData = nullptr;
1369 WizPageData* pPageData = m_pFirstPage;
1370 while ( pPageData )
1372 if (pPageData->mxPage.get() == pPage)
1374 if (pPrevPageData)
1375 pPrevPageData->mpNext = pPageData->mpNext;
1376 else
1377 m_pFirstPage = pPageData->mpNext;
1378 if (pPage == m_pCurTabPage)
1379 m_pCurTabPage = nullptr;
1380 delete pPageData;
1381 return;
1384 pPrevPageData = pPageData;
1385 pPageData = pPageData->mpNext;
1388 OSL_FAIL( "WizardMachine::RemovePage() - Page not in list" );
1391 void WizardMachine::SetPage(WizardTypes::WizardState nLevel, std::unique_ptr<BuilderPage> xPage)
1393 sal_uInt16 nTempLevel = 0;
1394 WizPageData* pPageData = m_pFirstPage;
1395 while ( pPageData )
1397 if ( (nTempLevel == nLevel) || !pPageData->mpNext )
1398 break;
1400 nTempLevel++;
1401 pPageData = pPageData->mpNext;
1404 if ( pPageData )
1406 if (pPageData->mxPage.get() == m_pCurTabPage)
1407 m_pCurTabPage = nullptr;
1408 pPageData->mxPage = std::move(xPage);
1412 BuilderPage* WizardMachine::GetPage(WizardTypes::WizardState nLevel) const
1414 sal_uInt16 nTempLevel = 0;
1416 for (WizPageData* pPageData = m_pFirstPage; pPageData;
1417 pPageData = pPageData->mpNext)
1419 if ( nTempLevel == nLevel )
1420 return pPageData->mxPage.get();
1421 nTempLevel++;
1424 return nullptr;
1426 } // namespace svt
1429 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */