1 /****************************************************************************
4 ** Definition of the QWizard class.
8 ** Copyright (C) 1999 by Trolltech AS. All rights reserved.
10 ** This file is part of the dialogs module of the Qt GUI Toolkit.
12 ** This file may be distributed under the terms of the Q Public License
13 ** as defined by Trolltech AS of Norway and appearing in the file
14 ** LICENSE.QPL included in the packaging of this file.
16 ** This file may be distributed and/or modified under the terms of the
17 ** GNU General Public License version 2 as published by the Free Software
18 ** Foundation and appearing in the file LICENSE.GPL included in the
19 ** packaging of this file.
21 ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22 ** licenses may use this file in accordance with the Qt Commercial License
23 ** Agreement provided with the Software.
25 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
28 ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29 ** information about Qt Commercial License Agreements.
30 ** See http://www.trolltech.com/qpl/ for QPL licensing information.
31 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
33 ** Contact info@trolltech.com if any conditions of this licensing are
36 **********************************************************************/
38 #include "InstallWizard.h"
41 #include <qpushbutton.h>
42 #include <qtoolbutton.h>
45 #include <qwidgetstack.h>
46 #include <qapplication.h>
51 #include <qobjectlist.h>
54 #define PROCESS_EVENT QEvent::User+100
56 class ProcessEvent : public QCustomEvent
59 ProcessEvent( int retValue = 0, void* data = 0 ): QCustomEvent( PROCESS_EVENT ), myReturnValue( retValue ), myData( data ) {}
60 const int returnValue() const { return myReturnValue; }
61 void* data() const { return myData; }
67 class InstallWizardPrivate
71 Page( QWidget * widget, const QString & title ):
72 w( widget ), t( title ),
73 backEnabled( TRUE ), nextEnabled( TRUE ), finishEnabled( FALSE ),
93 QPushButton * backButton;
94 QPushButton * nextButton;
95 QPushButton * finishButton;
96 QPushButton * cancelButton;
97 QPushButton * helpButton;
98 QFrame * hbar1, * hbar2;
99 QToolButton * aboutButton;
107 Page * page( const QWidget * w )
111 int i = pages.count();
112 while( --i >= 0 && pages.at( i ) && pages.at( i )->w != w ) { }
113 return i >= 0 ? pages.at( i ) : 0;
118 /*! Constructs an empty wizard dialog.
119 The \a parent, \a name, \a modal and \a f arguments are passed to
120 the QDialog constructor.
124 InstallWizard::InstallWizard( QWidget *parent, const char *name, bool modal,
126 : QDialog( parent, name, modal, f )
128 d = new InstallWizardPrivate();
129 d->current = 0; // not quite true, but...
130 d->ws = new QWidgetStack( this, "qt_widgetstack" );
131 d->pages.setAutoDelete( TRUE );
132 d->titleBox = new QHBox( this, "title box" );
133 d->aboutButton = new QToolButton( d->titleBox, "about button");
134 d->aboutButton->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) );
135 d->aboutButton->setAutoRaise( true );
136 QPalette pal = d->aboutButton->palette();
137 pal.setColor( QColorGroup::Button, palette().color( QPalette::Active, QColorGroup::Background ) );
138 d->aboutButton->setPalette( pal );
139 d->title = new QLabel( d->titleBox, "title label" );
140 d->logoBox = new QHBox( d->titleBox, "logo box" );
141 d->logoBox->setSpacing( 2 );
142 d->titleBox->setStretchFactor( d->title, 10 );
143 // create in nice tab order
144 d->nextButton = new QPushButton( this, "next" );
145 d->finishButton = new QPushButton( this, "finish" );
146 d->helpButton = new QPushButton( this, "help" );
147 d->backButton = new QPushButton( this, "back" );
148 d->cancelButton = new QPushButton( this, "cancel" );
150 d->ws->installEventFilter( this );
156 d->cancelButton->setText( tr( "&Cancel" ) );
157 d->backButton->setText( tr( "< &Back" ) );
158 d->nextButton->setText( tr( "&Next >" ) );
159 d->finishButton->setText( tr( "&Finish" ) );
160 d->helpButton->setText( tr( "&Help" ) );
162 d->nextButton->setDefault( TRUE );
164 connect( d->backButton, SIGNAL(clicked()),
165 this, SIGNAL(backClicked()) );
166 connect( this, SIGNAL(backClicked()),
167 this, SLOT(back()) );
168 connect( d->nextButton, SIGNAL(clicked()),
169 this, SIGNAL(nextClicked()) );
170 connect( this, SIGNAL(nextClicked()),
171 this, SLOT(next()) );
172 connect( d->finishButton, SIGNAL(clicked()),
173 this, SLOT(accept()) );
174 connect( d->cancelButton, SIGNAL(clicked()),
175 this, SLOT(reject()) );
176 connect( d->helpButton, SIGNAL(clicked()),
177 this, SLOT(help()) );
178 connect( d->aboutButton, SIGNAL(clicked()),
179 this, SIGNAL(aboutClicked()) );
182 d->accel = new QAccel( this, "arrow-key accel" );
183 d->backAccel = d->accel->insertItem( Qt::ALT + Qt::Key_Left );
184 d->accel->connectItem( d->backAccel, this, SIGNAL(backClicked()) );
185 d->nextAccel = d->accel->insertItem( Qt::ALT + Qt::Key_Right );
186 d->accel->connectItem( d->nextAccel, this, SIGNAL(nextClicked()) );
189 showAboutBtn( false );
194 Destroys the object and frees any allocated resources, including
195 all pages and controllers.
198 InstallWizard::~InstallWizard()
206 void InstallWizard::show()
209 showPage( d->current->w );
210 else if ( pageCount() > 0 )
211 showPage( d->pages.at( 0 )->w );
221 void InstallWizard::setFont( const QFont & font )
223 QApplication::postEvent( this, new QEvent( QEvent::LayoutHint ) );
224 QDialog::setFont( font );
228 /*! Adds \a page to the end of the page sequence, with the title, \a title.
231 void InstallWizard::addPage( QWidget * page, const QString & title )
235 if ( d->page( page ) ) {
236 #if defined(QT_CHECK_STATE)
237 qWarning( "InstallWizard::addPage(): already added %s/%s to %s/%s",
238 page->className(), page->name(),
239 className(), name() );
243 int i = d->pages.count();
246 d->pages.at( i - 1 )->nextEnabled = TRUE;
248 InstallWizardPrivate::Page * p = new InstallWizardPrivate::Page( page, title );
249 p->backEnabled = ( i > 0 );
250 d->ws->addWidget( page, i );
251 d->pages.append( p );
255 Inserts \a page at position \a index into the page sequence, with
256 title \a title. If \a index is -1, the page will be appended to
257 the end of the wizard's page sequence.
260 void InstallWizard::insertPage( QWidget * page, const QString & title, int index )
264 if ( d->page( page ) ) {
265 #if defined(QT_CHECK_STATE)
266 qWarning( "InstallWizard::insertPage(): already added %s/%s to %s/%s",
267 page->className(), page->name(),
268 className(), name() );
273 if ( index < 0 || index > (int)d->pages.count() )
274 index = d->pages.count();
276 if( index > 0 && ( index == (int)d->pages.count() ) )
277 d->pages.at( index - 1 )->nextEnabled = TRUE;
279 InstallWizardPrivate::Page * p = new InstallWizardPrivate::Page( page, title );
280 p->backEnabled = ( index > 0 );
281 p->nextEnabled = ( index < (int)d->pages.count() );
283 d->ws->addWidget( page, index );
284 d->pages.insert( index, p );
288 \fn void InstallWizard::selected(const QString&)
290 This signal is emitted when the current page changes. The parameter
291 contains the title of the selected page.
295 /*! Makes \a page the current page and emits the selected() signal. */
297 void InstallWizard::showPage( QWidget * page )
299 InstallWizardPrivate::Page * p = d->page( page );
302 for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != p; i++ );
303 bool notFirst( FALSE );
307 while( ( i >= 0 ) && !notFirst ) {
308 notFirst |= appropriate( d->pages.at( i )->w );
312 setBackEnabled( notFirst );
313 setNextEnabled( TRUE );
314 d->ws->raiseWidget( page );
320 emit selected( p ? p->t : QString::null );
324 /*! Returns the number of pages in the wizard. */
326 int InstallWizard::pageCount() const
328 return d->pages.count();
332 Returns the position of page \a page.
333 If the page is not part of the wizard -1 is returned.
336 int InstallWizard::indexOf( QWidget* page ) const
338 InstallWizardPrivate::Page * p = d->page( page );
341 return d->pages.find( p );
345 Called when the user clicks the Back button; this function shows
346 the preceding relevant page in the sequence.
350 void InstallWizard::back()
354 while( i < (int)d->pages.count() && d->pages.at( i ) &&
355 d->current && d->pages.at( i )->w != d->current->w )
359 while( i >= 0 && ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
363 if( d->pages.at( i ) )
364 showPage( d->pages.at( i )->w );
369 Called when the user clicks the Next button, this function shows
370 the next relevant page in the sequence.
374 void InstallWizard::next()
377 while( i < (int)d->pages.count() && d->pages.at( i ) &&
378 d->current && d->pages.at( i )->w != d->current->w )
381 while( i <= (int)d->pages.count()-1 &&
382 ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
384 // if we fell of the end of the world, step back
385 while ( i > 0 && (i >= (int)d->pages.count() || !d->pages.at( i ) ) )
387 if ( d->pages.at( i ) ) {
389 setNextEnabled( false );
390 setBackEnabled( false );
391 if ( !acceptData( d->current->t ) ) {
392 setNextEnabled( true );
393 setBackEnabled( true );
397 // VSR : commented 10/02/05 --->
398 // Next page will be shown later in processValidateEvent() method
399 // this allows custom validation, for instance by using external processing threads.
400 // See SALOME_InstallWizard.cxx for details where it is used.
401 //showPage( d->pages.at( i )->w );
402 // VSR : commented 10/02/05 <---
408 \fn void InstallWizard::helpClicked()
410 This signal is emitted when the user clicks on the Help button.
413 /*! Called when the user clicks the Help button, this function emits the
414 helpClicked() signal.
417 void InstallWizard::help()
419 QWidget * page = d->ws->visibleWidget();
424 if ( page->inherits( "InstallWizardPage" ) )
425 emit ((InstallWizardPage *)page)->helpClicked();
431 Enables/disables <Back> button
433 void InstallWizard::setBackEnabled( bool enable )
435 d->backButton->setEnabled( enable );
437 d->accel->setItemEnabled( d->backAccel, enable );
442 Enables/disables <Next> button
444 void InstallWizard::setNextEnabled( bool enable )
446 d->nextButton->setEnabled( enable );
448 d->accel->setItemEnabled( d->nextAccel, enable );
453 Enables/disables <Help> button
455 void InstallWizard::setHelpEnabled( bool enable )
457 d->helpButton->setEnabled( enable );
461 \fn void InstallWizard::setFinish( QWidget *, bool )
464 Use setFinishEnabled instead
468 If \a enable is TRUE, page \a page has a Back button; otherwise \a
469 page has no Back button.
470 By default all pages have this button.
472 void InstallWizard::setBackEnabled( QWidget * page, bool enable )
474 InstallWizardPrivate::Page * p = d->page( page );
478 p->backEnabled = enable;
484 If \a enable is TRUE, page \a page has a Next button; otherwise
485 the Next button on \a page is disabled. By default all pages have
489 void InstallWizard::setNextEnabled( QWidget * page, bool enable )
491 InstallWizardPrivate::Page * p = d->page( page );
495 p->nextEnabled = enable;
501 If \a enable is TRUE, page \a page has a Finish button; otherwise \a
502 page has no Finish button.
503 By default \e no page has this button.
505 void InstallWizard::setFinishEnabled( QWidget * page, bool enable )
507 InstallWizardPrivate::Page * p = d->page( page );
511 p->finishEnabled = enable;
517 If \a enable is TRUE, page \a page has a Help button; otherwise \a
518 page has no Help button.
519 By default all pages have this button.
521 void InstallWizard::setHelpEnabled( QWidget * page, bool enable )
523 InstallWizardPrivate::Page * p = d->page( page );
527 p->helpEnabled = enable;
533 Called when the Next button is clicked; this virtual function
534 returns TRUE if \a page is relevant for display in the current
535 context; otherwise it is ignored by InstallWizard and returns FALSE. The
536 default implementation returns the value set using
537 setAppropriate(). The ultimate default is TRUE.
539 \warning The last page of the wizard will be displayed if no page is relevant
540 in the current context.
543 bool InstallWizard::appropriate( QWidget * page ) const
545 InstallWizardPrivate::Page * p = d->page( page );
546 return p ? p->appropriate : TRUE;
551 If \a appropriate is TRUE then page \a page is considered relevant
552 in the current context and should be displayed in the page sequence;
553 otherwise \a page should not be displayed in the page sequence.
557 void InstallWizard::setAppropriate( QWidget * page, bool appropriate )
559 InstallWizardPrivate::Page * p = d->page( page );
561 p->appropriate = appropriate;
565 void InstallWizard::updateButtons()
571 for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != d->current; i++ );
572 bool notFirst( FALSE );
575 while( ( i >= 0 ) && !notFirst ) {
576 notFirst |= appropriate( d->pages.at( i )->w );
580 setBackEnabled( d->current->backEnabled && notFirst );
581 setNextEnabled( d->current->nextEnabled );
582 d->finishButton->setEnabled( d->current->finishEnabled );
583 d->helpButton->setEnabled( d->current->helpEnabled );
585 if ( ( d->current->finishEnabled && !d->finishButton->isVisible() ) ||
586 ( d->current->backEnabled && !d->backButton->isVisible() ) ||
587 ( d->current->nextEnabled && !d->nextButton->isVisible() ) ||
588 ( d->current->helpEnabled && !d->helpButton->isVisible() ) )
593 /*! Returns a pointer to the current page in the sequence.
594 Although the wizard does its best to make sure that this value is
595 never 0, it can be if you try hard enough.
598 QWidget * InstallWizard::currentPage() const
600 return d->ws->visibleWidget();
604 /*! Returns the title of page \a page.
607 QString InstallWizard::title( QWidget * page ) const
609 InstallWizardPrivate::Page * p = d->page( page );
610 return p ? p->t : QString::null;
613 /*! Sets the title for page \a page to \a title.
616 void InstallWizard::setTitle( QWidget *page, const QString &title )
618 InstallWizardPrivate::Page * p = d->page( page );
621 if ( page == currentPage() )
622 d->title->setText( title );
626 \property InstallWizard::titleFont
627 \brief the font used for page titles
629 The default is QApplication::font().
631 QFont InstallWizard::titleFont() const
633 return d->title->font();
636 void InstallWizard::setTitleFont( const QFont & font )
638 d->title->setFont( font );
643 Returns a pointer to the dialog's Back button
645 By default, this button is connected to the back() slot,
646 which is virtual so you can reimplement it in a InstallWizard subclass.
648 QPushButton * InstallWizard::backButton() const
650 return d->backButton;
655 Returns a pointer to the dialog's Next button
657 By default, this button is connected to the next() slot,
658 which is virtual so you can reimplement it in a InstallWizard subclass.
660 QPushButton * InstallWizard::nextButton() const
662 return d->nextButton;
667 Returns a pointer to the dialog's Finish button
669 By default, this button is connected to the QDialog::accept() slot,
670 which is virtual so you can reimplement it in a InstallWizard subclass.
672 QPushButton * InstallWizard::finishButton() const
674 return d->finishButton;
679 Returns a pointer to the dialog's Cancel button
681 By default, this button is connected to the QDialog::reject() slot,
682 which is virtual so you can reimplement it in a InstallWizard subclass.
684 QPushButton * InstallWizard::cancelButton() const
686 return d->cancelButton;
691 Returns a pointer to the dialog's Help button
693 By default, this button is connected to the help() slot,
694 which is virtual so you can reimplement it in a InstallWizard subclass.
696 QPushButton * InstallWizard::helpButton() const
698 return d->helpButton;
702 /*! This virtual function is responsible for adding the bottom
703 divider and the buttons below it.
705 \a layout is the vertical layout of the entire wizard.
708 void InstallWizard::layOutButtonRow( QHBoxLayout * layout )
710 bool hasHelp = FALSE;
711 bool hasEarlyFinish = FALSE;
713 int i = d->pages.count() - 2;
714 while ( !hasEarlyFinish && i >= 0 ) {
715 if ( d->pages.at( i ) && d->pages.at( i )->finishEnabled )
716 hasEarlyFinish = TRUE;
720 while ( !hasHelp && i < (int)d->pages.count() ) {
721 if ( d->pages.at( i ) && d->pages.at( i )->helpEnabled )
726 QBoxLayout * h = new QBoxLayout( QBoxLayout::LeftToRight );
727 layout->addLayout( h );
729 h->addWidget( d->cancelButton );
733 h->addWidget( d->backButton );
737 if ( hasEarlyFinish ) {
738 d->nextButton->show();
739 d->finishButton->show();
740 h->addWidget( d->nextButton );
742 h->addWidget( d->finishButton );
743 } else if ( d->pages.count() == 0 ||
744 d->current->finishEnabled ||
745 d->current == d->pages.at( d->pages.count()-1 ) ) {
746 d->nextButton->hide();
747 d->finishButton->show();
748 h->addWidget( d->finishButton );
750 d->nextButton->show();
751 d->finishButton->hide();
752 h->addWidget( d->nextButton );
755 // if last page is disabled - show finished btn. at lastpage-1
756 i = d->pages.count()-1;
757 if ( i >= 0 && !appropriate( d->pages.at( i )->w ) &&
758 d->current == d->pages.at( d->pages.count()-2 ) ) {
759 d->nextButton->hide();
760 d->finishButton->show();
761 h->addWidget( d->finishButton );
766 h->addWidget( d->helpButton );
768 d->helpButton->hide();
774 This virtual function is responsible for laying out the title row
775 and adding the vertical divider between the title and the wizard
776 page. \a layout is the vertical layout for the wizard, and \a
777 title is the title for this page. This function is called every
778 time \a title changes.
781 void InstallWizard::layOutTitleRow( QHBoxLayout * layout, const QString & title )
783 d->title->setText( title );
784 layout->addWidget( d->titleBox, 10 );
788 Validates page when 'Next' or 'Finish' button is clicked.
789 Should return true in success
791 bool InstallWizard::acceptData( const QString& )
793 postValidateEvent( this );
801 void InstallWizard::layOut()
804 d->v = new QVBoxLayout( this, 11, 0, "top-level layout" );
807 l = new QHBoxLayout( 6 );
808 d->v->addLayout( l, 0 );
809 layOutTitleRow( l, d->current ? d->current->t : QString::null );
812 d->hbar1 = new QFrame( this, "<hr>", 0 );
813 d->hbar1->setFrameStyle( QFrame::Sunken + QFrame::HLine );
814 d->hbar1->setFixedHeight( 12 );
817 d->v->addWidget( d->hbar1 );
819 d->v->addWidget( d->ws, 10 );
822 d->hbar2 = new QFrame( this, "<hr>", 0 );
823 d->hbar2->setFrameStyle( QFrame::Sunken + QFrame::HLine );
824 d->hbar2->setFixedHeight( 12 );
826 d->v->addWidget( d->hbar2 );
828 l = new QHBoxLayout( 6 );
829 d->v->addLayout( l );
830 layOutButtonRow( l );
837 bool InstallWizard::eventFilter( QObject * o, QEvent * e )
839 if ( o == d->ws && e && e->type() == QEvent::ChildRemoved ) {
840 QChildEvent * c = (QChildEvent*)e;
841 if ( c->child() && c->child()->isWidgetType() )
842 removePage( (QWidget *)c->child() );
844 return QDialog::eventFilter( o, e );
849 Removes \a page from the page sequence but does not delete the page.
850 If \a page is currently being displayed, InstallWizard will display the
851 page that precedes it, or the first page if this was the first page.
854 void InstallWizard::removePage( QWidget * page )
859 int i = d->pages.count();
860 QWidget* cp = currentPage();
861 while( --i >= 0 && d->pages.at( i ) && d->pages.at( i )->w != page ) { }
864 InstallWizardPrivate::Page * p = d->pages.at( i );
865 d->pages.removeRef( p );
866 d->ws->removeWidget( page );
872 if ( pageCount() > 0 )
873 showPage( InstallWizard::page( i ) );
879 Returns a pointer to the page at position \a index in the sequence,
880 or 0 if \a index is out of range. The first page has index 0.
883 QWidget* InstallWizard::page( int index ) const
885 if ( index >= pageCount() || index < 0 )
888 return d->pages.at( index )->w;
892 Returns a pointer to the page with a title \a title in the sequence,
895 QWidget* InstallWizard::page( const QString& title ) const
897 for( int i = 0; i < (int)d->pages.count(); i++ ) {
898 if ( d->pages.at( i )->t == title )
899 return d->pages.at( i )->w;
905 Adds logo to be shown at the right of the page title
907 void InstallWizard::addLogo( const QPixmap& pm )
909 QLabel* logo = new QLabel( d->logoBox, "logo" );
910 logo->setPixmap( pm );
911 logo->setAlignment( AlignCenter );
912 logo->setScaledContents( false );
919 void InstallWizard::removeLogos()
921 QObjectList* children = d->logoBox->queryList( "QLabel" );
923 QObjectListIt it( *children );
925 while ( (obj = it.current()) != 0 ) {
934 Show/hide "About" button
936 void InstallWizard::showAboutBtn( bool show )
938 show ? d->aboutButton->show() : d->aboutButton->hide();
942 Set icon for "About" button
944 void InstallWizard::setAboutIcon( const QPixmap& px )
946 d->aboutButton->setIconSet( px );
950 Posts validation event
952 void InstallWizard::postValidateEvent( InstallWizard* iw, const int val, void* data )
954 #if QT_VERSION > 0x030005
955 QApplication::postEvent( iw, new ProcessEvent( val, data ) );
957 QThread::postEvent( iw, new ProcessEvent( val, data ) );
962 Processes validation event: default implementation just to show next page
964 void InstallWizard::processValidateEvent( const int /* val */, void* /* data */ )
967 while( i < (int)d->pages.count() && d->pages.at( i ) &&
968 d->current && d->pages.at( i )->w != d->current->w )
971 while( i <= (int)d->pages.count()-1 &&
972 ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
974 // if we fell of the end of the world, step back
975 while ( i > 0 && (i >= (int)d->pages.count() || !d->pages.at( i ) ) )
977 if ( d->pages.at( i ) ) {
978 showPage( d->pages.at( i )->w );
980 setNextEnabled( true );
981 setBackEnabled( true );
985 Process events received
987 bool InstallWizard::event ( QEvent* e )
989 if ( e->type() == PROCESS_EVENT ) {
990 ProcessEvent* pe = (ProcessEvent*)e;
991 processValidateEvent( pe->returnValue(), pe->data() );
993 return QDialog::event( e );