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