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