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