sdext: adapt xpdfwrapper to poppler 24.12
[LibreOffice.git] / vcl / source / control / wizardmachine.cxx
blob45d42fc21e68fb86747c8e151461cec6fe4d080e
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 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
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::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
316 // the help button
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)));
321 m_pHelp->Show();
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));
334 m_pPrevPage->Show();
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
339 else
340 AddButton( m_pPrevPage, WIZARDDIALOG_BUTTON_STDOFFSET_X );
341 mpPrevBtn = m_pPrevPage;
342 m_pPrevPage->SetClickHdl( LINK( this, RoadmapWizard, OnPrevPage ) );
345 // the next button
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));
352 m_pNextPage->Show();
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 ) );
360 // the finish button
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));
366 m_pFinish->Show();
367 m_pFinish->set_id(u"finish"_ustr);
369 AddButton( m_pFinish, WIZARDDIALOG_BUTTON_STDOFFSET_X );
370 m_pFinish->SetClickHdl( LINK( this, RoadmapWizard, OnFinish ) );
373 // the cancel button
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)));
378 m_pCancel->Show();
380 AddButton( m_pCancel, WIZARDDIALOG_BUTTON_STDOFFSET_X );
384 void RoadmapWizard::Resize()
386 if ( IsReallyShown() && !IsInInitShow() )
388 ImplPosCtrls();
389 ImplPosTabPage();
392 Dialog::Resize();
395 void RoadmapWizard::CalcAndSetSize()
397 Size aDlgSize = GetPageSizePixel();
398 if ( !aDlgSize.Width() || !aDlgSize.Height() )
400 ImplWizPageData* pPageData = mpFirstPage;
401 while ( pPageData )
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() )
426 CalcAndSetSize();
429 ImplPosCtrls();
430 ImplPosTabPage();
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 );
456 mpPrevBtn->Click();
458 return true;
461 else
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 );
470 mpNextBtn->Click();
472 return true;
478 return Dialog::EventNotify( rNEvt );
481 void RoadmapWizard::GetOrCreatePage( const WizardTypes::WizardState i_nState )
483 if ( nullptr != GetPage( i_nState ) )
484 return;
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 )
492 AddPage( nullptr );
493 ++m_xWizardImpl->nFirstUnknownPage;
496 if ( m_xWizardImpl->nFirstUnknownPage == i_nState )
498 // encountered this page number the first time
499 AddPage( pNewPage );
500 ++m_xWizardImpl->nFirstUnknownPage;
502 else
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 )
517 mnCurLevel = nLevel;
518 ActivatePage();
519 ImplShowTabPage( ImplGetPage( mnCurLevel ) );
520 return true;
523 void RoadmapWizard::Finish( tools::Long nResult )
525 if ( IsInExecute() )
526 EndDialog( nResult );
527 else if ( GetStyle() & WB_CLOSEABLE )
528 Close();
531 void RoadmapWizard::AddPage( TabPage* pPage )
533 ImplWizPageData* pNewPageData = new ImplWizPageData;
534 pNewPageData->mpNext = nullptr;
535 pNewPageData->mpPage = pPage;
537 if ( !mpFirstPage )
538 mpFirstPage = pNewPageData;
539 else
541 pPage->Hide();
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;
553 while ( pPageData )
555 if ( pPageData->mpPage == pPage )
557 if ( pPrevPageData )
558 pPrevPageData->mpNext = pPageData->mpNext;
559 else
560 mpFirstPage = pPageData->mpNext;
561 if ( pPage == mpCurTabPage )
562 mpCurTabPage = nullptr;
563 delete pPageData;
564 return;
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;
578 while ( pPageData )
580 if ( (nTempLevel == nLevel) || !pPageData->mpNext )
581 break;
583 nTempLevel++;
584 pPageData = pPageData->mpNext;
587 if ( pPageData )
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;
604 nTempLevel++;
607 return nullptr;
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;
617 if ( !mpFirstBtn )
618 mpFirstBtn = pNewBtnData;
619 else
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;
632 while ( pBtnData )
634 if ( pBtnData->mpButton == pButton )
636 if ( pPrevBtnData )
637 pPrevBtnData->mpNext = pBtnData->mpNext;
638 else
639 mpFirstBtn = pBtnData->mpNext;
640 delete pBtnData;
641 return;
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() )
654 return;
655 RoadmapWizardTravelSuspension aTravelGuard( *this );
656 Finish( RET_OK );
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);
676 return false;
678 return true;
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!" );
694 return false;
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);
711 return false;
713 return true;
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)
722 return;
724 // the state history is used by the enterState method
725 // all fine
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();
742 // show this page
743 if (!ShowPage(nPreviousState))
745 m_xWizardImpl->aStateHistory.push(nPreviousState);
748 // all fine
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 );
761 else
762 break;
764 while(!aTemp.empty())
766 m_xWizardImpl->aStateHistory.push( aTemp.top() );
767 aTemp.pop();
771 IMPL_LINK_NOARG(RoadmapWizard, OnPrevPage, Button*, void)
773 if ( isTravelingSuspended() )
774 return;
775 RoadmapWizardTravelSuspension aTravelGuard( *this );
776 travelPrevious();
779 IMPL_LINK_NOARG(RoadmapWizard, OnNextPage, Button*, void)
781 if ( isTravelingSuspended() )
782 return;
783 RoadmapWizardTravelSuspension aTravelGuard( *this );
784 travelNext();
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)
807 , m_nCurState(0)
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
826 // the help button
827 if (nButtonFlags & WizardButtonFlags::HELP && !bHideHelp)
828 m_xHelp->show();
829 else
830 m_xHelp->hide();
832 // the previous button
833 if (nButtonFlags & WizardButtonFlags::PREVIOUS)
835 m_xPrevPage->set_help_id( HID_WIZARD_PREVIOUS );
836 m_xPrevPage->show();
838 m_xPrevPage->connect_clicked( LINK( this, WizardMachine, OnPrevPage ) );
840 else
841 m_xPrevPage->hide();
843 // the next button
844 if (nButtonFlags & WizardButtonFlags::NEXT)
846 m_xNextPage->set_help_id( HID_WIZARD_NEXT );
847 m_xNextPage->show();
849 m_xNextPage->connect_clicked( LINK( this, WizardMachine, OnNextPage ) );
851 else
852 m_xNextPage->hide();
854 // the finish button
855 if (nButtonFlags & WizardButtonFlags::FINISH)
857 m_xFinish->show();
859 m_xFinish->connect_clicked( LINK( this, WizardMachine, OnFinish ) );
861 else
862 m_xFinish->hide();
864 // the cancel button
865 if (nButtonFlags & WizardButtonFlags::CANCEL)
867 m_xCancel->show();
868 m_xCancel->connect_clicked( LINK( this, WizardMachine, OnCancel ) );
870 else
871 m_xCancel->hide();
874 WizardMachine::~WizardMachine()
876 if (m_pImpl)
878 while (m_pFirstPage)
879 RemovePage(m_pFirstPage->mxPage.get());
880 m_pImpl.reset();
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;
901 implUpdateTitle();
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 )
924 AddPage( nullptr );
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;
934 else
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
977 // WB_DEFBUTTON
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)
997 // tell the page
998 IWizardPageController* pController = getPageController( GetPage( _nState ) );
999 OSL_ENSURE( pController, "WizardMachine::enterState: no controller for the given page!" );
1000 if ( pController )
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)
1009 implUpdateTitle();
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
1018 return true;
1021 bool WizardMachine::onFinish()
1023 return Finish(RET_OK);
1026 IMPL_LINK_NOARG(WizardMachine, OnFinish, weld::Button&, void)
1028 if ( isTravelingSuspended() )
1029 return;
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))
1037 return;
1041 onFinish();
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 ) )
1065 return false;
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);
1082 return false;
1084 return true;
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 ) )
1093 return false;
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!" );
1104 return false;
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);
1121 return false;
1123 return true;
1126 void WizardMachine::skip()
1128 // allowed to leave the current page?
1129 if ( !prepareLeaveCurrentState( WizardTypes::eTravelForward ) )
1130 return;
1132 WizardTypes::WizardState nCurrentState = getCurrentState();
1133 WizardTypes::WizardState nNextState = determineNextState(nCurrentState);
1135 if (WZS_INVALID_STATE == nNextState)
1136 return;
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 ...)
1153 return;
1156 // all fine
1159 bool WizardMachine::travelNext()
1161 // allowed to leave the current page?
1162 if ( !prepareLeaveCurrentState( WizardTypes::eTravelForward ) )
1163 return false;
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)
1169 return false;
1171 // the state history is used by the enterState method
1172 // all fine
1173 m_pImpl->aStateHistory.push(nCurrentState);
1174 if (!ShowPage(nNextState))
1176 m_pImpl->aStateHistory.pop();
1177 return false;
1180 return true;
1183 bool WizardMachine::ShowPage(WizardTypes::WizardState nState)
1185 if (DeactivatePage())
1187 BuilderPage* pOldTabPage = m_pCurTabPage;
1189 m_nCurState = nState;
1190 ActivatePage();
1192 if (pOldTabPage)
1193 pOldTabPage->Deactivate();
1195 m_xAssistant->set_current_page(getPageIdentForState(nState));
1197 m_pCurTabPage = GetPage(m_nCurState);
1198 m_pCurTabPage->Activate();
1200 return true;
1202 return false;
1205 bool WizardMachine::ShowNextPage()
1207 return ShowPage(m_nCurState + 1);
1210 bool WizardMachine::ShowPrevPage()
1212 if (!m_nCurState)
1213 return false;
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 ) )
1223 return false;
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();
1230 // show this page
1231 if (!ShowPage(nPreviousState))
1233 m_pImpl->aStateHistory.push(nPreviousState);
1234 return false;
1237 // all fine
1238 return true;
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 );
1252 else
1253 break;
1255 while(!aTemp.empty())
1257 m_pImpl->aStateHistory.push( aTemp.top() );
1258 aTemp.pop();
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() )
1277 return;
1278 WizardTravelSuspension aTravelGuard( *this );
1279 travelPrevious();
1282 IMPL_LINK_NOARG(WizardMachine, OnNextPage, weld::Button&, void)
1284 if ( isTravelingSuspended() )
1285 return;
1286 WizardTravelSuspension aTravelGuard( *this );
1287 travelNext();
1290 IWizardPageController* WizardMachine::getPageController(BuilderPage* pCurrentPage) const
1292 IWizardPageController* pController = dynamic_cast<IWizardPageController*>(pCurrentPage);
1293 return pController;
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() );
1302 aHistoryCopy.pop();
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!" );
1316 bool bCanAdvance =
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 )
1335 if (!m_pImpl)
1336 return;
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() )
1346 if (m_pCurTabPage)
1347 m_pCurTabPage->Deactivate();
1349 m_xAssistant->response(nResult);
1350 return true;
1352 else
1353 return false;
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;
1364 else
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;
1377 while ( pPageData )
1379 if (pPageData->mxPage.get() == pPage)
1381 if (pPrevPageData)
1382 pPrevPageData->mpNext = pPageData->mpNext;
1383 else
1384 m_pFirstPage = pPageData->mpNext;
1385 if (pPage == m_pCurTabPage)
1386 m_pCurTabPage = nullptr;
1387 delete pPageData;
1388 return;
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;
1402 while ( pPageData )
1404 if ( (nTempLevel == nLevel) || !pPageData->mpNext )
1405 break;
1407 nTempLevel++;
1408 pPageData = pPageData->mpNext;
1411 if ( pPageData )
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();
1428 nTempLevel++;
1431 return nullptr;
1433 } // namespace svt
1436 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */