Update git submodules
[LibreOffice.git] / vcl / source / control / roadmapwizard.cxx
blob1b3c9e96b4776e5e9e54de8af3632155b8bef2c7
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 <tools/json_writer.hxx>
24 #include <osl/diagnose.h>
26 #include <strings.hrc>
27 #include <svdata.hxx>
28 #include <wizdlg.hxx>
30 #include <vector>
32 #include "wizimpldata.hxx"
33 #include <uiobject-internal.hxx>
35 namespace vcl
37 sal_Int32 RoadmapWizardImpl::getStateIndexInPath( WizardTypes::WizardState _nState, const WizardPath& _rPath )
39 sal_Int32 nStateIndexInPath = 0;
40 bool bFound = false;
41 for (auto const& path : _rPath)
43 if (path == _nState)
45 bFound = true;
46 break;
48 ++nStateIndexInPath;
50 if (!bFound)
51 nStateIndexInPath = -1;
52 return nStateIndexInPath;
56 sal_Int32 RoadmapWizardImpl::getStateIndexInPath( WizardTypes::WizardState _nState, PathId _nPathId )
58 sal_Int32 nStateIndexInPath = -1;
59 Paths::const_iterator aPathPos = aPaths.find( _nPathId );
60 if ( aPathPos != aPaths.end( ) )
61 nStateIndexInPath = getStateIndexInPath( _nState, aPathPos->second );
62 return nStateIndexInPath;
66 sal_Int32 RoadmapWizardImpl::getFirstDifferentIndex( const WizardPath& _rLHS, const WizardPath& _rRHS )
68 sal_Int32 nMinLength = ::std::min( _rLHS.size(), _rRHS.size() );
69 for ( sal_Int32 nCheck = 0; nCheck < nMinLength; ++nCheck )
71 if ( _rLHS[ nCheck ] != _rRHS[ nCheck ] )
72 return nCheck;
74 return nMinLength;
77 //= RoadmapWizard
78 RoadmapWizard::RoadmapWizard(vcl::Window* pParent, WinBits nStyle, InitFlag eFlag)
79 : Dialog(pParent, nStyle, eFlag)
80 , maWizardLayoutIdle("vcl RoadmapWizard maWizardLayoutIdle")
81 , m_pFinish(nullptr)
82 , m_pCancel(nullptr)
83 , m_pNextPage(nullptr)
84 , m_pPrevPage(nullptr)
85 , m_pHelp(nullptr)
86 , m_xWizardImpl(new WizardMachineImplData)
87 , m_xRoadmapImpl(new RoadmapWizardImpl)
89 mpFirstPage = nullptr;
90 mpFirstBtn = nullptr;
91 mpCurTabPage = nullptr;
92 mpPrevBtn = nullptr;
93 mpNextBtn = nullptr;
94 mpViewWindow = nullptr;
95 mnCurLevel = 0;
96 mbEmptyViewMargin = false;
97 mnLeftAlignCount = 0;
99 maWizardLayoutIdle.SetPriority(TaskPriority::RESIZE);
100 maWizardLayoutIdle.SetInvokeHandler( LINK( this, RoadmapWizard, ImplHandleWizardLayoutTimerHdl ) );
102 implConstruct(WizardButtonFlags::NEXT | WizardButtonFlags::PREVIOUS | WizardButtonFlags::FINISH | WizardButtonFlags::CANCEL | WizardButtonFlags::HELP);
104 SetLeftAlignedButtonCount( 1 );
105 mbEmptyViewMargin = true;
107 m_xRoadmapImpl->pRoadmap.disposeAndReset( VclPtr<ORoadmap>::Create( this, WB_TABSTOP ) );
108 m_xRoadmapImpl->pRoadmap->SetText( VclResId( STR_WIZDLG_ROADMAP_TITLE ) );
109 m_xRoadmapImpl->pRoadmap->SetPosPixel( Point( 0, 0 ) );
110 m_xRoadmapImpl->pRoadmap->SetItemSelectHdl( LINK( this, RoadmapWizard, OnRoadmapItemSelected ) );
112 Size aRoadmapSize = LogicToPixel(Size(85, 0), MapMode(MapUnit::MapAppFont));
113 aRoadmapSize.setHeight( GetSizePixel().Height() );
114 m_xRoadmapImpl->pRoadmap->SetSizePixel( aRoadmapSize );
116 mpViewWindow = m_xRoadmapImpl->pRoadmap;
117 m_xRoadmapImpl->pRoadmap->Show();
120 RoadmapWizardMachine::RoadmapWizardMachine(weld::Window* pParent)
121 : WizardMachine(pParent, WizardButtonFlags::NEXT | WizardButtonFlags::PREVIOUS | WizardButtonFlags::FINISH | WizardButtonFlags::CANCEL | WizardButtonFlags::HELP)
122 , m_pImpl( new RoadmapWizardImpl )
124 m_xAssistant->connect_jump_page(LINK(this, RoadmapWizardMachine, OnRoadmapItemSelected));
127 void RoadmapWizard::ShowRoadmap(bool bShow)
129 m_xRoadmapImpl->pRoadmap->Show(bShow);
130 CalcAndSetSize();
133 RoadmapWizard::~RoadmapWizard()
135 disposeOnce();
138 RoadmapWizardMachine::~RoadmapWizardMachine()
142 void RoadmapWizard::dispose()
144 m_xRoadmapImpl.reset();
146 m_pFinish.disposeAndClear();
147 m_pCancel.disposeAndClear();
148 m_pNextPage.disposeAndClear();
149 m_pPrevPage.disposeAndClear();
150 m_pHelp.disposeAndClear();
152 if (m_xWizardImpl)
154 for (WizardTypes::WizardState i = 0; i < m_xWizardImpl->nFirstUnknownPage; ++i)
156 TabPage *pPage = GetPage(i);
157 if (pPage)
158 pPage->disposeOnce();
160 m_xWizardImpl.reset();
163 maWizardLayoutIdle.Stop();
165 // Remove all buttons
166 while ( mpFirstBtn )
167 RemoveButton( mpFirstBtn->mpButton );
169 // Remove all pages
170 while ( mpFirstPage )
171 RemovePage( mpFirstPage->mpPage );
173 mpCurTabPage.clear();
174 mpPrevBtn.clear();
175 mpNextBtn.clear();
176 mpViewWindow.clear();
177 Dialog::dispose();
180 void RoadmapWizard::SetRoadmapHelpId( const OUString& _rId )
182 m_xRoadmapImpl->pRoadmap->SetHelpId( _rId );
185 void RoadmapWizard::SetRoadmapBitmap(const BitmapEx& rBmp)
187 m_xRoadmapImpl->pRoadmap->SetRoadmapBitmap(rBmp);
190 void RoadmapWizardMachine::SetRoadmapHelpId(const OUString& rId)
192 m_xAssistant->set_page_side_help_id(rId);
195 void RoadmapWizardMachine::declarePath( PathId _nPathId, const WizardPath& _lWizardStates)
197 m_pImpl->aPaths.emplace( _nPathId, _lWizardStates );
199 if ( m_pImpl->aPaths.size() == 1 )
200 // the very first path -> activate it
201 activatePath( _nPathId );
202 else
203 implUpdateRoadmap( );
206 void RoadmapWizardMachine::activatePath( PathId _nPathId, bool _bDecideForIt )
208 if ( ( _nPathId == m_pImpl->nActivePath ) && ( _bDecideForIt == m_pImpl->bActivePathIsDefinite ) )
209 // nothing to do
210 return;
212 // does the given path exist?
213 Paths::const_iterator aNewPathPos = m_pImpl->aPaths.find( _nPathId );
214 DBG_ASSERT( aNewPathPos != m_pImpl->aPaths.end(), "RoadmapWizard::activate: there is no such path!" );
215 if ( aNewPathPos == m_pImpl->aPaths.end() )
216 return;
218 // determine the index of the current state in the current path
219 sal_Int32 nCurrentStatePathIndex = -1;
220 if ( m_pImpl->nActivePath != -1 )
221 nCurrentStatePathIndex = m_pImpl->getStateIndexInPath( getCurrentState(), m_pImpl->nActivePath );
223 DBG_ASSERT( static_cast<sal_Int32>(aNewPathPos->second.size()) > nCurrentStatePathIndex,
224 "RoadmapWizard::activate: you cannot activate a path which has less states than we've already advanced!" );
225 // If this asserts, this for instance means that we are already in state number, say, 5
226 // of our current path, and the caller tries to activate a path which has less than 5
227 // states
228 if ( static_cast<sal_Int32>(aNewPathPos->second.size()) <= nCurrentStatePathIndex )
229 return;
231 // assert that the current and the new path are equal, up to nCurrentStatePathIndex
232 Paths::const_iterator aActivePathPos = m_pImpl->aPaths.find( m_pImpl->nActivePath );
233 if ( aActivePathPos != m_pImpl->aPaths.end() )
235 if ( RoadmapWizardImpl::getFirstDifferentIndex( aActivePathPos->second, aNewPathPos->second ) <= nCurrentStatePathIndex )
237 OSL_FAIL( "RoadmapWizard::activate: you cannot activate a path which conflicts with the current one *before* the current state!" );
238 return;
242 m_pImpl->nActivePath = _nPathId;
243 m_pImpl->bActivePathIsDefinite = _bDecideForIt;
245 implUpdateRoadmap( );
248 void RoadmapWizard::implUpdateRoadmap( )
250 DBG_ASSERT( m_xRoadmapImpl->aPaths.find( m_xRoadmapImpl->nActivePath ) != m_xRoadmapImpl->aPaths.end(),
251 "RoadmapWizard::implUpdateRoadmap: there is no such path!" );
252 const WizardPath& rActivePath( m_xRoadmapImpl->aPaths[ m_xRoadmapImpl->nActivePath ] );
254 sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
255 if (nCurrentStatePathIndex < 0)
256 return;
258 // determine up to which index (in the new path) we have to display the items
259 RoadmapTypes::ItemIndex nUpperStepBoundary = static_cast<RoadmapTypes::ItemIndex>(rActivePath.size());
260 bool bIncompletePath = false;
261 if ( !m_xRoadmapImpl->bActivePathIsDefinite )
263 for (auto const& path : m_xRoadmapImpl->aPaths)
265 if ( path.first == m_xRoadmapImpl->nActivePath )
266 // it's the path we are just activating -> no need to check anything
267 continue;
268 // the index from which on both paths differ
269 sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
270 if ( nDivergenceIndex <= nCurrentStatePathIndex )
271 // they differ in an index which we have already left behind us
272 // -> this is no conflict anymore
273 continue;
275 // the path conflicts with our new path -> don't activate the
276 // *complete* new path, but only up to the step which is unambiguous
277 nUpperStepBoundary = nDivergenceIndex;
278 bIncompletePath = true;
282 // now, we have to remove all items after nCurrentStatePathIndex, and insert the items from the active
283 // path, up to (excluding) nUpperStepBoundary
284 RoadmapTypes::ItemIndex nLoopUntil = ::std::max( nUpperStepBoundary, m_xRoadmapImpl->pRoadmap->GetItemCount() );
285 for ( RoadmapTypes::ItemIndex nItemIndex = nCurrentStatePathIndex; nItemIndex < nLoopUntil; ++nItemIndex )
287 bool bExistentItem = ( nItemIndex < m_xRoadmapImpl->pRoadmap->GetItemCount() );
288 bool bNeedItem = ( nItemIndex < nUpperStepBoundary );
290 bool bInsertItem = false;
291 if ( bExistentItem )
293 if ( !bNeedItem )
295 while ( nItemIndex < m_xRoadmapImpl->pRoadmap->GetItemCount() )
296 m_xRoadmapImpl->pRoadmap->DeleteRoadmapItem( nItemIndex );
297 break;
299 else
301 // there is an item with this index in the roadmap - does it match what is requested by
302 // the respective state in the active path?
303 RoadmapTypes::ItemId nPresentItemId = m_xRoadmapImpl->pRoadmap->GetItemID( nItemIndex );
304 WizardTypes::WizardState nRequiredState = rActivePath[ nItemIndex ];
305 if ( nPresentItemId != nRequiredState )
307 m_xRoadmapImpl->pRoadmap->DeleteRoadmapItem( nItemIndex );
308 bInsertItem = true;
312 else
314 DBG_ASSERT( bNeedItem, "RoadmapWizard::implUpdateRoadmap: ehm - none needed, none present - why did the loop not terminate?" );
315 bInsertItem = bNeedItem;
318 WizardTypes::WizardState nState( rActivePath[ nItemIndex ] );
319 if ( bInsertItem )
321 m_xRoadmapImpl->pRoadmap->InsertRoadmapItem(
322 nItemIndex,
323 getStateDisplayName( nState ),
324 nState,
325 true
329 const bool bEnable = m_xRoadmapImpl->aDisabledStates.find( nState ) == m_xRoadmapImpl->aDisabledStates.end();
330 m_xRoadmapImpl->pRoadmap->EnableRoadmapItem( m_xRoadmapImpl->pRoadmap->GetItemID( nItemIndex ), bEnable );
333 m_xRoadmapImpl->pRoadmap->SetRoadmapComplete( !bIncompletePath );
336 void RoadmapWizardMachine::implUpdateRoadmap( )
339 DBG_ASSERT( m_pImpl->aPaths.find( m_pImpl->nActivePath ) != m_pImpl->aPaths.end(),
340 "RoadmapWizard::implUpdateRoadmap: there is no such path!" );
341 const WizardPath& rActivePath( m_pImpl->aPaths[ m_pImpl->nActivePath ] );
343 sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
344 if (nCurrentStatePathIndex < 0)
345 return;
347 // determine up to which index (in the new path) we have to display the items
348 RoadmapTypes::ItemIndex nUpperStepBoundary = static_cast<RoadmapTypes::ItemIndex>(rActivePath.size());
349 if ( !m_pImpl->bActivePathIsDefinite )
351 for (auto const& path : m_pImpl->aPaths)
353 if ( path.first == m_pImpl->nActivePath )
354 // it's the path we are just activating -> no need to check anything
355 continue;
356 // the index from which on both paths differ
357 sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
358 if ( nDivergenceIndex <= nCurrentStatePathIndex )
359 // they differ in an index which we have already left behind us
360 // -> this is no conflict anymore
361 continue;
363 // the path conflicts with our new path -> don't activate the
364 // *complete* new path, but only up to the step which is unambiguous
365 nUpperStepBoundary = nDivergenceIndex;
369 // can we advance from the current page?
370 bool bCurrentPageCanAdvance = true;
371 BuilderPage* pCurrentPage = GetPage( getCurrentState() );
372 if ( pCurrentPage )
374 const IWizardPageController* pController = getPageController( GetPage( getCurrentState() ) );
375 OSL_ENSURE( pController != nullptr, "RoadmapWizard::implUpdateRoadmap: no controller for the current page!" );
376 bCurrentPageCanAdvance = !pController || pController->canAdvance();
379 // now, we have to remove all items after nCurrentStatePathIndex, and insert the items from the active
380 // path, up to (excluding) nUpperStepBoundary
381 RoadmapTypes::ItemIndex nRoadmapItems = m_xAssistant->get_n_pages();
382 RoadmapTypes::ItemIndex nLoopUntil = ::std::max( nUpperStepBoundary, nRoadmapItems );
383 for ( RoadmapTypes::ItemIndex nItemIndex = nCurrentStatePathIndex; nItemIndex < nLoopUntil; ++nItemIndex )
385 bool bExistentItem = ( nItemIndex < nRoadmapItems );
386 bool bNeedItem = ( nItemIndex < nUpperStepBoundary );
388 bool bInsertItem = false;
389 if ( bExistentItem )
391 if ( !bNeedItem )
393 int nPages = nRoadmapItems;
394 for (int i = nPages - 1; i >= nItemIndex; --i)
396 m_xAssistant->set_page_title(m_xAssistant->get_page_ident(i), "");
397 --nRoadmapItems;
399 break;
401 else
403 // there is an item with this index in the roadmap - does it match what is requested by
404 // the respective state in the active path?
405 RoadmapTypes::ItemId nPresentItemId = m_xAssistant->get_page_ident(nItemIndex).toInt32();
406 WizardTypes::WizardState nRequiredState = rActivePath[ nItemIndex ];
407 if ( nPresentItemId != nRequiredState )
409 m_xAssistant->set_page_title(OUString::number(nPresentItemId), "");
410 bInsertItem = true;
414 else
416 DBG_ASSERT( bNeedItem, "RoadmapWizard::implUpdateRoadmap: ehm - none needed, none present - why did the loop not terminate?" );
417 bInsertItem = bNeedItem;
420 WizardTypes::WizardState nState( rActivePath[ nItemIndex ] );
422 if ( bInsertItem )
424 GetOrCreatePage(nState);
427 OUString sIdent(getPageIdentForState(nState));
428 m_xAssistant->set_page_index(sIdent, nItemIndex);
429 m_xAssistant->set_page_title(sIdent, getStateDisplayName(nState));
431 // if the item is *after* the current state, but the current page does not
432 // allow advancing, the disable the state. This relieves derived classes
433 // from disabling all future states just because the current state does not
434 // (yet) allow advancing.
435 const bool bUnconditionedDisable = !bCurrentPageCanAdvance && ( nItemIndex > nCurrentStatePathIndex );
436 const bool bEnable = !bUnconditionedDisable && ( m_pImpl->aDisabledStates.find( nState ) == m_pImpl->aDisabledStates.end() );
437 m_xAssistant->set_page_sensitive(sIdent, bEnable);
441 WizardTypes::WizardState RoadmapWizard::determineNextState( WizardTypes::WizardState _nCurrentState ) const
443 sal_Int32 nCurrentStatePathIndex = -1;
445 Paths::const_iterator aActivePathPos = m_xRoadmapImpl->aPaths.find( m_xRoadmapImpl->nActivePath );
446 if ( aActivePathPos != m_xRoadmapImpl->aPaths.end() )
447 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( _nCurrentState, aActivePathPos->second );
449 DBG_ASSERT( nCurrentStatePathIndex != -1, "RoadmapWizard::determineNextState: ehm - how can we travel if there is no (valid) active path?" );
450 if ( nCurrentStatePathIndex == -1 )
451 return WZS_INVALID_STATE;
453 sal_Int32 nNextStateIndex = nCurrentStatePathIndex + 1;
455 while ( ( nNextStateIndex < static_cast<sal_Int32>(aActivePathPos->second.size()) )
456 && ( m_xRoadmapImpl->aDisabledStates.find( aActivePathPos->second[ nNextStateIndex ] ) != m_xRoadmapImpl->aDisabledStates.end() )
459 ++nNextStateIndex;
462 if ( nNextStateIndex >= static_cast<sal_Int32>(aActivePathPos->second.size()) )
463 // there is no next state in the current path (at least none which is enabled)
464 return WZS_INVALID_STATE;
466 return aActivePathPos->second[ nNextStateIndex ];
469 WizardTypes::WizardState RoadmapWizardMachine::determineNextState( WizardTypes::WizardState _nCurrentState ) const
471 sal_Int32 nCurrentStatePathIndex = -1;
473 Paths::const_iterator aActivePathPos = m_pImpl->aPaths.find( m_pImpl->nActivePath );
474 if ( aActivePathPos != m_pImpl->aPaths.end() )
475 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( _nCurrentState, aActivePathPos->second );
477 DBG_ASSERT( nCurrentStatePathIndex != -1, "RoadmapWizard::determineNextState: ehm - how can we travel if there is no (valid) active path?" );
478 if ( nCurrentStatePathIndex == -1 )
479 return WZS_INVALID_STATE;
481 sal_Int32 nNextStateIndex = nCurrentStatePathIndex + 1;
483 while ( ( nNextStateIndex < static_cast<sal_Int32>(aActivePathPos->second.size()) )
484 && ( m_pImpl->aDisabledStates.find( aActivePathPos->second[ nNextStateIndex ] ) != m_pImpl->aDisabledStates.end() )
487 ++nNextStateIndex;
490 if ( nNextStateIndex >= static_cast<sal_Int32>(aActivePathPos->second.size()) )
491 // there is no next state in the current path (at least none which is enabled)
492 return WZS_INVALID_STATE;
494 return aActivePathPos->second[ nNextStateIndex ];
497 bool RoadmapWizardMachine::canAdvance() const
499 if ( !m_pImpl->bActivePathIsDefinite )
501 // check how many paths are still allowed
502 const WizardPath& rActivePath( m_pImpl->aPaths[ m_pImpl->nActivePath ] );
504 // if current path has only the base item, it is not possible to proceed without activating another path
505 if(rActivePath.size()<=1)
506 return false;
508 sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
510 size_t nPossiblePaths(0);
511 for (auto const& path : m_pImpl->aPaths)
513 // the index from which on both paths differ
514 sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
516 if ( nDivergenceIndex > nCurrentStatePathIndex )
517 // this path is still a possible path
518 nPossiblePaths += 1;
521 // if we have more than one path which is still possible, then we assume
522 // to always have a next state. Though there might be scenarios where this
523 // is not true, but this is too sophisticated (means not really needed) right now.
524 if ( nPossiblePaths > 1 )
525 return true;
528 const WizardPath& rPath = m_pImpl->aPaths[ m_pImpl->nActivePath ];
529 return *rPath.rbegin() != getCurrentState();
532 void RoadmapWizardMachine::updateTravelUI()
534 WizardMachine::updateTravelUI();
536 // disable the "Previous" button if all states in our history are disabled
537 std::vector< WizardTypes::WizardState > aHistory;
538 getStateHistory( aHistory );
539 bool bHaveEnabledState = false;
540 for (auto const& state : aHistory)
542 if ( isStateEnabled(state) )
544 bHaveEnabledState = true;
545 break;
549 enableButtons( WizardButtonFlags::PREVIOUS, bHaveEnabledState );
551 implUpdateRoadmap();
554 IMPL_LINK_NOARG(RoadmapWizard, OnRoadmapItemSelected, LinkParamNone*, void)
556 RoadmapTypes::ItemId nCurItemId = m_xRoadmapImpl->pRoadmap->GetCurrentRoadmapItemID();
557 if ( nCurItemId == getCurrentState() )
558 // nothing to do
559 return;
561 if ( isTravelingSuspended() )
562 return;
564 RoadmapWizardTravelSuspension aTravelGuard( *this );
566 sal_Int32 nCurrentIndex = m_xRoadmapImpl->getStateIndexInPath( getCurrentState(), m_xRoadmapImpl->nActivePath );
567 sal_Int32 nNewIndex = m_xRoadmapImpl->getStateIndexInPath( nCurItemId, m_xRoadmapImpl->nActivePath );
569 DBG_ASSERT( ( nCurrentIndex != -1 ) && ( nNewIndex != -1 ),
570 "RoadmapWizard::OnRoadmapItemSelected: something's wrong here!" );
571 if ( ( nCurrentIndex == -1 ) || ( nNewIndex == -1 ) )
573 return;
576 bool bResult = true;
577 if ( nNewIndex > nCurrentIndex )
579 bResult = skipUntil( static_cast<WizardTypes::WizardState>(nCurItemId) );
580 WizardTypes::WizardState nTemp = static_cast<WizardTypes::WizardState>(nCurItemId);
581 while( nTemp )
583 if( m_xRoadmapImpl->aDisabledStates.find( --nTemp ) != m_xRoadmapImpl->aDisabledStates.end() )
584 removePageFromHistory( nTemp );
587 else
588 bResult = skipBackwardUntil( static_cast<WizardTypes::WizardState>(nCurItemId) );
590 if ( !bResult )
591 m_xRoadmapImpl->pRoadmap->SelectRoadmapItemByID( getCurrentState() );
594 IMPL_LINK(RoadmapWizardMachine, OnRoadmapItemSelected, const OUString&, rCurItemId, bool)
596 WizardTypes::WizardState nSelectedState = getStateFromPageIdent(rCurItemId);
598 if (nSelectedState == getCurrentState())
599 // nothing to do
600 return false;
602 if ( isTravelingSuspended() )
603 return false;
605 WizardTravelSuspension aTravelGuard( *this );
607 sal_Int32 nCurrentIndex = m_pImpl->getStateIndexInPath( getCurrentState(), m_pImpl->nActivePath );
608 sal_Int32 nNewIndex = m_pImpl->getStateIndexInPath( nSelectedState, m_pImpl->nActivePath );
610 DBG_ASSERT( ( nCurrentIndex != -1 ) && ( nNewIndex != -1 ),
611 "RoadmapWizard::OnRoadmapItemSelected: something's wrong here!" );
612 if ( ( nCurrentIndex == -1 ) || ( nNewIndex == -1 ) )
614 return false;
617 bool bResult = true;
618 if ( nNewIndex > nCurrentIndex )
620 bResult = skipUntil(nSelectedState);
621 WizardTypes::WizardState nTemp = nSelectedState;
622 while( nTemp )
624 if( m_pImpl->aDisabledStates.find( --nTemp ) != m_pImpl->aDisabledStates.end() )
625 removePageFromHistory( nTemp );
628 else
629 bResult = skipBackwardUntil(nSelectedState);
631 return bResult;
634 void RoadmapWizard::enterState(WizardTypes::WizardState /*nState*/)
636 // synchronize the roadmap
637 implUpdateRoadmap( );
638 m_xRoadmapImpl->pRoadmap->SelectRoadmapItemByID( getCurrentState() );
641 void RoadmapWizardMachine::enterState( WizardTypes::WizardState _nState )
643 WizardMachine::enterState( _nState );
645 // synchronize the roadmap
646 implUpdateRoadmap();
649 OUString RoadmapWizard::getStateDisplayName( WizardTypes::WizardState _nState ) const
651 OUString sDisplayName;
653 StateDescriptions::const_iterator pos = m_xRoadmapImpl->aStateDescriptors.find( _nState );
654 OSL_ENSURE( pos != m_xRoadmapImpl->aStateDescriptors.end(),
655 "RoadmapWizard::getStateDisplayName: no default implementation available for this state!" );
656 if ( pos != m_xRoadmapImpl->aStateDescriptors.end() )
657 sDisplayName = pos->second.first;
659 return sDisplayName;
662 OUString RoadmapWizardMachine::getStateDisplayName( WizardTypes::WizardState _nState ) const
664 OUString sDisplayName;
666 StateDescriptions::const_iterator pos = m_pImpl->aStateDescriptors.find( _nState );
667 OSL_ENSURE( pos != m_pImpl->aStateDescriptors.end(),
668 "RoadmapWizard::getStateDisplayName: no default implementation available for this state!" );
669 if ( pos != m_pImpl->aStateDescriptors.end() )
670 sDisplayName = pos->second.first;
672 return sDisplayName;
675 VclPtr<TabPage> RoadmapWizard::createPage( WizardTypes::WizardState _nState )
677 VclPtr<TabPage> pPage;
679 StateDescriptions::const_iterator pos = m_xRoadmapImpl->aStateDescriptors.find( _nState );
680 OSL_ENSURE( pos != m_xRoadmapImpl->aStateDescriptors.end(),
681 "RoadmapWizard::createPage: no default implementation available for this state!" );
682 if ( pos != m_xRoadmapImpl->aStateDescriptors.end() )
684 RoadmapPageFactory pFactory = pos->second.second;
685 pPage = (*pFactory)( *this );
688 return pPage;
691 void RoadmapWizardMachine::enableState( WizardTypes::WizardState _nState, bool _bEnable )
693 // remember this (in case the state appears in the roadmap later on)
694 if ( _bEnable )
695 m_pImpl->aDisabledStates.erase( _nState );
696 else
698 m_pImpl->aDisabledStates.insert( _nState );
699 removePageFromHistory( _nState );
702 // if the state is currently in the roadmap, reflect it's new status
703 m_xAssistant->set_page_sensitive(getPageIdentForState(_nState), _bEnable);
706 bool RoadmapWizardMachine::knowsState( WizardTypes::WizardState i_nState ) const
708 for (auto const& path : m_pImpl->aPaths)
710 for (auto const& state : path.second)
712 if ( state == i_nState )
713 return true;
716 return false;
719 bool RoadmapWizardMachine::isStateEnabled( WizardTypes::WizardState _nState ) const
721 return m_pImpl->aDisabledStates.find( _nState ) == m_pImpl->aDisabledStates.end();
724 void RoadmapWizard::InsertRoadmapItem(int nItemIndex, const OUString& rText, int nItemId, bool bEnable)
726 m_xRoadmapImpl->pRoadmap->InsertRoadmapItem(nItemIndex, rText, nItemId, bEnable);
729 void RoadmapWizard::SelectRoadmapItemByID(int nItemId, bool bGrabFocus)
731 m_xRoadmapImpl->pRoadmap->SelectRoadmapItemByID(nItemId, bGrabFocus);
734 void RoadmapWizard::DeleteRoadmapItems()
736 while (m_xRoadmapImpl->pRoadmap->GetItemCount())
737 m_xRoadmapImpl->pRoadmap->DeleteRoadmapItem(0);
740 void RoadmapWizard::SetItemSelectHdl( const Link<LinkParamNone*,void>& _rHdl )
742 m_xRoadmapImpl->pRoadmap->SetItemSelectHdl(_rHdl);
745 int RoadmapWizard::GetCurrentRoadmapItemID() const
747 return m_xRoadmapImpl->pRoadmap->GetCurrentRoadmapItemID();
750 FactoryFunction RoadmapWizard::GetUITestFactory() const
752 return RoadmapWizardUIObject::create;
755 namespace
757 bool isButton(WindowType eType)
759 return eType == WindowType::PUSHBUTTON || eType == WindowType::OKBUTTON
760 || eType == WindowType::CANCELBUTTON || eType == WindowType::HELPBUTTON;
764 void RoadmapWizard::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
766 rJsonWriter.put("id", get_id());
767 rJsonWriter.put("type", "dialog");
768 rJsonWriter.put("title", GetText());
770 OUString sDialogId = GetHelpId();
771 sal_Int32 nStartPos = sDialogId.lastIndexOf('/');
772 nStartPos = nStartPos >= 0 ? nStartPos + 1 : 0;
773 rJsonWriter.put("dialogid", sDialogId.copy(nStartPos));
775 auto aResponses = rJsonWriter.startArray("responses");
776 for (const auto& rResponse : m_xRoadmapImpl->maResponses)
778 auto aResponse = rJsonWriter.startStruct();
779 rJsonWriter.put("id", rResponse.first->get_id());
780 rJsonWriter.put("response", rResponse.second);
784 vcl::Window* pFocusControl = GetFirstControlForFocus();
785 if (pFocusControl)
786 rJsonWriter.put("init_focus_id", pFocusControl->get_id());
789 auto childrenNode = rJsonWriter.startArray("children");
791 auto containerNode = rJsonWriter.startStruct();
792 rJsonWriter.put("id", "container");
793 rJsonWriter.put("type", "container");
794 rJsonWriter.put("vertical", true);
797 auto containerChildrenNode = rJsonWriter.startArray("children");
799 // tabpages
800 for (int i = 0; i < GetChildCount(); i++)
802 vcl::Window* pChild = GetChild(i);
804 if (!isButton(pChild->GetType()) && pChild != mpViewWindow)
806 auto childNode = rJsonWriter.startStruct();
807 pChild->DumpAsPropertyTree(rJsonWriter);
811 // buttons
813 auto buttonsNode = rJsonWriter.startStruct();
814 rJsonWriter.put("id", "buttons");
815 rJsonWriter.put("type", "buttonbox");
816 rJsonWriter.put("layoutstyle", "end");
818 auto buttonsChildrenNode = rJsonWriter.startArray("children");
819 for (int i = 0; i < GetChildCount(); i++)
821 vcl::Window* pChild = GetChild(i);
823 if (isButton(pChild->GetType()))
825 auto childNode = rJsonWriter.startStruct();
826 pChild->DumpAsPropertyTree(rJsonWriter);
835 } // namespace vcl
837 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */