LibreOffice Module vcl (master)  1
roadmapwizard.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
21 #include <vcl/roadmapwizard.hxx>
22 #include <vcl/toolkit/roadmap.hxx>
23 #include <tools/debug.hxx>
24 #include <osl/diagnose.h>
25 
26 #include <strings.hrc>
27 #include <svdata.hxx>
28 
29 #include <vector>
30 #include <map>
31 #include <set>
32 
33 
34 namespace vcl
35 {
36  namespace
37  {
38  typedef ::std::set< WizardTypes::WizardState > StateSet;
39 
40  typedef ::std::map<
43  > Paths;
44 
45  typedef ::std::map<
47  ::std::pair<
48  OUString,
50  >
51  > StateDescriptions;
52  }
53 
55  {
57  Paths aPaths;
59  StateDescriptions aStateDescriptors;
60  StateSet aDisabledStates;
62 
64  :pRoadmap( nullptr )
65  ,nActivePath( -1 )
66  ,bActivePathIsDefinite( false )
67  {
68  }
69 
71  static sal_Int32 getStateIndexInPath( WizardTypes::WizardState _nState, const WizardPath& _rPath );
73  sal_Int32 getStateIndexInPath( WizardTypes::WizardState _nState, PathId _nPathId );
75  static sal_Int32 getFirstDifferentIndex( const WizardPath& _rLHS, const WizardPath& _rRHS );
76  };
77 
78 
80  {
81  sal_Int32 nStateIndexInPath = 0;
82  bool bFound = false;
83  for (auto const& path : _rPath)
84  {
85  if (path == _nState)
86  {
87  bFound = true;
88  break;
89  }
90  ++nStateIndexInPath;
91  }
92  if (!bFound)
93  nStateIndexInPath = -1;
94  return nStateIndexInPath;
95  }
96 
97 
99  {
100  sal_Int32 nStateIndexInPath = -1;
101  Paths::const_iterator aPathPos = aPaths.find( _nPathId );
102  if ( aPathPos != aPaths.end( ) )
103  nStateIndexInPath = getStateIndexInPath( _nState, aPathPos->second );
104  return nStateIndexInPath;
105  }
106 
107 
108  sal_Int32 RoadmapWizardImpl::getFirstDifferentIndex( const WizardPath& _rLHS, const WizardPath& _rRHS )
109  {
110  sal_Int32 nMinLength = ::std::min( _rLHS.size(), _rRHS.size() );
111  for ( sal_Int32 nCheck = 0; nCheck < nMinLength; ++nCheck )
112  {
113  if ( _rLHS[ nCheck ] != _rRHS[ nCheck ] )
114  return nCheck;
115  }
116  return nMinLength;
117  }
118 
119  //= RoadmapWizard
122  ,m_pImpl( new RoadmapWizardImpl )
123  {
124  impl_construct();
125  }
126 
129  , m_pImpl( new RoadmapWizardImpl )
130  {
131  m_xAssistant->connect_jump_page(LINK(this, RoadmapWizardMachine, OnRoadmapItemSelected));
132  }
133 
135  {
138 
139  m_pImpl->pRoadmap.disposeAndReset( VclPtr<ORoadmap>::Create( this, WB_TABSTOP ) );
140  m_pImpl->pRoadmap->SetText( VclResId( STR_WIZDLG_ROADMAP_TITLE ) );
141  m_pImpl->pRoadmap->SetPosPixel( Point( 0, 0 ) );
142  m_pImpl->pRoadmap->SetItemSelectHdl( LINK( this, RoadmapWizard, OnRoadmapItemSelected ) );
143 
144  Size aRoadmapSize = LogicToPixel(Size(85, 0), MapMode(MapUnit::MapAppFont));
145  aRoadmapSize.setHeight( GetSizePixel().Height() );
146  m_pImpl->pRoadmap->SetSizePixel( aRoadmapSize );
147 
148  SetViewWindow( m_pImpl->pRoadmap );
149  SetViewAlign( WindowAlign::Left );
150  m_pImpl->pRoadmap->Show();
151  }
152 
153  void RoadmapWizard::ShowRoadmap(bool bShow)
154  {
155  m_pImpl->pRoadmap->Show(bShow);
156  CalcAndSetSize();
157  }
158 
160  {
161  disposeOnce();
162  }
163 
165  {
166  }
167 
169  {
170  m_pImpl.reset();
172  }
173 
174  void RoadmapWizard::SetRoadmapHelpId( const OString& _rId )
175  {
176  m_pImpl->pRoadmap->SetHelpId( _rId );
177  }
178 
179  void RoadmapWizardMachine::SetRoadmapHelpId(const OString& rId)
180  {
181  m_xAssistant->set_page_side_help_id(rId);
182  }
183 
184  void RoadmapWizard::SetRoadmapInteractive( bool _bInteractive )
185  {
186  m_pImpl->pRoadmap->SetRoadmapInteractive( _bInteractive );
187  }
188 
189  void RoadmapWizard::declarePath( PathId _nPathId, const WizardPath& _lWizardStates)
190  {
191 
192  m_pImpl->aPaths.emplace( _nPathId, _lWizardStates );
193 
194  if ( m_pImpl->aPaths.size() == 1 )
195  // the very first path -> activate it
196  activatePath( _nPathId );
197  else
199  }
200 
201  void RoadmapWizardMachine::declarePath( PathId _nPathId, const WizardPath& _lWizardStates)
202  {
203 
204  m_pImpl->aPaths.emplace( _nPathId, _lWizardStates );
205 
206  if ( m_pImpl->aPaths.size() == 1 )
207  // the very first path -> activate it
208  activatePath( _nPathId );
209  else
211  }
212 
213  void RoadmapWizard::activatePath( PathId _nPathId, bool _bDecideForIt )
214  {
215 
216  if ( ( _nPathId == m_pImpl->nActivePath ) && ( _bDecideForIt == m_pImpl->bActivePathIsDefinite ) )
217  // nothing to do
218  return;
219 
220  // does the given path exist?
221  Paths::const_iterator aNewPathPos = m_pImpl->aPaths.find( _nPathId );
222  DBG_ASSERT( aNewPathPos != m_pImpl->aPaths.end(), "RoadmapWizard::activate: there is no such path!" );
223  if ( aNewPathPos == m_pImpl->aPaths.end() )
224  return;
225 
226  // determine the index of the current state in the current path
227  sal_Int32 nCurrentStatePathIndex = -1;
228  if ( m_pImpl->nActivePath != -1 )
229  nCurrentStatePathIndex = m_pImpl->getStateIndexInPath( getCurrentState(), m_pImpl->nActivePath );
230 
231  DBG_ASSERT( static_cast<sal_Int32>(aNewPathPos->second.size()) > nCurrentStatePathIndex,
232  "RoadmapWizard::activate: you cannot activate a path which has less states than we've already advanced!" );
233  // If this asserts, this for instance means that we are already in state number, say, 5
234  // of our current path, and the caller tries to activate a path which has less than 5
235  // states
236  if ( static_cast<sal_Int32>(aNewPathPos->second.size()) <= nCurrentStatePathIndex )
237  return;
238 
239  // assert that the current and the new path are equal, up to nCurrentStatePathIndex
240  Paths::const_iterator aActivePathPos = m_pImpl->aPaths.find( m_pImpl->nActivePath );
241  if ( aActivePathPos != m_pImpl->aPaths.end() )
242  {
243  if ( RoadmapWizardImpl::getFirstDifferentIndex( aActivePathPos->second, aNewPathPos->second ) <= nCurrentStatePathIndex )
244  {
245  OSL_FAIL( "RoadmapWizard::activate: you cannot activate a path which conflicts with the current one *before* the current state!" );
246  return;
247  }
248  }
249 
250  m_pImpl->nActivePath = _nPathId;
251  m_pImpl->bActivePathIsDefinite = _bDecideForIt;
252 
254  }
255 
256  void RoadmapWizardMachine::activatePath( PathId _nPathId, bool _bDecideForIt )
257  {
258  if ( ( _nPathId == m_pImpl->nActivePath ) && ( _bDecideForIt == m_pImpl->bActivePathIsDefinite ) )
259  // nothing to do
260  return;
261 
262  // does the given path exist?
263  Paths::const_iterator aNewPathPos = m_pImpl->aPaths.find( _nPathId );
264  DBG_ASSERT( aNewPathPos != m_pImpl->aPaths.end(), "RoadmapWizard::activate: there is no such path!" );
265  if ( aNewPathPos == m_pImpl->aPaths.end() )
266  return;
267 
268  // determine the index of the current state in the current path
269  sal_Int32 nCurrentStatePathIndex = -1;
270  if ( m_pImpl->nActivePath != -1 )
271  nCurrentStatePathIndex = m_pImpl->getStateIndexInPath( getCurrentState(), m_pImpl->nActivePath );
272 
273  DBG_ASSERT( static_cast<sal_Int32>(aNewPathPos->second.size()) > nCurrentStatePathIndex,
274  "RoadmapWizard::activate: you cannot activate a path which has less states than we've already advanced!" );
275  // If this asserts, this for instance means that we are already in state number, say, 5
276  // of our current path, and the caller tries to activate a path which has less than 5
277  // states
278  if ( static_cast<sal_Int32>(aNewPathPos->second.size()) <= nCurrentStatePathIndex )
279  return;
280 
281  // assert that the current and the new path are equal, up to nCurrentStatePathIndex
282  Paths::const_iterator aActivePathPos = m_pImpl->aPaths.find( m_pImpl->nActivePath );
283  if ( aActivePathPos != m_pImpl->aPaths.end() )
284  {
285  if ( RoadmapWizardImpl::getFirstDifferentIndex( aActivePathPos->second, aNewPathPos->second ) <= nCurrentStatePathIndex )
286  {
287  OSL_FAIL( "RoadmapWizard::activate: you cannot activate a path which conflicts with the current one *before* the current state!" );
288  return;
289  }
290  }
291 
292  m_pImpl->nActivePath = _nPathId;
293  m_pImpl->bActivePathIsDefinite = _bDecideForIt;
294 
296  }
297 
299  {
300 
301  DBG_ASSERT( m_pImpl->aPaths.find( m_pImpl->nActivePath ) != m_pImpl->aPaths.end(),
302  "RoadmapWizard::implUpdateRoadmap: there is no such path!" );
303  const WizardPath& rActivePath( m_pImpl->aPaths[ m_pImpl->nActivePath ] );
304 
305  sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
306  if (nCurrentStatePathIndex < 0)
307  return;
308 
309  // determine up to which index (in the new path) we have to display the items
310  RoadmapTypes::ItemIndex nUpperStepBoundary = static_cast<RoadmapTypes::ItemIndex>(rActivePath.size());
311  bool bIncompletePath = false;
312  if ( !m_pImpl->bActivePathIsDefinite )
313  {
314  for (auto const& path : m_pImpl->aPaths)
315  {
316  if ( path.first == m_pImpl->nActivePath )
317  // it's the path we are just activating -> no need to check anything
318  continue;
319  // the index from which on both paths differ
320  sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
321  if ( nDivergenceIndex <= nCurrentStatePathIndex )
322  // they differ in an index which we have already left behind us
323  // -> this is no conflict anymore
324  continue;
325 
326  // the path conflicts with our new path -> don't activate the
327  // *complete* new path, but only up to the step which is unambiguous
328  nUpperStepBoundary = nDivergenceIndex;
329  bIncompletePath = true;
330  }
331  }
332 
333  // can we advance from the current page?
334  bool bCurrentPageCanAdvance = true;
335  TabPage* pCurrentPage = GetPage( getCurrentState() );
336  if ( pCurrentPage )
337  {
338  const IWizardPageController* pController = getPageController( GetPage( getCurrentState() ) );
339  OSL_ENSURE( pController != nullptr, "RoadmapWizard::implUpdateRoadmap: no controller for the current page!" );
340  bCurrentPageCanAdvance = !pController || pController->canAdvance();
341  }
342 
343  // now, we have to remove all items after nCurrentStatePathIndex, and insert the items from the active
344  // path, up to (excluding) nUpperStepBoundary
345  RoadmapTypes::ItemIndex nLoopUntil = ::std::max( nUpperStepBoundary, m_pImpl->pRoadmap->GetItemCount() );
346  for ( RoadmapTypes::ItemIndex nItemIndex = nCurrentStatePathIndex; nItemIndex < nLoopUntil; ++nItemIndex )
347  {
348  bool bExistentItem = ( nItemIndex < m_pImpl->pRoadmap->GetItemCount() );
349  bool bNeedItem = ( nItemIndex < nUpperStepBoundary );
350 
351  bool bInsertItem = false;
352  if ( bExistentItem )
353  {
354  if ( !bNeedItem )
355  {
356  while ( nItemIndex < m_pImpl->pRoadmap->GetItemCount() )
357  m_pImpl->pRoadmap->DeleteRoadmapItem( nItemIndex );
358  break;
359  }
360  else
361  {
362  // there is an item with this index in the roadmap - does it match what is requested by
363  // the respective state in the active path?
364  RoadmapTypes::ItemId nPresentItemId = m_pImpl->pRoadmap->GetItemID( nItemIndex );
365  WizardState nRequiredState = rActivePath[ nItemIndex ];
366  if ( nPresentItemId != nRequiredState )
367  {
368  m_pImpl->pRoadmap->DeleteRoadmapItem( nItemIndex );
369  bInsertItem = true;
370  }
371  }
372  }
373  else
374  {
375  DBG_ASSERT( bNeedItem, "RoadmapWizard::implUpdateRoadmap: ehm - none needed, none present - why did the loop not terminate?" );
376  bInsertItem = bNeedItem;
377  }
378 
379  WizardState nState( rActivePath[ nItemIndex ] );
380  if ( bInsertItem )
381  {
382  m_pImpl->pRoadmap->InsertRoadmapItem(
383  nItemIndex,
384  getStateDisplayName( nState ),
385  nState,
386  true
387  );
388  }
389 
390  // if the item is *after* the current state, but the current page does not
391  // allow advancing, the disable the state. This relieves derived classes
392  // from disabling all future states just because the current state does not
393  // (yet) allow advancing.
394  const bool bUnconditionedDisable = !bCurrentPageCanAdvance && ( nItemIndex > nCurrentStatePathIndex );
395  const bool bEnable = !bUnconditionedDisable && ( m_pImpl->aDisabledStates.find( nState ) == m_pImpl->aDisabledStates.end() );
396 
397  m_pImpl->pRoadmap->EnableRoadmapItem( m_pImpl->pRoadmap->GetItemID( nItemIndex ), bEnable );
398  }
399 
400  m_pImpl->pRoadmap->SetRoadmapComplete( !bIncompletePath );
401  }
402 
404  {
405 
406  DBG_ASSERT( m_pImpl->aPaths.find( m_pImpl->nActivePath ) != m_pImpl->aPaths.end(),
407  "RoadmapWizard::implUpdateRoadmap: there is no such path!" );
408  const WizardPath& rActivePath( m_pImpl->aPaths[ m_pImpl->nActivePath ] );
409 
410  sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
411  if (nCurrentStatePathIndex < 0)
412  return;
413 
414  // determine up to which index (in the new path) we have to display the items
415  RoadmapTypes::ItemIndex nUpperStepBoundary = static_cast<RoadmapTypes::ItemIndex>(rActivePath.size());
416  if ( !m_pImpl->bActivePathIsDefinite )
417  {
418  for (auto const& path : m_pImpl->aPaths)
419  {
420  if ( path.first == m_pImpl->nActivePath )
421  // it's the path we are just activating -> no need to check anything
422  continue;
423  // the index from which on both paths differ
424  sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
425  if ( nDivergenceIndex <= nCurrentStatePathIndex )
426  // they differ in an index which we have already left behind us
427  // -> this is no conflict anymore
428  continue;
429 
430  // the path conflicts with our new path -> don't activate the
431  // *complete* new path, but only up to the step which is unambiguous
432  nUpperStepBoundary = nDivergenceIndex;
433  }
434  }
435 
436  // can we advance from the current page?
437  bool bCurrentPageCanAdvance = true;
438  TabPage* pCurrentPage = GetPage( getCurrentState() );
439  if ( pCurrentPage )
440  {
441  const IWizardPageController* pController = getPageController( GetPage( getCurrentState() ) );
442  OSL_ENSURE( pController != nullptr, "RoadmapWizard::implUpdateRoadmap: no controller for the current page!" );
443  bCurrentPageCanAdvance = !pController || pController->canAdvance();
444  }
445 
446  // now, we have to remove all items after nCurrentStatePathIndex, and insert the items from the active
447  // path, up to (excluding) nUpperStepBoundary
448  RoadmapTypes::ItemIndex nRoadmapItems = m_xAssistant->get_n_pages();
449  RoadmapTypes::ItemIndex nLoopUntil = ::std::max( nUpperStepBoundary, nRoadmapItems );
450  for ( RoadmapTypes::ItemIndex nItemIndex = nCurrentStatePathIndex; nItemIndex < nLoopUntil; ++nItemIndex )
451  {
452  bool bExistentItem = ( nItemIndex < nRoadmapItems );
453  bool bNeedItem = ( nItemIndex < nUpperStepBoundary );
454 
455  bool bInsertItem = false;
456  if ( bExistentItem )
457  {
458  if ( !bNeedItem )
459  {
460  int nPages = nRoadmapItems;
461  for (int i = nPages - 1; i >= nItemIndex; --i)
462  {
463  m_xAssistant->set_page_title(m_xAssistant->get_page_ident(i), "");
464  --nRoadmapItems;
465  }
466  break;
467  }
468  else
469  {
470  // there is an item with this index in the roadmap - does it match what is requested by
471  // the respective state in the active path?
472  RoadmapTypes::ItemId nPresentItemId = m_xAssistant->get_page_ident(nItemIndex).toInt32();
473  WizardState nRequiredState = rActivePath[ nItemIndex ];
474  if ( nPresentItemId != nRequiredState )
475  {
476  m_xAssistant->set_page_title(OString::number(nPresentItemId), "");
477  bInsertItem = true;
478  }
479  }
480  }
481  else
482  {
483  DBG_ASSERT( bNeedItem, "RoadmapWizard::implUpdateRoadmap: ehm - none needed, none present - why did the loop not terminate?" );
484  bInsertItem = bNeedItem;
485  }
486 
487  WizardState nState( rActivePath[ nItemIndex ] );
488 
489  if ( bInsertItem )
490  {
491  GetOrCreatePage(nState);
492  }
493 
494  OString sIdent(OString::number(nState));
495  m_xAssistant->set_page_index(sIdent, nItemIndex);
496  m_xAssistant->set_page_title(sIdent, getStateDisplayName(nState));
497 
498  // if the item is *after* the current state, but the current page does not
499  // allow advancing, the disable the state. This relieves derived classes
500  // from disabling all future states just because the current state does not
501  // (yet) allow advancing.
502  const bool bUnconditionedDisable = !bCurrentPageCanAdvance && ( nItemIndex > nCurrentStatePathIndex );
503  const bool bEnable = !bUnconditionedDisable && ( m_pImpl->aDisabledStates.find( nState ) == m_pImpl->aDisabledStates.end() );
504  m_xAssistant->set_page_sensitive(sIdent, bEnable);
505  }
506  }
507 
509  {
510 
511  sal_Int32 nCurrentStatePathIndex = -1;
512 
513  Paths::const_iterator aActivePathPos = m_pImpl->aPaths.find( m_pImpl->nActivePath );
514  if ( aActivePathPos != m_pImpl->aPaths.end() )
515  nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( _nCurrentState, aActivePathPos->second );
516 
517  DBG_ASSERT( nCurrentStatePathIndex != -1, "RoadmapWizard::determineNextState: ehm - how can we travel if there is no (valid) active path?" );
518  if ( nCurrentStatePathIndex == -1 )
519  return WZS_INVALID_STATE;
520 
521  sal_Int32 nNextStateIndex = nCurrentStatePathIndex + 1;
522 
523  while ( ( nNextStateIndex < static_cast<sal_Int32>(aActivePathPos->second.size()) )
524  && ( m_pImpl->aDisabledStates.find( aActivePathPos->second[ nNextStateIndex ] ) != m_pImpl->aDisabledStates.end() )
525  )
526  {
527  ++nNextStateIndex;
528  }
529 
530  if ( nNextStateIndex >= static_cast<sal_Int32>(aActivePathPos->second.size()) )
531  // there is no next state in the current path (at least none which is enabled)
532  return WZS_INVALID_STATE;
533 
534  return aActivePathPos->second[ nNextStateIndex ];
535  }
536 
538  {
539  sal_Int32 nCurrentStatePathIndex = -1;
540 
541  Paths::const_iterator aActivePathPos = m_pImpl->aPaths.find( m_pImpl->nActivePath );
542  if ( aActivePathPos != m_pImpl->aPaths.end() )
543  nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( _nCurrentState, aActivePathPos->second );
544 
545  DBG_ASSERT( nCurrentStatePathIndex != -1, "RoadmapWizard::determineNextState: ehm - how can we travel if there is no (valid) active path?" );
546  if ( nCurrentStatePathIndex == -1 )
547  return WZS_INVALID_STATE;
548 
549  sal_Int32 nNextStateIndex = nCurrentStatePathIndex + 1;
550 
551  while ( ( nNextStateIndex < static_cast<sal_Int32>(aActivePathPos->second.size()) )
552  && ( m_pImpl->aDisabledStates.find( aActivePathPos->second[ nNextStateIndex ] ) != m_pImpl->aDisabledStates.end() )
553  )
554  {
555  ++nNextStateIndex;
556  }
557 
558  if ( nNextStateIndex >= static_cast<sal_Int32>(aActivePathPos->second.size()) )
559  // there is no next state in the current path (at least none which is enabled)
560  return WZS_INVALID_STATE;
561 
562  return aActivePathPos->second[ nNextStateIndex ];
563  }
564 
566  {
567  if ( !m_pImpl->bActivePathIsDefinite )
568  {
569  // check how many paths are still allowed
570  const WizardPath& rActivePath( m_pImpl->aPaths[ m_pImpl->nActivePath ] );
571  sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
572 
573  size_t nPossiblePaths(0);
574  for (auto const& path : m_pImpl->aPaths)
575  {
576  // the index from which on both paths differ
577  sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
578 
579  if ( nDivergenceIndex > nCurrentStatePathIndex )
580  // this path is still a possible path
581  nPossiblePaths += 1;
582  }
583 
584  // if we have more than one path which is still possible, then we assume
585  // to always have a next state. Though there might be scenarios where this
586  // is not true, but this is too sophisticated (means not really needed) right now.
587  if ( nPossiblePaths > 1 )
588  return true;
589  }
590 
591  const WizardPath& rPath = m_pImpl->aPaths[ m_pImpl->nActivePath ];
592  return *rPath.rbegin() != getCurrentState();
593  }
594 
596  {
597  if ( !m_pImpl->bActivePathIsDefinite )
598  {
599  // check how many paths are still allowed
600  const WizardPath& rActivePath( m_pImpl->aPaths[ m_pImpl->nActivePath ] );
601  sal_Int32 nCurrentStatePathIndex = RoadmapWizardImpl::getStateIndexInPath( getCurrentState(), rActivePath );
602 
603  size_t nPossiblePaths(0);
604  for (auto const& path : m_pImpl->aPaths)
605  {
606  // the index from which on both paths differ
607  sal_Int32 nDivergenceIndex = RoadmapWizardImpl::getFirstDifferentIndex( rActivePath, path.second );
608 
609  if ( nDivergenceIndex > nCurrentStatePathIndex )
610  // this path is still a possible path
611  nPossiblePaths += 1;
612  }
613 
614  // if we have more than one path which is still possible, then we assume
615  // to always have a next state. Though there might be scenarios where this
616  // is not true, but this is too sophisticated (means not really needed) right now.
617  if ( nPossiblePaths > 1 )
618  return true;
619  }
620 
621  const WizardPath& rPath = m_pImpl->aPaths[ m_pImpl->nActivePath ];
622  return *rPath.rbegin() != getCurrentState();
623  }
624 
626  {
628 
629  // disable the "Previous" button if all states in our history are disabled
630  ::std::vector< WizardState > aHistory;
631  getStateHistory( aHistory );
632  bool bHaveEnabledState = false;
633  for (auto const& state : aHistory)
634  {
635  if ( isStateEnabled(state) )
636  {
637  bHaveEnabledState = true;
638  break;
639  }
640  }
641 
642  enableButtons( WizardButtonFlags::PREVIOUS, bHaveEnabledState );
643 
645  }
646 
648  {
650 
651  // disable the "Previous" button if all states in our history are disabled
652  ::std::vector< WizardState > aHistory;
653  getStateHistory( aHistory );
654  bool bHaveEnabledState = false;
655  for (auto const& state : aHistory)
656  {
657  if ( isStateEnabled(state) )
658  {
659  bHaveEnabledState = true;
660  break;
661  }
662  }
663 
664  enableButtons( WizardButtonFlags::PREVIOUS, bHaveEnabledState );
665 
667  }
668 
669  IMPL_LINK_NOARG(RoadmapWizard, OnRoadmapItemSelected, LinkParamNone*, void)
670  {
671 
672  RoadmapTypes::ItemId nCurItemId = m_pImpl->pRoadmap->GetCurrentRoadmapItemID();
673  if ( nCurItemId == getCurrentState() )
674  // nothing to do
675  return;
676 
677  if ( isTravelingSuspended() )
678  return;
679 
680  WizardTravelSuspension aTravelGuard( *this );
681 
682  sal_Int32 nCurrentIndex = m_pImpl->getStateIndexInPath( getCurrentState(), m_pImpl->nActivePath );
683  sal_Int32 nNewIndex = m_pImpl->getStateIndexInPath( nCurItemId, m_pImpl->nActivePath );
684 
685  DBG_ASSERT( ( nCurrentIndex != -1 ) && ( nNewIndex != -1 ),
686  "RoadmapWizard::OnRoadmapItemSelected: something's wrong here!" );
687  if ( ( nCurrentIndex == -1 ) || ( nNewIndex == -1 ) )
688  {
689  return;
690  }
691 
692  bool bResult = true;
693  if ( nNewIndex > nCurrentIndex )
694  {
695  bResult = skipUntil( static_cast<WizardState>(nCurItemId) );
696  WizardState nTemp = static_cast<WizardState>(nCurItemId);
697  while( nTemp )
698  {
699  if( m_pImpl->aDisabledStates.find( --nTemp ) != m_pImpl->aDisabledStates.end() )
700  removePageFromHistory( nTemp );
701  }
702  }
703  else
704  bResult = skipBackwardUntil( static_cast<WizardState>(nCurItemId) );
705 
706  if ( !bResult )
707  m_pImpl->pRoadmap->SelectRoadmapItemByID( getCurrentState() );
708  }
709 
710  IMPL_LINK(RoadmapWizardMachine, OnRoadmapItemSelected, const OString&, rCurItemId, bool)
711  {
712  int nCurItemId = rCurItemId.toInt32();
713 
714  if ( nCurItemId == getCurrentState() )
715  // nothing to do
716  return false;
717 
718  if ( isTravelingSuspended() )
719  return false;
720 
721  WizardTravelSuspension aTravelGuard( *this );
722 
723  sal_Int32 nCurrentIndex = m_pImpl->getStateIndexInPath( getCurrentState(), m_pImpl->nActivePath );
724  sal_Int32 nNewIndex = m_pImpl->getStateIndexInPath( nCurItemId, m_pImpl->nActivePath );
725 
726  DBG_ASSERT( ( nCurrentIndex != -1 ) && ( nNewIndex != -1 ),
727  "RoadmapWizard::OnRoadmapItemSelected: something's wrong here!" );
728  if ( ( nCurrentIndex == -1 ) || ( nNewIndex == -1 ) )
729  {
730  return false;
731  }
732 
733  bool bResult = true;
734  if ( nNewIndex > nCurrentIndex )
735  {
736  bResult = skipUntil( static_cast<WizardState>(nCurItemId) );
737  WizardState nTemp = static_cast<WizardState>(nCurItemId);
738  while( nTemp )
739  {
740  if( m_pImpl->aDisabledStates.find( --nTemp ) != m_pImpl->aDisabledStates.end() )
741  removePageFromHistory( nTemp );
742  }
743  }
744  else
745  bResult = skipBackwardUntil( static_cast<WizardState>(nCurItemId) );
746 
747  return bResult;
748  }
749 
751  {
752  OWizardMachine::enterState( _nState );
753 
754  // synchronize the roadmap
756  m_pImpl->pRoadmap->SelectRoadmapItemByID( getCurrentState() );
757  }
758 
760  {
761  WizardMachine::enterState( _nState );
762 
763  // synchronize the roadmap
765  }
766 
768  {
769  OUString sDisplayName;
770 
771  StateDescriptions::const_iterator pos = m_pImpl->aStateDescriptors.find( _nState );
772  OSL_ENSURE( pos != m_pImpl->aStateDescriptors.end(),
773  "RoadmapWizard::getStateDisplayName: no default implementation available for this state!" );
774  if ( pos != m_pImpl->aStateDescriptors.end() )
775  sDisplayName = pos->second.first;
776 
777  return sDisplayName;
778  }
779 
781  {
782  OUString sDisplayName;
783 
784  StateDescriptions::const_iterator pos = m_pImpl->aStateDescriptors.find( _nState );
785  OSL_ENSURE( pos != m_pImpl->aStateDescriptors.end(),
786  "RoadmapWizard::getStateDisplayName: no default implementation available for this state!" );
787  if ( pos != m_pImpl->aStateDescriptors.end() )
788  sDisplayName = pos->second.first;
789 
790  return sDisplayName;
791  }
792 
794  {
795  VclPtr<TabPage> pPage;
796 
797  StateDescriptions::const_iterator pos = m_pImpl->aStateDescriptors.find( _nState );
798  OSL_ENSURE( pos != m_pImpl->aStateDescriptors.end(),
799  "RoadmapWizard::createPage: no default implementation available for this state!" );
800  if ( pos != m_pImpl->aStateDescriptors.end() )
801  {
802  RoadmapPageFactory pFactory = pos->second.second;
803  pPage = (*pFactory)( *this );
804  }
805 
806  return pPage;
807  }
808 
809  void RoadmapWizard::enableState( WizardState _nState, bool _bEnable )
810  {
811 
812  // remember this (in case the state appears in the roadmap later on)
813  if ( _bEnable )
814  m_pImpl->aDisabledStates.erase( _nState );
815  else
816  {
817  m_pImpl->aDisabledStates.insert( _nState );
818  removePageFromHistory( _nState );
819  }
820 
821  // if the state is currently in the roadmap, reflect it's new status
822  m_pImpl->pRoadmap->EnableRoadmapItem( static_cast<RoadmapTypes::ItemId>(_nState), _bEnable );
823  }
824 
825  void RoadmapWizardMachine::enableState( WizardState _nState, bool _bEnable )
826  {
827  // remember this (in case the state appears in the roadmap later on)
828  if ( _bEnable )
829  m_pImpl->aDisabledStates.erase( _nState );
830  else
831  {
832  m_pImpl->aDisabledStates.insert( _nState );
833  removePageFromHistory( _nState );
834  }
835 
836  // if the state is currently in the roadmap, reflect it's new status
837  m_xAssistant->set_page_sensitive(OString::number(_nState), _bEnable);
838  }
839 
840  bool RoadmapWizard::knowsState( WizardState i_nState ) const
841  {
842  for (auto const& path : m_pImpl->aPaths)
843  {
844  for (auto const& state : path.second)
845  {
846  if ( state == i_nState )
847  return true;
848  }
849  }
850  return false;
851  }
852 
854  {
855  return m_pImpl->aDisabledStates.find( _nState ) == m_pImpl->aDisabledStates.end();
856  }
857 
859  {
860  return m_pImpl->aDisabledStates.find( _nState ) == m_pImpl->aDisabledStates.end();
861  }
862 
863  void RoadmapWizard::InsertRoadmapItem(int nItemIndex, const OUString& rText, int nItemId, bool bEnable)
864  {
865  m_pImpl->pRoadmap->InsertRoadmapItem(nItemIndex, rText, nItemId, bEnable);
866  }
867 
869  {
870  m_pImpl->pRoadmap->SelectRoadmapItemByID(nItemId);
871  }
872 
874  {
875  while (m_pImpl->pRoadmap->GetItemCount())
876  m_pImpl->pRoadmap->DeleteRoadmapItem(0);
877  }
878 
880  {
881  m_pImpl->pRoadmap->SetItemSelectHdl(_rHdl);
882  }
883 
885  {
886  return m_pImpl->pRoadmap->GetCurrentRoadmapItemID();
887  }
888 
889 } // namespace vcl
890 
891 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual VclPtr< TabPage > createPage(WizardState _nState) override
creates a page for a given state
sal_Int32 ItemIndex
Definition: roadmap.hxx:36
is - no, not a wizard for a roadmap, but the base class for wizards supporting a roadmap.
void SetRoadmapInteractive(bool _bInteractive)
virtual void dispose() override
This is intended to be used to clear any locally held references to other Window-subclass objects...
void SetEmptyViewMargin()
declares the view area to have an empty margin
Definition: wizdlg.cxx:72
VCL_DLLPRIVATE void implUpdateRoadmap()
updates the roadmap control to show the given path, as far as possible (modulo conflicts with other p...
void ShowRoadmap(bool bShow)
virtual void updateTravelUI() override
updates the user interface which deals with traveling in the wizard
A thin wrapper around rtl::Reference to implement the acquire and dispose semantics we want for refer...
Definition: button.hxx:32
RoadmapWizard(vcl::Window *_pParent)
bool isStateEnabled(WizardState _nState) const
virtual ~RoadmapWizardMachine() override
virtual void updateTravelUI() override
updates the user interface which deals with traveling in the wizard
virtual void enterState(WizardState _nState)
will be called when a new page is about to be displayed
void enableButtons(WizardButtonFlags _nWizardButtonFlags, bool _bEnable)
enable (or disable) buttons
TabPage * GetOrCreatePage(const WizardState i_nState)
void SetViewWindow(vcl::Window *pWindow)
Definition: wizdlg.hxx:251
virtual Size GetSizePixel() const
Definition: window.cxx:2364
void SetItemSelectHdl(const Link< LinkParamNone *, void > &_rHdl)
void activatePath(PathId _nPathId, bool _bDecideForIt=false)
activates a path which has previously been declared with declarePath ...
bool knowsState(WizardState _nState) const
returns true if and only if the given state is known in at least one declared path ...
WizardButtonFlags
Definition: vclenum.hxx:276
static sal_Int32 getStateIndexInPath(WizardTypes::WizardState _nState, const WizardPath &_rPath)
returns the index of the current state in given path, or -1
virtual IWizardPageController * getPageController(TabPage *_pCurrentPage) const
void CalcAndSetSize()
Definition: wizdlg.cxx:377
virtual void enterState(WizardState _nState)
will be called when a new page is about to be displayed
PREVIOUS
virtual WizardState determineNextState(WizardState _nCurrentState) const override
determine the next state to travel from the given one
#define WZS_INVALID_STATE
virtual void dispose() override
This is intended to be used to clear any locally held references to other Window-subclass objects...
virtual void enterState(WizardState _nState) override
will be called when a new page is about to be displayed
std::unique_ptr< RoadmapWizardImpl > m_pImpl
ScopedVclPtr< ORoadmap > pRoadmap
virtual bool canAdvance() const override
determines whether there is a next state to which we can advance
virtual bool canAdvance() const override
determines whether there is a next state to which we can advance
void SelectRoadmapItemByID(int nId)
virtual WizardState determineNextState(WizardState _nCurrentState) const override
determine the next state to travel from the given one
virtual IWizardPageController * getPageController(TabPage *_pCurrentPage) const
#define DBG_ASSERT(sCon, aError)
std::unique_ptr< RoadmapWizardImpl > m_pImpl
virtual bool canAdvance() const =0
determines whether or not it is allowed to advance to a next page
int i
void SetRoadmapHelpId(const OString &_rId)
bool isStateEnabled(WizardState _nState) const
void enableState(WizardState _nState, bool _bEnable=true)
en- or disables a state
void SetLeftAlignedButtonCount(sal_Int16 _nCount)
sets the number of buttons which should be left-aligned.
Definition: wizdlg.cxx:66
virtual void updateTravelUI()
updates the user interface which deals with traveling in the wizard
static sal_Int32 getFirstDifferentIndex(const WizardPath &_rLHS, const WizardPath &_rRHS)
returns the index of the first state in which the two given paths differ
VCL_DLLPRIVATE void implUpdateRoadmap()
updates the roadmap control to show the given path, as far as possible (modulo conflicts with other p...
sal_Int16 ItemId
Definition: roadmap.hxx:35
void declarePath(PathId _nPathId, const WizardPath &_lWizardStates)
declares a valid path in the wizard
Point LogicToPixel(const Point &rLogicPt) const
Definition: map.cxx:940
virtual OUString getStateDisplayName(WizardState _nState) const
returns a human readable name for a given state
NEXT
WizardState getCurrentState() const
returns the current state of the machine
void removePageFromHistory(WizardState nToRemove)
removes a page from the history.
void SetRoadmapHelpId(const OString &_rId)
VCL_DLLPRIVATE void impl_construct()
WizardState getCurrentState() const
returns the current state of the machine
void getStateHistory(::std::vector< WizardState > &_out_rHistory)
retrieves a copy of the state history, i.e.
sal_Int16 WizardState
TabPage * GetPage(sal_uInt16 nLevel) const
Definition: wizdlg.cxx:588
TabPage * GetPage(WizardState eState) const
StateDescriptions aStateDescriptors
void SetViewAlign(WindowAlign eAlign)
Definition: wizdlg.hxx:252
void enableButtons(WizardButtonFlags _nWizardButtonFlags, bool _bEnable)
enable (or disable) buttons
WinBits const WB_TABSTOP
void removePageFromHistory(WizardState nToRemove)
removes a page from the history.
int GetCurrentRoadmapItemID() const
IMPL_LINK(ORoadmap, ImplClickHdl, HyperLabel *, CurHyperLabel, void)
Definition: roadmap.cxx:625
virtual OUString getStateDisplayName(WizardState _nState) const
returns a human readable name for a given state
void activatePath(PathId _nPathId, bool _bDecideForIt=false)
activates a path which has previously been declared with declarePath ...
OUString VclResId(const char *pId)
Definition: svdata.cxx:257
implements some kind of finite automata, where the states of the automata exactly correlate with tab ...
virtual void enterState(WizardState _nState) override
will be called when a new page is about to be displayed
virtual void updateTravelUI()
updates the user interface which deals with traveling in the wizard
void InsertRoadmapItem(int nIndex, const OUString &rLabel, int nId, bool bEnabled)
RoadmapWizardMachine(weld::Window *_pParent)
IMPL_LINK_NOARG(QuickSelectionEngine_Data, SearchStringTimeout, Timer *, void)
std::unique_ptr< weld::Assistant > m_xAssistant
Definition: weld.hxx:1947
::std::vector< vcl::WizardTypes::WizardState > WizardPath
VclPtr< TabPage >(* RoadmapPageFactory)(RoadmapWizard &)
void declarePath(PathId _nPathId, const WizardPath &_lWizardStates)
declares a valid path in the wizard
void enableState(WizardState _nState, bool _bEnable=true)
en- or disables a state
void getStateHistory(::std::vector< WizardState > &_out_rHistory)
retrieves a copy of the state history, i.e.
helper class to temporarily suspend any traveling in the wizard
virtual ~RoadmapWizard() override
OUString sDisplayName
void setHeight(long nHeight)