Salome HOME
Fix misprint
[tools/install.git] / src / InstallWizard.cpp
1 /****************************************************************************
2 ** $Id$
3 **
4 ** Definition of the QWizard class.
5 **
6 ** Created : 990101
7 **
8 ** Copyright (C) 1999 by Trolltech AS.  All rights reserved.
9 **
10 ** This file is part of the dialogs module of the Qt GUI Toolkit.
11 **
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.
15 **
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.
20 **
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.
24 **
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.
27 **
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.
32 **
33 ** Contact info@trolltech.com if any conditions of this licensing are
34 ** not clear to you.
35 **
36 **********************************************************************/
37
38 #include "InstallWizard.h"
39
40 #include <qlayout.h>
41 #include <qpushbutton.h>
42 #include <qtoolbutton.h>
43 #include <qcursor.h>
44 #include <qlabel.h>
45 #include <qwidgetstack.h>
46 #include <qapplication.h>
47 #include <qptrlist.h>
48 #include <qpainter.h>
49 #include <qaccel.h>
50 #include <qhbox.h>
51 #include <qobjectlist.h>
52 #include <qthread.h>
53
54 #define PROCESS_EVENT QEvent::User+100
55
56 class ProcessEvent : public QCustomEvent
57 {
58 public:
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; }
62 private:
63   int   myReturnValue;
64   void* myData;
65 };
66
67 class InstallWizardPrivate
68 {
69 public:
70    struct Page {
71     Page( QWidget * widget, const QString & title ):
72           w( widget ), t( title ),
73     backEnabled( TRUE ), nextEnabled( TRUE ), finishEnabled( FALSE ),
74     helpEnabled( TRUE ),
75     appropriate( TRUE )
76   {}
77   QWidget * w;
78   QString t;
79   bool backEnabled;
80   bool nextEnabled;
81   bool finishEnabled;
82   bool helpEnabled;
83   bool appropriate;
84   };
85   
86   QVBoxLayout * v;
87   Page * current;
88   QWidgetStack * ws;
89   QPtrList<Page> pages;
90   QLabel * title;
91   QHBox *  titleBox;
92   QHBox *  logoBox;
93   QPushButton * backButton;
94   QPushButton * nextButton;
95   QPushButton * finishButton;
96   QPushButton * cancelButton;
97   QPushButton * helpButton;
98   QFrame * hbar1, * hbar2;
99   QToolButton * aboutButton;
100   
101 #ifndef QT_NO_ACCEL
102   QAccel * accel;
103   int backAccel;
104   int nextAccel;
105 #endif
106   
107   Page * page( const QWidget * w )
108   {
109     if ( !w )
110       return 0;
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;
114   }
115 };
116
117
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.
121
122 */
123
124 InstallWizard::InstallWizard( QWidget *parent, const char *name, bool modal,
125                              WFlags f )
126   : QDialog( parent, name, modal, f )
127 {
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" );
149   
150   d->ws->installEventFilter( this );
151   
152   d->v = 0;
153   d->hbar1 = 0;
154   d->hbar2 = 0;
155   
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" ) );
161   
162   d->nextButton->setDefault( TRUE );
163   
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()) );
180   
181 #ifndef QT_NO_ACCEL
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()) );
187 #endif
188
189   showAboutBtn( false );
190 }
191
192
193 /*!
194 Destroys the object and frees any allocated resources, including
195 all pages and controllers.
196 */
197
198 InstallWizard::~InstallWizard()
199 {
200   delete d;
201 }
202
203
204 /*!  \reimp  */
205
206 void InstallWizard::show()
207 {
208   if ( d->current )
209     showPage( d->current->w );
210   else if ( pageCount() > 0 )
211     showPage( d->pages.at( 0 )->w );
212   else
213     showPage( 0 );
214   
215   QDialog::show();
216 }
217
218
219 /*! \reimp */
220
221 void InstallWizard::setFont( const QFont & font )
222 {
223   QApplication::postEvent( this, new QEvent( QEvent::LayoutHint ) );
224   QDialog::setFont( font );
225 }
226
227
228 /*!  Adds \a page to the end of the page sequence, with the title, \a title.
229 */
230
231 void InstallWizard::addPage( QWidget * page, const QString & title )
232 {
233   if ( !page )
234     return;
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() );
240 #endif
241     return;
242   }
243   int i = d->pages.count();
244   
245   if( i > 0 )
246     d->pages.at( i - 1 )->nextEnabled = TRUE;
247   
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 );
252 }
253
254 /*!
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.
258 */
259
260 void InstallWizard::insertPage( QWidget * page, const QString & title, int index )
261 {
262   if ( !page )
263     return;
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() );
269 #endif
270     return;
271   }
272   
273   if ( index < 0  || index > (int)d->pages.count() )
274     index = d->pages.count();
275   
276   if( index > 0 && ( index == (int)d->pages.count() ) )
277     d->pages.at( index - 1 )->nextEnabled = TRUE;
278   
279   InstallWizardPrivate::Page * p = new InstallWizardPrivate::Page( page, title );
280   p->backEnabled = ( index > 0 );
281   p->nextEnabled = ( index < (int)d->pages.count() );
282   
283   d->ws->addWidget( page, index );
284   d->pages.insert( index, p );
285 }
286
287 /*!
288 \fn void InstallWizard::selected(const QString&)
289
290   This signal is emitted when the current page changes. The parameter
291   contains the title of the selected page.
292 */
293
294
295 /*!  Makes \a page the current page and emits the selected() signal. */
296
297 void InstallWizard::showPage( QWidget * page )
298 {
299   InstallWizardPrivate::Page * p = d->page( page );
300   if ( p ) {
301     int i;
302     for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != p; i++ );
303     bool notFirst( FALSE );
304     
305     if( i ) {
306       i--;
307       while( ( i >= 0 ) && !notFirst ) {
308         notFirst |= appropriate( d->pages.at( i )->w );
309         i--;
310       }
311     }
312     setBackEnabled( notFirst );
313     setNextEnabled( TRUE );
314     d->ws->raiseWidget( page );
315     d->current = p;
316   }
317   
318   layOut();
319   updateButtons();
320   emit selected( p ? p->t : QString::null );
321 }
322
323
324 /*!  Returns the number of pages in the wizard. */
325
326 int InstallWizard::pageCount() const
327 {
328   return d->pages.count();
329 }
330
331 /*!
332 Returns the position of page \a page.
333 If the page is not part of the wizard -1 is returned.
334 */
335
336 int InstallWizard::indexOf( QWidget* page ) const
337 {
338   InstallWizardPrivate::Page * p = d->page( page );
339   if ( !p ) return -1;
340   
341   return d->pages.find( p );
342 }
343
344 /*!
345 Called when the user clicks the Back button; this function shows
346 the preceding relevant page in the sequence.
347
348   \sa appropriate()
349 */
350 void InstallWizard::back()
351 {
352   int i = 0;
353   
354   while( i < (int)d->pages.count() && d->pages.at( i ) &&
355      d->current && d->pages.at( i )->w != d->current->w )
356      i++;
357   
358   i--;
359   while( i >= 0 && ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
360     i--;
361   
362   if( i >= 0 )
363     if( d->pages.at( i ) )
364       showPage( d->pages.at( i )->w );
365 }
366
367
368 /*!
369 Called when the user clicks the Next button, this function shows
370 the next relevant page in the sequence.
371
372   \sa appropriate()
373 */
374 void InstallWizard::next()
375 {
376   int i = 0;
377   while( i < (int)d->pages.count() && d->pages.at( i ) &&
378     d->current && d->pages.at( i )->w != d->current->w )
379     i++;
380   i++;
381   while( i <= (int)d->pages.count()-1 &&
382     ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
383      i++;
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 ) ) )
386     i--;
387   if ( d->pages.at( i ) ) {
388     if ( d->current ) {
389       setNextEnabled( false );
390       setBackEnabled( false );
391       if ( !acceptData( d->current->t ) ) {
392         setNextEnabled( true );
393         setBackEnabled( true );
394         return;
395       }
396     }
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 <---
403   }
404 }
405
406
407 /*!
408 \fn void InstallWizard::helpClicked()
409
410   This signal is emitted when the user clicks on the Help button.
411 */
412
413 /*!  Called when the user clicks the Help button, this function emits the
414 helpClicked() signal.
415 */
416
417 void InstallWizard::help()
418 {
419   QWidget * page = d->ws->visibleWidget();
420   if ( !page )
421     return;
422   
423 #if 0
424   if ( page->inherits( "InstallWizardPage" ) )
425     emit ((InstallWizardPage *)page)->helpClicked();
426 #endif
427   emit helpClicked();
428 }
429
430 /*!
431   Enables/disables <Back> button
432  */
433 void InstallWizard::setBackEnabled( bool enable )
434 {
435   d->backButton->setEnabled( enable );
436 #ifndef QT_NO_ACCEL
437   d->accel->setItemEnabled( d->backAccel, enable );
438 #endif
439 }
440
441 /*!
442   Enables/disables <Next> button
443  */
444 void InstallWizard::setNextEnabled( bool enable )
445 {
446   d->nextButton->setEnabled( enable );
447 #ifndef QT_NO_ACCEL
448   d->accel->setItemEnabled( d->nextAccel, enable );
449 #endif
450 }
451
452 /*!
453   Enables/disables <Help> button
454  */
455 void InstallWizard::setHelpEnabled( bool enable )
456 {
457   d->helpButton->setEnabled( enable );
458 }
459
460 /*!
461 \fn void InstallWizard::setFinish( QWidget *, bool )
462 \obsolete
463
464   Use setFinishEnabled instead
465 */
466
467 /*!
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.
471 */
472 void InstallWizard::setBackEnabled( QWidget * page, bool enable )
473 {
474   InstallWizardPrivate::Page * p = d->page( page );
475   if ( !p )
476     return;
477   
478   p->backEnabled = enable;
479   updateButtons();
480 }
481
482
483 /*!
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
486 this button.
487 */
488
489 void InstallWizard::setNextEnabled( QWidget * page, bool enable )
490 {
491   InstallWizardPrivate::Page * p = d->page( page );
492   if ( !p )
493     return;
494   
495   p->nextEnabled = enable;
496   updateButtons();
497 }
498
499
500 /*!
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.
504 */
505 void InstallWizard::setFinishEnabled( QWidget * page, bool enable )
506 {
507   InstallWizardPrivate::Page * p = d->page( page );
508   if ( !p )
509     return;
510   
511   p->finishEnabled = enable;
512   updateButtons();
513 }
514
515
516 /*!
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.
520 */
521 void InstallWizard::setHelpEnabled( QWidget * page, bool enable )
522 {
523   InstallWizardPrivate::Page * p = d->page( page );
524   if ( !p )
525     return;
526   
527   p->helpEnabled = enable;
528   updateButtons();
529 }
530
531
532 /*!
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.
538
539   \warning The last page of the wizard will be displayed if no page is relevant
540   in the current context.
541 */
542
543 bool InstallWizard::appropriate( QWidget * page ) const
544 {
545   InstallWizardPrivate::Page * p = d->page( page );
546   return p ? p->appropriate : TRUE;
547 }
548
549
550 /*!
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.
554
555   \sa appropriate()
556 */
557 void InstallWizard::setAppropriate( QWidget * page, bool appropriate )
558 {
559   InstallWizardPrivate::Page * p = d->page( page );
560   if ( p )
561     p->appropriate = appropriate;
562 }
563
564
565 void InstallWizard::updateButtons()
566 {
567   if ( !d->current )
568     return;
569   
570   int i;
571   for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != d->current; i++ );
572   bool notFirst( FALSE );
573   if( i ) {
574     i--;
575     while( ( i >= 0 ) && !notFirst ) {
576       notFirst |= appropriate( d->pages.at( i )->w );
577       i--;
578     }
579   }
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 );
584   
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() ) )
589     layOut();
590 }
591
592
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.
596 */
597
598 QWidget * InstallWizard::currentPage() const
599 {
600   return d->ws->visibleWidget();
601 }
602
603
604 /*!  Returns the title of page \a page.
605 */
606
607 QString InstallWizard::title( QWidget * page ) const
608 {
609   InstallWizardPrivate::Page * p = d->page( page );
610   return p ? p->t : QString::null;
611 }
612
613 /*!  Sets the title for page \a page to \a title.
614 */
615
616 void InstallWizard::setTitle( QWidget *page, const QString &title )
617 {
618   InstallWizardPrivate::Page * p = d->page( page );
619   if ( p )
620     p->t = title;
621   if ( page == currentPage() )
622     d->title->setText( title );
623 }
624
625 /*!
626 \property InstallWizard::titleFont
627 \brief the font used for page titles
628
629   The default is QApplication::font().
630 */
631 QFont InstallWizard::titleFont() const
632 {
633   return d->title->font();
634 }
635
636 void InstallWizard::setTitleFont( const QFont & font )
637 {
638   d->title->setFont( font );
639 }
640
641
642 /*!
643 Returns a pointer to the dialog's Back button
644
645   By default, this button is connected to the back() slot,
646   which is virtual so you can reimplement it in a InstallWizard subclass.
647 */
648 QPushButton * InstallWizard::backButton() const
649 {
650   return d->backButton;
651 }
652
653
654 /*!
655 Returns a pointer to the dialog's Next button
656
657   By default, this button is connected to the next() slot,
658   which is virtual so you can reimplement it in a InstallWizard subclass.
659 */
660 QPushButton * InstallWizard::nextButton() const
661 {
662   return d->nextButton;
663 }
664
665
666 /*!
667 Returns a pointer to the dialog's Finish button
668
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.
671 */
672 QPushButton * InstallWizard::finishButton() const
673 {
674   return d->finishButton;
675 }
676
677
678 /*!
679 Returns a pointer to the dialog's Cancel button
680
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.
683 */
684 QPushButton * InstallWizard::cancelButton() const
685 {
686   return d->cancelButton;
687 }
688
689
690 /*!
691 Returns a pointer to the dialog's Help button
692
693   By default, this button is connected to the help() slot,
694   which is virtual so you can reimplement it in a InstallWizard subclass.
695 */
696 QPushButton * InstallWizard::helpButton() const
697 {
698   return d->helpButton;
699 }
700
701
702 /*!  This virtual function is responsible for adding the bottom
703 divider and the buttons below it.
704
705   \a layout is the vertical layout of the entire wizard.
706 */
707
708 void InstallWizard::layOutButtonRow( QHBoxLayout * layout )
709 {
710   bool hasHelp = FALSE;
711   bool hasEarlyFinish = FALSE;
712   
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;
717     i--;
718   }
719   i = 0;
720   while ( !hasHelp && i < (int)d->pages.count() ) {
721     if ( d->pages.at( i ) && d->pages.at( i )->helpEnabled )
722       hasHelp = TRUE;
723     i++;
724   }
725   
726   QBoxLayout * h = new QBoxLayout( QBoxLayout::LeftToRight );
727   layout->addLayout( h );
728   
729   h->addWidget( d->cancelButton );
730   
731   h->addStretch( 42 );
732   
733   h->addWidget( d->backButton );
734   
735   h->addSpacing( 6 );
736   
737   if ( hasEarlyFinish ) {
738     d->nextButton->show();
739     d->finishButton->show();
740     h->addWidget( d->nextButton );
741     h->addSpacing( 12 );
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 );
749   } else {
750     d->nextButton->show();
751     d->finishButton->hide();
752     h->addWidget( d->nextButton );
753   }
754   
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 );
762   }
763   
764   if ( hasHelp ) {
765     h->addSpacing( 12 );
766     h->addWidget( d->helpButton );
767   } else {
768     d->helpButton->hide();
769   }
770 }
771
772
773 /*!
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.
779 */
780
781 void InstallWizard::layOutTitleRow( QHBoxLayout * layout, const QString & title )
782 {
783   d->title->setText( title );
784   layout->addWidget( d->titleBox, 10 );
785 }
786
787 /*!
788 Validates page when 'Next' or 'Finish' button is clicked.
789 Should return true in success
790 */
791 bool InstallWizard::acceptData( const QString& )
792 {
793   postValidateEvent( this );
794   return TRUE;
795 }
796
797 /*
798
799 */
800
801 void InstallWizard::layOut()
802 {
803   delete d->v;
804   d->v = new QVBoxLayout( this, 11, 0, "top-level layout" );
805   
806   QHBoxLayout * l;
807   l = new QHBoxLayout( 6 );
808   d->v->addLayout( l, 0 );
809   layOutTitleRow( l, d->current ? d->current->t : QString::null );
810   
811   if ( ! d->hbar1 ) {
812     d->hbar1 = new QFrame( this, "<hr>", 0 );
813     d->hbar1->setFrameStyle( QFrame::Sunken + QFrame::HLine );
814     d->hbar1->setFixedHeight( 12 );
815   }
816   
817   d->v->addWidget( d->hbar1 );
818   
819   d->v->addWidget( d->ws, 10 );
820   
821   if ( ! d->hbar2 ) {
822     d->hbar2 = new QFrame( this, "<hr>", 0 );
823     d->hbar2->setFrameStyle( QFrame::Sunken + QFrame::HLine );
824     d->hbar2->setFixedHeight( 12 );
825   }
826   d->v->addWidget( d->hbar2 );
827   
828   l = new QHBoxLayout( 6 );
829   d->v->addLayout( l );
830   layOutButtonRow( l );
831   d->v->activate();
832 }
833
834
835 /*! \reimp */
836
837 bool InstallWizard::eventFilter( QObject * o, QEvent * e )
838 {
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() );
843   }
844   return QDialog::eventFilter( o, e );
845 }
846
847
848 /*!
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.
852 */
853
854 void InstallWizard::removePage( QWidget * page )
855 {
856   if ( !page )
857     return;
858   
859   int i = d->pages.count();
860   QWidget* cp = currentPage();
861   while( --i >= 0 && d->pages.at( i ) && d->pages.at( i )->w != page ) { }
862   if ( i < 0 )
863     return;
864   InstallWizardPrivate::Page * p = d->pages.at( i );
865   d->pages.removeRef( p );
866   d->ws->removeWidget( page );
867   
868   if( cp == page ) {
869     i--;
870     if( i < 0 )
871       i = 0;
872     if ( pageCount() > 0 )
873       showPage( InstallWizard::page( i ) );
874   }
875 }
876
877
878 /*!
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.
881 */
882
883 QWidget* InstallWizard::page( int index ) const
884 {
885   if ( index >= pageCount() || index < 0 )
886     return 0;
887   
888   return d->pages.at( index )->w;
889 }
890
891 /*!
892 Returns a pointer to the page with a title \a title in the sequence,
893 or 0 if not found.
894 */
895 QWidget* InstallWizard::page( const QString& title ) const
896 {
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;
900   }
901   return 0;
902 }
903
904 /*!
905 Adds logo to be shown at the right of the page title
906 */
907 void InstallWizard::addLogo( const QPixmap& pm )
908 {
909   QLabel* logo = new QLabel( d->logoBox, "logo" );
910   logo->setPixmap( pm );
911   logo->setAlignment( AlignCenter );
912   logo->setScaledContents( false );
913   logo->show();
914 }
915
916 /*!
917 Remove all logos
918 */
919 void InstallWizard::removeLogos()
920 {
921   QObjectList* children = d->logoBox->queryList( "QLabel" );
922   if ( children ) {
923     QObjectListIt it( *children );
924     QObject *obj;
925     while ( (obj = it.current()) != 0 ) {
926       ++it;
927       delete obj;
928     }
929   }
930   delete children;
931 }
932
933 /*!
934 Show/hide "About" button
935 */
936 void InstallWizard::showAboutBtn( bool show )
937 {
938   show ? d->aboutButton->show() : d->aboutButton->hide();
939 }
940
941 /*!
942 Set icon for "About" button
943 */
944 void InstallWizard::setAboutIcon( const QPixmap& px )
945 {
946   d->aboutButton->setIconSet( px );
947 }
948
949 /*!
950 Posts validation event
951 */
952 void InstallWizard::postValidateEvent( InstallWizard* iw, const int val, void* data )
953 {
954 #if QT_VERSION > 0x030005
955   QApplication::postEvent( iw, new ProcessEvent( val, data ) );
956 #else
957   QThread::postEvent( iw, new ProcessEvent( val, data ) );
958 #endif
959 }
960
961 /*!
962 Processes validation event: default implementation just to show next page
963 */
964 void InstallWizard::processValidateEvent( const int /* val */, void* /* data */ )
965 {
966   int i = 0;
967   while( i < (int)d->pages.count() && d->pages.at( i ) &&
968     d->current && d->pages.at( i )->w != d->current->w )
969     i++;
970   i++;
971   while( i <= (int)d->pages.count()-1 &&
972     ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
973      i++;
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 ) ) )
976     i--;
977   if ( d->pages.at( i ) ) {
978     showPage( d->pages.at( i )->w );
979   }
980   setNextEnabled( true );
981   setBackEnabled( true );
982 }
983
984 /*!
985 Process events received
986 */
987 bool InstallWizard::event ( QEvent* e )
988 {
989   if ( e->type() == PROCESS_EVENT ) {
990     ProcessEvent* pe = (ProcessEvent*)e;
991     processValidateEvent( pe->returnValue(), pe->data() );
992   }
993   return QDialog::event( e );
994 }