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>
44 #include <qwidgetstack.h>
45 #include <qapplication.h>
50 #include <qobjectlist.h>
53 #define PROCESS_EVENT QEvent::User+100
55 class ProcessEvent : public QCustomEvent
58 ProcessEvent( int retValue = 0, void* data = 0 ): QCustomEvent( PROCESS_EVENT ), myReturnValue( retValue ), myData( data ) {}
59 const int returnValue() const { return myReturnValue; }
60 void* data() const { return myData; }
66 class InstallWizardPrivate
70 Page( QWidget * widget, const QString & title ):
71 w( widget ), t( title ),
72 backEnabled( TRUE ), nextEnabled( TRUE ), finishEnabled( FALSE ),
92 QPushButton * backButton;
93 QPushButton * nextButton;
94 QPushButton * finishButton;
95 QPushButton * cancelButton;
96 QPushButton * helpButton;
97 QFrame * hbar1, * hbar2;
105 Page * page( const QWidget * w )
109 int i = pages.count();
110 while( --i >= 0 && pages.at( i ) && pages.at( i )->w != w ) { }
111 return i >= 0 ? pages.at( i ) : 0;
116 /*! Constructs an empty wizard dialog.
117 The \a parent, \a name, \a modal and \a f arguments are passed to
118 the QDialog constructor.
122 InstallWizard::InstallWizard( QWidget *parent, const char *name, bool modal,
124 : QDialog( parent, name, modal, f )
126 d = new InstallWizardPrivate();
127 d->current = 0; // not quite true, but...
128 d->ws = new QWidgetStack( this, "qt_widgetstack" );
129 d->pages.setAutoDelete( TRUE );
130 d->titleBox = new QHBox( this, "title box" );
131 d->title = new QLabel( d->titleBox, "title label" );
132 d->logoBox = new QHBox( d->titleBox, "logo box" );
133 d->logoBox->setSpacing( 2 );
134 d->titleBox->setStretchFactor( d->title, 10 );
136 // create in nice tab order
137 d->nextButton = new QPushButton( this, "next" );
138 d->finishButton = new QPushButton( this, "finish" );
139 d->helpButton = new QPushButton( this, "help" );
140 d->backButton = new QPushButton( this, "back" );
141 d->cancelButton = new QPushButton( this, "cancel" );
143 d->ws->installEventFilter( this );
149 d->cancelButton->setText( tr( "&Cancel" ) );
150 d->backButton->setText( tr( "< &Back" ) );
151 d->nextButton->setText( tr( "&Next >" ) );
152 d->finishButton->setText( tr( "&Finish" ) );
153 d->helpButton->setText( tr( "&Help" ) );
155 d->nextButton->setDefault( TRUE );
157 connect( d->backButton, SIGNAL(clicked()),
158 this, SLOT(back()) );
159 connect( d->nextButton, SIGNAL(clicked()),
160 this, SLOT(next()) );
161 connect( d->finishButton, SIGNAL(clicked()),
162 this, SLOT(accept()) );
163 connect( d->cancelButton, SIGNAL(clicked()),
164 this, SLOT(reject()) );
165 connect( d->helpButton, SIGNAL(clicked()),
166 this, SLOT(help()) );
169 d->accel = new QAccel( this, "arrow-key accel" );
170 d->backAccel = d->accel->insertItem( Qt::ALT + Qt::Key_Left );
171 d->accel->connectItem( d->backAccel, this, SLOT(back()) );
172 d->nextAccel = d->accel->insertItem( Qt::ALT + Qt::Key_Right );
173 d->accel->connectItem( d->nextAccel, this, SLOT(next()) );
179 Destroys the object and frees any allocated resources, including
180 all pages and controllers.
183 InstallWizard::~InstallWizard()
191 void InstallWizard::show()
194 showPage( d->current->w );
195 else if ( pageCount() > 0 )
196 showPage( d->pages.at( 0 )->w );
206 void InstallWizard::setFont( const QFont & font )
208 QApplication::postEvent( this, new QEvent( QEvent::LayoutHint ) );
209 QDialog::setFont( font );
213 /*! Adds \a page to the end of the page sequence, with the title, \a title.
216 void InstallWizard::addPage( QWidget * page, const QString & title )
220 if ( d->page( page ) ) {
221 #if defined(QT_CHECK_STATE)
222 qWarning( "InstallWizard::addPage(): already added %s/%s to %s/%s",
223 page->className(), page->name(),
224 className(), name() );
228 int i = d->pages.count();
231 d->pages.at( i - 1 )->nextEnabled = TRUE;
233 InstallWizardPrivate::Page * p = new InstallWizardPrivate::Page( page, title );
234 p->backEnabled = ( i > 0 );
235 d->ws->addWidget( page, i );
236 d->pages.append( p );
240 Inserts \a page at position \a index into the page sequence, with
241 title \a title. If \a index is -1, the page will be appended to
242 the end of the wizard's page sequence.
245 void InstallWizard::insertPage( QWidget * page, const QString & title, int index )
249 if ( d->page( page ) ) {
250 #if defined(QT_CHECK_STATE)
251 qWarning( "InstallWizard::insertPage(): already added %s/%s to %s/%s",
252 page->className(), page->name(),
253 className(), name() );
258 if ( index < 0 || index > (int)d->pages.count() )
259 index = d->pages.count();
261 if( index > 0 && ( index == (int)d->pages.count() ) )
262 d->pages.at( index - 1 )->nextEnabled = TRUE;
264 InstallWizardPrivate::Page * p = new InstallWizardPrivate::Page( page, title );
265 p->backEnabled = ( index > 0 );
266 p->nextEnabled = ( index < (int)d->pages.count() );
268 d->ws->addWidget( page, index );
269 d->pages.insert( index, p );
273 \fn void InstallWizard::selected(const QString&)
275 This signal is emitted when the current page changes. The parameter
276 contains the title of the selected page.
280 /*! Makes \a page the current page and emits the selected() signal. */
282 void InstallWizard::showPage( QWidget * page )
284 InstallWizardPrivate::Page * p = d->page( page );
287 for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != p; i++ );
288 bool notFirst( FALSE );
292 while( ( i >= 0 ) && !notFirst ) {
293 notFirst |= appropriate( d->pages.at( i )->w );
297 setBackEnabled( notFirst );
298 setNextEnabled( TRUE );
299 d->ws->raiseWidget( page );
305 emit selected( p ? p->t : QString::null );
309 /*! Returns the number of pages in the wizard. */
311 int InstallWizard::pageCount() const
313 return d->pages.count();
317 Returns the position of page \a page.
318 If the page is not part of the wizard -1 is returned.
321 int InstallWizard::indexOf( QWidget* page ) const
323 InstallWizardPrivate::Page * p = d->page( page );
326 return d->pages.find( p );
330 Called when the user clicks the Back button; this function shows
331 the preceding relevant page in the sequence.
335 void InstallWizard::back()
339 while( i < (int)d->pages.count() && d->pages.at( i ) &&
340 d->current && d->pages.at( i )->w != d->current->w )
344 while( i >= 0 && ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
348 if( d->pages.at( i ) )
349 showPage( d->pages.at( i )->w );
354 Called when the user clicks the Next button, this function shows
355 the next relevant page in the sequence.
359 void InstallWizard::next()
362 while( i < (int)d->pages.count() && d->pages.at( i ) &&
363 d->current && d->pages.at( i )->w != d->current->w )
366 while( i <= (int)d->pages.count()-1 &&
367 ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
369 // if we fell of the end of the world, step back
370 while ( i > 0 && (i >= (int)d->pages.count() || !d->pages.at( i ) ) )
372 if ( d->pages.at( i ) ) {
374 setNextEnabled( false );
375 setBackEnabled( false );
376 if ( !acceptData( d->current->t ) ) {
377 setNextEnabled( true );
378 setBackEnabled( true );
382 // VSR : commented 10/02/05 --->
383 // Next page will be shown later in processValidateEvent() method
384 // this allows custom validation, for instance by using external processing threads.
385 // See SALOME_InstallWizard.cxx for details where it is used.
386 //showPage( d->pages.at( i )->w );
387 // VSR : commented 10/02/05 <---
393 \fn void InstallWizard::helpClicked()
395 This signal is emitted when the user clicks on the Help button.
398 /*! Called when the user clicks the Help button, this function emits the
399 helpClicked() signal.
402 void InstallWizard::help()
404 QWidget * page = d->ws->visibleWidget();
409 if ( page->inherits( "InstallWizardPage" ) )
410 emit ((InstallWizardPage *)page)->helpClicked();
416 void InstallWizard::setBackEnabled( bool enable )
418 d->backButton->setEnabled( enable );
420 d->accel->setItemEnabled( d->backAccel, enable );
425 void InstallWizard::setNextEnabled( bool enable )
427 d->nextButton->setEnabled( enable );
429 d->accel->setItemEnabled( d->nextAccel, enable );
434 void InstallWizard::setHelpEnabled( bool enable )
436 d->helpButton->setEnabled( enable );
441 \fn void InstallWizard::setFinish( QWidget *, bool )
444 Use setFinishEnabled instead
448 If \a enable is TRUE, page \a page has a Back button; otherwise \a
449 page has no Back button.
450 By default all pages have this button.
452 void InstallWizard::setBackEnabled( QWidget * page, bool enable )
454 InstallWizardPrivate::Page * p = d->page( page );
458 p->backEnabled = enable;
464 If \a enable is TRUE, page \a page has a Next button; otherwise
465 the Next button on \a page is disabled. By default all pages have
469 void InstallWizard::setNextEnabled( QWidget * page, bool enable )
471 InstallWizardPrivate::Page * p = d->page( page );
475 p->nextEnabled = enable;
481 If \a enable is TRUE, page \a page has a Finish button; otherwise \a
482 page has no Finish button.
483 By default \e no page has this button.
485 void InstallWizard::setFinishEnabled( QWidget * page, bool enable )
487 InstallWizardPrivate::Page * p = d->page( page );
491 p->finishEnabled = enable;
497 If \a enable is TRUE, page \a page has a Help button; otherwise \a
498 page has no Help button.
499 By default all pages have this button.
501 void InstallWizard::setHelpEnabled( QWidget * page, bool enable )
503 InstallWizardPrivate::Page * p = d->page( page );
507 p->helpEnabled = enable;
513 Called when the Next button is clicked; this virtual function
514 returns TRUE if \a page is relevant for display in the current
515 context; otherwise it is ignored by InstallWizard and returns FALSE. The
516 default implementation returns the value set using
517 setAppropriate(). The ultimate default is TRUE.
519 \warning The last page of the wizard will be displayed if no page is relevant
520 in the current context.
523 bool InstallWizard::appropriate( QWidget * page ) const
525 InstallWizardPrivate::Page * p = d->page( page );
526 return p ? p->appropriate : TRUE;
531 If \a appropriate is TRUE then page \a page is considered relevant
532 in the current context and should be displayed in the page sequence;
533 otherwise \a page should not be displayed in the page sequence.
537 void InstallWizard::setAppropriate( QWidget * page, bool appropriate )
539 InstallWizardPrivate::Page * p = d->page( page );
541 p->appropriate = appropriate;
545 void InstallWizard::updateButtons()
551 for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != d->current; i++ );
552 bool notFirst( FALSE );
555 while( ( i >= 0 ) && !notFirst ) {
556 notFirst |= appropriate( d->pages.at( i )->w );
560 setBackEnabled( d->current->backEnabled && notFirst );
561 setNextEnabled( d->current->nextEnabled );
562 d->finishButton->setEnabled( d->current->finishEnabled );
563 d->helpButton->setEnabled( d->current->helpEnabled );
565 if ( ( d->current->finishEnabled && !d->finishButton->isVisible() ) ||
566 ( d->current->backEnabled && !d->backButton->isVisible() ) ||
567 ( d->current->nextEnabled && !d->nextButton->isVisible() ) ||
568 ( d->current->helpEnabled && !d->helpButton->isVisible() ) )
573 /*! Returns a pointer to the current page in the sequence.
574 Although the wizard does its best to make sure that this value is
575 never 0, it can be if you try hard enough.
578 QWidget * InstallWizard::currentPage() const
580 return d->ws->visibleWidget();
584 /*! Returns the title of page \a page.
587 QString InstallWizard::title( QWidget * page ) const
589 InstallWizardPrivate::Page * p = d->page( page );
590 return p ? p->t : QString::null;
593 /*! Sets the title for page \a page to \a title.
596 void InstallWizard::setTitle( QWidget *page, const QString &title )
598 InstallWizardPrivate::Page * p = d->page( page );
601 if ( page == currentPage() )
602 d->title->setText( title );
606 \property InstallWizard::titleFont
607 \brief the font used for page titles
609 The default is QApplication::font().
611 QFont InstallWizard::titleFont() const
613 return d->title->font();
616 void InstallWizard::setTitleFont( const QFont & font )
618 d->title->setFont( font );
623 Returns a pointer to the dialog's Back button
625 By default, this button is connected to the back() slot,
626 which is virtual so you can reimplement it in a InstallWizard subclass.
628 QPushButton * InstallWizard::backButton() const
630 return d->backButton;
635 Returns a pointer to the dialog's Next button
637 By default, this button is connected to the next() slot,
638 which is virtual so you can reimplement it in a InstallWizard subclass.
640 QPushButton * InstallWizard::nextButton() const
642 return d->nextButton;
647 Returns a pointer to the dialog's Finish button
649 By default, this button is connected to the QDialog::accept() slot,
650 which is virtual so you can reimplement it in a InstallWizard subclass.
652 QPushButton * InstallWizard::finishButton() const
654 return d->finishButton;
659 Returns a pointer to the dialog's Cancel button
661 By default, this button is connected to the QDialog::reject() slot,
662 which is virtual so you can reimplement it in a InstallWizard subclass.
664 QPushButton * InstallWizard::cancelButton() const
666 return d->cancelButton;
671 Returns a pointer to the dialog's Help button
673 By default, this button is connected to the help() slot,
674 which is virtual so you can reimplement it in a InstallWizard subclass.
676 QPushButton * InstallWizard::helpButton() const
678 return d->helpButton;
682 /*! This virtual function is responsible for adding the bottom
683 divider and the buttons below it.
685 \a layout is the vertical layout of the entire wizard.
688 void InstallWizard::layOutButtonRow( QHBoxLayout * layout )
690 bool hasHelp = FALSE;
691 bool hasEarlyFinish = FALSE;
693 int i = d->pages.count() - 2;
694 while ( !hasEarlyFinish && i >= 0 ) {
695 if ( d->pages.at( i ) && d->pages.at( i )->finishEnabled )
696 hasEarlyFinish = TRUE;
700 while ( !hasHelp && i < (int)d->pages.count() ) {
701 if ( d->pages.at( i ) && d->pages.at( i )->helpEnabled )
706 QBoxLayout * h = new QBoxLayout( QBoxLayout::LeftToRight );
707 layout->addLayout( h );
709 h->addWidget( d->cancelButton );
713 h->addWidget( d->backButton );
717 if ( hasEarlyFinish ) {
718 d->nextButton->show();
719 d->finishButton->show();
720 h->addWidget( d->nextButton );
722 h->addWidget( d->finishButton );
723 } else if ( d->pages.count() == 0 ||
724 d->current->finishEnabled ||
725 d->current == d->pages.at( d->pages.count()-1 ) ) {
726 d->nextButton->hide();
727 d->finishButton->show();
728 h->addWidget( d->finishButton );
730 d->nextButton->show();
731 d->finishButton->hide();
732 h->addWidget( d->nextButton );
735 // if last page is disabled - show finished btn. at lastpage-1
736 i = d->pages.count()-1;
737 if ( i >= 0 && !appropriate( d->pages.at( i )->w ) &&
738 d->current == d->pages.at( d->pages.count()-2 ) ) {
739 d->nextButton->hide();
740 d->finishButton->show();
741 h->addWidget( d->finishButton );
746 h->addWidget( d->helpButton );
748 d->helpButton->hide();
754 This virtual function is responsible for laying out the title row
755 and adding the vertical divider between the title and the wizard
756 page. \a layout is the vertical layout for the wizard, and \a
757 title is the title for this page. This function is called every
758 time \a title changes.
761 void InstallWizard::layOutTitleRow( QHBoxLayout * layout, const QString & title )
763 d->title->setText( title );
764 layout->addWidget( d->titleBox, 10 );
768 Validates page when 'Next' or 'Finish' button is clicked.
769 Should return true in success
771 bool InstallWizard::acceptData( const QString& )
773 postValidateEvent( this );
781 void InstallWizard::layOut()
784 d->v = new QVBoxLayout( this, 11, 0, "top-level layout" );
787 l = new QHBoxLayout( 6 );
788 d->v->addLayout( l, 0 );
789 layOutTitleRow( l, d->current ? d->current->t : QString::null );
792 d->hbar1 = new QFrame( this, "<hr>", 0 );
793 d->hbar1->setFrameStyle( QFrame::Sunken + QFrame::HLine );
794 d->hbar1->setFixedHeight( 12 );
797 d->v->addWidget( d->hbar1 );
799 d->v->addWidget( d->ws, 10 );
802 d->hbar2 = new QFrame( this, "<hr>", 0 );
803 d->hbar2->setFrameStyle( QFrame::Sunken + QFrame::HLine );
804 d->hbar2->setFixedHeight( 12 );
806 d->v->addWidget( d->hbar2 );
808 l = new QHBoxLayout( 6 );
809 d->v->addLayout( l );
810 layOutButtonRow( l );
817 bool InstallWizard::eventFilter( QObject * o, QEvent * e )
819 if ( o == d->ws && e && e->type() == QEvent::ChildRemoved ) {
820 QChildEvent * c = (QChildEvent*)e;
821 if ( c->child() && c->child()->isWidgetType() )
822 removePage( (QWidget *)c->child() );
824 return QDialog::eventFilter( o, e );
829 Removes \a page from the page sequence but does not delete the page.
830 If \a page is currently being displayed, InstallWizard will display the
831 page that precedes it, or the first page if this was the first page.
834 void InstallWizard::removePage( QWidget * page )
839 int i = d->pages.count();
840 QWidget* cp = currentPage();
841 while( --i >= 0 && d->pages.at( i ) && d->pages.at( i )->w != page ) { }
844 InstallWizardPrivate::Page * p = d->pages.at( i );
845 d->pages.removeRef( p );
846 d->ws->removeWidget( page );
852 if ( pageCount() > 0 )
853 showPage( InstallWizard::page( i ) );
859 Returns a pointer to the page at position \a index in the sequence,
860 or 0 if \a index is out of range. The first page has index 0.
863 QWidget* InstallWizard::page( int index ) const
865 if ( index >= pageCount() || index < 0 )
868 return d->pages.at( index )->w;
872 Returns a pointer to the page with a title \a title in the sequence,
875 QWidget* InstallWizard::page( const QString& title ) const
877 for( int i = 0; i < (int)d->pages.count(); i++ ) {
878 if ( d->pages.at( i )->t == title )
879 return d->pages.at( i )->w;
885 Adds logo to be shown at the right of the page title
887 void InstallWizard::addLogo( const QPixmap& pm )
889 QLabel* logo = new QLabel( d->logoBox, "logo" );
890 logo->setPixmap( pm );
891 logo->setAlignment( AlignCenter );
892 logo->setScaledContents( false );
899 void InstallWizard::removeLogos()
901 QObjectList* children = d->logoBox->queryList( "QLabel" );
903 QObjectListIt it( *children );
905 while ( (obj = it.current()) != 0 ) {
914 Posts validation event
916 void InstallWizard::postValidateEvent( InstallWizard* iw, const int val, void* data )
918 QThread::postEvent( iw, new ProcessEvent( val, data ) );
922 Processes validation event: default implementation just to show next page
924 void InstallWizard::processValidateEvent( const int /* val */, void* /* data */ )
927 while( i < (int)d->pages.count() && d->pages.at( i ) &&
928 d->current && d->pages.at( i )->w != d->current->w )
931 while( i <= (int)d->pages.count()-1 &&
932 ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
934 // if we fell of the end of the world, step back
935 while ( i > 0 && (i >= (int)d->pages.count() || !d->pages.at( i ) ) )
937 if ( d->pages.at( i ) ) {
938 showPage( d->pages.at( i )->w );
940 setNextEnabled( true );
941 setBackEnabled( true );
945 Process events received
947 bool InstallWizard::event ( QEvent* e )
949 if ( e->type() == PROCESS_EVENT ) {
950 ProcessEvent* pe = (ProcessEvent*)e;
951 processValidateEvent( pe->returnValue(), pe->data() );
953 return QDialog::event( e );