moving classes where they are used
[duncan/yast2-qt4.git] / src / YQWizard.cc
1 /*---------------------------------------------------------------------\
2 |                                                                      |
3 |                      __   __    ____ _____ ____                      |
4 |                      \ \ / /_ _/ ___|_   _|___ \                     |
5 |                       \ V / _` \___ \ | |   __) |                    |
6 |                        | | (_| |___) || |  / __/                     |
7 |                        |_|\__,_|____/ |_| |_____|                    |
8 |                                                                      |
9 |                               core system                            |
10 |                                                    (c) SuSE Linux AG |
11 \----------------------------------------------------------------------/
12
13   File:         YQWizard.cc
14
15   Author:       Stefan Hundhammer <sh@suse.de>
16
17   Textdomain    "packages-qt"
18
19 /-*/
20
21 #include "YQWizard.h"
22 #define y2log_component "qt-wizard"
23 #include <ycp/y2log.h>
24
25 // For the command parser
26
27 #include <string>
28 #include <YShortcut.h>
29
30 #include <QDialog>
31 #include <QSvgRenderer>
32 #include <QPainter>
33 #include <QStackedWidget>
34 #include "ui_QHelpDialog.h"
35 #include <qimage.h>
36 #include <qlabel.h>
37 #include <qlayout.h>
38 #include <qmenubar.h>
39 #include <qmenudata.h>
40 #include <qobject.h>
41 #include <qpixmap.h>
42 #include <qpushbutton.h>
43 #include <qregexp.h>
44 #include <qtabwidget.h>
45 #include <qtoolbutton.h>
46
47 #include "QY2ListView.h"
48 #include <QGridLayout>
49 #include <qevent.h>
50
51 #include "utf8.h"
52 #include "YQi18n.h"
53 #include "YQUI.h"
54 #include "YQApplication.h"
55 #include "YQDialog.h"
56 #include "YQAlignment.h"
57 #include "YQReplacePoint.h"
58 #include "YQEmpty.h"
59 #include "YQLabel.h"
60 #include "YQWizardButton.h"
61 #include "YQIconPool.h"
62 #include "YQWidgetFactory.h"
63 #include "YQSignalBlocker.h"
64 #include "YEvent.h"
65
66 using std::string;
67
68
69 #define PIXMAP_DIR THEMEDIR "/wizard/"
70
71 #ifdef TEXTDOMAIN
72 #    undef TEXTDOMAIN
73 #endif
74
75 #define TEXTDOMAIN "packages-qt"
76
77 #define USE_ICON_ON_HELP_BUTTON         0
78
79
80 YQWizard::YQWizard( YWidget *           parent,
81                     const string &      backButtonLabel,
82                     const string &      abortButtonLabel,
83                     const string &      nextButtonLabel,
84                     YWizardMode         wizardMode )
85     : QFrame( (QWidget *) parent->widgetRep() )
86     , YWizard( parent,
87                backButtonLabel,
88                abortButtonLabel,
89                nextButtonLabel,
90                wizardMode )
91     , _backButtonLabel( backButtonLabel )
92     , _abortButtonLabel( abortButtonLabel )
93     , _nextButtonLabel( nextButtonLabel )
94 {
95     QHBoxLayout* layout = new QHBoxLayout( this );
96     layout->setSpacing( 0 );
97     layout->setMargin( 0 );
98
99     setWidgetRep( this );
100
101     _stepsEnabled = (wizardMode == YWizardMode_Steps);
102     _treeEnabled  = (wizardMode == YWizardMode_Tree);
103
104     _stepsDirty         = false;
105     _direction          = YQWizard::Forward;
106
107     _sideBar            = 0;
108     _stepsPanel         = 0;
109     _helpButton         = 0;
110     _stepsButton        = 0;
111     _treeButton         = 0;
112     _releaseNotesButton = 0;
113     _treePanel          = 0;
114     _tree               = 0;
115     _clientArea         = 0;
116     _menuBar            = 0;
117     _dialogIcon         = 0;
118     _dialogHeading      = 0;
119     _contents           = 0;
120     _backButton         = 0;
121     _abortButton        = 0;
122     _nextButton         = 0;
123     _sendButtonEvents   = true;
124     _contentsReplacePoint = 0;
125
126     //FIXME _stepsList.setAutoDelete( true );
127     //FIXME _stepsIDs.setAutoDelete( false );   // Only for one of both!
128
129     YQUI::setTextdomain( TEXTDOMAIN );
130
131     //layoutTitleBar( this );
132
133     layout->addLayout( layoutSideBar( this ) );
134     layout->addWidget( layoutWorkArea( this ) );
135 }
136
137
138
139 YQWizard::~YQWizard()
140 {
141     deleteSteps();
142 }
143
144
145
146 void YQWizard::layoutTitleBar( QWidget * parent )
147 {
148     QFrame * titleBar = new QFrame( parent );
149     YUI_CHECK_NEW( titleBar );
150
151     QHBoxLayout *layout = new QHBoxLayout( titleBar );
152     titleBar->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); // hor/vert
153
154     //
155     // Left logo
156     //
157
158     QLabel * left = new QLabel( titleBar );
159     layout->addWidget( left );
160     left->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) ); // hor/vert
161
162     QPixmap leftLogo( PIXMAP_DIR "title-bar-left.png" );
163
164     if ( ! leftLogo.isNull() )
165     {
166         left->setPixmap( leftLogo );
167         left->setFixedSize( leftLogo.size() );
168     }
169
170
171     //
172     // Center stretch space
173     //
174
175     layout->addStretch( 10 );
176
177
178     //
179     // Right logo
180     //
181
182     QLabel * right = new QLabel( titleBar );
183     YUI_CHECK_NEW( right );
184
185     layout->addWidget( right );
186
187     QPixmap rightLogo( PIXMAP_DIR "title-bar-right.png" );
188
189     if ( ! rightLogo.isNull() )
190     {
191         right->setPixmap( rightLogo );
192         right->setFixedSize( rightLogo.size() );
193     }
194 }
195
196
197
198 QLayout *YQWizard::layoutSideBar( QWidget * parent )
199 {
200     _sideBar = new QStackedWidget( parent );
201     YUI_CHECK_NEW( _sideBar );
202     _sideBar->setMinimumWidth( YQUI::ui()->defaultSize( YD_HORIZ ) / 5 );
203     _sideBar->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred ) ); // hor/vert
204
205     QVBoxLayout *vbox = new QVBoxLayout( );
206     vbox->addWidget( _sideBar );
207
208     layoutHelpPanel();
209
210     if ( _treeEnabled )
211     {
212         layoutTreePanel();
213         showTree();
214     }
215     else if ( _stepsEnabled )
216     {
217         layoutStepsPanel();
218         showSteps();
219     }
220
221     return vbox;
222 }
223
224 void YQWizard::layoutStepsPanel()
225 {
226     // Steps
227     _stepsPanel = new QFrame( _sideBar );
228     _sideBar->addWidget( _stepsPanel );
229     _sideBar->setObjectName( "steps" );
230     _stepsPanel->setProperty( "class", "steps QFrame" );
231
232     // Steps panel bottom buttons ("Help", "Release Notes")
233
234     // Layouts for the buttons
235
236     _releaseNotesButton = new QPushButton( _( "Release Notes..." ), _stepsPanel );
237     _releaseNotesButton->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Minimum ) ); // hor/vert
238
239     connect( _releaseNotesButton,       SIGNAL( clicked()  ),
240              this,                      SLOT  ( releaseNotesClicked() ) );
241
242     _releaseNotesButton->hide();        // hidden until showReleaseNotesButton() is called
243
244     _stepsDirty = true; // no layout yet
245 }
246
247
248
249 void YQWizard::addStep( const string & text, const string & id )
250 {
251     QString qId = fromUTF8( id );
252
253     if ( _stepsIDs[ qId ] )
254     {
255         y2error( "Step ID \"%s\" (\"%s\") already used for \"%s\"",
256                  id.c_str(),
257                  text.c_str(),
258                  qPrintable( _stepsIDs[ qId ]->name() ) );
259         return;
260     }
261
262     if ( _stepsList.last() && _stepsList.last()->name() == fromUTF8( text ) )
263     {
264         // Consecutive steps with the same name will be shown as one single step.
265         //
266         // Since steps are always added at the end of the list, it is
267         // sufficient to check the last step of the list. If the texts are the
268         // same, the other with the same text needs to get another (additional)
269         // ID to make sure setCurrentStep() works as it should.
270         _stepsList.last()->addID( qId );
271     }
272     else
273     {
274         _stepsList.append( new YQWizard::Step( fromUTF8( text ), qId ) );
275         _stepsDirty = true;
276     }
277
278     _stepsIDs.insert( qId, _stepsList.last() );
279 }
280
281
282
283 void YQWizard::addStepHeading( const string & text )
284 {
285     _stepsList.append( new YQWizard::StepHeading( fromUTF8( text ) ) );
286     _stepsDirty = true;
287 }
288
289
290
291 void YQWizard::updateSteps()
292 {
293     if ( ! _stepsPanel )
294         return;
295
296     // Create a grid layout for the steps
297     QVBoxLayout *_stepsVBox = new QVBoxLayout( _stepsPanel );
298
299     QGridLayout *_stepsGrid = new QGridLayout( );
300     YUI_CHECK_NEW( _stepsGrid );
301     _stepsVBox->addLayout( _stepsGrid );
302     _stepsGrid->setColumnMinimumWidth( 0, 10 );
303     _stepsGrid->setRowStretch( 0, 1 );
304     _stepsGrid->setRowStretch( 1, 1 );
305     _stepsGrid->setRowStretch( 2, 99 );
306
307     const int statusCol = 1;
308     const int nameCol   = 2;
309
310     int row = 0;
311
312     //
313     // Create widgets for all steps and step headings in the internal list
314     //
315
316     for ( QList<Step*>::iterator i = _stepsList.begin(); i != _stepsList.end(); ++i)
317     {
318         YQWizard::Step * step = *i;
319         if ( step->isHeading() )
320         {
321             //
322             // Heading
323             //
324
325             QLabel * label = new QLabel( step->name(), _stepsPanel );
326             YUI_CHECK_NEW( label );
327             label->setAlignment( Qt::AlignLeft | Qt::AlignTop );
328             label->setProperty( "class", "steps_heading" );
329
330             step->setNameLabel( label );
331             _stepsGrid->addWidget( label,
332                                    row, statusCol,
333                                    1, nameCol - statusCol + 1);
334         }
335         else    // No heading - ordinary step
336         {
337             //
338             // Step status
339             //
340
341             QLabel * statusLabel = new QLabel( _stepsPanel );
342             YUI_CHECK_NEW( statusLabel );
343
344             step->setStatusLabel( statusLabel );
345             statusLabel->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
346             _stepsGrid->addWidget( statusLabel, row, statusCol );
347
348             //
349             // Step name
350             //
351
352             QLabel * nameLabel = new QLabel( step->name(), _stepsPanel );
353             YUI_CHECK_NEW( nameLabel );
354             nameLabel->setAlignment( Qt::AlignLeft | Qt::AlignTop );
355
356             step->setNameLabel( nameLabel );
357             _stepsGrid->addWidget( nameLabel, row, nameCol );
358         }
359
360         row++;
361     }
362
363     _stepsVBox->addStretch( 99 );
364     QVBoxLayout *rbl = new QVBoxLayout();
365     rbl->addWidget( _releaseNotesButton, 0, Qt::AlignCenter );
366
367     _stepsVBox->addLayout( rbl );
368     _stepsVBox->addStretch( 29 );
369
370     _stepsDirty = false;
371 }
372
373
374 void YQWizard::updateStepStates()
375 {
376     if ( _stepsDirty )
377         updateSteps();
378
379     YQWizard::Step * currentStep = findStep( _currentStepID );
380     QList<YQWizard::Step*>::iterator step = _stepsList.begin();
381
382     if ( currentStep )
383     {
384         // Set status icon and color for the current step
385         currentStep->setStatus( Step::Current );
386
387         //
388         // Set all steps before the current to "done"
389         //
390
391         while ( step != _stepsList.end() && *step != currentStep )
392         {
393             ( *step )->setStatus( Step::Done );
394             step++;
395         }
396
397         // Skip the current step - continue with the step after it
398
399         if ( step != _stepsList.end() )
400             step++;
401     }
402
403     //
404     // Set all steps after the current to "to do"
405     //
406
407     while ( step != _stepsList.end() )
408     {
409         ( *step )->setStatus( Step::Todo );
410         step++;
411     }
412 }
413
414 void YQWizard::setCurrentStep( const string & id )
415 {
416     _currentStepID = fromUTF8( id );
417     updateStepStates();
418 }
419
420
421 void YQWizard::deleteSteps()
422 {
423     _stepsList.clear();
424     _stepsIDs.clear();
425 }
426
427
428 YQWizard::Step * YQWizard::findStep( const QString & id )
429 {
430     if ( id.isEmpty() )
431         return 0;
432
433     return _stepsIDs[ id ];
434 }
435
436
437 void YQWizard::layoutHelpPanel()
438 {
439     return;
440
441     //
442     // Button box with bottom gradient
443     //
444
445     QLabel * buttonBox = new QLabel( _sideBar );
446     YUI_CHECK_NEW( buttonBox );
447
448     QPushButton * button;
449     QPixmap pixmap;
450
451     if ( _treeEnabled )
452     {
453         // "Tree" button - intentionally without keyboard shortcut
454         button = new QPushButton( _( "Tree" ), buttonBox );
455         YUI_CHECK_NEW( button );
456         _treeButton = button;
457
458         pixmap = QPixmap( PIXMAP_DIR "tree-button.png" );
459     }
460     else
461         if ( _stepsEnabled )
462     {
463         // "Steps" button - intentionally without keyboard shortcut
464         button = new QPushButton( _( "Steps" ), buttonBox );
465         YUI_CHECK_NEW( button );
466         _stepsButton = button;
467         _stepsButton->hide();
468
469         pixmap = QPixmap( PIXMAP_DIR "steps-button.png" );
470     }
471     else
472     {
473         // Create a dummy button just to find out how high it would become
474         button = new QPushButton( "Dummy", buttonBox );
475         YUI_CHECK_NEW( button );
476     }
477
478
479 #if USE_ICON_ON_HELP_BUTTON
480     if ( ! pixmap.isNull() )
481         button->setPixmap( pixmap );
482 #endif
483
484     layoutSideBarButtonBox( buttonBox, button );
485
486     if ( _treeEnabled )
487     {
488         connect( button, SIGNAL( clicked()  ),
489                  this,   SLOT  ( showTree() ) );
490     }
491     else if ( _stepsEnabled )
492     {
493         connect( button, SIGNAL( clicked()   ),
494                  this,   SLOT  ( showSteps() ) );
495     }
496     else
497     {
498         // Hide the dummy button - the button box height is fixed now.
499         button->hide();
500     }
501 }
502
503
504 void YQWizard::layoutTreePanel()
505 {
506     _treePanel = new QFrame( _sideBar );
507     YUI_CHECK_NEW( _treePanel );
508     QHBoxLayout *layout = new QHBoxLayout( _treePanel );
509     _sideBar->addWidget( _treePanel );
510
511     QVBoxLayout * vbox = new QVBoxLayout();
512     YUI_CHECK_NEW( vbox );
513     layout->addLayout( vbox );
514
515     // Selection tree
516
517     _tree = new QY2ListView( _treePanel );
518     YUI_CHECK_NEW( _tree );
519     vbox->addWidget( _tree );
520
521     //FIXME
522 //     _tree->addColumn( "" );
523 //     _tree->header()->hide();
524
525     _tree->setRootIsDecorated( true );
526     _tree->setSortByInsertionSequence( true );
527
528     connect( _tree,     SIGNAL( selectionChanged     ( void ) ),
529              this,      SLOT  ( treeSelectionChanged ( void ) ) );
530
531     connect( _tree,     SIGNAL( spacePressed  ( QTreeWidgetItem * ) ),
532              this,      SLOT  ( sendTreeEvent ( QTreeWidgetItem * ) ) );
533
534     connect( _tree,     SIGNAL( doubleClicked ( QTreeWidgetItem * ) ),
535              this,      SLOT  ( sendTreeEvent ( QTreeWidgetItem * ) ) );
536
537 }
538
539
540 void YQWizard::addTreeItem( const string & parentID, const string & text, const string & id )
541 {
542     QString qId = fromUTF8( id );
543
544     if ( ! _tree )
545     {
546         y2error( "YQWizard widget not created with `opt(`treeEnabled) !" );
547         return;
548     }
549
550     YQWizard::TreeItem * item   = 0;
551     YQWizard::TreeItem * parent = 0;
552
553     if ( ! parentID.empty() )
554     {
555         parent = findTreeItem( parentID );
556     }
557
558     if ( parent )
559     {
560         item = new YQWizard::TreeItem( parent, fromUTF8( text ), qId );
561         YUI_CHECK_NEW( item );
562     }
563     else
564     {
565         item = new YQWizard::TreeItem( _tree, fromUTF8( text ), qId );
566         YUI_CHECK_NEW( item );
567     }
568
569     if ( ! qId.isEmpty() )
570         _treeIDs.insert( qId, item );
571 }
572
573
574
575 void YQWizard::deleteTreeItems()
576 {
577     if ( _tree )
578         _tree->clear();
579
580     _treeIDs.clear();
581 }
582
583
584
585 YQWizard::TreeItem * YQWizard::findTreeItem( const string & id )
586 {
587     if ( id.empty() )
588         return 0;
589
590     return _treeIDs[ fromUTF8( id ) ];
591 }
592
593
594 void YQWizard::selectTreeItem( const string & id )
595 {
596     if ( _tree )
597     {
598         YQWizard::TreeItem * item = findTreeItem( id );
599
600         if ( item )
601         {
602             YQSignalBlocker sigBlocker( _tree );
603
604       _tree->setCurrentItem(item);
605             _tree->scrollToItem(item);
606         }
607     }
608 }
609
610
611 void YQWizard::sendTreeEvent( QTreeWidgetItem * listViewItem )
612 {
613     if ( listViewItem )
614     {
615         YQWizard::TreeItem * item = dynamic_cast<YQWizard::TreeItem *> ( listViewItem );
616
617         if ( item && ! item->id().isEmpty() )
618             sendEvent( toUTF8( item->id() ) );
619     }
620 }
621
622
623 void YQWizard::treeSelectionChanged()
624 { //FIXME is currentItem correct or selected.first
625     if ( _tree )
626         sendTreeEvent( _tree->currentItem() );
627 }
628
629
630 string YQWizard::currentTreeSelection()
631 {
632     if ( _tree )
633     {
634         QTreeWidgetItem * sel = _tree->currentItem();
635
636         if ( sel )
637         {
638             YQWizard::TreeItem * item = dynamic_cast<YQWizard::TreeItem *> (sel);
639
640             if ( item && ! item->id().isEmpty() )
641                 return toUTF8( item->id() );
642         }
643     }
644
645     return string();
646 }
647
648
649
650 QWidget *YQWizard::layoutWorkArea( QWidget * parent )
651 {
652     QFrame *workArea = new QFrame( parent );
653     workArea->setObjectName( "work_area" );
654
655     QVBoxLayout *vbox = new QVBoxLayout( workArea );
656
657     _menuBar = new QMenuBar( workArea );
658     YUI_CHECK_NEW( _menuBar );
659
660     _menuBar->hide(); // will be made visible when menus are added
661     vbox->addWidget( _menuBar );
662
663     //
664     // Dialog icon and heading
665     //
666
667     QHBoxLayout * headingHBox = new QHBoxLayout();
668     YUI_CHECK_NEW( headingHBox );
669     //headingHBox->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ) ); // hor/vert
670     vbox->addLayout( headingHBox );
671
672     _dialogIcon = new QLabel( workArea );
673     YUI_CHECK_NEW( _dialogIcon );
674     headingHBox->addWidget( _dialogIcon );
675     _dialogIcon->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ) ); // hor/vert
676     _dialogIcon->setObjectName( "DialogIcon" );
677
678     _dialogHeading = new QLabel( workArea );
679     YUI_CHECK_NEW( _dialogHeading );
680     headingHBox->addWidget( _dialogHeading );
681     _dialogHeading->setAlignment( Qt::AlignLeft );
682     _dialogHeading->setWordWrap( true );
683     _dialogHeading->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ) ); // hor/vert
684     _dialogHeading->setObjectName( "DialogHeading" );
685
686     //
687     // Client area (the part that belongs to the YCP application)
688     //
689
690     layoutClientArea( workArea );
691     vbox->addWidget( _clientArea );
692
693     //
694     // Button box
695     //
696
697     QLayout *bb = layoutButtonBox( workArea );
698     vbox->addLayout( bb );
699
700     return workArea;
701 }
702
703
704
705 void YQWizard::layoutClientArea( QWidget * parent )
706 {
707     _clientArea = new QFrame( parent );
708     YUI_CHECK_NEW( _clientArea );
709     QVBoxLayout *layout = new QVBoxLayout( _clientArea );
710     // _clientArea->layout()->setMargin( 4 );
711
712     //
713     // HVCenter for wizard contents
714     //
715
716     _contents = new YQAlignment( this, _clientArea, YAlignCenter, YAlignCenter );
717     YUI_CHECK_NEW( _contents );
718     layout->addWidget( _contents );
719     _contents->QObject::setProperty( "class", "Contents" );
720
721     _contents->setStretchable( YD_HORIZ, true );
722     _contents->setStretchable( YD_VERT,  true );
723     _contents->installEventFilter( this );
724     _contents->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) ); // hor/vert
725
726     //
727     // Replace point for wizard contents
728     //
729
730     _contentsReplacePoint = YUI::widgetFactory()->createReplacePoint( _contents );
731
732     //
733     // Initial YEmpty widget contents of replace point
734     //
735
736     YUI::widgetFactory()->createEmpty( _contentsReplacePoint );
737     _contentsReplacePoint->showChild();
738
739 }
740
741
742
743 QLayout *YQWizard::layoutButtonBox( QWidget * parent )
744 {
745     //
746     // QHBoxLayout for the buttons
747     //
748
749     QHBoxLayout * hbox = new QHBoxLayout();             // parent, spacing
750     YUI_CHECK_NEW( hbox );
751
752     hbox->setSpacing( 0 );
753     hbox->setMargin( 0 );
754
755     // Help button - intentionally without keyboard shortcut
756     _helpButton = new QPushButton( _( "Help" ), parent );
757     YUI_CHECK_NEW( _helpButton );
758
759     connect( _helpButton, SIGNAL( clicked()  ),
760              this,       SLOT  ( showHelp() ) );
761
762 #if USE_ICON_ON_HELP_BUTTON
763     QPixmap pixmap = QPixmap( PIXMAP_DIR "help-button.png" );
764
765     if ( ! pixmap.isNull() )
766         _helpButton->setPixmap( pixmap );
767 #endif
768     hbox->addWidget( _helpButton );
769
770     hbox->addStretch( 10 );
771
772     //
773     // "Abort" button
774     //
775
776     _abortButton = new YQWizardButton( this, parent, _abortButtonLabel );
777     YUI_CHECK_NEW( _abortButton );
778
779     hbox->addWidget( (QWidget *) _abortButton->widgetRep() );
780     connect( _abortButton,      SIGNAL( clicked()               ),
781              this,              SLOT  ( slotAbortClicked()      ) );
782
783     hbox->addSpacing( 10 );
784
785     //
786     // "Back" button
787     //
788
789     _backButton  = new YQWizardButton( this, parent, _backButtonLabel );
790     YUI_CHECK_NEW( _backButton );
791
792     hbox->addWidget( (QWidget *) _backButton->widgetRep() );
793     connect( _backButton,       SIGNAL( clicked()               ),
794              this,              SLOT  ( slotBackClicked()       ) );
795
796     if ( _backButton->text().isEmpty() )
797         _backButton->hide();
798
799     //
800     // "Next" button
801     //
802
803     hbox->addSpacing( 5 );
804
805     _nextButton  = new YQWizardButton( this, parent, _nextButtonLabel );
806     YUI_CHECK_NEW( _nextButton );
807
808     hbox->addWidget( (QWidget *) _nextButton->widgetRep() );
809     connect( _nextButton,       SIGNAL( clicked()               ),
810              this,              SLOT  ( slotNextClicked()       ) );
811
812     return hbox;
813 }
814
815 void YQWizard::destroyButtons()
816 {
817     delete _backButton;
818     _backButton = 0;
819
820     delete _abortButton;
821     _abortButton = 0;
822
823     delete _nextButton;
824     _nextButton = 0;
825 }
826
827
828 void YQWizard::connectNotify ( const char * signal )
829 {
830     if ( QString( signal ).contains( "nextClicked()" ) )
831     {
832         y2debug( "nextClicked connected, no longer directly sending button events" );
833         _sendButtonEvents = false;
834     }
835 }
836
837
838 void YQWizard::disconnectNotify ( const char * signal )
839 {
840     if ( QString( signal ).contains( "nextClicked()" ) )
841     {
842         y2debug( "nextClicked disconnected, directly sending button events again" );
843         _sendButtonEvents = true;
844     }
845 }
846
847
848 void YQWizard::setDialogIcon( const string & iconName )
849 {
850     if ( _dialogIcon )
851     {
852         if ( ! iconName.empty() )
853         {
854             QPixmap icon( iconName.c_str() );
855
856             if ( icon.isNull() )
857                 y2warning( "Couldn't load dialog icon \"%s\"", iconName.c_str() );
858             else
859             {
860                 _dialogIcon->setPixmap( icon );
861                 topLevelWidget()->setWindowIcon( icon );
862             }
863         }
864         else
865         {
866             _dialogIcon->clear();
867             topLevelWidget()->setWindowIcon( QIcon() );
868         }
869     }
870 }
871
872
873 void YQWizard::setDialogHeading( const string & headingText )
874 {
875     if ( _dialogHeading )
876     {
877         if ( ! headingText.empty() )
878             _dialogHeading->setText( fromUTF8( headingText ) );
879         else
880             _dialogHeading->clear();
881     }
882 }
883
884 string YQWizard::debugLabel()
885 {
886     if ( _dialogHeading )
887     {
888         QString label = _dialogHeading->text();
889         label = label.simplified(); // Replace any embedded newline with a single blank
890
891         if ( ! label.isEmpty() )
892         {
893             label.prepend( "YQWizard \"" );
894             label.append( "\"" );
895
896             return toUTF8( label );
897         }
898     }
899
900     return "untitled YQWizard";
901 }
902
903 void YQWizard::setHelpText( const string & helpText )
904 {
905     _qHelpText = fromUTF8( helpText );
906     _qHelpText.replace( "&product;", YQUI::ui()->productName() );
907 }
908
909
910 void YQWizard::slotBackClicked()
911 {
912     emit backClicked();
913
914     if ( _sendButtonEvents )
915         YQUI::ui()->sendEvent( new YWidgetEvent( _backButton, YEvent::Activated ) );
916
917     _direction = YQWizard::Backward;
918 }
919
920
921 void YQWizard::slotAbortClicked()
922 {
923     emit abortClicked();
924
925     if ( _sendButtonEvents )
926         YQUI::ui()->sendEvent( new YWidgetEvent( _abortButton, YEvent::Activated ) );
927 }
928
929
930 void YQWizard::slotNextClicked()
931 {
932     emit nextClicked();
933
934     if ( _sendButtonEvents )
935         YQUI::ui()->sendEvent( new YWidgetEvent( _nextButton, YEvent::Activated ) );
936
937     _direction = YQWizard::Forward;
938 }
939
940
941 void YQWizard::showHelp()
942 {
943     QDialog helpDlg( this );
944     Ui_QHelpDialog ui;
945     ui.setupUi( &helpDlg );
946     ui.textBrowser->setText( _qHelpText );
947     helpDlg.exec();
948 }
949
950
951 void YQWizard::releaseNotesClicked()
952 {
953     YQUI::ui()->sendEvent( new YWidgetEvent( _nextButton, YEvent::Activated ) );
954
955     if ( ! _releaseNotesButtonId.empty() )
956     {
957         y2milestone( "Release Notes button clicked" );
958         sendEvent( _releaseNotesButtonId );
959     }
960 }
961
962
963 void YQWizard::showSteps()
964 {
965     if ( _sideBar && _stepsPanel )
966     {
967         _sideBar->setCurrentWidget( _stepsPanel );
968     }
969 }
970
971
972 void YQWizard::showTree()
973 {
974     if ( _sideBar && _treePanel )
975     {
976         _sideBar->setCurrentWidget( _treePanel );
977     }
978 }
979
980
981
982 void YQWizard::addMenu( const string & text,
983                         const string & id )
984 {
985     if ( _menuBar )
986     {
987         QMenu * menu = new QMenu( _menuBar );
988         YUI_CHECK_NEW( menu );
989
990         _menuIDs.insert( fromUTF8( id ), menu );
991         //FIXME _menuBar->insertItem( fromUTF8( text ), menu );
992
993         connect( menu, SIGNAL( activated    ( int ) ),
994                  this, SLOT  ( sendMenuEvent( int ) ) );
995
996         _menuBar->show();
997     }
998 }
999
1000
1001 void YQWizard::addSubMenu( const string & parentMenuID,
1002                            const string & text,
1003                            const string & id )
1004 {
1005     QMenu* parentMenu = _menuIDs[ fromUTF8( parentMenuID ) ];
1006
1007     if ( parentMenu )
1008     {
1009         QMenu * menu = new QMenu( _menuBar );
1010         YUI_CHECK_NEW( menu );
1011
1012         _menuIDs.insert( fromUTF8( id ), menu );
1013         //FIXME parentMenu->insertItem( fromUTF8( text ), menu );
1014
1015         connect( menu, SIGNAL( activated    ( int ) ),
1016                  this, SLOT  ( sendMenuEvent( int ) ) );
1017     }
1018     else
1019     {
1020         y2error( "Can't find menu with ID %s", parentMenuID.c_str() );
1021     }
1022 }
1023
1024
1025 void YQWizard::addMenuEntry( const string & parentMenuID,
1026                              const string & text,
1027                              const string & idString )
1028 {
1029     QMenu * parentMenu = _menuIDs[ fromUTF8( parentMenuID ) ];
1030
1031     if ( parentMenu )
1032     {
1033         int id = _menuEntryIDs.size();
1034         _menuEntryIDs.push_back( idString );
1035         //FIXME parentMenu->insertItem( fromUTF8( text ), id );
1036     }
1037     else
1038     {
1039         y2error( "Can't find menu with ID %s", parentMenuID.c_str() );
1040     }
1041 }
1042
1043
1044 void YQWizard::addMenuSeparator( const string & parentMenuID )
1045 {
1046     QMenu * parentMenu = _menuIDs[ fromUTF8( parentMenuID ) ];
1047
1048     if ( parentMenu )
1049     {
1050         parentMenu->addSeparator();
1051     }
1052     else
1053     {
1054         y2error( "Can't find menu with ID %s", parentMenuID.c_str() );
1055     }
1056 }
1057
1058
1059 void YQWizard::deleteMenus()
1060 {
1061     if ( _menuBar )
1062     {
1063         _menuBar->hide();
1064         _menuBar->clear();
1065         _menuIDs.clear();
1066         _menuEntryIDs.clear();
1067     }
1068 }
1069
1070
1071 void YQWizard::sendMenuEvent( int numID )
1072 {
1073     if ( numID >= 0 && numID < (int) _menuEntryIDs.size() )
1074     {
1075         sendEvent( _menuEntryIDs[ numID ] );
1076     }
1077     else
1078     {
1079         y2error( "Invalid menu ID: %d", numID );
1080     }
1081 }
1082
1083
1084 void YQWizard::sendEvent( const string & id )
1085 {
1086     YQUI::ui()->sendEvent( new YMenuEvent( id ) );
1087 }
1088
1089
1090 int YQWizard::preferredWidth()
1091 {
1092     return sizeHint().width();
1093 }
1094
1095
1096 int YQWizard::preferredHeight()
1097 {
1098     return sizeHint().height();
1099 }
1100
1101
1102 void YQWizard::setSize( int newWidth, int newHeight )
1103 {
1104     resize( newWidth, newHeight );
1105     resizeClientArea();
1106 }
1107
1108
1109
1110 void YQWizard::resizeClientArea()
1111 {
1112     y2debug( "resizing client area" );
1113     QRect contentsRect = _clientArea->contentsRect();
1114     _contents->setSize( contentsRect.width(), contentsRect.height() );
1115 }
1116
1117 bool YQWizard::eventFilter( QObject * obj, QEvent * ev )
1118 {
1119     if ( ev->type() == QEvent::Resize && obj == _contents )
1120     {
1121         resizeClientArea();
1122         return true;            // Event handled
1123     }
1124
1125     return QWidget::eventFilter( obj, ev );
1126 }
1127
1128
1129 void YQWizard::setButtonLabel( YPushButton * button, const string & newLabel )
1130 {
1131     button->setLabel( newLabel );
1132     YDialog::currentDialog()->checkShortcuts();
1133
1134     YQWizardButton * wizardButton = dynamic_cast<YQWizardButton *> (button);
1135
1136     if ( wizardButton )
1137         wizardButton->setVisible( !newLabel.empty() );
1138 }
1139
1140
1141 void YQWizard::showReleaseNotesButton( const string & label, const string & id )
1142 {
1143     return; // no longer supported!
1144
1145     if ( ! _releaseNotesButton )
1146     {
1147         y2error( "NULL Release Notes button" );
1148
1149         if ( ! _stepsPanel )
1150             y2error( "This works only if there is a \"steps\" panel!" );
1151
1152         return;
1153     }
1154
1155     // no way to check the shortcut, so strip it
1156     _releaseNotesButton->setText( fromUTF8( YShortcut::cleanShortcutString( label ) ) );
1157     _releaseNotesButtonId = id;
1158
1159     _releaseNotesButton->show();
1160 }
1161
1162
1163 void YQWizard::hideReleaseNotesButton()
1164 {
1165     if ( _releaseNotesButton && !_releaseNotesButton->isHidden() )
1166         _releaseNotesButton->hide();
1167 }
1168
1169
1170 void YQWizard::retranslateInternalButtons()
1171 {
1172     YQUI::setTextdomain( TEXTDOMAIN );
1173
1174     if ( _helpButton )
1175         // "Help" button - intentionally without keyboard shortcut
1176         _helpButton->setText( _( "Help" ) );
1177
1178     if ( _stepsButton )
1179         // "Steps" button - intentionally without keyboard shortcut
1180         _stepsButton->setText( _( "Steps" ) );
1181
1182     if ( _treeButton )
1183         // "Tree" button - intentionally without keyboard shortcut
1184         _treeButton->setText( _( "Tree" ) );
1185 }
1186
1187 void YQWizard::Step::setStatus( Status s )
1188 {
1189     if ( !_statusLabel || !_nameLabel || _status == s )
1190         return;
1191
1192     _status = s;
1193
1194     if ( s == Todo )
1195     {
1196         _statusLabel->setProperty( "class", "todo-step-status QLabel" );
1197         _nameLabel->setProperty( "class", "todo-step-name QLabel" );
1198     }
1199
1200     if ( s == Done )
1201     {
1202         _statusLabel->setProperty( "class", "done-step-status QLabel" );
1203         _nameLabel->setProperty( "class", "done-step-name QLabel" );
1204     }
1205
1206     if ( s == Current )
1207     {
1208         _statusLabel->setProperty( "class", "current-step-status QLabel" );
1209         _nameLabel->setProperty( "class", "current-step-name QLabel" );
1210     }
1211
1212     qApp->style()->polish( _statusLabel );
1213     qApp->style()->polish( _nameLabel );
1214 }
1215
1216 #include "YQWizard.moc"