]> SALOME platform Git repositories - tools/install.git/blob - src/InstallWizard.cpp
Salome HOME
Remove HappyDoc dependencies
[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   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" );
146   
147   d->ws->installEventFilter( this );
148   
149   d->v = 0;
150   d->hbar1 = 0;
151   d->hbar2 = 0;
152   
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" ) );
158   
159   d->nextButton->setDefault( TRUE );
160   
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()) );
177   
178 #ifndef QT_NO_ACCEL
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()) );
184 #endif
185
186   showAboutBtn( false );
187 }
188
189
190 /*!
191 Destroys the object and frees any allocated resources, including
192 all pages and controllers.
193 */
194
195 InstallWizard::~InstallWizard()
196 {
197   delete d;
198 }
199
200
201 /*!  \reimp  */
202
203 void InstallWizard::show()
204 {
205   if ( d->current )
206     showPage( d->current->w );
207   else if ( pageCount() > 0 )
208     showPage( d->pages.at( 0 )->w );
209   else
210     showPage( 0 );
211   
212   QDialog::show();
213 }
214
215
216 /*! \reimp */
217
218 void InstallWizard::setFont( const QFont & font )
219 {
220   QApplication::postEvent( this, new QEvent( QEvent::LayoutHint ) );
221   QDialog::setFont( font );
222 }
223
224
225 /*!  Adds \a page to the end of the page sequence, with the title, \a title.
226 */
227
228 void InstallWizard::addPage( QWidget * page, const QString & title )
229 {
230   if ( !page )
231     return;
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() );
237 #endif
238     return;
239   }
240   int i = d->pages.count();
241   
242   if( i > 0 )
243     d->pages.at( i - 1 )->nextEnabled = TRUE;
244   
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 );
249 }
250
251 /*!
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.
255 */
256
257 void InstallWizard::insertPage( QWidget * page, const QString & title, int index )
258 {
259   if ( !page )
260     return;
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() );
266 #endif
267     return;
268   }
269   
270   if ( index < 0  || index > (int)d->pages.count() )
271     index = d->pages.count();
272   
273   if( index > 0 && ( index == (int)d->pages.count() ) )
274     d->pages.at( index - 1 )->nextEnabled = TRUE;
275   
276   InstallWizardPrivate::Page * p = new InstallWizardPrivate::Page( page, title );
277   p->backEnabled = ( index > 0 );
278   p->nextEnabled = ( index < (int)d->pages.count() );
279   
280   d->ws->addWidget( page, index );
281   d->pages.insert( index, p );
282 }
283
284 /*!
285 \fn void InstallWizard::selected(const QString&)
286
287   This signal is emitted when the current page changes. The parameter
288   contains the title of the selected page.
289 */
290
291
292 /*!  Makes \a page the current page and emits the selected() signal. */
293
294 void InstallWizard::showPage( QWidget * page )
295 {
296   InstallWizardPrivate::Page * p = d->page( page );
297   if ( p ) {
298     int i;
299     for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != p; i++ );
300     bool notFirst( FALSE );
301     
302     if( i ) {
303       i--;
304       while( ( i >= 0 ) && !notFirst ) {
305         notFirst |= appropriate( d->pages.at( i )->w );
306         i--;
307       }
308     }
309     setBackEnabled( notFirst );
310     setNextEnabled( TRUE );
311     d->ws->raiseWidget( page );
312     d->current = p;
313   }
314   
315   layOut();
316   updateButtons();
317   emit selected( p ? p->t : QString::null );
318 }
319
320
321 /*!  Returns the number of pages in the wizard. */
322
323 int InstallWizard::pageCount() const
324 {
325   return d->pages.count();
326 }
327
328 /*!
329 Returns the position of page \a page.
330 If the page is not part of the wizard -1 is returned.
331 */
332
333 int InstallWizard::indexOf( QWidget* page ) const
334 {
335   InstallWizardPrivate::Page * p = d->page( page );
336   if ( !p ) return -1;
337   
338   return d->pages.find( p );
339 }
340
341 /*!
342 Called when the user clicks the Back button; this function shows
343 the preceding relevant page in the sequence.
344
345   \sa appropriate()
346 */
347 void InstallWizard::back()
348 {
349   int i = 0;
350   
351   while( i < (int)d->pages.count() && d->pages.at( i ) &&
352      d->current && d->pages.at( i )->w != d->current->w )
353      i++;
354   
355   i--;
356   while( i >= 0 && ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
357     i--;
358   
359   if( i >= 0 )
360     if( d->pages.at( i ) )
361       showPage( d->pages.at( i )->w );
362 }
363
364
365 /*!
366 Called when the user clicks the Next button, this function shows
367 the next relevant page in the sequence.
368
369   \sa appropriate()
370 */
371 void InstallWizard::next()
372 {
373   int i = 0;
374   while( i < (int)d->pages.count() && d->pages.at( i ) &&
375     d->current && d->pages.at( i )->w != d->current->w )
376     i++;
377   i++;
378   while( i <= (int)d->pages.count()-1 &&
379     ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
380      i++;
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 ) ) )
383     i--;
384   if ( d->pages.at( i ) ) {
385     if ( d->current ) {
386       setNextEnabled( false );
387       setBackEnabled( false );
388       if ( !acceptData( d->current->t ) ) {
389         setNextEnabled( true );
390         setBackEnabled( true );
391         return;
392       }
393     }
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 <---
400   }
401 }
402
403
404 /*!
405 \fn void InstallWizard::helpClicked()
406
407   This signal is emitted when the user clicks on the Help button.
408 */
409
410 /*!  Called when the user clicks the Help button, this function emits the
411 helpClicked() signal.
412 */
413
414 void InstallWizard::help()
415 {
416   QWidget * page = d->ws->visibleWidget();
417   if ( !page )
418     return;
419   
420 #if 0
421   if ( page->inherits( "InstallWizardPage" ) )
422     emit ((InstallWizardPage *)page)->helpClicked();
423 #endif
424   emit helpClicked();
425 }
426
427 /*!
428   Enables/disables <Back> button
429  */
430 void InstallWizard::setBackEnabled( bool enable )
431 {
432   d->backButton->setEnabled( enable );
433 #ifndef QT_NO_ACCEL
434   d->accel->setItemEnabled( d->backAccel, enable );
435 #endif
436 }
437
438 /*!
439   Enables/disables <Next> button
440  */
441 void InstallWizard::setNextEnabled( bool enable )
442 {
443   d->nextButton->setEnabled( enable );
444 #ifndef QT_NO_ACCEL
445   d->accel->setItemEnabled( d->nextAccel, enable );
446 #endif
447 }
448
449 /*!
450   Enables/disables <Help> button
451  */
452 void InstallWizard::setHelpEnabled( bool enable )
453 {
454   d->helpButton->setEnabled( enable );
455 }
456
457 /*!
458 \fn void InstallWizard::setFinish( QWidget *, bool )
459 \obsolete
460
461   Use setFinishEnabled instead
462 */
463
464 /*!
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.
468 */
469 void InstallWizard::setBackEnabled( QWidget * page, bool enable )
470 {
471   InstallWizardPrivate::Page * p = d->page( page );
472   if ( !p )
473     return;
474   
475   p->backEnabled = enable;
476   updateButtons();
477 }
478
479
480 /*!
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
483 this button.
484 */
485
486 void InstallWizard::setNextEnabled( QWidget * page, bool enable )
487 {
488   InstallWizardPrivate::Page * p = d->page( page );
489   if ( !p )
490     return;
491   
492   p->nextEnabled = enable;
493   updateButtons();
494 }
495
496
497 /*!
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.
501 */
502 void InstallWizard::setFinishEnabled( QWidget * page, bool enable )
503 {
504   InstallWizardPrivate::Page * p = d->page( page );
505   if ( !p )
506     return;
507   
508   p->finishEnabled = enable;
509   updateButtons();
510 }
511
512
513 /*!
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.
517 */
518 void InstallWizard::setHelpEnabled( QWidget * page, bool enable )
519 {
520   InstallWizardPrivate::Page * p = d->page( page );
521   if ( !p )
522     return;
523   
524   p->helpEnabled = enable;
525   updateButtons();
526 }
527
528
529 /*!
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.
535
536   \warning The last page of the wizard will be displayed if no page is relevant
537   in the current context.
538 */
539
540 bool InstallWizard::appropriate( QWidget * page ) const
541 {
542   InstallWizardPrivate::Page * p = d->page( page );
543   return p ? p->appropriate : TRUE;
544 }
545
546
547 /*!
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.
551
552   \sa appropriate()
553 */
554 void InstallWizard::setAppropriate( QWidget * page, bool appropriate )
555 {
556   InstallWizardPrivate::Page * p = d->page( page );
557   if ( p )
558     p->appropriate = appropriate;
559 }
560
561
562 void InstallWizard::updateButtons()
563 {
564   if ( !d->current )
565     return;
566   
567   int i;
568   for( i = 0; i < (int)d->pages.count() && d->pages.at( i ) != d->current; i++ );
569   bool notFirst( FALSE );
570   if( i ) {
571     i--;
572     while( ( i >= 0 ) && !notFirst ) {
573       notFirst |= appropriate( d->pages.at( i )->w );
574       i--;
575     }
576   }
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 );
581   
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() ) )
586     layOut();
587 }
588
589
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.
593 */
594
595 QWidget * InstallWizard::currentPage() const
596 {
597   return d->ws->visibleWidget();
598 }
599
600
601 /*!  Returns the title of page \a page.
602 */
603
604 QString InstallWizard::title( QWidget * page ) const
605 {
606   InstallWizardPrivate::Page * p = d->page( page );
607   return p ? p->t : QString::null;
608 }
609
610 /*!  Sets the title for page \a page to \a title.
611 */
612
613 void InstallWizard::setTitle( QWidget *page, const QString &title )
614 {
615   InstallWizardPrivate::Page * p = d->page( page );
616   if ( p )
617     p->t = title;
618   if ( page == currentPage() )
619     d->title->setText( title );
620 }
621
622 /*!
623 \property InstallWizard::titleFont
624 \brief the font used for page titles
625
626   The default is QApplication::font().
627 */
628 QFont InstallWizard::titleFont() const
629 {
630   return d->title->font();
631 }
632
633 void InstallWizard::setTitleFont( const QFont & font )
634 {
635   d->title->setFont( font );
636 }
637
638
639 /*!
640 Returns a pointer to the dialog's Back button
641
642   By default, this button is connected to the back() slot,
643   which is virtual so you can reimplement it in a InstallWizard subclass.
644 */
645 QPushButton * InstallWizard::backButton() const
646 {
647   return d->backButton;
648 }
649
650
651 /*!
652 Returns a pointer to the dialog's Next button
653
654   By default, this button is connected to the next() slot,
655   which is virtual so you can reimplement it in a InstallWizard subclass.
656 */
657 QPushButton * InstallWizard::nextButton() const
658 {
659   return d->nextButton;
660 }
661
662
663 /*!
664 Returns a pointer to the dialog's Finish button
665
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.
668 */
669 QPushButton * InstallWizard::finishButton() const
670 {
671   return d->finishButton;
672 }
673
674
675 /*!
676 Returns a pointer to the dialog's Cancel button
677
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.
680 */
681 QPushButton * InstallWizard::cancelButton() const
682 {
683   return d->cancelButton;
684 }
685
686
687 /*!
688 Returns a pointer to the dialog's Help button
689
690   By default, this button is connected to the help() slot,
691   which is virtual so you can reimplement it in a InstallWizard subclass.
692 */
693 QPushButton * InstallWizard::helpButton() const
694 {
695   return d->helpButton;
696 }
697
698
699 /*!  This virtual function is responsible for adding the bottom
700 divider and the buttons below it.
701
702   \a layout is the vertical layout of the entire wizard.
703 */
704
705 void InstallWizard::layOutButtonRow( QHBoxLayout * layout )
706 {
707   bool hasHelp = FALSE;
708   bool hasEarlyFinish = FALSE;
709   
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;
714     i--;
715   }
716   i = 0;
717   while ( !hasHelp && i < (int)d->pages.count() ) {
718     if ( d->pages.at( i ) && d->pages.at( i )->helpEnabled )
719       hasHelp = TRUE;
720     i++;
721   }
722   
723   QBoxLayout * h = new QBoxLayout( QBoxLayout::LeftToRight );
724   layout->addLayout( h );
725   
726   h->addWidget( d->cancelButton );
727   
728   h->addStretch( 42 );
729   
730   h->addWidget( d->backButton );
731   
732   h->addSpacing( 6 );
733   
734   if ( hasEarlyFinish ) {
735     d->nextButton->show();
736     d->finishButton->show();
737     h->addWidget( d->nextButton );
738     h->addSpacing( 12 );
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 );
746   } else {
747     d->nextButton->show();
748     d->finishButton->hide();
749     h->addWidget( d->nextButton );
750   }
751   
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 );
759   }
760   
761   if ( hasHelp ) {
762     h->addSpacing( 12 );
763     h->addWidget( d->helpButton );
764   } else {
765     d->helpButton->hide();
766   }
767 }
768
769
770 /*!
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.
776 */
777
778 void InstallWizard::layOutTitleRow( QHBoxLayout * layout, const QString & title )
779 {
780   d->title->setText( title );
781   layout->addWidget( d->titleBox, 10 );
782 }
783
784 /*!
785 Validates page when 'Next' or 'Finish' button is clicked.
786 Should return true in success
787 */
788 bool InstallWizard::acceptData( const QString& )
789 {
790   postValidateEvent( this );
791   return TRUE;
792 }
793
794 /*
795
796 */
797
798 void InstallWizard::layOut()
799 {
800   delete d->v;
801   d->v = new QVBoxLayout( this, 11, 0, "top-level layout" );
802   
803   QHBoxLayout * l;
804   l = new QHBoxLayout( 6 );
805   d->v->addLayout( l, 0 );
806   layOutTitleRow( l, d->current ? d->current->t : QString::null );
807   
808   if ( ! d->hbar1 ) {
809     d->hbar1 = new QFrame( this, "<hr>", 0 );
810     d->hbar1->setFrameStyle( QFrame::Sunken + QFrame::HLine );
811     d->hbar1->setFixedHeight( 12 );
812   }
813   
814   d->v->addWidget( d->hbar1 );
815   
816   d->v->addWidget( d->ws, 10 );
817   
818   if ( ! d->hbar2 ) {
819     d->hbar2 = new QFrame( this, "<hr>", 0 );
820     d->hbar2->setFrameStyle( QFrame::Sunken + QFrame::HLine );
821     d->hbar2->setFixedHeight( 12 );
822   }
823   d->v->addWidget( d->hbar2 );
824   
825   l = new QHBoxLayout( 6 );
826   d->v->addLayout( l );
827   layOutButtonRow( l );
828   d->v->activate();
829 }
830
831
832 /*! \reimp */
833
834 bool InstallWizard::eventFilter( QObject * o, QEvent * e )
835 {
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() );
840   }
841   return QDialog::eventFilter( o, e );
842 }
843
844
845 /*!
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.
849 */
850
851 void InstallWizard::removePage( QWidget * page )
852 {
853   if ( !page )
854     return;
855   
856   int i = d->pages.count();
857   QWidget* cp = currentPage();
858   while( --i >= 0 && d->pages.at( i ) && d->pages.at( i )->w != page ) { }
859   if ( i < 0 )
860     return;
861   InstallWizardPrivate::Page * p = d->pages.at( i );
862   d->pages.removeRef( p );
863   d->ws->removeWidget( page );
864   
865   if( cp == page ) {
866     i--;
867     if( i < 0 )
868       i = 0;
869     if ( pageCount() > 0 )
870       showPage( InstallWizard::page( i ) );
871   }
872 }
873
874
875 /*!
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.
878 */
879
880 QWidget* InstallWizard::page( int index ) const
881 {
882   if ( index >= pageCount() || index < 0 )
883     return 0;
884   
885   return d->pages.at( index )->w;
886 }
887
888 /*!
889 Returns a pointer to the page with a title \a title in the sequence,
890 or 0 if not found.
891 */
892 QWidget* InstallWizard::page( const QString& title ) const
893 {
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;
897   }
898   return 0;
899 }
900
901 /*!
902 Adds logo to be shown at the right of the page title
903 */
904 void InstallWizard::addLogo( const QPixmap& pm )
905 {
906   QLabel* logo = new QLabel( d->logoBox, "logo" );
907   logo->setPixmap( pm );
908   logo->setAlignment( AlignCenter );
909   logo->setScaledContents( false );
910   logo->show();
911 }
912
913 /*!
914 Remove all logos
915 */
916 void InstallWizard::removeLogos()
917 {
918   QObjectList* children = d->logoBox->queryList( "QLabel" );
919   if ( children ) {
920     QObjectListIt it( *children );
921     QObject *obj;
922     while ( (obj = it.current()) != 0 ) {
923       ++it;
924       delete obj;
925     }
926   }
927   delete children;
928 }
929
930 /*!
931 Show/hide "About" button
932 */
933 void InstallWizard::showAboutBtn( bool show )
934 {
935   show ? d->aboutButton->show() : d->aboutButton->hide();
936 }
937
938 /*!
939 Set icon for "About" button
940 */
941 void InstallWizard::setAboutIcon( const QPixmap& px )
942 {
943   d->aboutButton->setIconSet( px );
944 }
945
946 /*!
947 Posts validation event
948 */
949 void InstallWizard::postValidateEvent( InstallWizard* iw, const int val, void* data )
950 {
951 #if QT_VERSION > 0x030005
952   QApplication::postEvent( iw, new ProcessEvent( val, data ) );
953 #else
954   QThread::postEvent( iw, new ProcessEvent( val, data ) );
955 #endif
956 }
957
958 /*!
959 Processes validation event: default implementation just to show next page
960 */
961 void InstallWizard::processValidateEvent( const int /* val */, void* /* data */ )
962 {
963   int i = 0;
964   while( i < (int)d->pages.count() && d->pages.at( i ) &&
965     d->current && d->pages.at( i )->w != d->current->w )
966     i++;
967   i++;
968   while( i <= (int)d->pages.count()-1 &&
969     ( !d->pages.at( i ) || !appropriate( d->pages.at( i )->w ) ) )
970      i++;
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 ) ) )
973     i--;
974   if ( d->pages.at( i ) ) {
975     showPage( d->pages.at( i )->w );
976   }
977   setNextEnabled( true );
978   setBackEnabled( true );
979 }
980
981 /*!
982 Process events received
983 */
984 bool InstallWizard::event ( QEvent* e )
985 {
986   if ( e->type() == PROCESS_EVENT ) {
987     ProcessEvent* pe = (ProcessEvent*)e;
988     processValidateEvent( pe->returnValue(), pe->data() );
989   }
990   return QDialog::event( e );
991 }