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