LibreOffice Module vcl (master)  1
wizardmachine.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 #include <vcl/wizardmachine.hxx>
21 #include <tools/debug.hxx>
22 #include <tools/diagnose_ex.h>
23 #include <vcl/svapp.hxx>
24 #include <strings.hrc>
25 #include <svdata.hxx>
26 #include <stack>
27 
28 #define HID_WIZARD_NEXT "SVT_HID_WIZARD_NEXT"
29 #define HID_WIZARD_PREVIOUS "SVT_HID_WIZARD_PREVIOUS"
30 
32 {
35 };
36 
37 namespace vcl
38 {
39  //= WizardPageImplData
40  OWizardPage::OWizardPage(vcl::Window *pParent, const OString& rID,
41  const OUString& rUIXMLDescription)
42  : TabPage(pParent, rID, rUIXMLDescription)
43  {
44  }
45 
46  OWizardPage::OWizardPage(TabPageParent pParent, const OUString& rUIXMLDescription, const OString& rID)
47  : TabPage(pParent.pPage ? Application::GetDefDialogParent() : pParent.pParent.get()) //just drag this along hidden in this scenario
48  , m_xBuilder(pParent.pPage ? Application::CreateBuilder(pParent.pPage, rUIXMLDescription)
49  : Application::CreateInterimBuilder(this, rUIXMLDescription))
50  , m_xContainer(m_xBuilder->weld_container(rID))
51  {
52  }
53 
55  {
56  disposeOnce();
57  }
58 
60  {
61  m_xBuilder.reset();
63  }
64 
66  {
67  }
68 
70  {
73  }
74 
76  {
77  OWizardMachine* pWizardMachine = dynamic_cast< OWizardMachine* >( GetParent() );
78  if ( pWizardMachine )
79  pWizardMachine->updateTravelUI();
80  }
81 
83  {
84  return true;
85  }
86 
88  {
89  return true;
90  }
91 
93  {
94  OUString sTitleBase; // the base for the title
95  ::std::stack< WizardState > aStateHistory; // the history of all states (used for implementing "Back")
96 
98  // the WizardDialog does not allow non-linear transitions (e.g. it's
99  // not possible to add pages in a non-linear order), so we need some own maintenance data
100 
102 
104 
106  :nFirstUnknownPage( 0 )
107  ,m_bAutoNextButtonState( false )
108  ,m_bTravelingSuspended( false )
109  {
110  }
111  };
112 
114  :WizardDialog( _pParent, "WizardDialog", "svt/ui/wizarddialog.ui" )
115  ,m_pFinish(nullptr)
116  ,m_pCancel(nullptr)
117  ,m_pNextPage(nullptr)
118  ,m_pPrevPage(nullptr)
119  ,m_pHelp(nullptr)
120  ,m_pImpl( new WizardMachineImplData )
121  {
122  implConstruct( _nButtonFlags );
123  }
124 
125 
127  {
128  m_pImpl->sTitleBase = GetText();
129 
130  // create the buttons according to the wizard button flags
131  // the help button
132  if (_nButtonFlags & WizardButtonFlags::HELP)
133  {
135  m_pHelp->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont)));
136  m_pHelp->Show();
138  }
139 
140  // the previous button
141  if (_nButtonFlags & WizardButtonFlags::PREVIOUS)
142  {
145  m_pPrevPage->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont)));
146  m_pPrevPage->SetText(VclResId(STR_WIZDLG_PREVIOUS));
147  m_pPrevPage->Show();
148  m_pPrevPage->set_id("previous");
149 
150  if (_nButtonFlags & WizardButtonFlags::NEXT)
151  AddButton( m_pPrevPage, ( WIZARDDIALOG_BUTTON_SMALLSTDOFFSET_X) ); // half x-offset to the next button
152  else
155  m_pPrevPage->SetClickHdl( LINK( this, OWizardMachine, OnPrevPage ) );
156  }
157 
158  // the next button
159  if (_nButtonFlags & WizardButtonFlags::NEXT)
160  {
163  m_pNextPage->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont)));
164  m_pNextPage->SetText(VclResId(STR_WIZDLG_NEXT));
165  m_pNextPage->Show();
166  m_pNextPage->set_id("next");
167 
170  m_pNextPage->SetClickHdl( LINK( this, OWizardMachine, OnNextPage ) );
171  }
172 
173  // the finish button
174  if (_nButtonFlags & WizardButtonFlags::FINISH)
175  {
177  m_pFinish->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont)));
178  m_pFinish->SetText(VclResId(STR_WIZDLG_FINISH));
179  m_pFinish->Show();
180  m_pFinish->set_id("finish");
181 
183  m_pFinish->SetClickHdl( LINK( this, OWizardMachine, OnFinish ) );
184  }
185 
186  // the cancel button
187  if (_nButtonFlags & WizardButtonFlags::CANCEL)
188  {
190  m_pCancel->SetSizePixel(LogicToPixel(Size(50, 14), MapMode(MapUnit::MapAppFont)));
191  m_pCancel->Show();
192 
194  }
195  }
196 
197 
199  {
200  disposeOnce();
201  }
202 
204  {
210 
211  if (m_pImpl)
212  {
213  for (WizardState i = 0; i < m_pImpl->nFirstUnknownPage; ++i)
214  {
215  TabPage *pPage = GetPage(i);
216  if (pPage)
217  pPage->disposeOnce();
218  }
219  m_pImpl.reset();
220  }
221 
223  }
224 
225 
227  {
228  OUString sCompleteTitle(m_pImpl->sTitleBase);
229 
230  // append the page title
231  TabPage* pCurrentPage = GetPage(getCurrentState());
232  if ( pCurrentPage && !pCurrentPage->GetText().isEmpty() )
233  {
234  sCompleteTitle += " - " + pCurrentPage->GetText();
235  }
236 
237  SetText(sCompleteTitle);
238  }
239 
240 
241  void OWizardMachine::setTitleBase(const OUString& _rTitleBase)
242  {
243  m_pImpl->sTitleBase = _rTitleBase;
244  implUpdateTitle();
245  }
246 
247 
249  {
250  if ( nullptr == GetPage( i_nState ) )
251  {
252  VclPtr<TabPage> pNewPage = createPage( i_nState );
253  DBG_ASSERT( pNewPage, "OWizardMachine::GetOrCreatePage: invalid new page (NULL)!" );
254 
255  // fill up the page sequence of our base class (with dummies)
256  while ( m_pImpl->nFirstUnknownPage < i_nState )
257  {
258  AddPage( nullptr );
259  ++m_pImpl->nFirstUnknownPage;
260  }
261 
262  if ( m_pImpl->nFirstUnknownPage == i_nState )
263  {
264  // encountered this page number the first time
265  AddPage( pNewPage );
266  ++m_pImpl->nFirstUnknownPage;
267  }
268  else
269  // already had this page - just change it
270  SetPage( i_nState, pNewPage );
271  }
272  return GetPage( i_nState );
273  }
274 
275 
277  {
279 
280  WizardState nCurrentLevel = GetCurLevel();
281  GetOrCreatePage( nCurrentLevel );
282 
283  enterState( nCurrentLevel );
284  }
285 
286 
288  {
289  WizardState nCurrentState = getCurrentState();
290  return leaveState(nCurrentState) && WizardDialog::DeactivatePage();
291  }
292 
293 
295  {
296  // the new default button
297  PushButton* pNewDefButton = nullptr;
298  if (m_pFinish && (_nWizardButtonFlags & WizardButtonFlags::FINISH))
299  pNewDefButton = m_pFinish;
300  if (m_pNextPage && (_nWizardButtonFlags & WizardButtonFlags::NEXT))
301  pNewDefButton = m_pNextPage;
302  if (m_pPrevPage && (_nWizardButtonFlags & WizardButtonFlags::PREVIOUS))
303  pNewDefButton = m_pPrevPage;
304  if (m_pHelp && (_nWizardButtonFlags & WizardButtonFlags::HELP))
305  pNewDefButton = m_pHelp;
306  if (m_pCancel && (_nWizardButtonFlags & WizardButtonFlags::CANCEL))
307  pNewDefButton = m_pCancel;
308 
309  if ( pNewDefButton )
310  defaultButton( pNewDefButton );
311  else
312  implResetDefault( this );
313  }
314 
315 
317  {
318  vcl::Window* pChildLoop = _pWindow->GetWindow(GetWindowType::FirstChild);
319  while (pChildLoop)
320  {
321  // does the window participate in the tabbing order?
322  if (pChildLoop->GetStyle() & WB_DIALOGCONTROL)
323  implResetDefault(pChildLoop);
324 
325  // is it a button?
326  WindowType eType = pChildLoop->GetType();
327  if ( (WindowType::PUSHBUTTON == eType)
328  || (WindowType::OKBUTTON == eType)
329  || (WindowType::CANCELBUTTON == eType)
330  || (WindowType::HELPBUTTON == eType)
331  || (WindowType::IMAGEBUTTON == eType)
332  || (WindowType::MENUBUTTON == eType)
333  || (WindowType::MOREBUTTON == eType)
334  )
335  {
336  pChildLoop->SetStyle(pChildLoop->GetStyle() & ~WB_DEFBUTTON);
337  }
338 
339  // the next one ...
340  pChildLoop = pChildLoop->GetWindow(GetWindowType::Next);
341  }
342  }
343 
344 
346  {
347  // loop through all (direct and indirect) descendants which participate in our tabbing order, and
348  // reset the WB_DEFBUTTON for every window which is a button
349  implResetDefault(this);
350 
351  // set its new style
352  if (_pNewDefButton)
353  _pNewDefButton->SetStyle(_pNewDefButton->GetStyle() | WB_DEFBUTTON);
354  }
355 
356 
357  void OWizardMachine::enableButtons(WizardButtonFlags _nWizardButtonFlags, bool _bEnable)
358  {
359  if (m_pFinish && (_nWizardButtonFlags & WizardButtonFlags::FINISH))
360  m_pFinish->Enable(_bEnable);
361  if (m_pNextPage && (_nWizardButtonFlags & WizardButtonFlags::NEXT))
362  m_pNextPage->Enable(_bEnable);
363  if (m_pPrevPage && (_nWizardButtonFlags & WizardButtonFlags::PREVIOUS))
364  m_pPrevPage->Enable(_bEnable);
365  if (m_pHelp && (_nWizardButtonFlags & WizardButtonFlags::HELP))
366  m_pHelp->Enable(_bEnable);
367  if (m_pCancel && (_nWizardButtonFlags & WizardButtonFlags::CANCEL))
368  m_pCancel->Enable(_bEnable);
369  }
370 
372  {
373  // tell the page
374  IWizardPageController* pController = getPageController( GetPage( _nState ) );
375  if (!pController)
376  return;
377  pController->initializePage();
378 
381 
382  enableButtons( WizardButtonFlags::PREVIOUS, !m_pImpl->aStateHistory.empty() );
383 
384  // set the new title - it depends on the current page (i.e. state)
385  implUpdateTitle();
386  }
387 
389  {
390  // no need to ask the page here.
391  // If we reach this point, we already gave the current page the chance to commit it's data,
392  // and it was allowed to commit it's data
393 
394  return true;
395  }
396 
397 
399  {
400  return Finish( RET_OK );
401  }
402 
403 
405  {
406  if ( isTravelingSuspended() )
407  return;
408  WizardTravelSuspension aTravelGuard( *this );
409  if ( !prepareLeaveCurrentState( eFinish ) )
410  {
411  return;
412  }
413  onFinish();
414  }
415 
416 
418  {
419  return _nCurrentState + 1;
420  }
421 
422 
424  {
426  ENSURE_OR_RETURN( pController != nullptr, "OWizardMachine::prepareLeaveCurrentState: no controller for the current page!", true );
427  return pController->commitPage( _eReason );
428  }
429 
430 
432  {
433  // allowed to leave the current page?
435  return false;
436 
437  // don't travel directly on m_pImpl->aStateHistory, in case something goes wrong
438  ::std::stack< WizardState > aTravelVirtually = m_pImpl->aStateHistory;
439  ::std::stack< WizardState > aOldStateHistory = m_pImpl->aStateHistory;
440 
441  WizardState nCurrentRollbackState = getCurrentState();
442  while ( nCurrentRollbackState != _nTargetState )
443  {
444  DBG_ASSERT( !aTravelVirtually.empty(), "OWizardMachine::skipBackwardUntil: this target state does not exist in the history!" );
445  nCurrentRollbackState = aTravelVirtually.top();
446  aTravelVirtually.pop();
447  }
448  m_pImpl->aStateHistory = aTravelVirtually;
449  if ( !ShowPage( _nTargetState ) )
450  {
451  m_pImpl->aStateHistory = aOldStateHistory;
452  return false;
453  }
454  return true;
455  }
456 
457 
459  {
460  WizardState nCurrentState = getCurrentState();
461 
462  // allowed to leave the current page?
463  if ( !prepareLeaveCurrentState( nCurrentState < _nTargetState ? eTravelForward : eTravelBackward ) )
464  return false;
465 
466  // don't travel directly on m_pImpl->aStateHistory, in case something goes wrong
467  ::std::stack< WizardState > aTravelVirtually = m_pImpl->aStateHistory;
468  ::std::stack< WizardState > aOldStateHistory = m_pImpl->aStateHistory;
469  while ( nCurrentState != _nTargetState )
470  {
471  WizardState nNextState = determineNextState( nCurrentState );
472  if ( WZS_INVALID_STATE == nNextState )
473  {
474  OSL_FAIL( "OWizardMachine::skipUntil: the given target state does not exist!" );
475  return false;
476  }
477 
478  // remember the skipped state in the history
479  aTravelVirtually.push( nCurrentState );
480 
481  // get the next state
482  nCurrentState = nNextState;
483  }
484  m_pImpl->aStateHistory = aTravelVirtually;
485  // show the target page
486  if ( !ShowPage( nCurrentState ) )
487  {
488  // argh! prepareLeaveCurrentPage succeeded, determineNextState succeeded,
489  // but ShowPage doesn't? Somebody behaves very strange here...
490  OSL_FAIL( "OWizardMachine::skipUntil: very unpolite..." );
491  m_pImpl->aStateHistory = aOldStateHistory;
492  return false;
493  }
494  return true;
495  }
496 
497 
499  {
500  // allowed to leave the current page?
502  return;
503 
504  WizardState nCurrentState = getCurrentState();
505  WizardState nNextState = determineNextState(nCurrentState);
506 
507  if (WZS_INVALID_STATE == nNextState)
508  return;
509 
510  // remember the skipped state in the history
511  m_pImpl->aStateHistory.push(nCurrentState);
512 
513  // get the next state
514  nCurrentState = nNextState;
515 
516  // show the (n+1)th page
517  if (!ShowPage(nCurrentState))
518  {
519  // TODO: this leaves us in a state where we have no current page and an inconsistent state history.
520  // Perhaps we should rollback the skipping here...
521  OSL_FAIL("OWizardMachine::skip: very unpolite...");
522  // if somebody does a skip and then does not allow to leave...
523  // (can't be a commit error, as we've already committed the current page. So if ShowPage fails here,
524  // somebody behaves really strange...)
525  return;
526  }
527 
528  // all fine
529  }
530 
532  {
533  // allowed to leave the current page?
535  return false;
536 
537  // determine the next state to travel to
538  WizardState nCurrentState = getCurrentState();
539  WizardState nNextState = determineNextState(nCurrentState);
540  if (WZS_INVALID_STATE == nNextState)
541  return false;
542 
543  // the state history is used by the enterState method
544  // all fine
545  m_pImpl->aStateHistory.push(nCurrentState);
546  if (!ShowPage(nNextState))
547  {
548  m_pImpl->aStateHistory.pop();
549  return false;
550  }
551 
552  return true;
553  }
554 
555 
557  {
558  DBG_ASSERT(!m_pImpl->aStateHistory.empty(), "OWizardMachine::travelPrevious: have no previous page!");
559 
560  // allowed to leave the current page?
562  return false;
563 
564  // the next state to switch to
565  WizardState nPreviousState = m_pImpl->aStateHistory.top();
566 
567  // the state history is used by the enterState method
568  m_pImpl->aStateHistory.pop();
569  // show this page
570  if (!ShowPage(nPreviousState))
571  {
572  m_pImpl->aStateHistory.push(nPreviousState);
573  return false;
574  }
575 
576  // all fine
577  return true;
578  }
579 
580 
582  {
583 
584  ::std::stack< WizardState > aTemp;
585  while(!m_pImpl->aStateHistory.empty())
586  {
587  WizardState nPreviousState = m_pImpl->aStateHistory.top();
588  m_pImpl->aStateHistory.pop();
589  if(nPreviousState != nToRemove)
590  aTemp.push( nPreviousState );
591  else
592  break;
593  }
594  while(!aTemp.empty())
595  {
596  m_pImpl->aStateHistory.push( aTemp.top() );
597  aTemp.pop();
598  }
599  }
600 
601 
603  {
604  m_pImpl->m_bAutoNextButtonState = true;
605  }
606 
607 
609  {
610  return m_pImpl->m_bAutoNextButtonState;
611  }
612 
613 
614  IMPL_LINK_NOARG(OWizardMachine, OnPrevPage, Button*, void)
615  {
616  if ( isTravelingSuspended() )
617  return;
618  WizardTravelSuspension aTravelGuard( *this );
619  travelPrevious();
620  }
621 
622 
623  IMPL_LINK_NOARG(OWizardMachine, OnNextPage, Button*, void)
624  {
625  if ( isTravelingSuspended() )
626  return;
627  WizardTravelSuspension aTravelGuard( *this );
628  travelNext();
629  }
630 
631 
633  {
634  IWizardPageController* pController = dynamic_cast< IWizardPageController* >( _pCurrentPage );
635  return pController;
636  }
637 
638 
639  void OWizardMachine::getStateHistory( ::std::vector< WizardState >& _out_rHistory )
640  {
641  ::std::stack< WizardState > aHistoryCopy( m_pImpl->aStateHistory );
642  while ( !aHistoryCopy.empty() )
643  {
644  _out_rHistory.push_back( aHistoryCopy.top() );
645  aHistoryCopy.pop();
646  }
647  }
648 
649 
651  {
653  }
654 
655 
657  {
658  const IWizardPageController* pController = getPageController( GetPage( getCurrentState() ) );
659  OSL_ENSURE( pController != nullptr, "RoadmapWizard::updateTravelUI: no controller for the current page!" );
660 
661  bool bCanAdvance =
662  ( !pController || pController->canAdvance() ) // the current page allows to advance
663  && canAdvance(); // the dialog as a whole allows to advance
664  enableButtons( WizardButtonFlags::NEXT, bCanAdvance );
665  }
666 
667 
669  {
670  return m_pImpl->m_bTravelingSuspended;
671  }
672 
673 
675  {
676  DBG_ASSERT( !m_pImpl->m_bTravelingSuspended, "OWizardMachine::suspendTraveling: already suspended!" );
677  m_pImpl->m_bTravelingSuspended = true;
678  }
679 
680 
682  {
683  DBG_ASSERT( m_pImpl->m_bTravelingSuspended, "OWizardMachine::resumeTraveling: nothing to resume!" );
684  m_pImpl->m_bTravelingSuspended = false;
685  }
686 
688  : AssistantController(pParent, "vcl/ui/wizard.ui", "Wizard")
689  , m_nCurState(0)
690  , m_pFirstPage(nullptr)
691  , m_xFinish(m_xAssistant->weld_widget_for_response(RET_OK))
692  , m_xCancel(m_xAssistant->weld_widget_for_response(RET_CANCEL))
693  , m_xNextPage(m_xAssistant->weld_widget_for_response(RET_YES))
694  , m_xPrevPage(m_xAssistant->weld_widget_for_response(RET_NO))
695  , m_xHelp(m_xAssistant->weld_widget_for_response(RET_HELP))
696  , m_pImpl(new WizardMachineImplData)
697  {
698  implConstruct(nButtonFlags);
699  }
700 
702  {
703  m_pImpl->sTitleBase = m_xAssistant->get_title();
704 
705  // create the buttons according to the wizard button flags
706  // the help button
707  if (nButtonFlags & WizardButtonFlags::HELP)
708  m_xHelp->show();
709  else
710  m_xHelp->hide();
711 
712  // the previous button
713  if (nButtonFlags & WizardButtonFlags::PREVIOUS)
714  {
715  m_xPrevPage->set_help_id( HID_WIZARD_PREVIOUS );
716  m_xPrevPage->show();
717 
718  m_xPrevPage->connect_clicked( LINK( this, WizardMachine, OnPrevPage ) );
719  }
720  else
721  m_xPrevPage->hide();
722 
723  // the next button
724  if (nButtonFlags & WizardButtonFlags::NEXT)
725  {
726  m_xNextPage->set_help_id( HID_WIZARD_NEXT );
727  m_xNextPage->show();
728 
729  m_xNextPage->connect_clicked( LINK( this, WizardMachine, OnNextPage ) );
730  }
731  else
732  m_xNextPage->hide();
733 
734  // the finish button
735  if (nButtonFlags & WizardButtonFlags::FINISH)
736  {
737  m_xFinish->show();
738 
739  m_xFinish->connect_clicked( LINK( this, WizardMachine, OnFinish ) );
740  }
741  else
742  m_xFinish->hide();
743 
744  // the cancel button
745  if (nButtonFlags & WizardButtonFlags::CANCEL)
746  {
747  m_xCancel->show();
748  m_xCancel->connect_clicked( LINK( this, WizardMachine, OnCancel ) );
749  }
750  else
751  m_xCancel->hide();
752  }
753 
755  {
756  if (m_pImpl)
757  {
758  for (WizardState i = 0; i < m_pImpl->nFirstUnknownPage; ++i)
759  {
760  TabPage *pPage = GetPage(i);
761  if (pPage)
762  pPage->disposeOnce();
763  }
764  m_pImpl.reset();
765  }
766  }
767 
769  {
770  OUString sCompleteTitle(m_pImpl->sTitleBase);
771 
772  // append the page title
773  TabPage* pCurrentPage = GetPage(getCurrentState());
774  if ( pCurrentPage && !pCurrentPage->GetText().isEmpty() )
775  {
776  sCompleteTitle += " - " + pCurrentPage->GetText();
777  }
778 
779  m_xAssistant->set_title(sCompleteTitle);
780  }
781 
782  void WizardMachine::setTitleBase(const OUString& _rTitleBase)
783  {
784  m_pImpl->sTitleBase = _rTitleBase;
785  implUpdateTitle();
786  }
787 
789  {
790  if ( nullptr == GetPage( i_nState ) )
791  {
792  VclPtr<TabPage> pNewPage = createPage( i_nState );
793  DBG_ASSERT( pNewPage, "WizardMachine::GetOrCreatePage: invalid new page (NULL)!" );
794 
795  // fill up the page sequence of our base class (with dummies)
796  while ( m_pImpl->nFirstUnknownPage < i_nState )
797  {
798  AddPage( nullptr );
799  ++m_pImpl->nFirstUnknownPage;
800  }
801 
802  if ( m_pImpl->nFirstUnknownPage == i_nState )
803  {
804  // encountered this page number the first time
805  AddPage( pNewPage );
806  ++m_pImpl->nFirstUnknownPage;
807  }
808  else
809  // already had this page - just change it
810  SetPage( i_nState, pNewPage );
811  }
812  return GetPage( i_nState );
813  }
814 
816  {
817  WizardState nCurrentLevel = m_nCurState;
818  GetOrCreatePage( nCurrentLevel );
819 
820  enterState( nCurrentLevel );
821  }
822 
824  {
825  WizardState nCurrentState = getCurrentState();
826  return leaveState(nCurrentState);
827  }
828 
830  {
831  // the new default button
832  weld::Button* pNewDefButton = nullptr;
833  if (_nWizardButtonFlags & WizardButtonFlags::FINISH)
834  pNewDefButton = m_xFinish.get();
835  if (_nWizardButtonFlags & WizardButtonFlags::NEXT)
836  pNewDefButton = m_xNextPage.get();
837  if (_nWizardButtonFlags & WizardButtonFlags::PREVIOUS)
838  pNewDefButton = m_xPrevPage.get();
839  if (_nWizardButtonFlags & WizardButtonFlags::HELP)
840  pNewDefButton = m_xHelp.get();
841  if (_nWizardButtonFlags & WizardButtonFlags::CANCEL)
842  pNewDefButton = m_xCancel.get();
843 
844  if ( pNewDefButton )
845  defaultButton( pNewDefButton );
846  else
847  m_xAssistant->recursively_unset_default_buttons();
848  }
849 
851  {
852  // loop through all (direct and indirect) descendants which participate in our tabbing order, and
853  // reset the WB_DEFBUTTON for every window which is a button
854  m_xAssistant->recursively_unset_default_buttons();
855 
856  // set its new style
857  if (_pNewDefButton)
858  _pNewDefButton->set_has_default(true);
859  }
860 
861  void WizardMachine::enableButtons(WizardButtonFlags _nWizardButtonFlags, bool _bEnable)
862  {
863  if (_nWizardButtonFlags & WizardButtonFlags::FINISH)
864  m_xFinish->set_sensitive(_bEnable);
865  if (_nWizardButtonFlags & WizardButtonFlags::NEXT)
866  m_xNextPage->set_sensitive(_bEnable);
867  if (_nWizardButtonFlags & WizardButtonFlags::PREVIOUS)
868  m_xPrevPage->set_sensitive(_bEnable);
869  if (_nWizardButtonFlags & WizardButtonFlags::HELP)
870  m_xHelp->set_sensitive(_bEnable);
871  if (_nWizardButtonFlags & WizardButtonFlags::CANCEL)
872  m_xCancel->set_sensitive(_bEnable);
873  }
874 
876  {
877  // tell the page
878  IWizardPageController* pController = getPageController( GetPage( _nState ) );
879  OSL_ENSURE( pController, "WizardMachine::enterState: no controller for the given page!" );
880  if ( pController )
881  pController->initializePage();
882 
885 
886  enableButtons( WizardButtonFlags::PREVIOUS, !m_pImpl->aStateHistory.empty() );
887 
888  // set the new title - it depends on the current page (i.e. state)
889  implUpdateTitle();
890  }
891 
893  {
894  // no need to ask the page here.
895  // If we reach this point, we already gave the current page the chance to commit it's data,
896  // and it was allowed to commit it's data
897 
898  return true;
899  }
900 
902  {
903  return Finish(RET_OK);
904  }
905 
907  {
908  if ( isTravelingSuspended() )
909  return;
910  WizardTravelSuspension aTravelGuard( *this );
911  if ( !prepareLeaveCurrentState( eFinish ) )
912  {
913  return;
914  }
915  onFinish();
916  }
917 
919  {
920  m_xAssistant->response(RET_CANCEL);
921  }
922 
924  {
925  return _nCurrentState + 1;
926  }
927 
929  {
931  ENSURE_OR_RETURN( pController != nullptr, "WizardMachine::prepareLeaveCurrentState: no controller for the current page!", true );
932  return pController->commitPage( _eReason );
933  }
934 
935 
937  {
938  // allowed to leave the current page?
940  return false;
941 
942  // don't travel directly on m_pImpl->aStateHistory, in case something goes wrong
943  ::std::stack< WizardState > aTravelVirtually = m_pImpl->aStateHistory;
944  ::std::stack< WizardState > aOldStateHistory = m_pImpl->aStateHistory;
945 
946  WizardState nCurrentRollbackState = getCurrentState();
947  while ( nCurrentRollbackState != _nTargetState )
948  {
949  DBG_ASSERT( !aTravelVirtually.empty(), "WizardMachine::skipBackwardUntil: this target state does not exist in the history!" );
950  nCurrentRollbackState = aTravelVirtually.top();
951  aTravelVirtually.pop();
952  }
953  m_pImpl->aStateHistory = aTravelVirtually;
954  if ( !ShowPage( _nTargetState ) )
955  {
956  m_pImpl->aStateHistory = aOldStateHistory;
957  return false;
958  }
959  return true;
960  }
961 
962 
963  bool WizardMachine::skipUntil( WizardState _nTargetState )
964  {
965  WizardState nCurrentState = getCurrentState();
966 
967  // allowed to leave the current page?
968  if ( !prepareLeaveCurrentState( nCurrentState < _nTargetState ? eTravelForward : eTravelBackward ) )
969  return false;
970 
971  // don't travel directly on m_pImpl->aStateHistory, in case something goes wrong
972  ::std::stack< WizardState > aTravelVirtually = m_pImpl->aStateHistory;
973  ::std::stack< WizardState > aOldStateHistory = m_pImpl->aStateHistory;
974  while ( nCurrentState != _nTargetState )
975  {
976  WizardState nNextState = determineNextState( nCurrentState );
977  if ( WZS_INVALID_STATE == nNextState )
978  {
979  OSL_FAIL( "WizardMachine::skipUntil: the given target state does not exist!" );
980  return false;
981  }
982 
983  // remember the skipped state in the history
984  aTravelVirtually.push( nCurrentState );
985 
986  // get the next state
987  nCurrentState = nNextState;
988  }
989  m_pImpl->aStateHistory = aTravelVirtually;
990  // show the target page
991  if ( !ShowPage( nCurrentState ) )
992  {
993  // argh! prepareLeaveCurrentPage succeeded, determineNextState succeeded,
994  // but ShowPage doesn't? Somebody behaves very strange here...
995  OSL_FAIL( "WizardMachine::skipUntil: very unpolite..." );
996  m_pImpl->aStateHistory = aOldStateHistory;
997  return false;
998  }
999  return true;
1000  }
1001 
1003  {
1004  // allowed to leave the current page?
1006  return;
1007 
1008  WizardState nCurrentState = getCurrentState();
1009  WizardState nNextState = determineNextState(nCurrentState);
1010 
1011  if (WZS_INVALID_STATE == nNextState)
1012  return;
1013 
1014  // remember the skipped state in the history
1015  m_pImpl->aStateHistory.push(nCurrentState);
1016 
1017  // get the next state
1018  nCurrentState = nNextState;
1019 
1020  // show the (n+1)th page
1021  if (!ShowPage(nCurrentState))
1022  {
1023  // TODO: this leaves us in a state where we have no current page and an inconsistent state history.
1024  // Perhaps we should rollback the skipping here...
1025  OSL_FAIL("OWizardMachine::skip: very unpolite...");
1026  // if somebody does a skip and then does not allow to leave...
1027  // (can't be a commit error, as we've already committed the current page. So if ShowPage fails here,
1028  // somebody behaves really strange ...)
1029  return;
1030  }
1031 
1032  // all fine
1033  }
1034 
1036  {
1037  // allowed to leave the current page?
1039  return false;
1040 
1041  // determine the next state to travel to
1042  WizardState nCurrentState = getCurrentState();
1043  WizardState nNextState = determineNextState(nCurrentState);
1044  if (WZS_INVALID_STATE == nNextState)
1045  return false;
1046 
1047  // the state history is used by the enterState method
1048  // all fine
1049  m_pImpl->aStateHistory.push(nCurrentState);
1050  if (!ShowPage(nNextState))
1051  {
1052  m_pImpl->aStateHistory.pop();
1053  return false;
1054  }
1055 
1056  return true;
1057  }
1058 
1060  {
1061  if (DeactivatePage())
1062  {
1063  TabPage* pOldTabPage = m_xCurTabPage;
1064 
1065  m_nCurState = nState;
1066  ActivatePage();
1067 
1068  if (pOldTabPage)
1069  pOldTabPage->DeactivatePage();
1070 
1071  m_xAssistant->set_current_page(OString::number(nState));
1072 
1075 
1076  return true;
1077  }
1078  return false;
1079  }
1080 
1082  {
1083  DBG_ASSERT(!m_pImpl->aStateHistory.empty(), "WizardMachine::travelPrevious: have no previous page!");
1084 
1085  // allowed to leave the current page?
1087  return false;
1088 
1089  // the next state to switch to
1090  WizardState nPreviousState = m_pImpl->aStateHistory.top();
1091 
1092  // the state history is used by the enterState method
1093  m_pImpl->aStateHistory.pop();
1094  // show this page
1095  if (!ShowPage(nPreviousState))
1096  {
1097  m_pImpl->aStateHistory.push(nPreviousState);
1098  return false;
1099  }
1100 
1101  // all fine
1102  return true;
1103  }
1104 
1105 
1107  {
1108 
1109  ::std::stack< WizardState > aTemp;
1110  while(!m_pImpl->aStateHistory.empty())
1111  {
1112  WizardState nPreviousState = m_pImpl->aStateHistory.top();
1113  m_pImpl->aStateHistory.pop();
1114  if(nPreviousState != nToRemove)
1115  aTemp.push( nPreviousState );
1116  else
1117  break;
1118  }
1119  while(!aTemp.empty())
1120  {
1121  m_pImpl->aStateHistory.push( aTemp.top() );
1122  aTemp.pop();
1123  }
1124  }
1125 
1126 
1128  {
1129  m_pImpl->m_bAutoNextButtonState = true;
1130  }
1131 
1132 
1134  {
1135  return m_pImpl->m_bAutoNextButtonState;
1136  }
1137 
1139  {
1140  if ( isTravelingSuspended() )
1141  return;
1142  WizardTravelSuspension aTravelGuard( *this );
1143  travelPrevious();
1144  }
1145 
1147  {
1148  if ( isTravelingSuspended() )
1149  return;
1150  WizardTravelSuspension aTravelGuard( *this );
1151  travelNext();
1152  }
1153 
1155  {
1156  IWizardPageController* pController = dynamic_cast< IWizardPageController* >( _pCurrentPage );
1157  return pController;
1158  }
1159 
1160 
1161  void WizardMachine::getStateHistory( ::std::vector< WizardState >& _out_rHistory )
1162  {
1163  ::std::stack< WizardState > aHistoryCopy( m_pImpl->aStateHistory );
1164  while ( !aHistoryCopy.empty() )
1165  {
1166  _out_rHistory.push_back( aHistoryCopy.top() );
1167  aHistoryCopy.pop();
1168  }
1169  }
1170 
1171 
1173  {
1175  }
1176 
1177 
1179  {
1180  const IWizardPageController* pController = getPageController( GetPage( getCurrentState() ) );
1181  OSL_ENSURE( pController != nullptr, "RoadmapWizard::updateTravelUI: no controller for the current page!" );
1182 
1183  bool bCanAdvance =
1184  ( !pController || pController->canAdvance() ) // the current page allows to advance
1185  && canAdvance(); // the dialog as a whole allows to advance
1186  enableButtons( WizardButtonFlags::NEXT, bCanAdvance );
1187  }
1188 
1189 
1191  {
1192  return m_pImpl->m_bTravelingSuspended;
1193  }
1194 
1195 
1197  {
1198  DBG_ASSERT( !m_pImpl->m_bTravelingSuspended, "WizardMachine::suspendTraveling: already suspended!" );
1199  m_pImpl->m_bTravelingSuspended = true;
1200  }
1201 
1203  {
1204  DBG_ASSERT( m_pImpl->m_bTravelingSuspended, "WizardMachine::resumeTraveling: nothing to resume!" );
1205  m_pImpl->m_bTravelingSuspended = false;
1206  }
1207 
1208  bool WizardMachine::Finish(short nResult)
1209  {
1210  if ( DeactivatePage() )
1211  {
1212  if (m_xCurTabPage)
1214 
1215  m_xAssistant->response(nResult);
1216  return true;
1217  }
1218  else
1219  return false;
1220  }
1221 
1223  {
1224  ImplWizPageData* pNewPageData = new ImplWizPageData;
1225  pNewPageData->mpNext = nullptr;
1226  pNewPageData->mpPage = pPage;
1227 
1228  if ( !m_pFirstPage )
1229  m_pFirstPage = pNewPageData;
1230  else
1231  {
1232  ImplWizPageData* pPageData = m_pFirstPage;
1233  while ( pPageData->mpNext )
1234  pPageData = pPageData->mpNext;
1235  pPageData->mpNext = pNewPageData;
1236  }
1237  }
1238 
1240  {
1241  sal_uInt16 nTempLevel = 0;
1242  ImplWizPageData* pPageData = m_pFirstPage;
1243  while ( pPageData )
1244  {
1245  if ( (nTempLevel == nLevel) || !pPageData->mpNext )
1246  break;
1247 
1248  nTempLevel++;
1249  pPageData = pPageData->mpNext;
1250  }
1251 
1252  if ( pPageData )
1253  {
1254  if ( pPageData->mpPage == m_xCurTabPage )
1255  m_xCurTabPage = nullptr;
1256  pPageData->mpPage = pPage;
1257  }
1258  }
1259 
1261  {
1262  sal_uInt16 nTempLevel = 0;
1263 
1264  for (ImplWizPageData* pPageData = m_pFirstPage; pPageData;
1265  pPageData = pPageData->mpNext)
1266  {
1267  if ( nTempLevel == nLevel )
1268  return pPageData->mpPage;
1269  nTempLevel++;
1270  }
1271 
1272  return nullptr;
1273  }
1274 } // namespace svt
1275 
1276 
1277 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
std::unique_ptr< weld::Button > m_xPrevPage
VclPtr< TabPage > mpPage
void SetPage(sal_uInt16 nLevel, TabPage *pPage)
Definition: wizdlg.cxx:566
bool isTravelingSuspended() const
virtual bool canAdvance() const override
determines whether or not it is allowed to advance to a next page
virtual void dispose() override
This is intended to be used to clear any locally held references to other Window-subclass objects...
VclPtr< PushButton > m_pNextPage
bool travelPrevious()
travel to the previous state
bool Finish(long nResult=0)
Definition: wizdlg.cxx:504
void SetClickHdl(const Link< Button *, void > &rLink)
Definition: button.hxx:72
virtual void initializePage()=0
void suspendTraveling(AccessGuard)
bool skipBackwardUntil(WizardState _nTargetState)
moves back one or more states, until a given state is reached
virtual WizardState determineNextState(WizardState _nCurrentState) const
determine the next state to travel from the given one
void AddButton(Button *pButton, long nOffset=0)
Definition: wizdlg.cxx:604
virtual void ActivatePage()
virtual VclPtr< TabPage > createPage(WizardState _nState)=0
to override to create new pages
void AddPage(TabPage *pPage)
Definition: wizdlg.cxx:522
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
void disposeAndClear()
Definition: vclptr.hxx:200
OWizardPage(vcl::Window *pParent, const OString &rID, const OUString &rUIXMLDescription)
std::unique_ptr< WizardMachineImplData > m_pImpl
WizardState m_nCurState
TabPage * GetOrCreatePage(const WizardState i_nState)
virtual bool onFinish()
called when the finish button is pressed
virtual bool DeactivatePage()
Definition: wizdlg.cxx:470
virtual void SetSizePixel(const Size &rNewSize)
Definition: window2.cxx:1256
void enableAutomaticNextButtonState()
enables the automatic enabled/disabled state of the "Next" button
bool skipBackwardUntil(WizardState _nTargetState)
moves back one or more states, until a given state is reached
virtual bool canAdvance() const
determines whether there is a next state to which we can advance
virtual ~WizardMachine() override
void SetHelpId(const OString &)
Definition: window2.cxx:823
VclPtr< CancelButton > m_pCancel
virtual void ActivatePage() override
std::unique_ptr< weld::Button > m_xHelp
void defaultButton(WizardButtonFlags _nWizardButtonFlags)
set the default style for a button
virtual void ActivatePage() override
ImplWizPageData * m_pFirstPage
sal_uInt16 GetCurLevel() const
Definition: wizdlg.hxx:238
WizardButtonFlags
Definition: vclenum.hxx:276
#define HID_WIZARD_NEXT
virtual IWizardPageController * getPageController(TabPage *_pCurrentPage) const
#define WIZARDDIALOG_BUTTON_SMALLSTDOFFSET_X
Definition: wizdlg.hxx:174
bool isAutomaticNextButtonStateEnabled() const
void Enable(bool bEnable=true, bool bChild=true)
Definition: window.cxx:2395
bool ShowPage(sal_uInt16 nLevel)
Definition: wizdlg.cxx:490
virtual void enterState(WizardState _nState)
will be called when a new page is about to be displayed
WinBits const WB_DEFBUTTON
virtual bool canAdvance() const
determines whether there is a next state to which we can advance
virtual void set_has_default(bool has_default)=0
bool travelNext()
travel to the next state
VclPtr< PushButton > m_pPrevPage
#define WIZARDDIALOG_BUTTON_STDOFFSET_X
Definition: wizdlg.hxx:173
void SetNextButton(PushButton *pButton)
Definition: wizdlg.hxx:249
#define WZS_INVALID_STATE
virtual OUString GetText() const
Definition: window.cxx:3051
VCL_DLLPRIVATE void implConstruct(const WizardButtonFlags _nButtonFlags)
virtual void dispose() override
This is intended to be used to clear any locally held references to other Window-subclass objects...
Definition: wizdlg.cxx:346
virtual void ActivatePage()
Definition: tabpage.cxx:171
void SetPrevButton(PushButton *pButton)
Definition: wizdlg.hxx:248
std::unique_ptr< weld::Button > m_xCancel
bool isTravelingSuspended() const
void updateDialogTravelUI()
updates the travel-related UI elements of the OWizardMachine we live in (if any)
void setTitleBase(const OUString &_rTitleBase)
set the base of the title to use - the title of the current page is appended
WinBits const WB_DIALOGCONTROL
virtual void dispose() override
This is intended to be used to clear any locally held references to other Window-subclass objects...
std::unique_ptr< weld::Button > m_xNextPage
bool isAutomaticNextButtonStateEnabled() const
virtual IWizardPageController * getPageController(TabPage *_pCurrentPage) const
#define DBG_ASSERT(sCon, aError)
TabPage * GetOrCreatePage(const WizardState i_nState)
virtual bool canAdvance() const =0
determines whether or not it is allowed to advance to a next page
virtual void SetText(const OUString &rStr) override
Definition: ctrl.cxx:95
void SetPage(WizardState nLevel, TabPage *pPage)
bool skipUntil(WizardState _nTargetState)
skips one or more states, until a given state is reached
int i
virtual bool DeactivatePage() override
OWizardMachine(vcl::Window *_pParent, WizardButtonFlags _nButtonFlags)
virtual bool commitPage(WizardTypes::CommitPageReason _eReason)=0
bool skipUntil(WizardState _nTargetState)
skips one or more states, until a given state is reached
virtual VclPtr< TabPage > createPage(WizardState _nState)=0
to override to create new pages
virtual bool DeactivatePage()
#define ENSURE_OR_RETURN(c, m, r)
ImplWizPageData * mpNext
void suspendTraveling(AccessGuard)
virtual bool leaveState(WizardState _nState)
will be called when the given state is left
virtual void updateTravelUI()
updates the user interface which deals with traveling in the wizard
virtual ~OWizardPage() override
Base class used mainly for the LibreOffice Desktop class.
Definition: svapp.hxx:238
DocumentType const eType
vcl::Window * GetParent() const
Definition: window2.cxx:1091
virtual void ActivatePage()
Definition: wizdlg.cxx:464
bool ShowPage(WizardState nState)
void SetStyle(WinBits nStyle)
Definition: window.cxx:1925
virtual bool onFinish()
called when the finish button is pressed
std::unique_ptr< weld::Builder > m_xBuilder
vcl::Window * GetWindow(GetWindowType nType) const
Definition: stacking.cxx:1035
VclPtr< TabPage > m_xCurTabPage
bool travelNext()
travel to the next state
Point LogicToPixel(const Point &rLogicPt) const
Definition: map.cxx:940
VCL_DLLPRIVATE void implUpdateTitle()
void set_id(const OUString &rID)
Sets an ID.
Definition: window.cxx:3756
virtual bool commitPage(WizardTypes::CommitPageReason _eReason) override
WizardState getCurrentState() const
returns the current state of the machine
void removePageFromHistory(WizardState nToRemove)
removes a page from the history.
VCL_DLLPRIVATE void implResetDefault(vcl::Window const *_pWindow)
std::unique_ptr< weld::Button > m_xFinish
virtual bool prepareLeaveCurrentState(CommitPageReason _eReason)
will be called when the current state is about to be left for the given reason
WizardMachine(weld::Window *_pParent, WizardButtonFlags _nButtonFlags)
WindowType
virtual bool prepareLeaveCurrentState(CommitPageReason _eReason)
will be called when the current state is about to be left for the given reason
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.
void enableAutomaticNextButtonState()
enables the automatic enabled/disabled state of the "Next" button
virtual OUString GetText() const override
Definition: syswin.cxx:1105
sal_Int16 WizardState
bool Finish(short nResult=RET_CANCEL)
void setTitleBase(const OUString &_rTitleBase)
set the base of the title to use - the title of the current page is appended
void skip()
skip a state
virtual void SetText(const OUString &rStr) override
Definition: syswin.cxx:1099
TabPage * GetPage(sal_uInt16 nLevel) const
Definition: wizdlg.cxx:588
TabPage * GetPage(WizardState eState) const
static VclPtr< reference_type > Create(Arg &&...arg)
A construction helper for VclPtr.
Definition: vclptr.hxx:127
bool travelPrevious()
travel to the previous state
VCL_DLLPRIVATE void implUpdateTitle()
virtual ~OWizardMachine() override
void resumeTraveling(AccessGuard)
void AddPage(TabPage *pPage)
VclPtr< HelpButton > m_pHelp
void enableButtons(WizardButtonFlags _nWizardButtonFlags, bool _bEnable)
enable (or disable) buttons
WinBits const WB_TABSTOP
void removePageFromHistory(WizardState nToRemove)
removes a page from the history.
WindowType GetType() const
Definition: window2.cxx:968
VclPtr< OKButton > m_pFinish
virtual void dispose() override
This is intended to be used to clear any locally held references to other Window-subclass objects...
Definition: tabpage.cxx:88
#define HID_WIZARD_PREVIOUS
OUString VclResId(const char *pId)
Definition: svdata.cxx:257
WinBits GetStyle() const
Definition: window2.cxx:947
void resumeTraveling(AccessGuard)
implements some kind of finite automata, where the states of the automata exactly correlate with tab ...
virtual void DeactivatePage()
Definition: tabpage.cxx:175
void skip()
skip a state
virtual void updateTravelUI()
updates the user interface which deals with traveling in the wizard
void defaultButton(WizardButtonFlags _nWizardButtonFlags)
set the default style for a button
IMPL_LINK_NOARG(QuickSelectionEngine_Data, SearchStringTimeout, Timer *, void)
virtual bool leaveState(WizardState _nState)
will be called when the given state is left
std::unique_ptr< weld::Assistant > m_xAssistant
Definition: weld.hxx:1947
std::unique_ptr< WizardMachineImplData > m_pImpl
virtual WizardState determineNextState(WizardState _nCurrentState) const
determine the next state to travel from the given one
VCL_DLLPRIVATE void implConstruct(const WizardButtonFlags _nButtonFlags)
::std::stack< WizardState > aStateHistory
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
void Show(bool bVisible=true, ShowFlags nFlags=ShowFlags::NONE)
Definition: window.cxx:2150
virtual void initializePage() override