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