/************************************************************************* * * $RCSfile: wizardmachine.cxx,v $ * * $Revision: 1.6 $ * * last change: $Author: fs $ $Date: 2001-08-08 14:57:10 $ * * The Contents of this file are made available subject to the terms of * either of the following licenses * * - GNU Lesser General Public License Version 2.1 * - Sun Industry Standards Source License Version 1.1 * * Sun Microsystems Inc., October, 2000 * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2000 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * * Sun Industry Standards Source License Version 1.1 * ================================================= * The contents of this file are subject to the Sun Industry Standards * Source License Version 1.1 (the "License"); You may not use this file * except in compliance with the License. You may obtain a copy of the * License at http://www.openoffice.org/license.html. * * Software provided under this License is provided on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. * See the License for the specific provisions governing your rights and * obligations concerning the Software. * * The Initial Developer of the Original Code is: Sun Microsystems, Inc.. * * Copyright: 2000 by Sun Microsystems, Inc. * * All Rights Reserved. * * Contributor(s): _______________________________________ * * ************************************************************************/ #ifndef _SVTOOLS_WIZARDMACHINE_HXX_ #include "wizardmachine.hxx" #endif #ifndef _TOOLS_DEBUG_HXX #include <tools/debug.hxx> #endif #ifndef _SV_MSGBOX_HXX #include <vcl/msgbox.hxx> #endif #ifndef _SVTOOLS_SVTDATA_HXX #include "svtdata.hxx" #endif #ifndef _SVTOOLS_HRC #include "svtools.hrc" #endif #ifndef SVTOOLS_WIZARDHEADER_HXX #include "wizardheader.hxx" #endif #ifndef _SV_BITMAP_HXX #include <vcl/bitmap.hxx> #endif #define HEADER_SIZE_Y 30 //......................................................................... namespace svt { //......................................................................... //===================================================================== //= WizardPageImplData //===================================================================== struct WizardPageImplData { WizardHeader* pHeader; WizardPageImplData() :pHeader( NULL ) { } }; //===================================================================== //= OWizardPage //===================================================================== //--------------------------------------------------------------------- OWizardPage::OWizardPage( OWizardMachine* _pParent, WinBits _nStyle ) :TabPage( _pParent, _nStyle ) ,m_pImpl( new WizardPageImplData ) { } //--------------------------------------------------------------------- OWizardPage::OWizardPage( OWizardMachine* _pParent, const ResId& _rResId ) :TabPage( _pParent, _rResId ) ,m_pImpl( new WizardPageImplData ) { } //--------------------------------------------------------------------- OWizardPage::~OWizardPage() { delete m_pImpl->pHeader; delete m_pImpl; } //--------------------------------------------------------------------- sal_Bool OWizardPage::isHeaderEnabled( ) const { return NULL != m_pImpl->pHeader; } //--------------------------------------------------------------------- void OWizardPage::enableHeader( const Bitmap& _rBitmap, sal_Int32 _nPixelHeight, GrantAccess ) { //................................................................. // create the header control m_pImpl->pHeader = new WizardHeader( this ); // move it to the upper left corner m_pImpl->pHeader->SetPosPixel( Point( 0, 0 ) ); // size it: as wide as we are, and 30 APPFONT units high Size aHeaderSize( GetSizePixel().Width(), _nPixelHeight ); m_pImpl->pHeader->SetSizePixel( aHeaderSize ); // set bitmap / text of the control m_pImpl->pHeader->setHeaderBitmap( _rBitmap ); m_pImpl->pHeader->setHeaderText( GetText() ); // show m_pImpl->pHeader->Show( ); //................................................................. // move all the other controls down Point aChildPos; // loop through the children Window* pChildLoop = GetWindow( WINDOW_FIRSTCHILD ); while ( pChildLoop ) { if ( pChildLoop != m_pImpl->pHeader ) { // it's not the header itself aChildPos = pChildLoop->GetPosPixel(); aChildPos.Y() += aHeaderSize.Height(); pChildLoop->SetPosPixel( aChildPos ); } pChildLoop = pChildLoop->GetWindow( WINDOW_NEXT ); } } //--------------------------------------------------------------------- void OWizardPage::setHeaderText( const String& _rHeaderText ) { if ( !isHeaderEnabled() ) { DBG_ERROR( "OWizardPage::setHeaderText: have no header!" ); return; } m_pImpl->pHeader->setHeaderText( _rHeaderText ); } //--------------------------------------------------------------------- void OWizardPage::initializePage() { } //--------------------------------------------------------------------- void OWizardPage::ActivatePage() { TabPage::ActivatePage(); implCheckNextButton(); } //--------------------------------------------------------------------- void OWizardPage::implCheckNextButton() { static_cast< OWizardMachine* >(GetParent())->enableButtons(WZB_NEXT, determineNextButtonState()); } //--------------------------------------------------------------------- sal_Bool OWizardPage::determineNextButtonState() { return sal_True; } //--------------------------------------------------------------------- sal_Bool OWizardPage::commitPage(COMMIT_REASON _eReason) { return sal_True; } //===================================================================== //= WizardMachineImplData //===================================================================== struct WizardMachineImplData { String sTitleBase; // the base for the title ::std::stack< sal_uInt16 > aStateHistory; // the history of all states (used for implementing "Back") Bitmap aHeaderBitmap; // the bitmap to use for the page header sal_Int32 nHeaderHeight; // the height (in pixels) of the page header sal_uInt16 nFirstUnknownPage; // the WizardDialog does not allow non-linear transitions (e.g. it's // not possible to add pages in a non-linear order), so we need some own maintainance data sal_Bool bUsingHeaders; // do we use page headers? WizardMachineImplData() :nFirstUnknownPage( 0 ) ,bUsingHeaders( sal_False ) { } }; //===================================================================== //= OWizardMachine //===================================================================== //--------------------------------------------------------------------- OWizardMachine::OWizardMachine(Window* _pParent, const ResId& _rRes, sal_uInt32 _nButtonFlags) :WizardDialog(_pParent, _rRes) ,m_pFinish(NULL) ,m_pCancel(NULL) ,m_pNextPage(NULL) ,m_pPrevPage(NULL) ,m_pHelp(NULL) ,m_pImpl( new WizardMachineImplData ) { m_pImpl->sTitleBase = GetText(); // create the buttons according to the wizard button flags // the help button if (_nButtonFlags & WZB_HELP) { m_pHelp= new HelpButton(this, WB_TABSTOP); m_pHelp->SetSizePixel( LogicToPixel( Size( 50, 14 ), MAP_APPFONT ) ); m_pHelp->Show(); AddButton( m_pHelp, WIZARDDIALOG_BUTTON_STDOFFSET_X ); } // the cancel button if (_nButtonFlags & WZB_CANCEL) { m_pCancel = new CancelButton(this, WB_TABSTOP); m_pCancel->SetSizePixel( LogicToPixel( Size( 50, 14 ), MAP_APPFONT ) ); m_pCancel->Show(); AddButton( m_pCancel, WIZARDDIALOG_BUTTON_STDOFFSET_X ); } // the previous button if (_nButtonFlags & WZB_PREVIOUS) { m_pPrevPage = new PushButton(this, WB_TABSTOP); m_pPrevPage->SetSizePixel( LogicToPixel( Size( 50, 14 ), MAP_APPFONT ) ); m_pPrevPage->SetText(String(SvtResId(STR_WIZDLG_PREVIOUS))); m_pPrevPage->Show(); if (_nButtonFlags & WZB_NEXT) AddButton( m_pPrevPage ); // no x-offset to the next button else AddButton( m_pPrevPage, WIZARDDIALOG_BUTTON_STDOFFSET_X ); SetPrevButton( m_pPrevPage ); m_pPrevPage->SetClickHdl( LINK( this, OWizardMachine, OnPrevPage ) ); } // the next button if (_nButtonFlags & WZB_NEXT) { m_pNextPage = new PushButton(this, WB_TABSTOP); m_pNextPage->SetSizePixel( LogicToPixel( Size( 50, 14 ), MAP_APPFONT ) ); m_pNextPage->SetText(String(SvtResId(STR_WIZDLG_NEXT))); m_pNextPage->Show(); AddButton( m_pNextPage, WIZARDDIALOG_BUTTON_STDOFFSET_X ); SetNextButton( m_pNextPage ); m_pNextPage->SetClickHdl( LINK( this, OWizardMachine, OnNextPage ) ); } // the finish button if (_nButtonFlags & WZB_FINISH) { m_pFinish = new OKButton(this, WB_TABSTOP); m_pFinish->SetSizePixel( LogicToPixel( Size( 50, 14 ), MAP_APPFONT ) ); m_pFinish->SetText(String(SvtResId(STR_WIZDLG_FINISH))); m_pFinish->Show(); AddButton( m_pFinish, WIZARDDIALOG_BUTTON_STDOFFSET_X ); m_pFinish->SetClickHdl( LINK( this, OWizardMachine, OnFinish ) ); } } //--------------------------------------------------------------------- OWizardMachine::~OWizardMachine() { delete m_pFinish; delete m_pCancel; delete m_pNextPage; delete m_pPrevPage; delete m_pHelp; for (sal_uInt16 i=0; i<m_pImpl->nFirstUnknownPage; ++i) delete GetPage(i); delete m_pImpl; } //--------------------------------------------------------------------- void OWizardMachine::implUpdateTitle() { String sCompleteTitle(m_pImpl->sTitleBase); if ( !m_pImpl->bUsingHeaders ) { // append the page title only if we're not using headers - in this case, the title // would be part of the header OWizardPage* pCurrentPage = getPage(getCurrentState()); if (pCurrentPage) { sCompleteTitle += String::CreateFromAscii(" - "); sCompleteTitle += pCurrentPage->GetText(); } } SetText(sCompleteTitle); } //--------------------------------------------------------------------- void OWizardMachine::enableHeader( const Bitmap& _rBitmap, sal_Int32 _nPixelHeight ) { if ( m_pImpl->bUsingHeaders ) // nothing to do return; // is the height defaulted? if ( -1 == _nPixelHeight ) { _nPixelHeight = LogicToPixel( Size( 0, 30 ), MAP_APPFONT ).Height(); } m_pImpl->bUsingHeaders = sal_True; m_pImpl->aHeaderBitmap = _rBitmap; m_pImpl->nHeaderHeight = _nPixelHeight; #ifdef DBG_UTIL for (sal_uInt16 i=0; i<m_pImpl->nFirstUnknownPage; ++i) { DBG_ASSERT( NULL == GetPage( i ), "OWizardMachine::enableHeader: there already are pages!" ); // this method has not to be called if there already have been created any pages } #endif } //--------------------------------------------------------------------- const String& OWizardMachine::getTitleBase() const { return m_pImpl->sTitleBase; } //--------------------------------------------------------------------- void OWizardMachine::setTitleBase(const String& _rTitleBase) { m_pImpl->sTitleBase = _rTitleBase; implUpdateTitle(); } //--------------------------------------------------------------------- void OWizardMachine::ActivatePage() { WizardDialog::ActivatePage(); sal_uInt16 nCurrentLevel = GetCurLevel(); if (NULL == GetPage(nCurrentLevel)) { OWizardPage* pNewPage = createPage(nCurrentLevel); DBG_ASSERT(pNewPage, "OWizardMachine::ActivatePage: invalid new page (NULL)!"); // announce our header bitmap to the page if ( m_pImpl->bUsingHeaders ) pNewPage->enableHeader( m_pImpl->aHeaderBitmap, m_pImpl->nHeaderHeight, OWizardPage::GrantAccess() ); // fill up the page sequence of our base class (with dummies) while (m_pImpl->nFirstUnknownPage < nCurrentLevel) { AddPage(NULL); ++m_pImpl->nFirstUnknownPage; } if (m_pImpl->nFirstUnknownPage == nCurrentLevel) { // encountered this page number the first time AddPage(pNewPage); ++m_pImpl->nFirstUnknownPage; } else // already had this page - just change it SetPage(nCurrentLevel, pNewPage); } enterState(nCurrentLevel); } //--------------------------------------------------------------------- long OWizardMachine::DeactivatePage() { sal_uInt16 nCurrentState = getCurrentState(); if (!leaveState(nCurrentState) || !WizardDialog::DeactivatePage()) return sal_False; return sal_True; } //--------------------------------------------------------------------- void OWizardMachine::defaultButton(sal_uInt32 _nWizardButtonFlags) { // the new default button PushButton* pNewDefButton = NULL; if (m_pFinish && (_nWizardButtonFlags & WZB_FINISH)) pNewDefButton = m_pFinish; if (m_pNextPage && (_nWizardButtonFlags & WZB_NEXT)) pNewDefButton = m_pNextPage; if (m_pPrevPage && (_nWizardButtonFlags & WZB_PREVIOUS)) pNewDefButton = m_pPrevPage; if (m_pHelp && (_nWizardButtonFlags & WZB_HELP)) pNewDefButton = m_pHelp; if (m_pCancel && (_nWizardButtonFlags & WZB_CANCEL)) pNewDefButton = m_pCancel; if (pNewDefButton) defaultButton(pNewDefButton); } //--------------------------------------------------------------------- void OWizardMachine::implResetDefault(Window* _pWindow) { Window* pChildLoop = _pWindow->GetWindow(WINDOW_FIRSTCHILD); while (pChildLoop) { // does the window participate in the tabbing order? if (pChildLoop->GetStyle() & WB_DIALOGCONTROL) implResetDefault(pChildLoop); // is it a button? WindowType eType = pChildLoop->GetType(); if ( (WINDOW_BUTTON == eType) || (WINDOW_PUSHBUTTON == eType) || (WINDOW_OKBUTTON == eType) || (WINDOW_CANCELBUTTON == eType) || (WINDOW_HELPBUTTON == eType) || (WINDOW_IMAGEBUTTON == eType) || (WINDOW_MENUBUTTON == eType) || (WINDOW_MOREBUTTON == eType) ) { pChildLoop->SetStyle(pChildLoop->GetStyle() & ~WB_DEFBUTTON); // give the button the focus, this is the best method to enforce it to be repainted sal_uInt32 nSaveFocusId = Window::SaveFocus(); pChildLoop->GrabFocus(); Window::EndSaveFocus(nSaveFocusId); } // the next one ... pChildLoop = pChildLoop->GetWindow(WINDOW_NEXT); } } //--------------------------------------------------------------------- void OWizardMachine::defaultButton(PushButton* _pNewDefButton) { // loop through all (direct and indirect) descendants which participate in our tabbing order, and // reset the WB_DEFBUTTON for every window which is a button implResetDefault(this); // set it's new style if (_pNewDefButton) { _pNewDefButton->SetStyle(_pNewDefButton->GetStyle() | WB_DEFBUTTON); // give the button the focus, this is the best method to enforce it to be repainted sal_uInt32 nSaveFocusId = Window::SaveFocus(); _pNewDefButton->GrabFocus(); Window::EndSaveFocus(nSaveFocusId); } } //--------------------------------------------------------------------- void OWizardMachine::enableButtons(sal_uInt32 _nWizardButtonFlags, sal_Bool _bEnable) { if (m_pFinish && (_nWizardButtonFlags & WZB_FINISH)) m_pFinish->Enable(_bEnable); if (m_pNextPage && (_nWizardButtonFlags & WZB_NEXT)) m_pNextPage->Enable(_bEnable); if (m_pPrevPage && (_nWizardButtonFlags & WZB_PREVIOUS)) m_pPrevPage->Enable(_bEnable); if (m_pHelp && (_nWizardButtonFlags & WZB_HELP)) m_pHelp->Enable(_bEnable); if (m_pCancel && (_nWizardButtonFlags & WZB_CANCEL)) m_pCancel->Enable(_bEnable); } //--------------------------------------------------------------------- void OWizardMachine::enterState(sal_uInt16 _nState) { // tell the page OWizardPage* pCurrentPage = getPage(_nState); if (pCurrentPage) pCurrentPage->initializePage(); // set the new title - it depends on the current page (i.e. state) implUpdateTitle(); } //--------------------------------------------------------------------- sal_Bool OWizardMachine::leaveState(sal_uInt16 _nState) { // no need to ask the page here. // If we reach this point, we already gave the current page the chance to commit it's data, // and it was allowed to commit it's data return sal_True; } //--------------------------------------------------------------------- sal_Bool OWizardMachine::onFinish(sal_Int32 _nResult) { return Finnish(_nResult); } //--------------------------------------------------------------------- IMPL_LINK(OWizardMachine, OnFinish, PushButton*, NOINTERESTEDIN) { if (!implCommitCurrentPage(OWizardPage::CR_FINISH)) return 0L; return onFinish( RET_OK ); } //--------------------------------------------------------------------- sal_uInt16 OWizardMachine::determineNextState(sal_uInt16 _nCurrentState) { return _nCurrentState + 1; } //--------------------------------------------------------------------- sal_Bool OWizardMachine::implCommitCurrentPage(OWizardPage::COMMIT_REASON _eReason) { OWizardPage* pCurrentPage = getPage(getCurrentState()); if (pCurrentPage) return pCurrentPage->commitPage(_eReason); return sal_True; } //--------------------------------------------------------------------- sal_Bool OWizardMachine::skip(sal_Int32 _nSteps) { DBG_ASSERT(_nSteps > 0, "OWizardMachine::skip: invalid number of steps!"); // alowed to leave the current page? if (!implCommitCurrentPage(OWizardPage::CR_TRAVEL_NEXT)) return sal_False; sal_uInt16 nCurrentState = getCurrentState(); sal_uInt16 nNextState = determineNextState(nCurrentState); // loop _nSteps steps while (_nSteps-- > 0) { if (WZS_INVALID_STATE == nNextState) return sal_False; // remember the skipped state in the history m_pImpl->aStateHistory.push(nCurrentState); // get the next state nCurrentState = nNextState; nNextState = determineNextState(nCurrentState); } // show the (n+1)th page if (!ShowPage(nCurrentState)) { // TODO: this leaves us in a state where we have no current page and an inconsistent state history. // Perhaps we should rollback the skipping here .... DBG_ERROR("OWizardMachine::skip: very unpolite ...."); // if somebody does a skip and then does not allow to leave ... // (can't be a commit error, as we've already committed the current page. So if ShowPage fails here, // somebody behaves really strange ...) return sal_False; } // all fine return sal_True; } //--------------------------------------------------------------------- sal_Bool OWizardMachine::travelNext() { // alowed to leave the current page? if (!implCommitCurrentPage(OWizardPage::CR_TRAVEL_NEXT)) return sal_False; // determine the next state to travel to sal_uInt16 nCurrentState = getCurrentState(); sal_uInt16 nNextState = determineNextState(nCurrentState); if (WZS_INVALID_STATE == nNextState) return sal_False; if (!ShowPage(nNextState)) return sal_False; // all fine m_pImpl->aStateHistory.push(nCurrentState); return sal_True; } //--------------------------------------------------------------------- sal_Bool OWizardMachine::travelPrevious() { DBG_ASSERT(m_pImpl->aStateHistory.size() > 0, "OWizardMachine::travelPrevious: have no previous page!"); // alowed to leave the current page? if (!implCommitCurrentPage(OWizardPage::CR_TRAVEL_PREVIOUS)) return sal_False; // the next state to switch to sal_uInt16 nPreviousState = m_pImpl->aStateHistory.top(); // show this page if (!ShowPage(nPreviousState)) return sal_False; // all fine m_pImpl->aStateHistory.pop(); return sal_True; } //--------------------------------------------------------------------- IMPL_LINK(OWizardMachine, OnPrevPage, PushButton*, NOINTERESTEDIN) { return travelPrevious(); } //--------------------------------------------------------------------- IMPL_LINK(OWizardMachine, OnNextPage, PushButton*, NOINTERESTEDIN) { return travelNext(); } //......................................................................... } // namespace svt //......................................................................... /************************************************************************* * history: * $Log: not supported by cvs2svn $ * Revision 1.5 2001/08/02 10:37:38 fs * #88530# added functionality for adding a WizardHeader (upon request of the derived class) * * Revision 1.4 2001/02/23 10:55:00 fs * small fixes in skip * * Revision 1.3 2001/02/23 08:21:21 fs * +skip / +implCheckNextButton / +determineNextButtonState * * Revision 1.2 2001/02/19 16:20:00 kz * locale using of projectheaders * * Revision 1.1 2001/02/15 14:08:27 fs * initial checkin - a wizard dialog base class * * Revision 1.1 2001/02/12 07:16:52 fs * initial checkin - importing StarOffice 5.2 database files * * * Revision 1.0 30.01.01 17:04:12 fs ************************************************************************/