1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: wizardmachine.cxx,v $
10 * $Revision: 1.21.10.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svtools.hxx"
33 #include <svtools/wizardmachine.hxx>
34 #include <svtools/helpid.hrc>
35 #include <tools/debug.hxx>
36 #include <vcl/msgbox.hxx>
37 #include <svtools/svtdata.hxx>
39 #include <svtools/svtools.hrc>
42 //.........................................................................
45 //.........................................................................
47 //=====================================================================
48 //= WizardPageImplData
49 //=====================================================================
50 struct WizardPageImplData
57 //=====================================================================
59 //=====================================================================
60 //---------------------------------------------------------------------
61 OWizardPage::OWizardPage( Window
* _pParent
, WinBits _nStyle
)
62 :TabPage( _pParent
, _nStyle
)
63 ,m_pImpl( new WizardPageImplData
)
67 //---------------------------------------------------------------------
68 OWizardPage::OWizardPage( Window
* _pParent
, const ResId
& _rResId
)
69 :TabPage( _pParent
, _rResId
)
70 ,m_pImpl( new WizardPageImplData
)
74 //---------------------------------------------------------------------
75 OWizardPage::~OWizardPage()
80 //---------------------------------------------------------------------
81 void OWizardPage::initializePage()
85 //---------------------------------------------------------------------
86 void OWizardPage::ActivatePage()
88 TabPage::ActivatePage();
89 updateDialogTravelUI();
92 //---------------------------------------------------------------------
93 void OWizardPage::updateDialogTravelUI()
95 OWizardMachine
* pWizardMachine
= dynamic_cast< OWizardMachine
* >( GetParent() );
96 OSL_ENSURE( pWizardMachine
, "OWizardPage::updateDialogTravelUI: where am I?" );
98 pWizardMachine
->updateTravelUI();
101 //---------------------------------------------------------------------
102 bool OWizardPage::canAdvance() const
107 //---------------------------------------------------------------------
108 sal_Bool
OWizardPage::commitPage( CommitPageReason
)
113 //=====================================================================
114 //= WizardMachineImplData
115 //=====================================================================
116 struct WizardMachineImplData
: public WizardTypes
118 String sTitleBase
; // the base for the title
119 ::std::stack
< WizardState
> aStateHistory
; // the history of all states (used for implementing "Back")
121 WizardState nFirstUnknownPage
;
122 // the WizardDialog does not allow non-linear transitions (e.g. it's
123 // not possible to add pages in a non-linear order), so we need some own maintainance data
125 sal_Bool m_bAutoNextButtonState
;
127 bool m_bTravelingSuspended
;
129 WizardMachineImplData()
130 :nFirstUnknownPage( 0 )
131 ,m_bAutoNextButtonState( sal_False
)
132 ,m_bTravelingSuspended( false )
137 long OWizardMachine::calcRightHelpOffset(sal_uInt32 _nButtonFlags
)
140 sal_Int32 nRightAlignedButtonCount
= -1;
141 for (unsigned int i
= 0; i
< 8*sizeof(_nButtonFlags
); i
++ )
143 if( ( _nButtonFlags
& nMask
) != 0 )
144 nRightAlignedButtonCount
++;
147 Size aSize
= GetPageSizePixel();
148 sal_Int32 nTotButtonWidth
= nRightAlignedButtonCount
* LogicalCoordinateToPixel(50);
149 sal_Int32 nTotRightButtonSpaceOffset
= (nRightAlignedButtonCount
) * WIZARDDIALOG_BUTTON_STDOFFSET_X
;
150 if ((_nButtonFlags
& WZB_NEXT
) && (_nButtonFlags
& WZB_NEXT
))
151 nTotRightButtonSpaceOffset
= (nTotRightButtonSpaceOffset
- WIZARDDIALOG_BUTTON_STDOFFSET_X
) + WIZARDDIALOG_BUTTON_SMALLSTDOFFSET_X
;
152 return aSize
.Width() - nTotButtonWidth
- nTotRightButtonSpaceOffset
;
155 //=====================================================================
157 //=====================================================================
158 //---------------------------------------------------------------------
159 OWizardMachine::OWizardMachine(Window
* _pParent
, const ResId
& _rRes
, sal_uInt32 _nButtonFlags
)
160 :WizardDialog( _pParent
, _rRes
)
166 ,m_pImpl( new WizardMachineImplData
)
168 m_pImpl
->sTitleBase
= GetText();
170 // create the buttons according to the wizard button flags
172 if (_nButtonFlags
& WZB_HELP
)
174 m_pHelp
= new HelpButton(this, WB_TABSTOP
);
175 m_pHelp
->SetSizePixel( LogicToPixel( Size( 50, 14 ), MAP_APPFONT
) );
177 AddButton( m_pHelp
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
180 // the previous button
181 if (_nButtonFlags
& WZB_PREVIOUS
)
183 m_pPrevPage
= new PushButton(this, WB_TABSTOP
);
184 m_pPrevPage
->SetSmartHelpId( SmartId(HID_WIZARD_PREVIOUS
) );
185 m_pPrevPage
->SetSizePixel( LogicToPixel( Size( 50, 14 ), MAP_APPFONT
) );
186 m_pPrevPage
->SetText(String(SvtResId(STR_WIZDLG_PREVIOUS
)));
189 if (_nButtonFlags
& WZB_NEXT
)
190 AddButton( m_pPrevPage
, ( WIZARDDIALOG_BUTTON_SMALLSTDOFFSET_X
) ); // half x-offset to the next button
192 AddButton( m_pPrevPage
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
193 SetPrevButton( m_pPrevPage
);
194 m_pPrevPage
->SetClickHdl( LINK( this, OWizardMachine
, OnPrevPage
) );
198 if (_nButtonFlags
& WZB_NEXT
)
200 m_pNextPage
= new PushButton(this, WB_TABSTOP
);
201 m_pNextPage
->SetSmartHelpId( SmartId(HID_WIZARD_NEXT
) );
202 m_pNextPage
->SetSizePixel( LogicToPixel( Size( 50, 14 ), MAP_APPFONT
) );
203 m_pNextPage
->SetText(String(SvtResId(STR_WIZDLG_NEXT
)));
206 AddButton( m_pNextPage
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
207 SetNextButton( m_pNextPage
);
208 m_pNextPage
->SetClickHdl( LINK( this, OWizardMachine
, OnNextPage
) );
212 if (_nButtonFlags
& WZB_FINISH
)
214 m_pFinish
= new OKButton(this, WB_TABSTOP
);
215 m_pFinish
->SetSizePixel( LogicToPixel( Size( 50, 14 ), MAP_APPFONT
) );
216 m_pFinish
->SetText(String(SvtResId(STR_WIZDLG_FINISH
)));
219 AddButton( m_pFinish
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
220 m_pFinish
->SetClickHdl( LINK( this, OWizardMachine
, OnFinish
) );
224 if (_nButtonFlags
& WZB_CANCEL
)
226 m_pCancel
= new CancelButton(this, WB_TABSTOP
);
227 m_pCancel
->SetSizePixel( LogicToPixel( Size( 50, 14 ), MAP_APPFONT
) );
230 AddButton( m_pCancel
, WIZARDDIALOG_BUTTON_STDOFFSET_X
);
236 //---------------------------------------------------------------------
237 OWizardMachine::~OWizardMachine()
245 for (WizardState i
=0; i
<m_pImpl
->nFirstUnknownPage
; ++i
)
251 //---------------------------------------------------------------------
252 void OWizardMachine::implUpdateTitle()
254 String
sCompleteTitle(m_pImpl
->sTitleBase
);
256 // append the page title
257 TabPage
* pCurrentPage
= GetPage(getCurrentState());
258 if ( pCurrentPage
&& pCurrentPage
->GetText().Len() )
260 sCompleteTitle
+= String::CreateFromAscii(" - ");
261 sCompleteTitle
+= pCurrentPage
->GetText();
264 SetText(sCompleteTitle
);
267 //---------------------------------------------------------------------
268 const String
& OWizardMachine::getTitleBase() const
270 return m_pImpl
->sTitleBase
;
273 //---------------------------------------------------------------------
274 void OWizardMachine::setTitleBase(const String
& _rTitleBase
)
276 m_pImpl
->sTitleBase
= _rTitleBase
;
280 //---------------------------------------------------------------------
281 void OWizardMachine::ActivatePage()
283 WizardDialog::ActivatePage();
285 WizardState nCurrentLevel
= GetCurLevel();
286 if (NULL
== GetPage(nCurrentLevel
))
288 TabPage
* pNewPage
= createPage(nCurrentLevel
);
289 DBG_ASSERT(pNewPage
, "OWizardMachine::ActivatePage: invalid new page (NULL)!");
291 // fill up the page sequence of our base class (with dummies)
292 while (m_pImpl
->nFirstUnknownPage
< nCurrentLevel
)
295 ++m_pImpl
->nFirstUnknownPage
;
298 if (m_pImpl
->nFirstUnknownPage
== nCurrentLevel
)
300 // encountered this page number the first time
302 ++m_pImpl
->nFirstUnknownPage
;
305 // already had this page - just change it
306 SetPage(nCurrentLevel
, pNewPage
);
309 enterState(nCurrentLevel
);
312 //---------------------------------------------------------------------
313 long OWizardMachine::DeactivatePage()
315 WizardState nCurrentState
= getCurrentState();
316 if (!leaveState(nCurrentState
) || !WizardDialog::DeactivatePage())
321 //---------------------------------------------------------------------
322 void OWizardMachine::defaultButton(sal_uInt32 _nWizardButtonFlags
)
324 // the new default button
325 PushButton
* pNewDefButton
= NULL
;
326 if (m_pFinish
&& (_nWizardButtonFlags
& WZB_FINISH
))
327 pNewDefButton
= m_pFinish
;
328 if (m_pNextPage
&& (_nWizardButtonFlags
& WZB_NEXT
))
329 pNewDefButton
= m_pNextPage
;
330 if (m_pPrevPage
&& (_nWizardButtonFlags
& WZB_PREVIOUS
))
331 pNewDefButton
= m_pPrevPage
;
332 if (m_pHelp
&& (_nWizardButtonFlags
& WZB_HELP
))
333 pNewDefButton
= m_pHelp
;
334 if (m_pCancel
&& (_nWizardButtonFlags
& WZB_CANCEL
))
335 pNewDefButton
= m_pCancel
;
338 defaultButton(pNewDefButton
);
341 //---------------------------------------------------------------------
342 void OWizardMachine::implResetDefault(Window
* _pWindow
)
344 Window
* pChildLoop
= _pWindow
->GetWindow(WINDOW_FIRSTCHILD
);
347 // does the window participate in the tabbing order?
348 if (pChildLoop
->GetStyle() & WB_DIALOGCONTROL
)
349 implResetDefault(pChildLoop
);
352 WindowType eType
= pChildLoop
->GetType();
353 if ( (WINDOW_BUTTON
== eType
)
354 || (WINDOW_PUSHBUTTON
== eType
)
355 || (WINDOW_OKBUTTON
== eType
)
356 || (WINDOW_CANCELBUTTON
== eType
)
357 || (WINDOW_HELPBUTTON
== eType
)
358 || (WINDOW_IMAGEBUTTON
== eType
)
359 || (WINDOW_MENUBUTTON
== eType
)
360 || (WINDOW_MOREBUTTON
== eType
)
363 pChildLoop
->SetStyle(pChildLoop
->GetStyle() & ~WB_DEFBUTTON
);
367 pChildLoop
= pChildLoop
->GetWindow(WINDOW_NEXT
);
371 //---------------------------------------------------------------------
372 void OWizardMachine::defaultButton(PushButton
* _pNewDefButton
)
374 // loop through all (direct and indirect) descendants which participate in our tabbing order, and
375 // reset the WB_DEFBUTTON for every window which is a button
376 implResetDefault(this);
378 // set it's new style
380 _pNewDefButton
->SetStyle(_pNewDefButton
->GetStyle() | WB_DEFBUTTON
);
383 //---------------------------------------------------------------------
384 void OWizardMachine::enableButtons(sal_uInt32 _nWizardButtonFlags
, sal_Bool _bEnable
)
386 if (m_pFinish
&& (_nWizardButtonFlags
& WZB_FINISH
))
387 m_pFinish
->Enable(_bEnable
);
388 if (m_pNextPage
&& (_nWizardButtonFlags
& WZB_NEXT
))
389 m_pNextPage
->Enable(_bEnable
);
390 if (m_pPrevPage
&& (_nWizardButtonFlags
& WZB_PREVIOUS
))
391 m_pPrevPage
->Enable(_bEnable
);
392 if (m_pHelp
&& (_nWizardButtonFlags
& WZB_HELP
))
393 m_pHelp
->Enable(_bEnable
);
394 if (m_pCancel
&& (_nWizardButtonFlags
& WZB_CANCEL
))
395 m_pCancel
->Enable(_bEnable
);
398 //---------------------------------------------------------------------
399 void OWizardMachine::enterState(WizardState _nState
)
402 IWizardPage
* pCurrentPage
= getWizardPage(GetPage(_nState
));
404 pCurrentPage
->initializePage();
406 if ( isAutomaticNextButtonStateEnabled() )
407 enableButtons( WZB_NEXT
, canAdvance() );
409 enableButtons( WZB_PREVIOUS
, !m_pImpl
->aStateHistory
.empty() );
411 // set the new title - it depends on the current page (i.e. state)
415 //---------------------------------------------------------------------
416 sal_Bool
OWizardMachine::leaveState(WizardState
)
418 // no need to ask the page here.
419 // If we reach this point, we already gave the current page the chance to commit it's data,
420 // and it was allowed to commit it's data
425 //---------------------------------------------------------------------
426 sal_Bool
OWizardMachine::onFinish(sal_Int32 _nResult
)
428 return Finnish(_nResult
);
431 //---------------------------------------------------------------------
432 IMPL_LINK(OWizardMachine
, OnFinish
, PushButton
*, EMPTYARG
)
434 if ( isTravelingSuspended() )
436 WizardTravelSuspension
aTravelGuard( *this );
437 if ( !prepareLeaveCurrentState( eFinish
) )
441 long nRet
= onFinish( RET_OK
);
445 //---------------------------------------------------------------------
446 OWizardMachine::WizardState
OWizardMachine::determineNextState( WizardState _nCurrentState
) const
448 return _nCurrentState
+ 1;
451 //---------------------------------------------------------------------
452 sal_Bool
OWizardMachine::prepareLeaveCurrentState( CommitPageReason _eReason
)
454 IWizardPage
* pCurrentPage
= getWizardPage(GetPage(getCurrentState()));
456 return pCurrentPage
->commitPage( _eReason
);
460 //---------------------------------------------------------------------
461 sal_Bool
OWizardMachine::skipBackwardUntil( WizardState _nTargetState
)
463 // alowed to leave the current page?
464 if ( !prepareLeaveCurrentState( eTravelBackward
) )
467 // don't travel directly on m_pImpl->aStateHistory, in case something goes wrong
468 ::std::stack
< WizardState
> aTravelVirtually
= m_pImpl
->aStateHistory
;
469 ::std::stack
< WizardState
> aOldStateHistory
= m_pImpl
->aStateHistory
;
471 WizardState nCurrentRollbackState
= getCurrentState();
472 while ( nCurrentRollbackState
!= _nTargetState
)
474 DBG_ASSERT( !aTravelVirtually
.empty(), "OWizardMachine::skipBackwardUntil: this target state does not exist in the history!" );
475 nCurrentRollbackState
= aTravelVirtually
.top();
476 aTravelVirtually
.pop();
478 m_pImpl
->aStateHistory
= aTravelVirtually
;
479 if ( !ShowPage( _nTargetState
) )
481 m_pImpl
->aStateHistory
= aOldStateHistory
;
487 //---------------------------------------------------------------------
488 sal_Bool
OWizardMachine::skipUntil( WizardState _nTargetState
)
490 WizardState nCurrentState
= getCurrentState();
492 // alowed to leave the current page?
493 if ( !prepareLeaveCurrentState( nCurrentState
< _nTargetState
? eTravelForward
: eTravelBackward
) )
496 // don't travel directly on m_pImpl->aStateHistory, in case something goes wrong
497 ::std::stack
< WizardState
> aTravelVirtually
= m_pImpl
->aStateHistory
;
498 ::std::stack
< WizardState
> aOldStateHistory
= m_pImpl
->aStateHistory
;
499 while ( nCurrentState
!= _nTargetState
)
501 WizardState nNextState
= determineNextState( nCurrentState
);
502 if ( WZS_INVALID_STATE
== nNextState
)
504 DBG_ERROR( "OWizardMachine::skipUntil: the given target state does not exist!" );
508 // remember the skipped state in the history
509 aTravelVirtually
.push( nCurrentState
);
511 // get the next state
512 nCurrentState
= nNextState
;
514 m_pImpl
->aStateHistory
= aTravelVirtually
;
515 // show the target page
516 if ( !ShowPage( nCurrentState
) )
518 // argh! prepareLeaveCurrentPage succeeded, determineNextState succeeded,
519 // but ShowPage doesn't? Somebody behaves very strange here ....
520 DBG_ERROR( "OWizardMachine::skipUntil: very unpolite ...." );
521 m_pImpl
->aStateHistory
= aOldStateHistory
;
527 //---------------------------------------------------------------------
528 sal_Bool
OWizardMachine::skip(sal_Int32 _nSteps
)
530 DBG_ASSERT(_nSteps
> 0, "OWizardMachine::skip: invalid number of steps!");
531 // alowed to leave the current page?
532 if ( !prepareLeaveCurrentState( eTravelForward
) )
535 WizardState nCurrentState
= getCurrentState();
536 WizardState nNextState
= determineNextState(nCurrentState
);
537 // loop _nSteps steps
538 while (_nSteps
-- > 0)
540 if (WZS_INVALID_STATE
== nNextState
)
543 // remember the skipped state in the history
544 m_pImpl
->aStateHistory
.push(nCurrentState
);
546 // get the next state
547 nCurrentState
= nNextState
;
548 nNextState
= determineNextState(nCurrentState
);
551 // show the (n+1)th page
552 if (!ShowPage(nCurrentState
))
554 // TODO: this leaves us in a state where we have no current page and an inconsistent state history.
555 // Perhaps we should rollback the skipping here ....
556 DBG_ERROR("OWizardMachine::skip: very unpolite ....");
557 // if somebody does a skip and then does not allow to leave ...
558 // (can't be a commit error, as we've already committed the current page. So if ShowPage fails here,
559 // somebody behaves really strange ...)
567 //---------------------------------------------------------------------
568 sal_Bool
OWizardMachine::travelNext()
570 // allowed to leave the current page?
571 if ( !prepareLeaveCurrentState( eTravelForward
) )
574 // determine the next state to travel to
575 WizardState nCurrentState
= getCurrentState();
576 WizardState nNextState
= determineNextState(nCurrentState
);
577 if (WZS_INVALID_STATE
== nNextState
)
580 // the state history is used by the enterState method
582 m_pImpl
->aStateHistory
.push(nCurrentState
);
583 if (!ShowPage(nNextState
))
585 m_pImpl
->aStateHistory
.pop();
592 //---------------------------------------------------------------------
593 sal_Bool
OWizardMachine::travelPrevious()
595 DBG_ASSERT(m_pImpl
->aStateHistory
.size() > 0, "OWizardMachine::travelPrevious: have no previous page!");
597 // alowed to leave the current page?
598 if ( !prepareLeaveCurrentState( eTravelBackward
) )
601 // the next state to switch to
602 WizardState nPreviousState
= m_pImpl
->aStateHistory
.top();
604 // the state history is used by the enterState method
605 m_pImpl
->aStateHistory
.pop();
607 if (!ShowPage(nPreviousState
))
609 m_pImpl
->aStateHistory
.push(nPreviousState
);
617 //---------------------------------------------------------------------
618 void OWizardMachine::removePageFromHistory( WizardState nToRemove
)
621 ::std::stack
< WizardState
> aTemp
;
622 while(!m_pImpl
->aStateHistory
.empty())
624 WizardState nPreviousState
= m_pImpl
->aStateHistory
.top();
625 m_pImpl
->aStateHistory
.pop();
626 if(nPreviousState
!= nToRemove
)
627 aTemp
.push( nPreviousState
);
631 while(!aTemp
.empty())
633 m_pImpl
->aStateHistory
.push( aTemp
.top() );
638 //---------------------------------------------------------------------
639 void OWizardMachine::enableAutomaticNextButtonState( bool _bEnable
)
641 m_pImpl
->m_bAutoNextButtonState
= _bEnable
;
644 //---------------------------------------------------------------------
645 bool OWizardMachine::isAutomaticNextButtonStateEnabled() const
647 return m_pImpl
->m_bAutoNextButtonState
;
650 //---------------------------------------------------------------------
651 IMPL_LINK(OWizardMachine
, OnPrevPage
, PushButton
*, EMPTYARG
)
653 if ( isTravelingSuspended() )
655 WizardTravelSuspension
aTravelGuard( *this );
656 sal_Int32 nRet
= travelPrevious();
660 //---------------------------------------------------------------------
661 IMPL_LINK(OWizardMachine
, OnNextPage
, PushButton
*, EMPTYARG
)
663 if ( isTravelingSuspended() )
665 WizardTravelSuspension
aTravelGuard( *this );
666 sal_Int32 nRet
= travelNext();
670 //---------------------------------------------------------------------
671 IWizardPage
* OWizardMachine::getWizardPage(TabPage
* _pCurrentPage
) const
673 OWizardPage
* pPage
= dynamic_cast< OWizardPage
* >( _pCurrentPage
);
677 //---------------------------------------------------------------------
678 void OWizardMachine::getStateHistory( ::std::vector
< WizardState
>& _out_rHistory
)
680 ::std::stack
< WizardState
> aHistoryCopy( m_pImpl
->aStateHistory
);
681 while ( !aHistoryCopy
.empty() )
683 _out_rHistory
.push_back( aHistoryCopy
.top() );
688 //---------------------------------------------------------------------
689 bool OWizardMachine::canAdvance() const
691 return WZS_INVALID_STATE
!= determineNextState( getCurrentState() );
694 //---------------------------------------------------------------------
695 void OWizardMachine::updateTravelUI()
697 OWizardPage
* pPage
= dynamic_cast< OWizardPage
* >( GetPage( getCurrentState() ) );
700 ( !pPage
|| pPage
->canAdvance() ) // the current page allows to advance
701 && canAdvance(); // the dialog as a whole allows to advance
702 enableButtons( WZB_NEXT
, bCanAdvance
);
705 //---------------------------------------------------------------------
706 bool OWizardMachine::isTravelingSuspended() const
708 return m_pImpl
->m_bTravelingSuspended
;
711 //---------------------------------------------------------------------
712 void OWizardMachine::suspendTraveling( AccessGuard
)
714 DBG_ASSERT( !m_pImpl
->m_bTravelingSuspended
, "OWizardMachine::suspendTraveling: already suspended!" );
715 m_pImpl
->m_bTravelingSuspended
= true;
718 //---------------------------------------------------------------------
719 void OWizardMachine::resumeTraveling( AccessGuard
)
721 DBG_ASSERT( m_pImpl
->m_bTravelingSuspended
, "OWizardMachine::resumeTraveling: nothing to resume!" );
722 m_pImpl
->m_bTravelingSuspended
= false;
725 //.........................................................................
727 //.........................................................................