bump product version to 6.4.0.3
[LibreOffice.git] / vcl / source / control / roadmapwizard.cxx
blob160724c78ab3a41a92594516d709a8af86480c82
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 .
21 #include <vcl/toolkit/roadmap.hxx>
22 #include <tools/debug.hxx>
23 #include <osl/diagnose.h>
25 #include <strings.hrc>
26 #include <svdata.hxx>
27 #include <wizdlg.hxx>
29 #include <vector>
30 #include <map>
31 #include <set>
33 #include "wizimpldata.hxx"
35 namespace vcl
37 using namespace RoadmapWizardTypes;
39 namespace
41 typedef ::std::set< WizardTypes::WizardState > StateSet;
43 typedef ::std::map<
44 PathId,
45 WizardPath
46 > Paths;
48 typedef ::std::map<
49 WizardTypes::WizardState,
50 ::std::pair<
51 OUString,
52 RoadmapPageFactory
54 > StateDescriptions;
57 struct RoadmapWizardImpl
59 ScopedVclPtr<ORoadmap> pRoadmap;
60 Paths aPaths;
61 PathId nActivePath;
62 StateDescriptions aStateDescriptors;
63 StateSet aDisabledStates;
64 bool bActivePathIsDefinite;
66 RoadmapWizardImpl()
67 :pRoadmap( nullptr )
68 ,nActivePath( -1 )
69 ,bActivePathIsDefinite( false )
73 /// returns the index of the current state in given path, or -1
74 static sal_Int32 getStateIndexInPath( WizardTypes::WizardState _nState, const WizardPath& _rPath );
75 /// returns the index of the current state in the path with the given id, or -1
76 sal_Int32 getStateIndexInPath( WizardTypes::WizardState _nState, PathId _nPathId );
77 /// returns the index of the first state in which the two given paths differ
78 static sal_Int32 getFirstDifferentIndex( const WizardPath& _rLHS, const WizardPath& _rRHS );
82 sal_Int32 RoadmapWizardImpl::getStateIndexInPath( WizardTypes::WizardState _nState, const WizardPath& _rPath )
84 sal_Int32 nStateIndexInPath = 0;
85 bool bFound = false;
86 for (auto const& path : _rPath)
88 if (path == _nState)
90 bFound = true;
91 break;
93 ++nStateIndexInPath;
95 if (!bFound)
96 nStateIndexInPath = -1;
97 return nStateIndexInPath;
101 sal_Int32 RoadmapWizardImpl::getStateIndexInPath( WizardTypes::WizardState _nState, PathId _nPathId )
103 sal_Int32 nStateIndexInPath = -1;
104 Paths::const_iterator aPathPos = aPaths.find( _nPathId );
105 if ( aPathPos != aPaths.end( ) )
106 nStateIndexInPath = getStateIndexInPath( _nState, aPathPos->second );
107 return nStateIndexInPath;
111 sal_Int32 RoadmapWizardImpl::getFirstDifferentIndex( const WizardPath& _rLHS, const WizardPath& _rRHS )
113 sal_Int32 nMinLength = ::std::min( _rLHS.size(), _rRHS.size() );
114 for ( sal_Int32 nCheck = 0; nCheck < nMinLength; ++nCheck )
116 if ( _rLHS[ nCheck ] != _rRHS[ nCheck ] )
117 return nCheck;
119 return nMinLength;
122 //= RoadmapWizard
123 RoadmapWizard::RoadmapWizard(vcl::Window* pParent, WinBits nStyle, InitFlag eFlag)
124 : Dialog(pParent, nStyle, eFlag)
125 , m_pFinish(nullptr)
126 , m_pCancel(nullptr)
127 , m_pNextPage(nullptr)
128 , m_pPrevPage(nullptr)
129 , m_pHelp(nullptr)
130 , m_xWizardImpl(new WizardMachineImplData)
131 , m_xRoadmapImpl(new RoadmapWizardImpl)
133 ImplInitData();
135 implConstruct(WizardButtonFlags::NEXT | WizardButtonFlags::PREVIOUS | WizardButtonFlags::FINISH | WizardButtonFlags::CANCEL | WizardButtonFlags::HELP);
137 impl_construct();
140 RoadmapWizardMachine::RoadmapWizardMachine(weld::Window* pParent)
141 : WizardMachine(pParent, WizardButtonFlags::NEXT | WizardButtonFlags::PREVIOUS | WizardButtonFlags::FINISH | WizardButtonFlags::CANCEL | WizardButtonFlags::HELP)
142 , m_pImpl( new RoadmapWizardImpl )
144 m_xAssistant->connect_jump_page(LINK(this, RoadmapWizardMachine, OnRoadmapItemSelected));
147 void RoadmapWizard::impl_construct()
149 SetLeftAlignedButtonCount( 1 );
150 SetEmptyViewMargin();
152 m_xRoadmapImpl->pRoadmap.disposeAndReset( VclPtr<ORoadmap>::Create( this, WB_TABSTOP ) );
153 m_xRoadmapImpl->pRoadmap->SetText( VclResId( STR_WIZDLG_ROADMAP_TITLE ) );
154 m_xRoadmapImpl->pRoadmap->SetPosPixel( Point( 0, 0 ) );
155 m_xRoadmapImpl->pRoadmap->SetItemSelectHdl( LINK( this, RoadmapWizard, OnRoadmapItemSelected ) );
157 Size aRoadmapSize = LogicToPixel(Size(85, 0), MapMode(MapUnit::MapAppFont));
158 aRoadmapSize.setHeight( GetSizePixel().Height() );
159 m_xRoadmapImpl->pRoadmap->SetSizePixel( aRoadmapSize );
161 SetViewWindow( m_xRoadmapImpl->pRoadmap );
162 SetViewAlign( WindowAlign::Left );
163 m_xRoadmapImpl->pRoadmap->Show();
166 void RoadmapWizard::ShowRoadmap(bool bShow)
168 m_xRoadmapImpl->pRoadmap->Show(bShow);
169 CalcAndSetSize();
172 RoadmapWizard::~RoadmapWizard()
174 disposeOnce();
177 RoadmapWizardMachine::~RoadmapWizardMachine()
181 void RoadmapWizard::dispose()
183 m_xRoadmapImpl.reset();
185 m_pFinish.disposeAndClear();
186 m_pCancel.disposeAndClear();
187 m_pNextPage.disposeAndClear();
188 m_pPrevPage.disposeAndClear();
189 m_pHelp.disposeAndClear();
191 if (m_xWizardImpl)
193 for (WizardTypes::WizardState i = 0; i < m_xWizardImpl->nFirstUnknownPage; ++i)
195 TabPage *pPage = GetPage(i);
196 if (pPage)
197 pPage->disposeOnce();
199 m_xWizardImpl.reset();
202 maWizardLayoutIdle.Stop();
204 // Remove all buttons
205 while ( mpFirstBtn )
206 RemoveButton( mpFirstBtn->mpButton );
208 // Remove all pages
209 while ( mpFirstPage )
210 RemovePage( mpFirstPage->mpPage );
212 mpCurTabPage.clear();
213 mpPrevBtn.clear();
214 mpNextBtn.clear();
215 mpViewWindow.clear();
216 Dialog::dispose();
219 void RoadmapWizard::SetRoadmapHelpId( const OString& _rId )
221 m_xRoadmapImpl->pRoadmap->SetHelpId( _rId );
224 void RoadmapWizardMachine::SetRoadmapHelpId(const OString& rId)
226 m_xAssistant->set_page_side_help_id(rId);
229 void RoadmapWizardMachine::declarePath( PathId _nPathId, const WizardPath& _lWizardStates)
231 m_pImpl->aPaths.emplace( _nPathId, _lWizardStates );
233 if ( m_pImpl->aPaths.size() == 1 )
234 // the very first path -> activate it
235 activatePath( _nPathId );
236 else
237 implUpdateRoadmap( );
240 void RoadmapWizardMachine::activatePath( PathId _nPathId, bool _bDecideForIt )
242 if ( ( _nPathId == m_pImpl->nActivePath ) && ( _bDecideForIt == m_pImpl->bActivePathIsDefinite ) )
243 // nothing to do
244 return;
246 // does the given path exist?
247 Paths::const_iterator aNewPathPos = m_pImpl->aPaths.find( _nPathId );
248 DBG_ASSERT( aNewPathPos != m_pImpl->aPaths.end(), "RoadmapWizard::activate: there is no such path!" );
249 if ( aNewPathPos == m_pImpl->aPaths.end() )
250 return;
252 // determine the index of the current state in the current path
253 sal_Int32 nCurrentStatePathIndex = -1;
254 if ( m_pImpl->nActivePath != -1 )
255 nCurrentStatePathIndex = m_pImpl->getStateIndexInPath( getCurrentState(), m_pImpl->nActivePath );
257 DBG_ASSERT( static_cast<sal_Int32>(aNewPathPos->second.size()) > nCurrentStatePathIndex,
258 "RoadmapWizard::activate: you cannot activate a path which has less states than we've already advanced!" );
259 // If this asserts, this for instance means that we are already in state number, say, 5
260 // of our current path, and the caller tries to activate a path which has less than 5
261 // states
262 if ( static_cast<sal_Int32>(aNewPathPos->second.size()) <= nCurrentStatePathIndex )
263 return;
265 // assert that the current and the new path are equal, up to nCurrentStatePathIndex
266 Paths::const_iterator aActivePathPos = m_pImpl->aPaths.find( m_pImpl->nActivePath );
267 if ( aActivePathPos != m_pImpl->aPaths.end() )
269 if ( RoadmapWizardImpl::getFirstDifferentIndex( aActivePathPos->second, aNewPathPos->second ) <= nCurrentStatePathIndex )
271 OSL_FAIL( "RoadmapWizard::activate: you cannot activate a path which conflicts with the current one *before* the current state!" );
272 return;
276 m_pImpl->nActivePath = _nPathId;
277 m_pImpl->bActivePathIsDefinite = _bDecideForIt;
279 implUpdateRoadmap( );
282 void RoadmapWizard::implUpdateRoadmap( )
284 DBG_ASSERT( m_xRoadmapImpl->aPaths.find( m_xRoadmapImpl->nActivePath ) != m_xRoadmapImpl->aPaths.end(),
285 "RoadmapWizard::implUpdateRoadmap: there is no such path!" );
286 const WizardPath& rActivePath( m_xRoadmapImpl->aPaths[ m_xRoadmapImpl->nActivePath ] );
288 sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
289 if (nCurrentStatePathIndex < 0)
290 return;
292 // determine up to which index (in the new path) we have to display the items
293 RoadmapTypes::ItemIndex nUpperStepBoundary = static_cast<RoadmapTypes::ItemIndex>(rActivePath.size());
294 bool bIncompletePath = false;
295 if ( !m_xRoadmapImpl->bActivePathIsDefinite )
297 for (auto const& path : m_xRoadmapImpl->aPaths)
299 if ( path.first == m_xRoadmapImpl->nActivePath )
300 // it's the path we are just activating -> no need to check anything
301 continue;
302 // the index from which on both paths differ
303 sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
304 if ( nDivergenceIndex <= nCurrentStatePathIndex )
305 // they differ in an index which we have already left behind us
306 // -> this is no conflict anymore
307 continue;
309 // the path conflicts with our new path -> don't activate the
310 // *complete* new path, but only up to the step which is unambiguous
311 nUpperStepBoundary = nDivergenceIndex;
312 bIncompletePath = true;
316 // can we advance from the current page?
317 bool bCurrentPageCanAdvance = true;
318 TabPage* pCurrentPage = GetPage( getCurrentState() );
319 if ( pCurrentPage )
321 const IWizardPageController* pController = getPageController( GetPage( getCurrentState() ) );
322 OSL_ENSURE( pController != nullptr, "RoadmapWizard::implUpdateRoadmap: no controller for the current page!" );
323 bCurrentPageCanAdvance = !pController || pController->canAdvance();
326 // now, we have to remove all items after nCurrentStatePathIndex, and insert the items from the active
327 // path, up to (excluding) nUpperStepBoundary
328 RoadmapTypes::ItemIndex nLoopUntil = ::std::max( nUpperStepBoundary, m_xRoadmapImpl->pRoadmap->GetItemCount() );
329 for ( RoadmapTypes::ItemIndex nItemIndex = nCurrentStatePathIndex; nItemIndex < nLoopUntil; ++nItemIndex )
331 bool bExistentItem = ( nItemIndex < m_xRoadmapImpl->pRoadmap->GetItemCount() );
332 bool bNeedItem = ( nItemIndex < nUpperStepBoundary );
334 bool bInsertItem = false;
335 if ( bExistentItem )
337 if ( !bNeedItem )
339 while ( nItemIndex < m_xRoadmapImpl->pRoadmap->GetItemCount() )
340 m_xRoadmapImpl->pRoadmap->DeleteRoadmapItem( nItemIndex );
341 break;
343 else
345 // there is an item with this index in the roadmap - does it match what is requested by
346 // the respective state in the active path?
347 RoadmapTypes::ItemId nPresentItemId = m_xRoadmapImpl->pRoadmap->GetItemID( nItemIndex );
348 WizardTypes::WizardState nRequiredState = rActivePath[ nItemIndex ];
349 if ( nPresentItemId != nRequiredState )
351 m_xRoadmapImpl->pRoadmap->DeleteRoadmapItem( nItemIndex );
352 bInsertItem = true;
356 else
358 DBG_ASSERT( bNeedItem, "RoadmapWizard::implUpdateRoadmap: ehm - none needed, none present - why did the loop not terminate?" );
359 bInsertItem = bNeedItem;
362 WizardTypes::WizardState nState( rActivePath[ nItemIndex ] );
363 if ( bInsertItem )
365 m_xRoadmapImpl->pRoadmap->InsertRoadmapItem(
366 nItemIndex,
367 getStateDisplayName( nState ),
368 nState,
369 true
373 // if the item is *after* the current state, but the current page does not
374 // allow advancing, the disable the state. This relieves derived classes
375 // from disabling all future states just because the current state does not
376 // (yet) allow advancing.
377 const bool bUnconditionedDisable = !bCurrentPageCanAdvance && ( nItemIndex > nCurrentStatePathIndex );
378 const bool bEnable = !bUnconditionedDisable && ( m_xRoadmapImpl->aDisabledStates.find( nState ) == m_xRoadmapImpl->aDisabledStates.end() );
380 m_xRoadmapImpl->pRoadmap->EnableRoadmapItem( m_xRoadmapImpl->pRoadmap->GetItemID( nItemIndex ), bEnable );
383 m_xRoadmapImpl->pRoadmap->SetRoadmapComplete( !bIncompletePath );
386 void RoadmapWizardMachine::implUpdateRoadmap( )
389 DBG_ASSERT( m_pImpl->aPaths.find( m_pImpl->nActivePath ) != m_pImpl->aPaths.end(),
390 "RoadmapWizard::implUpdateRoadmap: there is no such path!" );
391 const WizardPath& rActivePath( m_pImpl->aPaths[ m_pImpl->nActivePath ] );
393 sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
394 if (nCurrentStatePathIndex < 0)
395 return;
397 // determine up to which index (in the new path) we have to display the items
398 RoadmapTypes::ItemIndex nUpperStepBoundary = static_cast<RoadmapTypes::ItemIndex>(rActivePath.size());
399 if ( !m_pImpl->bActivePathIsDefinite )
401 for (auto const& path : m_pImpl->aPaths)
403 if ( path.first == m_pImpl->nActivePath )
404 // it's the path we are just activating -> no need to check anything
405 continue;
406 // the index from which on both paths differ
407 sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
408 if ( nDivergenceIndex <= nCurrentStatePathIndex )
409 // they differ in an index which we have already left behind us
410 // -> this is no conflict anymore
411 continue;
413 // the path conflicts with our new path -> don't activate the
414 // *complete* new path, but only up to the step which is unambiguous
415 nUpperStepBoundary = nDivergenceIndex;
419 // can we advance from the current page?
420 bool bCurrentPageCanAdvance = true;
421 BuilderPage* pCurrentPage = GetPage( getCurrentState() );
422 if ( pCurrentPage )
424 const IWizardPageController* pController = getPageController( GetPage( getCurrentState() ) );
425 OSL_ENSURE( pController != nullptr, "RoadmapWizard::implUpdateRoadmap: no controller for the current page!" );
426 bCurrentPageCanAdvance = !pController || pController->canAdvance();
429 // now, we have to remove all items after nCurrentStatePathIndex, and insert the items from the active
430 // path, up to (excluding) nUpperStepBoundary
431 RoadmapTypes::ItemIndex nRoadmapItems = m_xAssistant->get_n_pages();
432 RoadmapTypes::ItemIndex nLoopUntil = ::std::max( nUpperStepBoundary, nRoadmapItems );
433 for ( RoadmapTypes::ItemIndex nItemIndex = nCurrentStatePathIndex; nItemIndex < nLoopUntil; ++nItemIndex )
435 bool bExistentItem = ( nItemIndex < nRoadmapItems );
436 bool bNeedItem = ( nItemIndex < nUpperStepBoundary );
438 bool bInsertItem = false;
439 if ( bExistentItem )
441 if ( !bNeedItem )
443 int nPages = nRoadmapItems;
444 for (int i = nPages - 1; i >= nItemIndex; --i)
446 m_xAssistant->set_page_title(m_xAssistant->get_page_ident(i), "");
447 --nRoadmapItems;
449 break;
451 else
453 // there is an item with this index in the roadmap - does it match what is requested by
454 // the respective state in the active path?
455 RoadmapTypes::ItemId nPresentItemId = m_xAssistant->get_page_ident(nItemIndex).toInt32();
456 WizardTypes::WizardState nRequiredState = rActivePath[ nItemIndex ];
457 if ( nPresentItemId != nRequiredState )
459 m_xAssistant->set_page_title(OString::number(nPresentItemId), "");
460 bInsertItem = true;
464 else
466 DBG_ASSERT( bNeedItem, "RoadmapWizard::implUpdateRoadmap: ehm - none needed, none present - why did the loop not terminate?" );
467 bInsertItem = bNeedItem;
470 WizardTypes::WizardState nState( rActivePath[ nItemIndex ] );
472 if ( bInsertItem )
474 GetOrCreatePage(nState);
477 OString sIdent(OString::number(nState));
478 m_xAssistant->set_page_index(sIdent, nItemIndex);
479 m_xAssistant->set_page_title(sIdent, getStateDisplayName(nState));
481 // if the item is *after* the current state, but the current page does not
482 // allow advancing, the disable the state. This relieves derived classes
483 // from disabling all future states just because the current state does not
484 // (yet) allow advancing.
485 const bool bUnconditionedDisable = !bCurrentPageCanAdvance && ( nItemIndex > nCurrentStatePathIndex );
486 const bool bEnable = !bUnconditionedDisable && ( m_pImpl->aDisabledStates.find( nState ) == m_pImpl->aDisabledStates.end() );
487 m_xAssistant->set_page_sensitive(sIdent, bEnable);
491 WizardTypes::WizardState RoadmapWizard::determineNextState( WizardTypes::WizardState _nCurrentState ) const
493 sal_Int32 nCurrentStatePathIndex = -1;
495 Paths::const_iterator aActivePathPos = m_xRoadmapImpl->aPaths.find( m_xRoadmapImpl->nActivePath );
496 if ( aActivePathPos != m_xRoadmapImpl->aPaths.end() )
497 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( _nCurrentState, aActivePathPos->second );
499 DBG_ASSERT( nCurrentStatePathIndex != -1, "RoadmapWizard::determineNextState: ehm - how can we travel if there is no (valid) active path?" );
500 if ( nCurrentStatePathIndex == -1 )
501 return WZS_INVALID_STATE;
503 sal_Int32 nNextStateIndex = nCurrentStatePathIndex + 1;
505 while ( ( nNextStateIndex < static_cast<sal_Int32>(aActivePathPos->second.size()) )
506 && ( m_xRoadmapImpl->aDisabledStates.find( aActivePathPos->second[ nNextStateIndex ] ) != m_xRoadmapImpl->aDisabledStates.end() )
509 ++nNextStateIndex;
512 if ( nNextStateIndex >= static_cast<sal_Int32>(aActivePathPos->second.size()) )
513 // there is no next state in the current path (at least none which is enabled)
514 return WZS_INVALID_STATE;
516 return aActivePathPos->second[ nNextStateIndex ];
519 WizardTypes::WizardState RoadmapWizardMachine::determineNextState( WizardTypes::WizardState _nCurrentState ) const
521 sal_Int32 nCurrentStatePathIndex = -1;
523 Paths::const_iterator aActivePathPos = m_pImpl->aPaths.find( m_pImpl->nActivePath );
524 if ( aActivePathPos != m_pImpl->aPaths.end() )
525 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( _nCurrentState, aActivePathPos->second );
527 DBG_ASSERT( nCurrentStatePathIndex != -1, "RoadmapWizard::determineNextState: ehm - how can we travel if there is no (valid) active path?" );
528 if ( nCurrentStatePathIndex == -1 )
529 return WZS_INVALID_STATE;
531 sal_Int32 nNextStateIndex = nCurrentStatePathIndex + 1;
533 while ( ( nNextStateIndex < static_cast<sal_Int32>(aActivePathPos->second.size()) )
534 && ( m_pImpl->aDisabledStates.find( aActivePathPos->second[ nNextStateIndex ] ) != m_pImpl->aDisabledStates.end() )
537 ++nNextStateIndex;
540 if ( nNextStateIndex >= static_cast<sal_Int32>(aActivePathPos->second.size()) )
541 // there is no next state in the current path (at least none which is enabled)
542 return WZS_INVALID_STATE;
544 return aActivePathPos->second[ nNextStateIndex ];
547 bool RoadmapWizard::canAdvance() const
549 if ( !m_xRoadmapImpl->bActivePathIsDefinite )
551 // check how many paths are still allowed
552 const WizardPath& rActivePath( m_xRoadmapImpl->aPaths[ m_xRoadmapImpl->nActivePath ] );
553 sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
555 size_t nPossiblePaths(0);
556 for (auto const& path : m_xRoadmapImpl->aPaths)
558 // the index from which on both paths differ
559 sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
561 if ( nDivergenceIndex > nCurrentStatePathIndex )
562 // this path is still a possible path
563 nPossiblePaths += 1;
566 // if we have more than one path which is still possible, then we assume
567 // to always have a next state. Though there might be scenarios where this
568 // is not true, but this is too sophisticated (means not really needed) right now.
569 if ( nPossiblePaths > 1 )
570 return true;
573 const WizardPath& rPath = m_xRoadmapImpl->aPaths[ m_xRoadmapImpl->nActivePath ];
574 return *rPath.rbegin() != getCurrentState();
577 bool RoadmapWizardMachine::canAdvance() const
579 if ( !m_pImpl->bActivePathIsDefinite )
581 // check how many paths are still allowed
582 const WizardPath& rActivePath( m_pImpl->aPaths[ m_pImpl->nActivePath ] );
583 sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
585 size_t nPossiblePaths(0);
586 for (auto const& path : m_pImpl->aPaths)
588 // the index from which on both paths differ
589 sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
591 if ( nDivergenceIndex > nCurrentStatePathIndex )
592 // this path is still a possible path
593 nPossiblePaths += 1;
596 // if we have more than one path which is still possible, then we assume
597 // to always have a next state. Though there might be scenarios where this
598 // is not true, but this is too sophisticated (means not really needed) right now.
599 if ( nPossiblePaths > 1 )
600 return true;
603 const WizardPath& rPath = m_pImpl->aPaths[ m_pImpl->nActivePath ];
604 return *rPath.rbegin() != getCurrentState();
607 void RoadmapWizardMachine::updateTravelUI()
609 WizardMachine::updateTravelUI();
611 // disable the "Previous" button if all states in our history are disabled
612 std::vector< WizardTypes::WizardState > aHistory;
613 getStateHistory( aHistory );
614 bool bHaveEnabledState = false;
615 for (auto const& state : aHistory)
617 if ( isStateEnabled(state) )
619 bHaveEnabledState = true;
620 break;
624 enableButtons( WizardButtonFlags::PREVIOUS, bHaveEnabledState );
626 implUpdateRoadmap();
629 IMPL_LINK_NOARG(RoadmapWizard, OnRoadmapItemSelected, LinkParamNone*, void)
631 RoadmapTypes::ItemId nCurItemId = m_xRoadmapImpl->pRoadmap->GetCurrentRoadmapItemID();
632 if ( nCurItemId == getCurrentState() )
633 // nothing to do
634 return;
636 if ( isTravelingSuspended() )
637 return;
639 RoadmapWizardTravelSuspension aTravelGuard( *this );
641 sal_Int32 nCurrentIndex = m_xRoadmapImpl->getStateIndexInPath( getCurrentState(), m_xRoadmapImpl->nActivePath );
642 sal_Int32 nNewIndex = m_xRoadmapImpl->getStateIndexInPath( nCurItemId, m_xRoadmapImpl->nActivePath );
644 DBG_ASSERT( ( nCurrentIndex != -1 ) && ( nNewIndex != -1 ),
645 "RoadmapWizard::OnRoadmapItemSelected: something's wrong here!" );
646 if ( ( nCurrentIndex == -1 ) || ( nNewIndex == -1 ) )
648 return;
651 bool bResult = true;
652 if ( nNewIndex > nCurrentIndex )
654 bResult = skipUntil( static_cast<WizardTypes::WizardState>(nCurItemId) );
655 WizardTypes::WizardState nTemp = static_cast<WizardTypes::WizardState>(nCurItemId);
656 while( nTemp )
658 if( m_xRoadmapImpl->aDisabledStates.find( --nTemp ) != m_xRoadmapImpl->aDisabledStates.end() )
659 removePageFromHistory( nTemp );
662 else
663 bResult = skipBackwardUntil( static_cast<WizardTypes::WizardState>(nCurItemId) );
665 if ( !bResult )
666 m_xRoadmapImpl->pRoadmap->SelectRoadmapItemByID( getCurrentState() );
669 IMPL_LINK(RoadmapWizardMachine, OnRoadmapItemSelected, const OString&, rCurItemId, bool)
671 int nCurItemId = rCurItemId.toInt32();
673 if ( nCurItemId == getCurrentState() )
674 // nothing to do
675 return false;
677 if ( isTravelingSuspended() )
678 return false;
680 WizardTravelSuspension aTravelGuard( *this );
682 sal_Int32 nCurrentIndex = m_pImpl->getStateIndexInPath( getCurrentState(), m_pImpl->nActivePath );
683 sal_Int32 nNewIndex = m_pImpl->getStateIndexInPath( nCurItemId, m_pImpl->nActivePath );
685 DBG_ASSERT( ( nCurrentIndex != -1 ) && ( nNewIndex != -1 ),
686 "RoadmapWizard::OnRoadmapItemSelected: something's wrong here!" );
687 if ( ( nCurrentIndex == -1 ) || ( nNewIndex == -1 ) )
689 return false;
692 bool bResult = true;
693 if ( nNewIndex > nCurrentIndex )
695 bResult = skipUntil( static_cast<WizardTypes::WizardState>(nCurItemId) );
696 WizardTypes::WizardState nTemp = static_cast<WizardTypes::WizardState>(nCurItemId);
697 while( nTemp )
699 if( m_pImpl->aDisabledStates.find( --nTemp ) != m_pImpl->aDisabledStates.end() )
700 removePageFromHistory( nTemp );
703 else
704 bResult = skipBackwardUntil( static_cast<WizardTypes::WizardState>(nCurItemId) );
706 return bResult;
709 void RoadmapWizard::enterState(WizardTypes::WizardState nState)
711 // tell the page
712 IWizardPageController* pController = getPageController( GetPage( nState ) );
713 if (pController)
715 pController->initializePage();
717 if ( isAutomaticNextButtonStateEnabled() )
718 enableButtons( WizardButtonFlags::NEXT, canAdvance() );
720 enableButtons( WizardButtonFlags::PREVIOUS, !m_xWizardImpl->aStateHistory.empty() );
722 // set the new title - it depends on the current page (i.e. state)
723 implUpdateTitle();
726 // synchronize the roadmap
727 implUpdateRoadmap( );
728 m_xRoadmapImpl->pRoadmap->SelectRoadmapItemByID( getCurrentState() );
731 void RoadmapWizardMachine::enterState( WizardTypes::WizardState _nState )
733 WizardMachine::enterState( _nState );
735 // synchronize the roadmap
736 implUpdateRoadmap();
739 OUString RoadmapWizard::getStateDisplayName( WizardTypes::WizardState _nState ) const
741 OUString sDisplayName;
743 StateDescriptions::const_iterator pos = m_xRoadmapImpl->aStateDescriptors.find( _nState );
744 OSL_ENSURE( pos != m_xRoadmapImpl->aStateDescriptors.end(),
745 "RoadmapWizard::getStateDisplayName: no default implementation available for this state!" );
746 if ( pos != m_xRoadmapImpl->aStateDescriptors.end() )
747 sDisplayName = pos->second.first;
749 return sDisplayName;
752 OUString RoadmapWizardMachine::getStateDisplayName( WizardTypes::WizardState _nState ) const
754 OUString sDisplayName;
756 StateDescriptions::const_iterator pos = m_pImpl->aStateDescriptors.find( _nState );
757 OSL_ENSURE( pos != m_pImpl->aStateDescriptors.end(),
758 "RoadmapWizard::getStateDisplayName: no default implementation available for this state!" );
759 if ( pos != m_pImpl->aStateDescriptors.end() )
760 sDisplayName = pos->second.first;
762 return sDisplayName;
765 VclPtr<TabPage> RoadmapWizard::createPage( WizardTypes::WizardState _nState )
767 VclPtr<TabPage> pPage;
769 StateDescriptions::const_iterator pos = m_xRoadmapImpl->aStateDescriptors.find( _nState );
770 OSL_ENSURE( pos != m_xRoadmapImpl->aStateDescriptors.end(),
771 "RoadmapWizard::createPage: no default implementation available for this state!" );
772 if ( pos != m_xRoadmapImpl->aStateDescriptors.end() )
774 RoadmapPageFactory pFactory = pos->second.second;
775 pPage = (*pFactory)( *this );
778 return pPage;
781 void RoadmapWizardMachine::enableState( WizardTypes::WizardState _nState, bool _bEnable )
783 // remember this (in case the state appears in the roadmap later on)
784 if ( _bEnable )
785 m_pImpl->aDisabledStates.erase( _nState );
786 else
788 m_pImpl->aDisabledStates.insert( _nState );
789 removePageFromHistory( _nState );
792 // if the state is currently in the roadmap, reflect it's new status
793 m_xAssistant->set_page_sensitive(OString::number(_nState), _bEnable);
796 bool RoadmapWizardMachine::knowsState( WizardTypes::WizardState i_nState ) const
798 for (auto const& path : m_pImpl->aPaths)
800 for (auto const& state : path.second)
802 if ( state == i_nState )
803 return true;
806 return false;
809 bool RoadmapWizardMachine::isStateEnabled( WizardTypes::WizardState _nState ) const
811 return m_pImpl->aDisabledStates.find( _nState ) == m_pImpl->aDisabledStates.end();
814 void RoadmapWizard::InsertRoadmapItem(int nItemIndex, const OUString& rText, int nItemId, bool bEnable)
816 m_xRoadmapImpl->pRoadmap->InsertRoadmapItem(nItemIndex, rText, nItemId, bEnable);
819 void RoadmapWizard::SelectRoadmapItemByID(int nItemId)
821 m_xRoadmapImpl->pRoadmap->SelectRoadmapItemByID(nItemId);
824 void RoadmapWizard::DeleteRoadmapItems()
826 while (m_xRoadmapImpl->pRoadmap->GetItemCount())
827 m_xRoadmapImpl->pRoadmap->DeleteRoadmapItem(0);
830 void RoadmapWizard::SetItemSelectHdl( const Link<LinkParamNone*,void>& _rHdl )
832 m_xRoadmapImpl->pRoadmap->SetItemSelectHdl(_rHdl);
835 int RoadmapWizard::GetCurrentRoadmapItemID() const
837 return m_xRoadmapImpl->pRoadmap->GetCurrentRoadmapItemID();
840 } // namespace vcl
842 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */