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 d->title = new QLabel( d->titleBox, "title label" );
137 d->logoBox = new QHBox( d->titleBox, "logo box" );
138 d->logoBox->setSpacing( 2 );
139 d->titleBox->setStretchFactor( d->title, 10 );
140 // create in nice tab order
141 d->nextButton = new QPushButton( this, "next" );
142 d->finishButton = new QPushButton( this, "finish" );
143 d->helpButton = new QPushButton( this, "help" );
144 d->backButton = new QPushButton( this, "back" );
145 d->cancelButton = new QPushButton( this, "cancel" );
147 d->ws->installEventFilter( this );
153 d->cancelButton->setText( tr( "&Cancel" ) );
154 d->backButton->setText( tr( "< &Back" ) );
155 d->nextButton->setText( tr( "&Next >" ) );
156 d->finishButton->setText( tr( "&Finish" ) );
157 d->helpButton->setText( tr( "&Help" ) );
159 d->nextButton->setDefault( TRUE );
161 connect( d->backButton, SIGNAL(clicked()),
162 this, SIGNAL(backClicked()) );
163 connect( this, SIGNAL(backClicked()),
164 this, SLOT(back()) );
165 connect( d->nextButton, SIGNAL(clicked()),
166 this, SIGNAL(nextClicked()) );
167 connect( this, SIGNAL(nextClicked()),
168 this, SLOT(next()) );
169 connect( d->finishButton, SIGNAL(clicked()),
170 this, SLOT(accept()) );
171 connect( d->cancelButton, SIGNAL(clicked()),
172 this, SLOT(reject()) );
173 connect( d->helpButton, SIGNAL(clicked()),
174 this, SLOT(help()) );
175 connect( d->aboutButton, SIGNAL(clicked()),
176 this, SIGNAL(aboutClicked()) );
179 d->accel = new QAccel( this, "arrow-key accel" );
180 d->backAccel = d->accel->insertItem( Qt::ALT + Qt::Key_Left );
181 d->accel->connectItem( d->backAccel, this, SIGNAL(backClicked()) );
182 d->nextAccel = d->accel->insertItem( Qt::ALT + Qt::Key_Right );
183 d->accel->connectItem( d->nextAccel, this, SIGNAL(nextClicked()) );
186 showAboutBtn( false );
191 Destroys the object and frees any allocated resources, including
192 all pages and controllers.
195 InstallWizard::~InstallWizard()
203 void InstallWizard::show()
206 showPage( d->current->w );
207 else if ( pageCount() > 0 )
208 showPage( d->pages.at( 0 )->w );
218 void InstallWizard::setFont( const QFont & font )
220 QApplication::postEvent( this, new QEvent( QEvent::LayoutHint ) );
221 QDialog::setFont( font );
225 /*! Adds \a page to the end of the page sequence, with the title, \a title.
228 void InstallWizard::addPage( QWidget * page, const QString & title )
232 if ( d->page( page ) ) {
233 #if defined(QT_CHECK_STATE)
234 qWarning( "InstallWizard::addPage(): already added %s/%s to %s/%s",
235 page->className(), page->name(),
236 className(), name() );
240 int i = d->pages.count();
243 d->pages.at( i - 1 )->nextEnabled = TRUE;
245 InstallWizardPrivate::Page * p = new InstallWizardPrivate::Page( page, title );
246 p->backEnabled = ( i > 0 );
247 d->ws->addWidget( page, i );
248 d->pages.append( p );
252 Inserts \a page at position \a index into the page sequence, with
253 title \a title. If \a index is -1, the page will be appended to
254 the end of the wizard's page sequence.
257 void InstallWizard::insertPage( QWidget * page, const QString & title, int index )
261 if ( d->page( page ) ) {
262 #if defined(QT_CHECK_STATE)
263 qWarning( "InstallWizard::insertPage(): already added %s/%s to %s/%s",
264 page->className(), page->name(),
265 className(), name() );
270 if ( index < 0 || index > (int)d->pages.count() )
271 index = d->pages.count();
273 if( index > 0 && ( index == (int)d->pages.count() ) )
274 d->pages.at( index - 1 )->nextEnabled = TRUE;
276 InstallWizardPrivate::Page * p = new InstallWizardPrivate::Page( page, title );
277 p->backEnabled = ( index > 0 );
278 p->nextEnabled = ( index < (int)d->pages.count() );
280 d->ws->addWidget( page, index );
281 d->pages.insert( index, p );
285 \fn void InstallWizard::selected(const QString&)
287 This signal is emitted when the current page changes. The parameter
288 contains the title of the selected page.
292 /*! Makes \a page the current page and emits the selected() signal. */
294 void InstallWizard::showPage( QWidget * page )
296 InstallWizardPrivate::Page * p = d->page( page );
299 for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != p; i++ );
300 bool notFirst( FALSE );
304 while( ( i >= 0 ) && !notFirst ) {
305 notFirst |= appropriate( d->pages.at( i )->w );
309 setBackEnabled( notFirst );
310 setNextEnabled( TRUE );
311 d->ws->raiseWidget( page );
317 emit selected( p ? p->t : QString::null );
321 /*! Returns the number of pages in the wizard. */
323 int InstallWizard::pageCount() const
325 return d->pages.count();
329 Returns the position of page \a page.
330 If the page is not part of the wizard -1 is returned.
333 int InstallWizard::indexOf( QWidget* page ) const
335 InstallWizardPrivate::Page * p = d->page( page );
338 return d->pages.find( p );
342 Called when the user clicks the Back button; this function shows
343 the preceding relevant page in the sequence.
347 void InstallWizard::back()
351 while( i < (int)d->pages.count() && d->pages.at( i ) &&
352 d->current && d->pages.at( i )->w != d->current->w )
356 while( i >= 0 && ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
360 if( d->pages.at( i ) )
361 showPage( d->pages.at( i )->w );
366 Called when the user clicks the Next button, this function shows
367 the next relevant page in the sequence.
371 void InstallWizard::next()
374 while( i < (int)d->pages.count() && d->pages.at( i ) &&
375 d->current && d->pages.at( i )->w != d->current->w )
378 while( i <= (int)d->pages.count()-1 &&
379 ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
381 // if we fell of the end of the world, step back
382 while ( i > 0 && (i >= (int)d->pages.count() || !d->pages.at( i ) ) )
384 if ( d->pages.at( i ) ) {
386 setNextEnabled( false );
387 setBackEnabled( false );
388 if ( !acceptData( d->current->t ) ) {
389 setNextEnabled( true );
390 setBackEnabled( true );
394 // VSR : commented 10/02/05 --->
395 // Next page will be shown later in processValidateEvent() method
396 // this allows custom validation, for instance by using external processing threads.
397 // See SALOME_InstallWizard.cxx for details where it is used.
398 //showPage( d->pages.at( i )->w );
399 // VSR : commented 10/02/05 <---
405 \fn void InstallWizard::helpClicked()
407 This signal is emitted when the user clicks on the Help button.
410 /*! Called when the user clicks the Help button, this function emits the
411 helpClicked() signal.
414 void InstallWizard::help()
416 QWidget * page = d->ws->visibleWidget();
421 if ( page->inherits( "InstallWizardPage" ) )
422 emit ((InstallWizardPage *)page)->helpClicked();
428 Enables/disables <Back> button
430 void InstallWizard::setBackEnabled( bool enable )
432 d->backButton->setEnabled( enable );
434 d->accel->setItemEnabled( d->backAccel, enable );
439 Enables/disables <Next> button
441 void InstallWizard::setNextEnabled( bool enable )
443 d->nextButton->setEnabled( enable );
445 d->accel->setItemEnabled( d->nextAccel, enable );
450 Enables/disables <Help> button
452 void InstallWizard::setHelpEnabled( bool enable )
454 d->helpButton->setEnabled( enable );
458 \fn void InstallWizard::setFinish( QWidget *, bool )
461 Use setFinishEnabled instead
465 If \a enable is TRUE, page \a page has a Back button; otherwise \a
466 page has no Back button.
467 By default all pages have this button.
469 void InstallWizard::setBackEnabled( QWidget * page, bool enable )
471 InstallWizardPrivate::Page * p = d->page( page );
475 p->backEnabled = enable;
481 If \a enable is TRUE, page \a page has a Next button; otherwise
482 the Next button on \a page is disabled. By default all pages have
486 void InstallWizard::setNextEnabled( QWidget * page, bool enable )
488 InstallWizardPrivate::Page * p = d->page( page );
492 p->nextEnabled = enable;
498 If \a enable is TRUE, page \a page has a Finish button; otherwise \a
499 page has no Finish button.
500 By default \e no page has this button.
502 void InstallWizard::setFinishEnabled( QWidget * page, bool enable )
504 InstallWizardPrivate::Page * p = d->page( page );
508 p->finishEnabled = enable;
514 If \a enable is TRUE, page \a page has a Help button; otherwise \a
515 page has no Help button.
516 By default all pages have this button.
518 void InstallWizard::setHelpEnabled( QWidget * page, bool enable )
520 InstallWizardPrivate::Page * p = d->page( page );
524 p->helpEnabled = enable;
530 Called when the Next button is clicked; this virtual function
531 returns TRUE if \a page is relevant for display in the current
532 context; otherwise it is ignored by InstallWizard and returns FALSE. The
533 default implementation returns the value set using
534 setAppropriate(). The ultimate default is TRUE.
536 \warning The last page of the wizard will be displayed if no page is relevant
537 in the current context.
540 bool InstallWizard::appropriate( QWidget * page ) const
542 InstallWizardPrivate::Page * p = d->page( page );
543 return p ? p->appropriate : TRUE;
548 If \a appropriate is TRUE then page \a page is considered relevant
549 in the current context and should be displayed in the page sequence;
550 otherwise \a page should not be displayed in the page sequence.
554 void InstallWizard::setAppropriate( QWidget * page, bool appropriate )
556 InstallWizardPrivate::Page * p = d->page( page );
558 p->appropriate = appropriate;
562 void InstallWizard::updateButtons()
568 for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != d->current; i++ );
569 bool notFirst( FALSE );
572 while( ( i >= 0 ) && !notFirst ) {
573 notFirst |= appropriate( d->pages.at( i )->w );
577 setBackEnabled( d->current->backEnabled && notFirst );
578 setNextEnabled( d->current->nextEnabled );
579 d->finishButton->setEnabled( d->current->finishEnabled );
580 d->helpButton->setEnabled( d->current->helpEnabled );
582 if ( ( d->current->finishEnabled && !d->finishButton->isVisible() ) ||
583 ( d->current->backEnabled && !d->backButton->isVisible() ) ||
584 ( d->current->nextEnabled && !d->nextButton->isVisible() ) ||
585 ( d->current->helpEnabled && !d->helpButton->isVisible() ) )
590 /*! Returns a pointer to the current page in the sequence.
591 Although the wizard does its best to make sure that this value is
592 never 0, it can be if you try hard enough.
595 QWidget * InstallWizard::currentPage() const
597 return d->ws->visibleWidget();
601 /*! Returns the title of page \a page.
604 QString InstallWizard::title( QWidget * page ) const
606 InstallWizardPrivate::Page * p = d->page( page );
607 return p ? p->t : QString::null;
610 /*! Sets the title for page \a page to \a title.
613 void InstallWizard::setTitle( QWidget *page, const QString &title )
615 InstallWizardPrivate::Page * p = d->page( page );
618 if ( page == currentPage() )
619 d->title->setText( title );
623 \property InstallWizard::titleFont
624 \brief the font used for page titles
626 The default is QApplication::font().
628 QFont InstallWizard::titleFont() const
630 return d->title->font();
633 void InstallWizard::setTitleFont( const QFont & font )
635 d->title->setFont( font );
640 Returns a pointer to the dialog's Back button
642 By default, this button is connected to the back() slot,
643 which is virtual so you can reimplement it in a InstallWizard subclass.
645 QPushButton * InstallWizard::backButton() const
647 return d->backButton;
652 Returns a pointer to the dialog's Next button
654 By default, this button is connected to the next() slot,
655 which is virtual so you can reimplement it in a InstallWizard subclass.
657 QPushButton * InstallWizard::nextButton() const
659 return d->nextButton;
664 Returns a pointer to the dialog's Finish button
666 By default, this button is connected to the QDialog::accept() slot,
667 which is virtual so you can reimplement it in a InstallWizard subclass.
669 QPushButton * InstallWizard::finishButton() const
671 return d->finishButton;
676 Returns a pointer to the dialog's Cancel button
678 By default, this button is connected to the QDialog::reject() slot,
679 which is virtual so you can reimplement it in a InstallWizard subclass.
681 QPushButton * InstallWizard::cancelButton() const
683 return d->cancelButton;
688 Returns a pointer to the dialog's Help button
690 By default, this button is connected to the help() slot,
691 which is virtual so you can reimplement it in a InstallWizard subclass.
693 QPushButton * InstallWizard::helpButton() const
695 return d->helpButton;
699 /*! This virtual function is responsible for adding the bottom
700 divider and the buttons below it.
702 \a layout is the vertical layout of the entire wizard.
705 void InstallWizard::layOutButtonRow( QHBoxLayout * layout )
707 bool hasHelp = FALSE;
708 bool hasEarlyFinish = FALSE;
710 int i = d->pages.count() - 2;
711 while ( !hasEarlyFinish && i >= 0 ) {
712 if ( d->pages.at( i ) && d->pages.at( i )->finishEnabled )
713 hasEarlyFinish = TRUE;
717 while ( !hasHelp && i < (int)d->pages.count() ) {
718 if ( d->pages.at( i ) && d->pages.at( i )->helpEnabled )
723 QBoxLayout * h = new QBoxLayout( QBoxLayout::LeftToRight );
724 layout->addLayout( h );
726 h->addWidget( d->cancelButton );
730 h->addWidget( d->backButton );
734 if ( hasEarlyFinish ) {
735 d->nextButton->show();
736 d->finishButton->show();
737 h->addWidget( d->nextButton );
739 h->addWidget( d->finishButton );
740 } else if ( d->pages.count() == 0 ||
741 d->current->finishEnabled ||
742 d->current == d->pages.at( d->pages.count()-1 ) ) {
743 d->nextButton->hide();
744 d->finishButton->show();
745 h->addWidget( d->finishButton );
747 d->nextButton->show();
748 d->finishButton->hide();
749 h->addWidget( d->nextButton );
752 // if last page is disabled - show finished btn. at lastpage-1
753 i = d->pages.count()-1;
754 if ( i >= 0 && !appropriate( d->pages.at( i )->w ) &&
755 d->current == d->pages.at( d->pages.count()-2 ) ) {
756 d->nextButton->hide();
757 d->finishButton->show();
758 h->addWidget( d->finishButton );
763 h->addWidget( d->helpButton );
765 d->helpButton->hide();
771 This virtual function is responsible for laying out the title row
772 and adding the vertical divider between the title and the wizard
773 page. \a layout is the vertical layout for the wizard, and \a
774 title is the title for this page. This function is called every
775 time \a title changes.
778 void InstallWizard::layOutTitleRow( QHBoxLayout * layout, const QString & title )
780 d->title->setText( title );
781 layout->addWidget( d->titleBox, 10 );
785 Validates page when 'Next' or 'Finish' button is clicked.
786 Should return true in success
788 bool InstallWizard::acceptData( const QString& )
790 postValidateEvent( this );
798 void InstallWizard::layOut()
801 d->v = new QVBoxLayout( this, 11, 0, "top-level layout" );
804 l = new QHBoxLayout( 6 );
805 d->v->addLayout( l, 0 );
806 layOutTitleRow( l, d->current ? d->current->t : QString::null );
809 d->hbar1 = new QFrame( this, "<hr>", 0 );
810 d->hbar1->setFrameStyle( QFrame::Sunken + QFrame::HLine );
811 d->hbar1->setFixedHeight( 12 );
814 d->v->addWidget( d->hbar1 );
816 d->v->addWidget( d->ws, 10 );
819 d->hbar2 = new QFrame( this, "<hr>", 0 );
820 d->hbar2->setFrameStyle( QFrame::Sunken + QFrame::HLine );
821 d->hbar2->setFixedHeight( 12 );
823 d->v->addWidget( d->hbar2 );
825 l = new QHBoxLayout( 6 );
826 d->v->addLayout( l );
827 layOutButtonRow( l );
834 bool InstallWizard::eventFilter( QObject * o, QEvent * e )
836 if ( o == d->ws && e && e->type() == QEvent::ChildRemoved ) {
837 QChildEvent * c = (QChildEvent*)e;
838 if ( c->child() && c->child()->isWidgetType() )
839 removePage( (QWidget *)c->child() );
841 return QDialog::eventFilter( o, e );
846 Removes \a page from the page sequence but does not delete the page.
847 If \a page is currently being displayed, InstallWizard will display the
848 page that precedes it, or the first page if this was the first page.
851 void InstallWizard::removePage( QWidget * page )
856 int i = d->pages.count();
857 QWidget* cp = currentPage();
858 while( --i >= 0 && d->pages.at( i ) && d->pages.at( i )->w != page ) { }
861 InstallWizardPrivate::Page * p = d->pages.at( i );
862 d->pages.removeRef( p );
863 d->ws->removeWidget( page );
869 if ( pageCount() > 0 )
870 showPage( InstallWizard::page( i ) );
876 Returns a pointer to the page at position \a index in the sequence,
877 or 0 if \a index is out of range. The first page has index 0.
880 QWidget* InstallWizard::page( int index ) const
882 if ( index >= pageCount() || index < 0 )
885 return d->pages.at( index )->w;
889 Returns a pointer to the page with a title \a title in the sequence,
892 QWidget* InstallWizard::page( const QString& title ) const
894 for( int i = 0; i < (int)d->pages.count(); i++ ) {
895 if ( d->pages.at( i )->t == title )
896 return d->pages.at( i )->w;
902 Adds logo to be shown at the right of the page title
904 void InstallWizard::addLogo( const QPixmap& pm )
906 QLabel* logo = new QLabel( d->logoBox, "logo" );
907 logo->setPixmap( pm );
908 logo->setAlignment( AlignCenter );
909 logo->setScaledContents( false );
916 void InstallWizard::removeLogos()
918 QObjectList* children = d->logoBox->queryList( "QLabel" );
920 QObjectListIt it( *children );
922 while ( (obj = it.current()) != 0 ) {
931 Show/hide "About" button
933 void InstallWizard::showAboutBtn( bool show )
935 show ? d->aboutButton->show() : d->aboutButton->hide();
939 Set icon for "About" button
941 void InstallWizard::setAboutIcon( const QPixmap& px )
943 d->aboutButton->setIconSet( px );
947 Posts validation event
949 void InstallWizard::postValidateEvent( InstallWizard* iw, const int val, void* data )
951 #if QT_VERSION > 0x030005
952 QApplication::postEvent( iw, new ProcessEvent( val, data ) );
954 QThread::postEvent( iw, new ProcessEvent( val, data ) );
959 Processes validation event: default implementation just to show next page
961 void InstallWizard::processValidateEvent( const int /* val */, void* /* data */ )
964 while( i < (int)d->pages.count() && d->pages.at( i ) &&
965 d->current && d->pages.at( i )->w != d->current->w )
968 while( i <= (int)d->pages.count()-1 &&
969 ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
971 // if we fell of the end of the world, step back
972 while ( i > 0 && (i >= (int)d->pages.count() || !d->pages.at( i ) ) )
974 if ( d->pages.at( i ) ) {
975 showPage( d->pages.at( i )->w );
977 setNextEnabled( true );
978 setBackEnabled( true );
982 Process events received
984 bool InstallWizard::event ( QEvent* e )
986 if ( e->type() == PROCESS_EVENT ) {
987 ProcessEvent* pe = (ProcessEvent*)e;
988 processValidateEvent( pe->returnValue(), pe->data() );
990 return QDialog::event( e );