bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / control / roadmapwizard.cxx
blob846e6f2ee2f375832f71cc7e9943f6aeabb00e8a
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>
31 #include <map>
32 #include <set>
34 #include "wizimpldata.hxx"
35 #include <uiobject-internal.hxx>
37 namespace vcl
39 using namespace RoadmapWizardTypes;
41 namespace
43 typedef ::std::set< WizardTypes::WizardState > StateSet;
45 typedef ::std::map<
46 PathId,
47 WizardPath
48 > Paths;
50 typedef ::std::map<
51 WizardTypes::WizardState,
52 ::std::pair<
53 OUString,
54 RoadmapPageFactory
56 > StateDescriptions;
59 struct RoadmapWizardImpl
61 ScopedVclPtr<ORoadmap> pRoadmap;
62 Paths aPaths;
63 PathId nActivePath;
64 StateDescriptions aStateDescriptors;
65 StateSet aDisabledStates;
66 bool bActivePathIsDefinite;
68 RoadmapWizardImpl()
69 :pRoadmap( nullptr )
70 ,nActivePath( -1 )
71 ,bActivePathIsDefinite( false )
75 /// returns the index of the current state in given path, or -1
76 static sal_Int32 getStateIndexInPath( WizardTypes::WizardState _nState, const WizardPath& _rPath );
77 /// returns the index of the current state in the path with the given id, or -1
78 sal_Int32 getStateIndexInPath( WizardTypes::WizardState _nState, PathId _nPathId );
79 /// returns the index of the first state in which the two given paths differ
80 static sal_Int32 getFirstDifferentIndex( const WizardPath& _rLHS, const WizardPath& _rRHS );
84 sal_Int32 RoadmapWizardImpl::getStateIndexInPath( WizardTypes::WizardState _nState, const WizardPath& _rPath )
86 sal_Int32 nStateIndexInPath = 0;
87 bool bFound = false;
88 for (auto const& path : _rPath)
90 if (path == _nState)
92 bFound = true;
93 break;
95 ++nStateIndexInPath;
97 if (!bFound)
98 nStateIndexInPath = -1;
99 return nStateIndexInPath;
103 sal_Int32 RoadmapWizardImpl::getStateIndexInPath( WizardTypes::WizardState _nState, PathId _nPathId )
105 sal_Int32 nStateIndexInPath = -1;
106 Paths::const_iterator aPathPos = aPaths.find( _nPathId );
107 if ( aPathPos != aPaths.end( ) )
108 nStateIndexInPath = getStateIndexInPath( _nState, aPathPos->second );
109 return nStateIndexInPath;
113 sal_Int32 RoadmapWizardImpl::getFirstDifferentIndex( const WizardPath& _rLHS, const WizardPath& _rRHS )
115 sal_Int32 nMinLength = ::std::min( _rLHS.size(), _rRHS.size() );
116 for ( sal_Int32 nCheck = 0; nCheck < nMinLength; ++nCheck )
118 if ( _rLHS[ nCheck ] != _rRHS[ nCheck ] )
119 return nCheck;
121 return nMinLength;
124 //= RoadmapWizard
125 RoadmapWizard::RoadmapWizard(vcl::Window* pParent, WinBits nStyle, InitFlag eFlag)
126 : Dialog(pParent, nStyle, eFlag)
127 , maWizardLayoutIdle("vcl RoadmapWizard maWizardLayoutIdle")
128 , m_pFinish(nullptr)
129 , m_pCancel(nullptr)
130 , m_pNextPage(nullptr)
131 , m_pPrevPage(nullptr)
132 , m_pHelp(nullptr)
133 , m_xWizardImpl(new WizardMachineImplData)
134 , m_xRoadmapImpl(new RoadmapWizardImpl)
136 mpFirstPage = nullptr;
137 mpFirstBtn = nullptr;
138 mpCurTabPage = nullptr;
139 mpPrevBtn = nullptr;
140 mpNextBtn = nullptr;
141 mpViewWindow = nullptr;
142 mnCurLevel = 0;
143 mbEmptyViewMargin = false;
144 mnLeftAlignCount = 0;
146 maWizardLayoutIdle.SetPriority(TaskPriority::RESIZE);
147 maWizardLayoutIdle.SetInvokeHandler( LINK( this, RoadmapWizard, ImplHandleWizardLayoutTimerHdl ) );
149 implConstruct(WizardButtonFlags::NEXT | WizardButtonFlags::PREVIOUS | WizardButtonFlags::FINISH | WizardButtonFlags::CANCEL | WizardButtonFlags::HELP);
151 SetLeftAlignedButtonCount( 1 );
152 mbEmptyViewMargin = true;
154 m_xRoadmapImpl->pRoadmap.disposeAndReset( VclPtr<ORoadmap>::Create( this, WB_TABSTOP ) );
155 m_xRoadmapImpl->pRoadmap->SetText( VclResId( STR_WIZDLG_ROADMAP_TITLE ) );
156 m_xRoadmapImpl->pRoadmap->SetPosPixel( Point( 0, 0 ) );
157 m_xRoadmapImpl->pRoadmap->SetItemSelectHdl( LINK( this, RoadmapWizard, OnRoadmapItemSelected ) );
159 Size aRoadmapSize = LogicToPixel(Size(85, 0), MapMode(MapUnit::MapAppFont));
160 aRoadmapSize.setHeight( GetSizePixel().Height() );
161 m_xRoadmapImpl->pRoadmap->SetSizePixel( aRoadmapSize );
163 mpViewWindow = m_xRoadmapImpl->pRoadmap;
164 m_xRoadmapImpl->pRoadmap->Show();
167 RoadmapWizardMachine::RoadmapWizardMachine(weld::Window* pParent)
168 : WizardMachine(pParent, WizardButtonFlags::NEXT | WizardButtonFlags::PREVIOUS | WizardButtonFlags::FINISH | WizardButtonFlags::CANCEL | WizardButtonFlags::HELP)
169 , m_pImpl( new RoadmapWizardImpl )
171 m_xAssistant->connect_jump_page(LINK(this, RoadmapWizardMachine, OnRoadmapItemSelected));
174 void RoadmapWizard::ShowRoadmap(bool bShow)
176 m_xRoadmapImpl->pRoadmap->Show(bShow);
177 CalcAndSetSize();
180 RoadmapWizard::~RoadmapWizard()
182 disposeOnce();
185 RoadmapWizardMachine::~RoadmapWizardMachine()
189 void RoadmapWizard::dispose()
191 m_xRoadmapImpl.reset();
193 m_pFinish.disposeAndClear();
194 m_pCancel.disposeAndClear();
195 m_pNextPage.disposeAndClear();
196 m_pPrevPage.disposeAndClear();
197 m_pHelp.disposeAndClear();
199 if (m_xWizardImpl)
201 for (WizardTypes::WizardState i = 0; i < m_xWizardImpl->nFirstUnknownPage; ++i)
203 TabPage *pPage = GetPage(i);
204 if (pPage)
205 pPage->disposeOnce();
207 m_xWizardImpl.reset();
210 maWizardLayoutIdle.Stop();
212 // Remove all buttons
213 while ( mpFirstBtn )
214 RemoveButton( mpFirstBtn->mpButton );
216 // Remove all pages
217 while ( mpFirstPage )
218 RemovePage( mpFirstPage->mpPage );
220 mpCurTabPage.clear();
221 mpPrevBtn.clear();
222 mpNextBtn.clear();
223 mpViewWindow.clear();
224 Dialog::dispose();
227 void RoadmapWizard::SetRoadmapHelpId( const OUString& _rId )
229 m_xRoadmapImpl->pRoadmap->SetHelpId( _rId );
232 void RoadmapWizard::SetRoadmapBitmap(const BitmapEx& rBmp)
234 m_xRoadmapImpl->pRoadmap->SetRoadmapBitmap(rBmp);
237 void RoadmapWizardMachine::SetRoadmapHelpId(const OUString& rId)
239 m_xAssistant->set_page_side_help_id(rId);
242 void RoadmapWizardMachine::declarePath( PathId _nPathId, const WizardPath& _lWizardStates)
244 m_pImpl->aPaths.emplace( _nPathId, _lWizardStates );
246 if ( m_pImpl->aPaths.size() == 1 )
247 // the very first path -> activate it
248 activatePath( _nPathId );
249 else
250 implUpdateRoadmap( );
253 void RoadmapWizardMachine::activatePath( PathId _nPathId, bool _bDecideForIt )
255 if ( ( _nPathId == m_pImpl->nActivePath ) && ( _bDecideForIt == m_pImpl->bActivePathIsDefinite ) )
256 // nothing to do
257 return;
259 // does the given path exist?
260 Paths::const_iterator aNewPathPos = m_pImpl->aPaths.find( _nPathId );
261 DBG_ASSERT( aNewPathPos != m_pImpl->aPaths.end(), "RoadmapWizard::activate: there is no such path!" );
262 if ( aNewPathPos == m_pImpl->aPaths.end() )
263 return;
265 // determine the index of the current state in the current path
266 sal_Int32 nCurrentStatePathIndex = -1;
267 if ( m_pImpl->nActivePath != -1 )
268 nCurrentStatePathIndex = m_pImpl->getStateIndexInPath( getCurrentState(), m_pImpl->nActivePath );
270 DBG_ASSERT( static_cast<sal_Int32>(aNewPathPos->second.size()) > nCurrentStatePathIndex,
271 "RoadmapWizard::activate: you cannot activate a path which has less states than we've already advanced!" );
272 // If this asserts, this for instance means that we are already in state number, say, 5
273 // of our current path, and the caller tries to activate a path which has less than 5
274 // states
275 if ( static_cast<sal_Int32>(aNewPathPos->second.size()) <= nCurrentStatePathIndex )
276 return;
278 // assert that the current and the new path are equal, up to nCurrentStatePathIndex
279 Paths::const_iterator aActivePathPos = m_pImpl->aPaths.find( m_pImpl->nActivePath );
280 if ( aActivePathPos != m_pImpl->aPaths.end() )
282 if ( RoadmapWizardImpl::getFirstDifferentIndex( aActivePathPos->second, aNewPathPos->second ) <= nCurrentStatePathIndex )
284 OSL_FAIL( "RoadmapWizard::activate: you cannot activate a path which conflicts with the current one *before* the current state!" );
285 return;
289 m_pImpl->nActivePath = _nPathId;
290 m_pImpl->bActivePathIsDefinite = _bDecideForIt;
292 implUpdateRoadmap( );
295 void RoadmapWizard::implUpdateRoadmap( )
297 DBG_ASSERT( m_xRoadmapImpl->aPaths.find( m_xRoadmapImpl->nActivePath ) != m_xRoadmapImpl->aPaths.end(),
298 "RoadmapWizard::implUpdateRoadmap: there is no such path!" );
299 const WizardPath& rActivePath( m_xRoadmapImpl->aPaths[ m_xRoadmapImpl->nActivePath ] );
301 sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
302 if (nCurrentStatePathIndex < 0)
303 return;
305 // determine up to which index (in the new path) we have to display the items
306 RoadmapTypes::ItemIndex nUpperStepBoundary = static_cast<RoadmapTypes::ItemIndex>(rActivePath.size());
307 bool bIncompletePath = false;
308 if ( !m_xRoadmapImpl->bActivePathIsDefinite )
310 for (auto const& path : m_xRoadmapImpl->aPaths)
312 if ( path.first == m_xRoadmapImpl->nActivePath )
313 // it's the path we are just activating -> no need to check anything
314 continue;
315 // the index from which on both paths differ
316 sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
317 if ( nDivergenceIndex <= nCurrentStatePathIndex )
318 // they differ in an index which we have already left behind us
319 // -> this is no conflict anymore
320 continue;
322 // the path conflicts with our new path -> don't activate the
323 // *complete* new path, but only up to the step which is unambiguous
324 nUpperStepBoundary = nDivergenceIndex;
325 bIncompletePath = true;
329 // now, we have to remove all items after nCurrentStatePathIndex, and insert the items from the active
330 // path, up to (excluding) nUpperStepBoundary
331 RoadmapTypes::ItemIndex nLoopUntil = ::std::max( nUpperStepBoundary, m_xRoadmapImpl->pRoadmap->GetItemCount() );
332 for ( RoadmapTypes::ItemIndex nItemIndex = nCurrentStatePathIndex; nItemIndex < nLoopUntil; ++nItemIndex )
334 bool bExistentItem = ( nItemIndex < m_xRoadmapImpl->pRoadmap->GetItemCount() );
335 bool bNeedItem = ( nItemIndex < nUpperStepBoundary );
337 bool bInsertItem = false;
338 if ( bExistentItem )
340 if ( !bNeedItem )
342 while ( nItemIndex < m_xRoadmapImpl->pRoadmap->GetItemCount() )
343 m_xRoadmapImpl->pRoadmap->DeleteRoadmapItem( nItemIndex );
344 break;
346 else
348 // there is an item with this index in the roadmap - does it match what is requested by
349 // the respective state in the active path?
350 RoadmapTypes::ItemId nPresentItemId = m_xRoadmapImpl->pRoadmap->GetItemID( nItemIndex );
351 WizardTypes::WizardState nRequiredState = rActivePath[ nItemIndex ];
352 if ( nPresentItemId != nRequiredState )
354 m_xRoadmapImpl->pRoadmap->DeleteRoadmapItem( nItemIndex );
355 bInsertItem = true;
359 else
361 DBG_ASSERT( bNeedItem, "RoadmapWizard::implUpdateRoadmap: ehm - none needed, none present - why did the loop not terminate?" );
362 bInsertItem = bNeedItem;
365 WizardTypes::WizardState nState( rActivePath[ nItemIndex ] );
366 if ( bInsertItem )
368 m_xRoadmapImpl->pRoadmap->InsertRoadmapItem(
369 nItemIndex,
370 getStateDisplayName( nState ),
371 nState,
372 true
376 const bool bEnable = m_xRoadmapImpl->aDisabledStates.find( nState ) == m_xRoadmapImpl->aDisabledStates.end();
377 m_xRoadmapImpl->pRoadmap->EnableRoadmapItem( m_xRoadmapImpl->pRoadmap->GetItemID( nItemIndex ), bEnable );
380 m_xRoadmapImpl->pRoadmap->SetRoadmapComplete( !bIncompletePath );
383 void RoadmapWizardMachine::implUpdateRoadmap( )
386 DBG_ASSERT( m_pImpl->aPaths.find( m_pImpl->nActivePath ) != m_pImpl->aPaths.end(),
387 "RoadmapWizard::implUpdateRoadmap: there is no such path!" );
388 const WizardPath& rActivePath( m_pImpl->aPaths[ m_pImpl->nActivePath ] );
390 sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
391 if (nCurrentStatePathIndex < 0)
392 return;
394 // determine up to which index (in the new path) we have to display the items
395 RoadmapTypes::ItemIndex nUpperStepBoundary = static_cast<RoadmapTypes::ItemIndex>(rActivePath.size());
396 if ( !m_pImpl->bActivePathIsDefinite )
398 for (auto const& path : m_pImpl->aPaths)
400 if ( path.first == m_pImpl->nActivePath )
401 // it's the path we are just activating -> no need to check anything
402 continue;
403 // the index from which on both paths differ
404 sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
405 if ( nDivergenceIndex <= nCurrentStatePathIndex )
406 // they differ in an index which we have already left behind us
407 // -> this is no conflict anymore
408 continue;
410 // the path conflicts with our new path -> don't activate the
411 // *complete* new path, but only up to the step which is unambiguous
412 nUpperStepBoundary = nDivergenceIndex;
416 // can we advance from the current page?
417 bool bCurrentPageCanAdvance = true;
418 BuilderPage* pCurrentPage = GetPage( getCurrentState() );
419 if ( pCurrentPage )
421 const IWizardPageController* pController = getPageController( GetPage( getCurrentState() ) );
422 OSL_ENSURE( pController != nullptr, "RoadmapWizard::implUpdateRoadmap: no controller for the current page!" );
423 bCurrentPageCanAdvance = !pController || pController->canAdvance();
426 // now, we have to remove all items after nCurrentStatePathIndex, and insert the items from the active
427 // path, up to (excluding) nUpperStepBoundary
428 RoadmapTypes::ItemIndex nRoadmapItems = m_xAssistant->get_n_pages();
429 RoadmapTypes::ItemIndex nLoopUntil = ::std::max( nUpperStepBoundary, nRoadmapItems );
430 for ( RoadmapTypes::ItemIndex nItemIndex = nCurrentStatePathIndex; nItemIndex < nLoopUntil; ++nItemIndex )
432 bool bExistentItem = ( nItemIndex < nRoadmapItems );
433 bool bNeedItem = ( nItemIndex < nUpperStepBoundary );
435 bool bInsertItem = false;
436 if ( bExistentItem )
438 if ( !bNeedItem )
440 int nPages = nRoadmapItems;
441 for (int i = nPages - 1; i >= nItemIndex; --i)
443 m_xAssistant->set_page_title(m_xAssistant->get_page_ident(i), "");
444 --nRoadmapItems;
446 break;
448 else
450 // there is an item with this index in the roadmap - does it match what is requested by
451 // the respective state in the active path?
452 RoadmapTypes::ItemId nPresentItemId = m_xAssistant->get_page_ident(nItemIndex).toInt32();
453 WizardTypes::WizardState nRequiredState = rActivePath[ nItemIndex ];
454 if ( nPresentItemId != nRequiredState )
456 m_xAssistant->set_page_title(OUString::number(nPresentItemId), "");
457 bInsertItem = true;
461 else
463 DBG_ASSERT( bNeedItem, "RoadmapWizard::implUpdateRoadmap: ehm - none needed, none present - why did the loop not terminate?" );
464 bInsertItem = bNeedItem;
467 WizardTypes::WizardState nState( rActivePath[ nItemIndex ] );
469 if ( bInsertItem )
471 GetOrCreatePage(nState);
474 OUString sIdent(getPageIdentForState(nState));
475 m_xAssistant->set_page_index(sIdent, nItemIndex);
476 m_xAssistant->set_page_title(sIdent, getStateDisplayName(nState));
478 // if the item is *after* the current state, but the current page does not
479 // allow advancing, the disable the state. This relieves derived classes
480 // from disabling all future states just because the current state does not
481 // (yet) allow advancing.
482 const bool bUnconditionedDisable = !bCurrentPageCanAdvance && ( nItemIndex > nCurrentStatePathIndex );
483 const bool bEnable = !bUnconditionedDisable && ( m_pImpl->aDisabledStates.find( nState ) == m_pImpl->aDisabledStates.end() );
484 m_xAssistant->set_page_sensitive(sIdent, bEnable);
488 WizardTypes::WizardState RoadmapWizard::determineNextState( WizardTypes::WizardState _nCurrentState ) const
490 sal_Int32 nCurrentStatePathIndex = -1;
492 Paths::const_iterator aActivePathPos = m_xRoadmapImpl->aPaths.find( m_xRoadmapImpl->nActivePath );
493 if ( aActivePathPos != m_xRoadmapImpl->aPaths.end() )
494 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( _nCurrentState, aActivePathPos->second );
496 DBG_ASSERT( nCurrentStatePathIndex != -1, "RoadmapWizard::determineNextState: ehm - how can we travel if there is no (valid) active path?" );
497 if ( nCurrentStatePathIndex == -1 )
498 return WZS_INVALID_STATE;
500 sal_Int32 nNextStateIndex = nCurrentStatePathIndex + 1;
502 while ( ( nNextStateIndex < static_cast<sal_Int32>(aActivePathPos->second.size()) )
503 && ( m_xRoadmapImpl->aDisabledStates.find( aActivePathPos->second[ nNextStateIndex ] ) != m_xRoadmapImpl->aDisabledStates.end() )
506 ++nNextStateIndex;
509 if ( nNextStateIndex >= static_cast<sal_Int32>(aActivePathPos->second.size()) )
510 // there is no next state in the current path (at least none which is enabled)
511 return WZS_INVALID_STATE;
513 return aActivePathPos->second[ nNextStateIndex ];
516 WizardTypes::WizardState RoadmapWizardMachine::determineNextState( WizardTypes::WizardState _nCurrentState ) const
518 sal_Int32 nCurrentStatePathIndex = -1;
520 Paths::const_iterator aActivePathPos = m_pImpl->aPaths.find( m_pImpl->nActivePath );
521 if ( aActivePathPos != m_pImpl->aPaths.end() )
522 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( _nCurrentState, aActivePathPos->second );
524 DBG_ASSERT( nCurrentStatePathIndex != -1, "RoadmapWizard::determineNextState: ehm - how can we travel if there is no (valid) active path?" );
525 if ( nCurrentStatePathIndex == -1 )
526 return WZS_INVALID_STATE;
528 sal_Int32 nNextStateIndex = nCurrentStatePathIndex + 1;
530 while ( ( nNextStateIndex < static_cast<sal_Int32>(aActivePathPos->second.size()) )
531 && ( m_pImpl->aDisabledStates.find( aActivePathPos->second[ nNextStateIndex ] ) != m_pImpl->aDisabledStates.end() )
534 ++nNextStateIndex;
537 if ( nNextStateIndex >= static_cast<sal_Int32>(aActivePathPos->second.size()) )
538 // there is no next state in the current path (at least none which is enabled)
539 return WZS_INVALID_STATE;
541 return aActivePathPos->second[ nNextStateIndex ];
544 bool RoadmapWizardMachine::canAdvance() const
546 if ( !m_pImpl->bActivePathIsDefinite )
548 // check how many paths are still allowed
549 const WizardPath& rActivePath( m_pImpl->aPaths[ m_pImpl->nActivePath ] );
551 // if current path has only the base item, it is not possible to proceed without activating another path
552 if(rActivePath.size()<=1)
553 return false;
555 sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
557 size_t nPossiblePaths(0);
558 for (auto const& path : m_pImpl->aPaths)
560 // the index from which on both paths differ
561 sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
563 if ( nDivergenceIndex > nCurrentStatePathIndex )
564 // this path is still a possible path
565 nPossiblePaths += 1;
568 // if we have more than one path which is still possible, then we assume
569 // to always have a next state. Though there might be scenarios where this
570 // is not true, but this is too sophisticated (means not really needed) right now.
571 if ( nPossiblePaths > 1 )
572 return true;
575 const WizardPath& rPath = m_pImpl->aPaths[ m_pImpl->nActivePath ];
576 return *rPath.rbegin() != getCurrentState();
579 void RoadmapWizardMachine::updateTravelUI()
581 WizardMachine::updateTravelUI();
583 // disable the "Previous" button if all states in our history are disabled
584 std::vector< WizardTypes::WizardState > aHistory;
585 getStateHistory( aHistory );
586 bool bHaveEnabledState = false;
587 for (auto const& state : aHistory)
589 if ( isStateEnabled(state) )
591 bHaveEnabledState = true;
592 break;
596 enableButtons( WizardButtonFlags::PREVIOUS, bHaveEnabledState );
598 implUpdateRoadmap();
601 IMPL_LINK_NOARG(RoadmapWizard, OnRoadmapItemSelected, LinkParamNone*, void)
603 RoadmapTypes::ItemId nCurItemId = m_xRoadmapImpl->pRoadmap->GetCurrentRoadmapItemID();
604 if ( nCurItemId == getCurrentState() )
605 // nothing to do
606 return;
608 if ( isTravelingSuspended() )
609 return;
611 RoadmapWizardTravelSuspension aTravelGuard( *this );
613 sal_Int32 nCurrentIndex = m_xRoadmapImpl->getStateIndexInPath( getCurrentState(), m_xRoadmapImpl->nActivePath );
614 sal_Int32 nNewIndex = m_xRoadmapImpl->getStateIndexInPath( nCurItemId, m_xRoadmapImpl->nActivePath );
616 DBG_ASSERT( ( nCurrentIndex != -1 ) && ( nNewIndex != -1 ),
617 "RoadmapWizard::OnRoadmapItemSelected: something's wrong here!" );
618 if ( ( nCurrentIndex == -1 ) || ( nNewIndex == -1 ) )
620 return;
623 bool bResult = true;
624 if ( nNewIndex > nCurrentIndex )
626 bResult = skipUntil( static_cast<WizardTypes::WizardState>(nCurItemId) );
627 WizardTypes::WizardState nTemp = static_cast<WizardTypes::WizardState>(nCurItemId);
628 while( nTemp )
630 if( m_xRoadmapImpl->aDisabledStates.find( --nTemp ) != m_xRoadmapImpl->aDisabledStates.end() )
631 removePageFromHistory( nTemp );
634 else
635 bResult = skipBackwardUntil( static_cast<WizardTypes::WizardState>(nCurItemId) );
637 if ( !bResult )
638 m_xRoadmapImpl->pRoadmap->SelectRoadmapItemByID( getCurrentState() );
641 IMPL_LINK(RoadmapWizardMachine, OnRoadmapItemSelected, const OUString&, rCurItemId, bool)
643 WizardTypes::WizardState nSelectedState = getStateFromPageIdent(rCurItemId);
645 if (nSelectedState == getCurrentState())
646 // nothing to do
647 return false;
649 if ( isTravelingSuspended() )
650 return false;
652 WizardTravelSuspension aTravelGuard( *this );
654 sal_Int32 nCurrentIndex = m_pImpl->getStateIndexInPath( getCurrentState(), m_pImpl->nActivePath );
655 sal_Int32 nNewIndex = m_pImpl->getStateIndexInPath( nSelectedState, m_pImpl->nActivePath );
657 DBG_ASSERT( ( nCurrentIndex != -1 ) && ( nNewIndex != -1 ),
658 "RoadmapWizard::OnRoadmapItemSelected: something's wrong here!" );
659 if ( ( nCurrentIndex == -1 ) || ( nNewIndex == -1 ) )
661 return false;
664 bool bResult = true;
665 if ( nNewIndex > nCurrentIndex )
667 bResult = skipUntil(nSelectedState);
668 WizardTypes::WizardState nTemp = nSelectedState;
669 while( nTemp )
671 if( m_pImpl->aDisabledStates.find( --nTemp ) != m_pImpl->aDisabledStates.end() )
672 removePageFromHistory( nTemp );
675 else
676 bResult = skipBackwardUntil(nSelectedState);
678 return bResult;
681 void RoadmapWizard::enterState(WizardTypes::WizardState /*nState*/)
683 // synchronize the roadmap
684 implUpdateRoadmap( );
685 m_xRoadmapImpl->pRoadmap->SelectRoadmapItemByID( getCurrentState() );
688 void RoadmapWizardMachine::enterState( WizardTypes::WizardState _nState )
690 WizardMachine::enterState( _nState );
692 // synchronize the roadmap
693 implUpdateRoadmap();
696 OUString RoadmapWizard::getStateDisplayName( WizardTypes::WizardState _nState ) const
698 OUString sDisplayName;
700 StateDescriptions::const_iterator pos = m_xRoadmapImpl->aStateDescriptors.find( _nState );
701 OSL_ENSURE( pos != m_xRoadmapImpl->aStateDescriptors.end(),
702 "RoadmapWizard::getStateDisplayName: no default implementation available for this state!" );
703 if ( pos != m_xRoadmapImpl->aStateDescriptors.end() )
704 sDisplayName = pos->second.first;
706 return sDisplayName;
709 OUString RoadmapWizardMachine::getStateDisplayName( WizardTypes::WizardState _nState ) const
711 OUString sDisplayName;
713 StateDescriptions::const_iterator pos = m_pImpl->aStateDescriptors.find( _nState );
714 OSL_ENSURE( pos != m_pImpl->aStateDescriptors.end(),
715 "RoadmapWizard::getStateDisplayName: no default implementation available for this state!" );
716 if ( pos != m_pImpl->aStateDescriptors.end() )
717 sDisplayName = pos->second.first;
719 return sDisplayName;
722 VclPtr<TabPage> RoadmapWizard::createPage( WizardTypes::WizardState _nState )
724 VclPtr<TabPage> pPage;
726 StateDescriptions::const_iterator pos = m_xRoadmapImpl->aStateDescriptors.find( _nState );
727 OSL_ENSURE( pos != m_xRoadmapImpl->aStateDescriptors.end(),
728 "RoadmapWizard::createPage: no default implementation available for this state!" );
729 if ( pos != m_xRoadmapImpl->aStateDescriptors.end() )
731 RoadmapPageFactory pFactory = pos->second.second;
732 pPage = (*pFactory)( *this );
735 return pPage;
738 void RoadmapWizardMachine::enableState( WizardTypes::WizardState _nState, bool _bEnable )
740 // remember this (in case the state appears in the roadmap later on)
741 if ( _bEnable )
742 m_pImpl->aDisabledStates.erase( _nState );
743 else
745 m_pImpl->aDisabledStates.insert( _nState );
746 removePageFromHistory( _nState );
749 // if the state is currently in the roadmap, reflect it's new status
750 m_xAssistant->set_page_sensitive(getPageIdentForState(_nState), _bEnable);
753 bool RoadmapWizardMachine::knowsState( WizardTypes::WizardState i_nState ) const
755 for (auto const& path : m_pImpl->aPaths)
757 for (auto const& state : path.second)
759 if ( state == i_nState )
760 return true;
763 return false;
766 bool RoadmapWizardMachine::isStateEnabled( WizardTypes::WizardState _nState ) const
768 return m_pImpl->aDisabledStates.find( _nState ) == m_pImpl->aDisabledStates.end();
771 void RoadmapWizard::InsertRoadmapItem(int nItemIndex, const OUString& rText, int nItemId, bool bEnable)
773 m_xRoadmapImpl->pRoadmap->InsertRoadmapItem(nItemIndex, rText, nItemId, bEnable);
776 void RoadmapWizard::SelectRoadmapItemByID(int nItemId, bool bGrabFocus)
778 m_xRoadmapImpl->pRoadmap->SelectRoadmapItemByID(nItemId, bGrabFocus);
781 void RoadmapWizard::DeleteRoadmapItems()
783 while (m_xRoadmapImpl->pRoadmap->GetItemCount())
784 m_xRoadmapImpl->pRoadmap->DeleteRoadmapItem(0);
787 void RoadmapWizard::SetItemSelectHdl( const Link<LinkParamNone*,void>& _rHdl )
789 m_xRoadmapImpl->pRoadmap->SetItemSelectHdl(_rHdl);
792 int RoadmapWizard::GetCurrentRoadmapItemID() const
794 return m_xRoadmapImpl->pRoadmap->GetCurrentRoadmapItemID();
797 FactoryFunction RoadmapWizard::GetUITestFactory() const
799 return RoadmapWizardUIObject::create;
802 namespace
804 bool isButton(WindowType eType)
806 return eType == WindowType::PUSHBUTTON || eType == WindowType::OKBUTTON
807 || eType == WindowType::CANCELBUTTON || eType == WindowType::HELPBUTTON;
811 void RoadmapWizard::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
813 rJsonWriter.put("id", get_id());
814 rJsonWriter.put("type", "dialog");
815 rJsonWriter.put("title", GetText());
817 OUString sDialogId = GetHelpId();
818 sal_Int32 nStartPos = sDialogId.lastIndexOf('/');
819 nStartPos = nStartPos >= 0 ? nStartPos + 1 : 0;
820 rJsonWriter.put("dialogid", sDialogId.copy(nStartPos));
822 vcl::Window* pFocusControl = GetFirstControlForFocus();
823 if (pFocusControl)
824 rJsonWriter.put("init_focus_id", pFocusControl->get_id());
827 auto childrenNode = rJsonWriter.startArray("children");
829 auto containerNode = rJsonWriter.startStruct();
830 rJsonWriter.put("id", "container");
831 rJsonWriter.put("type", "container");
832 rJsonWriter.put("vertical", true);
835 auto containerChildrenNode = rJsonWriter.startArray("children");
837 // tabpages
838 for (int i = 0; i < GetChildCount(); i++)
840 vcl::Window* pChild = GetChild(i);
842 if (!isButton(pChild->GetType()) && pChild != mpViewWindow)
844 auto childNode = rJsonWriter.startStruct();
845 pChild->DumpAsPropertyTree(rJsonWriter);
849 // buttons
851 auto buttonsNode = rJsonWriter.startStruct();
852 rJsonWriter.put("id", "buttons");
853 rJsonWriter.put("type", "buttonbox");
854 rJsonWriter.put("layoutstyle", "end");
856 auto buttonsChildrenNode = rJsonWriter.startArray("children");
857 for (int i = 0; i < GetChildCount(); i++)
859 vcl::Window* pChild = GetChild(i);
861 if (isButton(pChild->GetType()))
863 auto childNode = rJsonWriter.startStruct();
864 pChild->DumpAsPropertyTree(rJsonWriter);
873 } // namespace vcl
875 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */