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, SIGNAL(backClicked()) );
159 connect( this, SIGNAL(backClicked()),
160 this, SLOT(back()) );
161 connect( d->nextButton, SIGNAL(clicked()),
162 this, SIGNAL(nextClicked()) );
163 connect( this, SIGNAL(nextClicked()),
164 this, SLOT(next()) );
165 connect( d->finishButton, SIGNAL(clicked()),
166 this, SLOT(accept()) );
167 connect( d->cancelButton, SIGNAL(clicked()),
168 this, SLOT(reject()) );
169 connect( d->helpButton, SIGNAL(clicked()),
170 this, SLOT(help()) );
173 d->accel = new QAccel( this, "arrow-key accel" );
174 d->backAccel = d->accel->insertItem( Qt::ALT + Qt::Key_Left );
175 d->accel->connectItem( d->backAccel, this, SIGNAL(backClicked()) );
176 d->nextAccel = d->accel->insertItem( Qt::ALT + Qt::Key_Right );
177 d->accel->connectItem( d->nextAccel, this, SIGNAL(nextClicked()) );
183 Destroys the object and frees any allocated resources, including
184 all pages and controllers.
187 InstallWizard::~InstallWizard()
195 void InstallWizard::show()
198 showPage( d->current->w );
199 else if ( pageCount() > 0 )
200 showPage( d->pages.at( 0 )->w );
210 void InstallWizard::setFont( const QFont & font )
212 QApplication::postEvent( this, new QEvent( QEvent::LayoutHint ) );
213 QDialog::setFont( font );
217 /*! Adds \a page to the end of the page sequence, with the title, \a title.
220 void InstallWizard::addPage( QWidget * page, const QString & title )
224 if ( d->page( page ) ) {
225 #if defined(QT_CHECK_STATE)
226 qWarning( "InstallWizard::addPage(): already added %s/%s to %s/%s",
227 page->className(), page->name(),
228 className(), name() );
232 int i = d->pages.count();
235 d->pages.at( i - 1 )->nextEnabled = TRUE;
237 InstallWizardPrivate::Page * p = new InstallWizardPrivate::Page( page, title );
238 p->backEnabled = ( i > 0 );
239 d->ws->addWidget( page, i );
240 d->pages.append( p );
244 Inserts \a page at position \a index into the page sequence, with
245 title \a title. If \a index is -1, the page will be appended to
246 the end of the wizard's page sequence.
249 void InstallWizard::insertPage( QWidget * page, const QString & title, int index )
253 if ( d->page( page ) ) {
254 #if defined(QT_CHECK_STATE)
255 qWarning( "InstallWizard::insertPage(): already added %s/%s to %s/%s",
256 page->className(), page->name(),
257 className(), name() );
262 if ( index < 0 || index > (int)d->pages.count() )
263 index = d->pages.count();
265 if( index > 0 && ( index == (int)d->pages.count() ) )
266 d->pages.at( index - 1 )->nextEnabled = TRUE;
268 InstallWizardPrivate::Page * p = new InstallWizardPrivate::Page( page, title );
269 p->backEnabled = ( index > 0 );
270 p->nextEnabled = ( index < (int)d->pages.count() );
272 d->ws->addWidget( page, index );
273 d->pages.insert( index, p );
277 \fn void InstallWizard::selected(const QString&)
279 This signal is emitted when the current page changes. The parameter
280 contains the title of the selected page.
284 /*! Makes \a page the current page and emits the selected() signal. */
286 void InstallWizard::showPage( QWidget * page )
288 InstallWizardPrivate::Page * p = d->page( page );
291 for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != p; i++ );
292 bool notFirst( FALSE );
296 while( ( i >= 0 ) && !notFirst ) {
297 notFirst |= appropriate( d->pages.at( i )->w );
301 setBackEnabled( notFirst );
302 setNextEnabled( TRUE );
303 d->ws->raiseWidget( page );
309 emit selected( p ? p->t : QString::null );
313 /*! Returns the number of pages in the wizard. */
315 int InstallWizard::pageCount() const
317 return d->pages.count();
321 Returns the position of page \a page.
322 If the page is not part of the wizard -1 is returned.
325 int InstallWizard::indexOf( QWidget* page ) const
327 InstallWizardPrivate::Page * p = d->page( page );
330 return d->pages.find( p );
334 Called when the user clicks the Back button; this function shows
335 the preceding relevant page in the sequence.
339 void InstallWizard::back()
343 while( i < (int)d->pages.count() && d->pages.at( i ) &&
344 d->current && d->pages.at( i )->w != d->current->w )
348 while( i >= 0 && ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
352 if( d->pages.at( i ) )
353 showPage( d->pages.at( i )->w );
358 Called when the user clicks the Next button, this function shows
359 the next relevant page in the sequence.
363 void InstallWizard::next()
366 while( i < (int)d->pages.count() && d->pages.at( i ) &&
367 d->current && d->pages.at( i )->w != d->current->w )
370 while( i <= (int)d->pages.count()-1 &&
371 ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
373 // if we fell of the end of the world, step back
374 while ( i > 0 && (i >= (int)d->pages.count() || !d->pages.at( i ) ) )
376 if ( d->pages.at( i ) ) {
378 setNextEnabled( false );
379 setBackEnabled( false );
380 if ( !acceptData( d->current->t ) ) {
381 setNextEnabled( true );
382 setBackEnabled( true );
386 // VSR : commented 10/02/05 --->
387 // Next page will be shown later in processValidateEvent() method
388 // this allows custom validation, for instance by using external processing threads.
389 // See SALOME_InstallWizard.cxx for details where it is used.
390 //showPage( d->pages.at( i )->w );
391 // VSR : commented 10/02/05 <---
397 \fn void InstallWizard::helpClicked()
399 This signal is emitted when the user clicks on the Help button.
402 /*! Called when the user clicks the Help button, this function emits the
403 helpClicked() signal.
406 void InstallWizard::help()
408 QWidget * page = d->ws->visibleWidget();
413 if ( page->inherits( "InstallWizardPage" ) )
414 emit ((InstallWizardPage *)page)->helpClicked();
420 void InstallWizard::setBackEnabled( bool enable )
422 d->backButton->setEnabled( enable );
424 d->accel->setItemEnabled( d->backAccel, enable );
429 void InstallWizard::setNextEnabled( bool enable )
431 d->nextButton->setEnabled( enable );
433 d->accel->setItemEnabled( d->nextAccel, enable );
438 void InstallWizard::setHelpEnabled( bool enable )
440 d->helpButton->setEnabled( enable );
445 \fn void InstallWizard::setFinish( QWidget *, bool )
448 Use setFinishEnabled instead
452 If \a enable is TRUE, page \a page has a Back button; otherwise \a
453 page has no Back button.
454 By default all pages have this button.
456 void InstallWizard::setBackEnabled( QWidget * page, bool enable )
458 InstallWizardPrivate::Page * p = d->page( page );
462 p->backEnabled = enable;
468 If \a enable is TRUE, page \a page has a Next button; otherwise
469 the Next button on \a page is disabled. By default all pages have
473 void InstallWizard::setNextEnabled( QWidget * page, bool enable )
475 InstallWizardPrivate::Page * p = d->page( page );
479 p->nextEnabled = enable;
485 If \a enable is TRUE, page \a page has a Finish button; otherwise \a
486 page has no Finish button.
487 By default \e no page has this button.
489 void InstallWizard::setFinishEnabled( QWidget * page, bool enable )
491 InstallWizardPrivate::Page * p = d->page( page );
495 p->finishEnabled = enable;
501 If \a enable is TRUE, page \a page has a Help button; otherwise \a
502 page has no Help button.
503 By default all pages have this button.
505 void InstallWizard::setHelpEnabled( QWidget * page, bool enable )
507 InstallWizardPrivate::Page * p = d->page( page );
511 p->helpEnabled = enable;
517 Called when the Next button is clicked; this virtual function
518 returns TRUE if \a page is relevant for display in the current
519 context; otherwise it is ignored by InstallWizard and returns FALSE. The
520 default implementation returns the value set using
521 setAppropriate(). The ultimate default is TRUE.
523 \warning The last page of the wizard will be displayed if no page is relevant
524 in the current context.
527 bool InstallWizard::appropriate( QWidget * page ) const
529 InstallWizardPrivate::Page * p = d->page( page );
530 return p ? p->appropriate : TRUE;
535 If \a appropriate is TRUE then page \a page is considered relevant
536 in the current context and should be displayed in the page sequence;
537 otherwise \a page should not be displayed in the page sequence.
541 void InstallWizard::setAppropriate( QWidget * page, bool appropriate )
543 InstallWizardPrivate::Page * p = d->page( page );
545 p->appropriate = appropriate;
549 void InstallWizard::updateButtons()
555 for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != d->current; i++ );
556 bool notFirst( FALSE );
559 while( ( i >= 0 ) && !notFirst ) {
560 notFirst |= appropriate( d->pages.at( i )->w );
564 setBackEnabled( d->current->backEnabled && notFirst );
565 setNextEnabled( d->current->nextEnabled );
566 d->finishButton->setEnabled( d->current->finishEnabled );
567 d->helpButton->setEnabled( d->current->helpEnabled );
569 if ( ( d->current->finishEnabled && !d->finishButton->isVisible() ) ||
570 ( d->current->backEnabled && !d->backButton->isVisible() ) ||
571 ( d->current->nextEnabled && !d->nextButton->isVisible() ) ||
572 ( d->current->helpEnabled && !d->helpButton->isVisible() ) )
577 /*! Returns a pointer to the current page in the sequence.
578 Although the wizard does its best to make sure that this value is
579 never 0, it can be if you try hard enough.
582 QWidget * InstallWizard::currentPage() const
584 return d->ws->visibleWidget();
588 /*! Returns the title of page \a page.
591 QString InstallWizard::title( QWidget * page ) const
593 InstallWizardPrivate::Page * p = d->page( page );
594 return p ? p->t : QString::null;
597 /*! Sets the title for page \a page to \a title.
600 void InstallWizard::setTitle( QWidget *page, const QString &title )
602 InstallWizardPrivate::Page * p = d->page( page );
605 if ( page == currentPage() )
606 d->title->setText( title );
610 \property InstallWizard::titleFont
611 \brief the font used for page titles
613 The default is QApplication::font().
615 QFont InstallWizard::titleFont() const
617 return d->title->font();
620 void InstallWizard::setTitleFont( const QFont & font )
622 d->title->setFont( font );
627 Returns a pointer to the dialog's Back button
629 By default, this button is connected to the back() slot,
630 which is virtual so you can reimplement it in a InstallWizard subclass.
632 QPushButton * InstallWizard::backButton() const
634 return d->backButton;
639 Returns a pointer to the dialog's Next button
641 By default, this button is connected to the next() slot,
642 which is virtual so you can reimplement it in a InstallWizard subclass.
644 QPushButton * InstallWizard::nextButton() const
646 return d->nextButton;
651 Returns a pointer to the dialog's Finish button
653 By default, this button is connected to the QDialog::accept() slot,
654 which is virtual so you can reimplement it in a InstallWizard subclass.
656 QPushButton * InstallWizard::finishButton() const
658 return d->finishButton;
663 Returns a pointer to the dialog's Cancel button
665 By default, this button is connected to the QDialog::reject() slot,
666 which is virtual so you can reimplement it in a InstallWizard subclass.
668 QPushButton * InstallWizard::cancelButton() const
670 return d->cancelButton;
675 Returns a pointer to the dialog's Help button
677 By default, this button is connected to the help() slot,
678 which is virtual so you can reimplement it in a InstallWizard subclass.
680 QPushButton * InstallWizard::helpButton() const
682 return d->helpButton;
686 /*! This virtual function is responsible for adding the bottom
687 divider and the buttons below it.
689 \a layout is the vertical layout of the entire wizard.
692 void InstallWizard::layOutButtonRow( QHBoxLayout * layout )
694 bool hasHelp = FALSE;
695 bool hasEarlyFinish = FALSE;
697 int i = d->pages.count() - 2;
698 while ( !hasEarlyFinish && i >= 0 ) {
699 if ( d->pages.at( i ) && d->pages.at( i )->finishEnabled )
700 hasEarlyFinish = TRUE;
704 while ( !hasHelp && i < (int)d->pages.count() ) {
705 if ( d->pages.at( i ) && d->pages.at( i )->helpEnabled )
710 QBoxLayout * h = new QBoxLayout( QBoxLayout::LeftToRight );
711 layout->addLayout( h );
713 h->addWidget( d->cancelButton );
717 h->addWidget( d->backButton );
721 if ( hasEarlyFinish ) {
722 d->nextButton->show();
723 d->finishButton->show();
724 h->addWidget( d->nextButton );
726 h->addWidget( d->finishButton );
727 } else if ( d->pages.count() == 0 ||
728 d->current->finishEnabled ||
729 d->current == d->pages.at( d->pages.count()-1 ) ) {
730 d->nextButton->hide();
731 d->finishButton->show();
732 h->addWidget( d->finishButton );
734 d->nextButton->show();
735 d->finishButton->hide();
736 h->addWidget( d->nextButton );
739 // if last page is disabled - show finished btn. at lastpage-1
740 i = d->pages.count()-1;
741 if ( i >= 0 && !appropriate( d->pages.at( i )->w ) &&
742 d->current == d->pages.at( d->pages.count()-2 ) ) {
743 d->nextButton->hide();
744 d->finishButton->show();
745 h->addWidget( d->finishButton );
750 h->addWidget( d->helpButton );
752 d->helpButton->hide();
758 This virtual function is responsible for laying out the title row
759 and adding the vertical divider between the title and the wizard
760 page. \a layout is the vertical layout for the wizard, and \a
761 title is the title for this page. This function is called every
762 time \a title changes.
765 void InstallWizard::layOutTitleRow( QHBoxLayout * layout, const QString & title )
767 d->title->setText( title );
768 layout->addWidget( d->titleBox, 10 );
772 Validates page when 'Next' or 'Finish' button is clicked.
773 Should return true in success
775 bool InstallWizard::acceptData( const QString& )
777 postValidateEvent( this );
785 void InstallWizard::layOut()
788 d->v = new QVBoxLayout( this, 11, 0, "top-level layout" );
791 l = new QHBoxLayout( 6 );
792 d->v->addLayout( l, 0 );
793 layOutTitleRow( l, d->current ? d->current->t : QString::null );
796 d->hbar1 = new QFrame( this, "<hr>", 0 );
797 d->hbar1->setFrameStyle( QFrame::Sunken + QFrame::HLine );
798 d->hbar1->setFixedHeight( 12 );
801 d->v->addWidget( d->hbar1 );
803 d->v->addWidget( d->ws, 10 );
806 d->hbar2 = new QFrame( this, "<hr>", 0 );
807 d->hbar2->setFrameStyle( QFrame::Sunken + QFrame::HLine );
808 d->hbar2->setFixedHeight( 12 );
810 d->v->addWidget( d->hbar2 );
812 l = new QHBoxLayout( 6 );
813 d->v->addLayout( l );
814 layOutButtonRow( l );
821 bool InstallWizard::eventFilter( QObject * o, QEvent * e )
823 if ( o == d->ws && e && e->type() == QEvent::ChildRemoved ) {
824 QChildEvent * c = (QChildEvent*)e;
825 if ( c->child() && c->child()->isWidgetType() )
826 removePage( (QWidget *)c->child() );
828 return QDialog::eventFilter( o, e );
833 Removes \a page from the page sequence but does not delete the page.
834 If \a page is currently being displayed, InstallWizard will display the
835 page that precedes it, or the first page if this was the first page.
838 void InstallWizard::removePage( QWidget * page )
843 int i = d->pages.count();
844 QWidget* cp = currentPage();
845 while( --i >= 0 && d->pages.at( i ) && d->pages.at( i )->w != page ) { }
848 InstallWizardPrivate::Page * p = d->pages.at( i );
849 d->pages.removeRef( p );
850 d->ws->removeWidget( page );
856 if ( pageCount() > 0 )
857 showPage( InstallWizard::page( i ) );
863 Returns a pointer to the page at position \a index in the sequence,
864 or 0 if \a index is out of range. The first page has index 0.
867 QWidget* InstallWizard::page( int index ) const
869 if ( index >= pageCount() || index < 0 )
872 return d->pages.at( index )->w;
876 Returns a pointer to the page with a title \a title in the sequence,
879 QWidget* InstallWizard::page( const QString& title ) const
881 for( int i = 0; i < (int)d->pages.count(); i++ ) {
882 if ( d->pages.at( i )->t == title )
883 return d->pages.at( i )->w;
889 Adds logo to be shown at the right of the page title
891 void InstallWizard::addLogo( const QPixmap& pm )
893 QLabel* logo = new QLabel( d->logoBox, "logo" );
894 logo->setPixmap( pm );
895 logo->setAlignment( AlignCenter );
896 logo->setScaledContents( false );
903 void InstallWizard::removeLogos()
905 QObjectList* children = d->logoBox->queryList( "QLabel" );
907 QObjectListIt it( *children );
909 while ( (obj = it.current()) != 0 ) {
918 Posts validation event
920 void InstallWizard::postValidateEvent( InstallWizard* iw, const int val, void* data )
922 QThread::postEvent( iw, new ProcessEvent( val, data ) );
926 Processes validation event: default implementation just to show next page
928 void InstallWizard::processValidateEvent( const int /* val */, void* /* data */ )
931 while( i < (int)d->pages.count() && d->pages.at( i ) &&
932 d->current && d->pages.at( i )->w != d->current->w )
935 while( i <= (int)d->pages.count()-1 &&
936 ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
938 // if we fell of the end of the world, step back
939 while ( i > 0 && (i >= (int)d->pages.count() || !d->pages.at( i ) ) )
941 if ( d->pages.at( i ) ) {
942 showPage( d->pages.at( i )->w );
944 setNextEnabled( true );
945 setBackEnabled( true );
949 Process events received
951 bool InstallWizard::event ( QEvent* e )
953 if ( e->type() == PROCESS_EVENT ) {
954 ProcessEvent* pe = (ProcessEvent*)e;
955 processValidateEvent( pe->returnValue(), pe->data() );
957 return QDialog::event( e );