1 /*---------------------------------------------------------------------\
3 | __ __ ____ _____ ____ |
4 | \ \ / /_ _/ ___|_ _|___ \ |
5 | \ V / _` \___ \ | | __) | |
6 | | | (_| |___) || | / __/ |
7 | |_|\__,_|____/ |_| |_____| |
11 \----------------------------------------------------------------------/
15 Author: Stefan Hundhammer <sh@suse.de>
17 Textdomain "packages-qt"
23 #define y2log_component "qt-wizard"
24 #include <ycp/y2log.h>
26 // For the command parser
29 #include <YShortcut.h>
37 #include <qmenudata.h>
40 #include <q3popupmenu.h>
41 #include <qpushbutton.h>
43 #include <qtabwidget.h>
44 #include <q3textbrowser.h>
45 #include <qtoolbutton.h>
46 #include <q3widgetstack.h>
48 #include "QY2ListView.h"
50 #include <q3boxlayout.h>
51 #include <q3gridlayout.h>
54 #include <q3boxlayout.h>
59 #include "YQApplication.h"
61 #include "YQAlignment.h"
62 #include "YQReplacePoint.h"
65 #include "YQWizardButton.h"
66 #include "YQIconPool.h"
67 #include "YQWidgetFactory.h"
68 #include "YQSignalBlocker.h"
69 #include "QY2LayoutUtils.h"
75 #define PIXMAP_DIR THEMEDIR "/wizard/"
81 #define TEXTDOMAIN "packages-qt"
83 #define ENABLE_GRADIENTS 1
85 #define USE_SEPARATOR 1
87 #define WORK_AREA_TOP_MARGIN 10
90 # define WORK_AREA_BOTTOM_MARGIN 8
91 # define WORK_AREA_RIGHT_MARGIN 8
93 # define WORK_AREA_BOTTOM_MARGIN 8
94 # define WORK_AREA_RIGHT_MARGIN 6
97 #define BUTTON_BOX_TOP_MARGIN 10
99 #define SEPARATOR_MARGIN 6
100 #define STEPS_MARGIN 10
101 #define STEPS_SPACING 2
102 #define STEPS_HEADING_SPACING 8
103 #define MENU_BAR_MARGIN 8
105 #define USE_FIXED_STEP_FONTS 0
106 #define STEPS_FONT_FAMILY "Sans Serif"
107 #define STEPS_FONT_SIZE 11
108 #define STEPS_HEADING_FONT_SIZE 11
110 #define USE_ICON_ON_HELP_BUTTON 0
113 YQWizard::YQWizard( YWidget * parent,
114 const string & backButtonLabel,
115 const string & abortButtonLabel,
116 const string & nextButtonLabel,
117 YWizardMode wizardMode )
118 : QFrame( (QWidget *) parent->widgetRep() )
124 , _backButtonLabel( backButtonLabel )
125 , _abortButtonLabel( abortButtonLabel )
126 , _nextButtonLabel( nextButtonLabel )
128 QVBoxLayout* layout = new QVBoxLayout( this );
129 layout->setSpacing( 0 );
130 layout->setMargin( 0 );
133 setWidgetRep( this );
135 _stepsEnabled = (wizardMode == YWizardMode_Steps);
136 _treeEnabled = (wizardMode == YWizardMode_Tree);
139 _direction = YQWizard::Forward;
148 _releaseNotesButton = 0;
160 _backButtonSpacer = 0;
163 _sendButtonEvents = true;
164 _contentsReplacePoint = 0;
166 _stepsList.setAutoDelete( true );
167 _stepsIDs.setAutoDelete( false ); // Only for one of both!
169 YQUI::setTextdomain( TEXTDOMAIN );
177 loadGradientPixmaps();
189 if ( ! _titleBarGradientPixmap.isNull() )
191 layoutTitleBar( this );
195 QWidget * spacer = addVSpacing( this, WORK_AREA_TOP_MARGIN );
196 Q_CHECK_PTR( spacer );
198 # if ENABLE_GRADIENTS
199 spacer->setPaletteBackgroundColor( _gradientTopColor );
203 Q3HBox * hBox = new Q3HBox( this );
204 YUI_CHECK_NEW( hBox );
205 layout->addWidget( hBox );
207 layoutSideBar( hBox );
208 layoutWorkArea( hBox );
210 y2debug( "Constructor finished." );
215 YQWizard::~YQWizard()
222 void YQWizard::layoutTitleBar( QWidget * parent )
224 if ( ! highColorDisplay() ) // 8 bit display or worse?
226 // No colorful title bar, just a spacing to match the one at the bottom.
227 addVSpacing( parent, WORK_AREA_BOTTOM_MARGIN );
233 Q3HBox * titleBar = new Q3HBox( parent );
234 YUI_CHECK_NEW( titleBar );
237 setGradient( titleBar, _titleBarGradientPixmap );
240 titleBar->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); // hor/vert
246 QLabel * left = new QLabel( titleBar );
247 left->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) ); // hor/vert
249 QPixmap leftLogo( PIXMAP_DIR "title-bar-left.png" );
251 if ( ! leftLogo.isNull() )
253 left->setPixmap( leftLogo );
254 left->setFixedSize( leftLogo.size() );
255 left->setBackgroundOrigin( QWidget::ParentOrigin );
260 // Center stretch space
263 addHStretch( titleBar );
270 QLabel * right = new QLabel( titleBar );
271 YUI_CHECK_NEW( right );
273 QPixmap rightLogo( PIXMAP_DIR "title-bar-right.png" );
275 if ( ! rightLogo.isNull() )
277 right->setPixmap( rightLogo );
278 right->setFixedSize( rightLogo.size() );
279 right->setBackgroundOrigin( QWidget::ParentOrigin );
285 void YQWizard::layoutSideBar( QWidget * parent )
287 _sideBar = new Q3WidgetStack( parent );
288 YUI_CHECK_NEW( _sideBar );
289 _sideBar->setMinimumWidth( YQUI::ui()->defaultSize( YD_HORIZ ) / 5 );
290 _sideBar->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred ) ); // hor/vert
291 _sideBar->setMargin( 0 );
301 else if ( _stepsEnabled )
310 void YQWizard::layoutStepsPanel()
312 _stepsPanel = new Q3VBox( _sideBar );
313 YUI_CHECK_NEW( _stepsPanel );
317 if ( ! _titleBarGradientPixmap.isNull() )
321 QLabel * topGradient = new QLabel( _stepsPanel );
322 Q_CHECK_PTR( topGradient );
323 setGradient( topGradient, _topGradientPixmap );
330 _sideBar->addWidget( _stepsPanel );
332 _stepsBox = new Q3VBox( _stepsPanel );
333 YUI_CHECK_NEW( _stepsBox );
335 _stepsBox->setPaletteBackgroundColor( _gradientCenterColor );
337 _stepsBox->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ) ); // hor/vert
339 QWidget * stretch = addVStretch( _stepsPanel );
340 YUI_CHECK_NEW( stretch );
342 stretch->setPaletteBackgroundColor( _gradientCenterColor );
346 // Steps panel bottom buttons ("Help", "Release Notes")
348 QLabel * helpButtonBox = new QLabel( _stepsPanel );
351 YUI_CHECK_NEW( helpButtonBox );
352 setGradient( helpButtonBox, _bottomGradientPixmap );
357 // Layouts for the buttons
359 QVBoxLayout * vbox = new QVBoxLayout( helpButtonBox, 0, 0 ); // parent, margin, spacing
360 YUI_CHECK_NEW( vbox );
361 vbox->addStretch( 99 );
364 QHBoxLayout * hbox = new QHBoxLayout( vbox, 0 ); // parent, spacing
365 hbox->addStretch( 99 );
367 _releaseNotesButton = new QPushButton( _( "Release Notes..." ), helpButtonBox );
368 hbox->addWidget( _releaseNotesButton );
371 connect( _releaseNotesButton, SIGNAL( clicked() ),
372 this, SLOT ( releaseNotesClicked() ) );
374 _releaseNotesButton->hide(); // hidden until showReleaseNotesButton() is called
376 hbox->addStretch( 99 );
377 vbox->addStretch( 99 );
379 hbox = new QHBoxLayout( vbox, 0 ); // parent, spacing
380 hbox->addStretch( 99 );
382 // Help button - intentionally without keyboard shortcut
383 _helpButton = new QPushButton( _( "Help" ), helpButtonBox );
384 YUI_CHECK_NEW( _helpButton );
386 hbox->addWidget( _helpButton );
387 hbox->addStretch( 99 );
389 connect( _helpButton, SIGNAL( clicked() ),
390 this, SLOT ( showHelp() ) );
392 #if USE_ICON_ON_HELP_BUTTON
393 QPixmap pixmap = QPixmap( PIXMAP_DIR "help-button.png" );
395 if ( ! pixmap.isNull() )
396 _helpButton->setPixmap( pixmap );
400 vbox->addSpacing( WORK_AREA_BOTTOM_MARGIN );
405 void YQWizard::addStep( const string & text, const string & id )
407 QString qId = fromUTF8( id );
409 if ( _stepsIDs[ qId ] )
411 y2error( "Step ID \"%s\" (\"%s\") already used for \"%s\"",
414 (const char *) _stepsIDs[ qId ]->name() );
418 if ( _stepsList.last() && _stepsList.last()->name() == fromUTF8( text ) )
420 // Consecutive steps with the same name will be shown as one single step.
422 // Since steps are always added at the end of the list, it is
423 // sufficient to check the last step of the list. If the texts are the
424 // same, the other with the same text needs to get another (additional)
425 // ID to make sure setCurrentStep() works as it should.
426 _stepsList.last()->addID( qId );
430 _stepsList.append( new YQWizard::Step( fromUTF8( text ), qId ) );
434 _stepsIDs.insert( qId, _stepsList.last() );
439 void YQWizard::addStepHeading( const string & text )
441 _stepsList.append( new YQWizard::StepHeading( fromUTF8( text ) ) );
447 void YQWizard::updateSteps()
453 // Delete any previous step widgets
458 delete _stepsGrid->mainWidget();
464 // Create a new parent widget for the steps
467 QWidget * stepsParent = new QWidget( _stepsBox );
468 YUI_CHECK_NEW( stepsParent );
469 stepsParent->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) ); // hor/vert
471 stepsParent->setPaletteBackgroundColor( _gradientCenterColor );
474 // Create a grid layout for the steps
476 _stepsGrid = new Q3GridLayout( stepsParent, // parent
477 _stepsList.count(), 4, // rows, cols
478 0, STEPS_SPACING ); // margin, spacing
479 YUI_CHECK_NEW( _stepsGrid );
481 const int statusCol = 1;
482 const int nameCol = 2;
484 _stepsGrid->setColStretch( 0, 99 ); // Left margin column - stretch
485 _stepsGrid->setColStretch( statusCol, 0 ); // Status column - don't stretch
486 _stepsGrid->setColStretch( nameCol, 0 ); // Name column - don't stretch
487 _stepsGrid->setColStretch( 3, 99 ); // Left margin column - stretch
490 // Work around Qt bug: Grid layout only works right if the parent widget isn't visible.
494 // Add left and right (but not top and bottom) margins
499 QWidget * leftSpacer = addHSpacing( stepsParent, STEPS_MARGIN );
500 YUI_CHECK_NEW( leftSpacer );
501 _stepsGrid->addWidget( leftSpacer, row, 0 );
503 QWidget * rightSpacer = addHSpacing( stepsParent, STEPS_MARGIN );
504 YUI_CHECK_NEW( rightSpacer );
505 _stepsGrid->addWidget( rightSpacer, row, 3 );
509 // Create widgets for all steps and step headings in the internal list
512 YQWizard::Step * step = _stepsList.first();
516 if ( step->isHeading() )
522 QWidget * spacer = addVSpacing( stepsParent, STEPS_HEADING_SPACING );
523 YUI_CHECK_NEW( spacer );
524 _stepsGrid->addWidget( spacer, row++, nameCol );
531 QLabel * label = new QLabel( step->name(), stepsParent );
532 YUI_CHECK_NEW( label );
533 label->setAlignment( Qt::AlignLeft | Qt::AlignTop );
535 #if USE_FIXED_STEP_FONTS
536 QFont font( STEPS_FONT_FAMILY, STEPS_HEADING_FONT_SIZE );
537 font.setWeight( QFont::Bold );
538 label->setFont( font );
540 QFont font = YQUI::yqApp()->currentFont();
542 int size = font.pointSize();
545 font.setPointSize( size + 2 );
547 font.setBold( true );
548 label->setFont( font );
551 step->setNameLabel( label );
552 _stepsGrid->addMultiCellWidget( label,
553 row, row, // from_row, to_row
554 statusCol, nameCol ); // from_col, to_col
556 else // No heading - ordinary step
562 QLabel * statusLabel = new QLabel( stepsParent );
563 YUI_CHECK_NEW( statusLabel );
565 step->setStatusLabel( statusLabel );
566 _stepsGrid->addWidget( statusLabel, row, statusCol );
573 QLabel * nameLabel = new QLabel( step->name(), stepsParent );
574 YUI_CHECK_NEW( nameLabel );
575 nameLabel->setAlignment( Qt::AlignLeft | Qt::AlignTop );
577 #if USE_FIXED_STEP_FONTS
578 nameLabel->setFont( QFont( STEPS_FONT_FAMILY, STEPS_FONT_SIZE ) );
580 nameLabel->setFont( YQUI::yqApp()->currentFont() );
583 step->setNameLabel( nameLabel );
584 _stepsGrid->addWidget( nameLabel, row, nameCol );
587 step = _stepsList.next();
591 _stepsGrid->activate();
597 void YQWizard::updateStepStates()
602 YQWizard::Step * currentStep = findStep( _currentStepID );
603 YQWizard::Step * step = _stepsList.first();
607 // Set status icon and color for the current step
608 setStepStatus( currentStep, _stepCurrentIcon, _stepCurrentColor );
612 // Set all steps before the current to "done"
615 while ( step && step != currentStep )
617 setStepStatus( step, _stepDoneIcon, _stepDoneColor );
618 step = _stepsList.next();
621 // Skip the current step - continue with the step after it
624 step = _stepsList.next();
628 // Set all steps after the current to "to do"
633 setStepStatus( step, _stepToDoIcon, _stepToDoColor );
634 step = _stepsList.next();
639 void YQWizard::setStepStatus( YQWizard::Step * step, const QPixmap & icon, const QColor & color )
643 if ( step->nameLabel() )
644 step->nameLabel()->setPaletteForegroundColor( color );
646 if ( step->statusLabel() )
647 step->statusLabel()->setPixmap( icon );
652 void YQWizard::setCurrentStep( const string & id )
654 _currentStepID = fromUTF8( id );
659 void YQWizard::deleteSteps()
666 YQWizard::Step * YQWizard::findStep( const QString & id )
671 return _stepsIDs[ id ];
675 void YQWizard::layoutHelpPanel()
677 _helpPanel = new Q3HBox( _sideBar );
678 YUI_CHECK_NEW( _helpPanel );
679 _sideBar->addWidget( _helpPanel );
681 addGradientColumn( _helpPanel );
683 Q3VBox * vbox = new Q3VBox( _helpPanel );
684 YUI_CHECK_NEW( vbox );
689 _helpBrowser = new Q3TextBrowser( vbox );
690 YUI_CHECK_NEW( _helpBrowser );
692 _helpBrowser->setMimeSourceFactory( 0 );
693 _helpBrowser->installEventFilter( this );
694 _helpBrowser->setTextFormat( Qt::RichText );
695 _helpBrowser->setMargin( 4 );
696 _helpBrowser->setResizePolicy( Q3ScrollView::Manual );
699 // Set help browser text color
700 QPixmap fgPixmap = QPixmap( PIXMAP_DIR "help-text-color.png" );
701 if (! fgPixmap.isNull() )
702 _helpBrowser->setPaletteForegroundColor( pixelColor( fgPixmap, 0, 0, paletteForegroundColor() ) );
705 if ( highColorDisplay() )
707 // Set fancy help browser background pixmap
709 QPixmap bgPixmap( PIXMAP_DIR "help-background.png" );
711 if ( ! bgPixmap.isNull() )
712 _helpBrowser->setPaletteBackgroundPixmap( bgPixmap );
718 // Button box with bottom gradient
722 QLabel * buttonBox = new QLabel( vbox );
723 YUI_CHECK_NEW( buttonBox );
725 QPushButton * button;
730 // "Tree" button - intentionally without keyboard shortcut
731 button = new QPushButton( _( "Tree" ), buttonBox );
732 YUI_CHECK_NEW( button );
733 _treeButton = button;
735 #if USE_ICON_ON_HELP_BUTTON
736 pixmap = QPixmap( PIXMAP_DIR "tree-button.png" );
742 // "Steps" button - intentionally without keyboard shortcut
743 button = new QPushButton( _( "Steps" ), buttonBox );
744 YUI_CHECK_NEW( button );
745 _stepsButton = button;
747 #if USE_ICON_ON_HELP_BUTTON
748 pixmap = QPixmap( PIXMAP_DIR "steps-button.png" );
753 // Create a dummy button just to find out how high it would become
754 button = new QPushButton( "Dummy", buttonBox );
755 YUI_CHECK_NEW( button );
759 if ( ! pixmap.isNull() )
760 button->setPixmap( pixmap );
762 layoutSideBarButtonBox( buttonBox, button );
766 connect( button, SIGNAL( clicked() ),
767 this, SLOT ( showTree() ) );
769 else if ( _stepsEnabled )
771 connect( button, SIGNAL( clicked() ),
772 this, SLOT ( showSteps() ) );
776 // Hide the dummy button - the button box height is fixed now.
780 addGradientColumn( _helpPanel );
785 void YQWizard::layoutSideBarButtonBox( QWidget * parent, QPushButton * button )
787 QVBoxLayout * vbox = new QVBoxLayout( parent, 0, 0 ); // parent, margin, spacing
788 YUI_CHECK_NEW( vbox );
789 vbox->addSpacing( BUTTON_BOX_TOP_MARGIN );
791 QHBoxLayout * hbox = new QHBoxLayout( vbox, 0 ); // parent, spacing
792 YUI_CHECK_NEW( hbox );
794 hbox->addStretch( 99 );
795 hbox->addWidget( button );
796 hbox->addStretch( 99 );
798 vbox->addSpacing( WORK_AREA_BOTTOM_MARGIN );
800 // For whatever strange reason, parent->sizeHint() does not return anything
801 // meaningful yet - not even after vbox->activate() or parent->adjustSize()
802 int height = button->sizeHint().height() + BUTTON_BOX_TOP_MARGIN + WORK_AREA_BOTTOM_MARGIN;
805 if ( ! _bottomGradientPixmap.isNull() )
806 setBottomCroppedGradient( parent, _bottomGradientPixmap, height );
809 parent->setFixedHeight( height );
814 void YQWizard::layoutTreePanel()
816 _treePanel = new Q3HBox( _sideBar );
817 YUI_CHECK_NEW( _treePanel );
818 _sideBar->addWidget( _treePanel );
820 // Left margin (with gradients)
821 addGradientColumn( _treePanel );
823 Q3VBox * vbox = new Q3VBox( _treePanel );
824 YUI_CHECK_NEW( vbox );
829 _tree = new QY2ListView( vbox );
830 YUI_CHECK_NEW( _tree );
833 // _tree->addColumn( "" );
834 // _tree->header()->hide();
836 _tree->setRootIsDecorated( true );
837 _tree->setSortByInsertionSequence( true );
839 connect( _tree, SIGNAL( selectionChanged ( void ) ),
840 this, SLOT ( treeSelectionChanged ( void ) ) );
842 connect( _tree, SIGNAL( spacePressed ( QTreeWidgetItem * ) ),
843 this, SLOT ( sendTreeEvent ( QTreeWidgetItem * ) ) );
845 connect( _tree, SIGNAL( doubleClicked ( QTreeWidgetItem * ) ),
846 this, SLOT ( sendTreeEvent ( QTreeWidgetItem * ) ) );
851 QLabel * buttonBox = new QLabel( vbox );
852 YUI_CHECK_NEW( buttonBox );
855 // "Help" button - intentionally without keyboard shortcut
856 QPushButton * button = new QPushButton( _( "Help" ), buttonBox );
857 YUI_CHECK_NEW( button );
859 #if USE_ICON_ON_HELP_BUTTON
860 QPixmap pixmap( PIXMAP_DIR "help-button.png" );
862 if ( ! pixmap.isNull() )
863 button->setPixmap( pixmap );
866 layoutSideBarButtonBox( buttonBox, button );
868 connect( button, SIGNAL( clicked() ),
869 this, SLOT ( showHelp() ) );
872 // Right margin (with gradients)
873 addGradientColumn( _treePanel );
878 void YQWizard::addTreeItem( const string & parentID, const string & text, const string & id )
880 QString qId = fromUTF8( id );
884 y2error( "YQWizard widget not created with `opt(`treeEnabled) !" );
888 YQWizard::TreeItem * item = 0;
889 YQWizard::TreeItem * parent = 0;
891 if ( ! parentID.empty() )
893 parent = findTreeItem( parentID );
898 item = new YQWizard::TreeItem( parent, fromUTF8( text ), qId );
899 YUI_CHECK_NEW( item );
903 item = new YQWizard::TreeItem( _tree, fromUTF8( text ), qId );
904 YUI_CHECK_NEW( item );
907 if ( ! qId.isEmpty() )
908 _treeIDs.insert( qId, item );
913 void YQWizard::deleteTreeItems()
923 YQWizard::TreeItem * YQWizard::findTreeItem( const string & id )
928 return _treeIDs[ fromUTF8( id ) ];
932 void YQWizard::selectTreeItem( const string & id )
936 YQWizard::TreeItem * item = findTreeItem( id );
940 YQSignalBlocker sigBlocker( _tree );
942 _tree->setCurrentItem(item);
943 _tree->scrollToItem(item);
949 void YQWizard::sendTreeEvent( QTreeWidgetItem * listViewItem )
953 YQWizard::TreeItem * item = dynamic_cast<YQWizard::TreeItem *> ( listViewItem );
955 if ( item && ! item->id().isEmpty() )
956 sendEvent( toUTF8( item->id() ) );
961 void YQWizard::treeSelectionChanged()
962 { //FIXME is currentItem correct or selected.first
964 sendTreeEvent( _tree->currentItem() );
968 string YQWizard::currentTreeSelection()
972 QTreeWidgetItem * sel = _tree->currentItem();
976 YQWizard::TreeItem * item = dynamic_cast<YQWizard::TreeItem *> (sel);
978 if ( item && ! item->id().isEmpty() )
979 return toUTF8( item->id() );
988 void YQWizard::layoutWorkArea( QFrame * parentHBox )
990 Q3VBox * workAreaVBox = new Q3VBox( parentHBox );
991 YUI_CHECK_NEW( workAreaVBox );
993 // An extra QVBox inside the workAreaVBox is needed for frame and margin
995 Q3VBox * workArea = new Q3VBox( workAreaVBox );
996 YUI_CHECK_NEW( workArea );
999 workArea->setFrameStyle( QFrame::Box | QFrame::Plain );
1000 workArea->setMargin( 4 );
1002 workArea->setFrameStyle( QFrame::Box | QFrame::Sunken );
1003 // workArea->setFrameStyle( QFrame::TabWidgetPanel | QFrame::Sunken );
1011 // Placed directly inside workArea the menu bar positions itself at (0,0)
1012 // and so obscures any kind of frame there might be.
1013 _menuBarBox = new Q3VBox( workArea );
1014 YUI_CHECK_NEW( _menuBarBox );
1016 _menuBar = new QMenuBar( _menuBarBox );
1017 YUI_CHECK_NEW( _menuBar );
1019 _menuBarBox->hide(); // will be made visible when menus are added
1023 // Dialog icon and heading
1026 Q3HBox * headingHBox = new Q3HBox( workArea );
1027 YUI_CHECK_NEW( headingHBox );
1028 headingHBox->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ) ); // hor/vert
1030 addHSpacing( headingHBox, SEPARATOR_MARGIN );
1032 _dialogIcon = new QLabel( headingHBox );
1033 YUI_CHECK_NEW( _dialogIcon );
1034 _dialogIcon->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ) ); // hor/vert
1036 addHSpacing( headingHBox );
1038 _dialogHeading = new QLabel( headingHBox );
1039 YUI_CHECK_NEW( _dialogHeading );
1040 _dialogHeading->setFont( YQUI::yqApp()->headingFont() );
1041 _dialogHeading->setAlignment( Qt::AlignLeft | Qt::WordBreak );
1042 _dialogHeading->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ) ); // hor/vert
1045 addHStretch( headingHBox );
1047 addVSpacing( workArea );
1051 Q3HBox * hbox = new Q3HBox( workArea );
1053 addHSpacing( hbox, SEPARATOR_MARGIN );
1055 QFrame * separator = new QFrame( hbox );
1056 YUI_CHECK_NEW( separator );
1057 separator->setFrameStyle( QFrame::HLine | QFrame::Sunken );
1059 addHSpacing( hbox, SEPARATOR_MARGIN );
1060 addVSpacing( workArea );
1064 // Client area (the part that belongs to the YCP application)
1067 layoutClientArea( workArea );
1074 layoutButtonBox( workAreaVBox );
1078 // Spacer (purely decorative) at the right of the client area
1081 addGradientColumn( parentHBox, WORK_AREA_RIGHT_MARGIN );
1086 void YQWizard::layoutClientArea( QWidget * parent )
1088 _clientArea = new Q3VBox( parent );
1089 YUI_CHECK_NEW( _clientArea );
1090 _clientArea->setMargin( 4 );
1093 _clientArea->setPaletteBackgroundColor( QColor( 0x60, 0x60, 0x60 ) );
1098 // HVCenter for wizard contents
1101 _contents = new YQAlignment( this, _clientArea, YAlignCenter, YAlignCenter );
1102 YUI_CHECK_NEW( _contents );
1104 _contents->setStretchable( YD_HORIZ, true );
1105 _contents->setStretchable( YD_VERT, true );
1106 _contents->installEventFilter( this );
1107 _contents->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) ); // hor/vert
1111 // Replace point for wizard contents
1114 _contentsReplacePoint = YUI::widgetFactory()->createReplacePoint( _contents );
1117 // Initial YEmpty widget contents of replace point
1120 YUI::widgetFactory()->createEmpty( _contentsReplacePoint );
1121 _contentsReplacePoint->showChild();
1126 void YQWizard::layoutButtonBox( QWidget * parent )
1129 // Button box and layout
1132 QWidget * buttonBox = new QWidget( parent );
1133 YUI_CHECK_NEW( buttonBox );
1135 // Using old-style layouts to enable a seamless background with the
1136 // gradient pixmap: Any sub-widgets (Q3VBox, QHBox) would have to get yet
1137 // another portion of that gradient as their backround pixmap, and it would
1138 // be very hard to cover all cases - resizing, hiding individual buttons, etc.
1140 QVBoxLayout * vbox = new QVBoxLayout( buttonBox, 0, 0 ); // parent, margin, spacing
1141 YUI_CHECK_NEW( vbox );
1143 vbox->addSpacing( BUTTON_BOX_TOP_MARGIN );
1147 // QHBoxLayout for the buttons
1150 QHBoxLayout * hbox = new QHBoxLayout( vbox, 2 ); // parent, spacing
1151 YUI_CHECK_NEW( hbox );
1158 _backButton = new YQWizardButton( this, buttonBox, _backButtonLabel );
1159 YUI_CHECK_NEW( _backButton );
1161 hbox->addWidget( (QWidget *) _backButton->widgetRep() );
1162 connect( _backButton, SIGNAL( clicked() ),
1163 this, SLOT ( slotBackClicked() ) );
1165 _backButtonSpacer = new QSpacerItem( 0, 0, // width, height
1166 QSizePolicy::Expanding, // horizontal
1167 QSizePolicy::Minimum ); // vertical
1168 YUI_CHECK_NEW( _backButtonSpacer );
1169 hbox->addItem( _backButtonSpacer );
1172 if ( _backButton->text().isEmpty() )
1174 _backButton->hide();
1176 // Minimize _backButtonSpacer
1177 _backButtonSpacer->changeSize( 0, 0, // width, height
1178 QSizePolicy::Minimum, // horizontal
1179 QSizePolicy::Minimum ); // vertical
1187 _abortButton = new YQWizardButton( this, buttonBox, _abortButtonLabel );
1188 YUI_CHECK_NEW( _abortButton );
1190 hbox->addWidget( (QWidget *) _abortButton->widgetRep() );
1191 connect( _abortButton, SIGNAL( clicked() ),
1192 this, SLOT ( slotAbortClicked() ) );
1195 // Using spacer rather than addSpacing() since the default stretchability
1196 // of a QSpacerItem is undefined, i.e. centering the middle button could
1197 // not be guaranteed.
1199 QSpacerItem * spacer = new QSpacerItem( 0, 0, // width, height
1200 QSizePolicy::Expanding, // horizontal
1201 QSizePolicy::Minimum ); // vertical
1202 YUI_CHECK_NEW( spacer );
1203 hbox->addItem( spacer );
1210 _nextButton = new YQWizardButton( this, buttonBox, _nextButtonLabel );
1211 YUI_CHECK_NEW( _nextButton );
1213 hbox->addWidget( (QWidget *) _nextButton->widgetRep() );
1214 connect( _nextButton, SIGNAL( clicked() ),
1215 this, SLOT ( slotNextClicked() ) );
1219 // Bottom margin and gradient
1222 vbox->addSpacing( WORK_AREA_BOTTOM_MARGIN );
1224 #if ENABLE_GRADIENTS
1225 setBottomCroppedGradient( buttonBox, _bottomGradientPixmap, buttonBox->sizeHint().height() );
1228 buttonBox->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) ); // hor/vert
1233 void YQWizard::loadGradientPixmaps()
1235 #if ENABLE_GRADIENTS
1236 if ( highColorDisplay() )
1238 _topGradientPixmap = QPixmap( PIXMAP_DIR "top-gradient.png" );
1239 _bottomGradientPixmap = QPixmap( PIXMAP_DIR "bottom-gradient.png" );
1240 _titleBarGradientPixmap = QPixmap( PIXMAP_DIR "title-bar-gradient.png" );
1241 _gradientCenterColor = pixelColor( _bottomGradientPixmap, 0, 0, paletteBackgroundColor() );
1242 _gradientTopColor = pixelColor( _topGradientPixmap, 0, 0 , paletteBackgroundColor() );
1244 else // 8 bit display or worse - don't use gradients
1246 // Gradient pixmaps remain empty. The respecive widgets will retain the
1247 // default widget background (grey, depending on the widget theme).
1249 // Use deault widget background (some shade of grey) for the center
1250 // stretchable part of the side bar.
1251 _gradientCenterColor = paletteBackgroundColor();
1252 _gradientTopColor = paletteBackgroundColor();
1258 void YQWizard::loadStepsIcons()
1260 _stepCurrentIcon = YQIconPool::stepCurrent();
1261 _stepToDoIcon = YQIconPool::stepToDo();
1262 _stepDoneIcon = YQIconPool::stepDone();
1264 if ( highColorDisplay() )
1266 _stepCurrentColor = pixelColor( QPixmap( PIXMAP_DIR "color-step-current.png" ), 0, 0, paletteForegroundColor() );
1267 _stepToDoColor = pixelColor( QPixmap( PIXMAP_DIR "color-step-todo.png" ), 0, 0, paletteForegroundColor() );
1268 _stepDoneColor = pixelColor( QPixmap( PIXMAP_DIR "color-step-done.png" ), 0, 0, paletteForegroundColor() );
1272 _stepCurrentColor = paletteForegroundColor();
1273 _stepToDoColor = paletteForegroundColor();
1274 _stepDoneColor = paletteForegroundColor();
1280 void YQWizard::setGradient( QWidget * widget, const QPixmap & pixmap )
1282 #if ENABLE_GRADIENTS
1283 if ( widget && ! pixmap.isNull() )
1285 widget->setFixedHeight( pixmap.height() );
1286 widget->setPaletteBackgroundPixmap( pixmap );
1293 void YQWizard::setBottomCroppedGradient( QWidget * widget, const QPixmap & pixmap, int croppedHeight )
1295 #if ENABLE_GRADIENTS
1296 setGradient( widget, bottomCropPixmap( pixmap, croppedHeight ) );
1302 QPixmap YQWizard::bottomCropPixmap( const QPixmap & full, int croppedHeight )
1306 #if ENABLE_GRADIENTS
1308 if ( full.height() > croppedHeight )
1310 pixmap = QPixmap( full.width(), croppedHeight );
1312 bitBlt( &pixmap, 0, 0, // dest, dest_x, dest_y
1313 &full, 0, full.height() - croppedHeight - 1, // src, src_x, src_y
1314 full.width(), croppedHeight ); // src_width, src_height
1327 QColor YQWizard::pixelColor( const QPixmap & pixmap, int x, int y, const QColor & defaultColor )
1329 if ( pixmap.isNull() )
1330 return defaultColor;
1332 // QPixmap doesn't allow direct access to pixel values (which makes some
1333 // sense since this requires a round-trip to the X server - pixmaps are X
1334 // server resources), so we need to convert the QPixmap to a QImage to get
1335 // that information. But since this conversion is expensive, we might save
1336 // some performance if we only convert the part we really need - so let's
1337 // cut out a tiny portion of the original pixmap and convert only that tiny
1341 QPixmap tiny( 1, 1 );
1343 bitBlt( &tiny, 0, 0, // dest, dest_x, dest_y
1344 &pixmap, x, y, // src, src_x, src_y
1345 1, 1 ); // src_width, src_height
1347 QImage image = tiny.convertToImage();
1350 return QColor( image.pixel( 0, 0 ) );
1355 void YQWizard::addGradientColumn( QWidget * parent, int width )
1360 Q3VBox * vbox = new Q3VBox( parent );
1361 YUI_CHECK_NEW( vbox );
1363 #if ENABLE_GRADIENTS
1364 QWidget * topGradient = addHSpacing( vbox, width );
1365 YUI_CHECK_NEW( topGradient );
1366 setGradient( topGradient, _topGradientPixmap );
1368 QWidget * centerStretch = new QWidget( vbox );
1369 YUI_CHECK_NEW( centerStretch );
1370 centerStretch->setPaletteBackgroundColor( _gradientCenterColor );
1373 QWidget * bottomGradient = new QWidget( vbox );
1374 YUI_CHECK_NEW( bottomGradient );
1375 setGradient( bottomGradient, _bottomGradientPixmap );
1377 vbox->setFixedWidth( width );
1383 void YQWizard::destroyButtons()
1393 delete _abortButton;
1406 bool YQWizard::highColorDisplay() const
1412 void YQWizard::connectNotify ( const char * signal )
1414 if ( QString( signal ).contains( "nextClicked()" ) )
1416 y2debug( "nextClicked connected, no longer directly sending button events" );
1417 _sendButtonEvents = false;
1422 void YQWizard::disconnectNotify ( const char * signal )
1424 if ( QString( signal ).contains( "nextClicked()" ) )
1426 y2debug( "nextClicked disconnected, directly sending button events again" );
1427 _sendButtonEvents = true;
1432 void YQWizard::setDialogIcon( const string & iconName )
1436 if ( ! iconName.empty() )
1438 QPixmap icon( iconName.c_str() );
1440 if ( icon.isNull() )
1441 y2warning( "Couldn't load dialog icon \"%s\"", iconName.c_str() );
1444 _dialogIcon->setPixmap( icon );
1445 topLevelWidget()->setIcon( icon );
1450 _dialogIcon->clear();
1451 topLevelWidget()->setIcon( QPixmap() );
1457 void YQWizard::setDialogHeading( const string & headingText )
1459 if ( _dialogHeading )
1461 if ( ! headingText.empty() )
1462 _dialogHeading->setText( fromUTF8( headingText ) );
1464 _dialogHeading->clear();
1468 string YQWizard::debugLabel()
1470 if ( _dialogHeading )
1472 QString label = _dialogHeading->text();
1473 label.simplifyWhiteSpace(); // Replace any embedded newline with a single blank
1475 if ( ! label.isEmpty() )
1477 label.prepend( "YQWizard \"" );
1478 label.append( "\"" );
1480 return toUTF8( label );
1484 return "untitled YQWizard";
1487 void YQWizard::setHelpText( const string & helpText )
1491 if ( ! helpText.empty() )
1493 QString qHelpText = fromUTF8( helpText );
1494 qHelpText.replace( "&product;", YQUI::ui()->productName() );
1495 _helpBrowser->setText( qHelpText );
1498 _helpBrowser->clear();
1503 void YQWizard::slotBackClicked()
1507 if ( _sendButtonEvents )
1508 YQUI::ui()->sendEvent( new YWidgetEvent( _backButton, YEvent::Activated ) );
1510 _direction = YQWizard::Backward;
1514 void YQWizard::slotAbortClicked()
1516 emit abortClicked();
1518 if ( _sendButtonEvents )
1519 YQUI::ui()->sendEvent( new YWidgetEvent( _abortButton, YEvent::Activated ) );
1523 void YQWizard::slotNextClicked()
1527 if ( _sendButtonEvents )
1528 YQUI::ui()->sendEvent( new YWidgetEvent( _nextButton, YEvent::Activated ) );
1530 _direction = YQWizard::Forward;
1534 void YQWizard::showHelp()
1536 if ( _sideBar && _helpPanel )
1538 _sideBar->raiseWidget( _helpPanel );
1543 void YQWizard::releaseNotesClicked()
1545 YQUI::ui()->sendEvent( new YWidgetEvent( _nextButton, YEvent::Activated ) );
1547 if ( ! _releaseNotesButtonId.empty() )
1549 y2milestone( "Release Notes button clicked" );
1550 sendEvent( _releaseNotesButtonId );
1555 void YQWizard::showSteps()
1557 if ( _sideBar && _stepsPanel )
1559 _sideBar->raiseWidget( _stepsPanel );
1564 void YQWizard::showTree()
1566 if ( _sideBar && _treePanel )
1568 _sideBar->raiseWidget( _treePanel );
1574 void YQWizard::addMenu( const string & text,
1579 Q3PopupMenu * menu = new Q3PopupMenu( _menuBar );
1580 YUI_CHECK_NEW( menu );
1582 _menuIDs.insert( fromUTF8( id ), menu );
1583 _menuBar->insertItem( fromUTF8( text ), menu );
1585 connect( menu, SIGNAL( activated ( int ) ),
1586 this, SLOT ( sendMenuEvent( int ) ) );
1588 if ( _menuBarBox && _menuBarBox->isHidden() )
1590 _menuBarBox->show();
1591 _menuBarBox->setFixedHeight( _menuBar->sizeHint().height() + MENU_BAR_MARGIN );
1597 void YQWizard::addSubMenu( const string & parentMenuID,
1598 const string & text,
1601 Q3PopupMenu * parentMenu = _menuIDs[ fromUTF8( parentMenuID ) ];
1605 Q3PopupMenu * menu = new Q3PopupMenu( _menuBar );
1606 YUI_CHECK_NEW( menu );
1608 _menuIDs.insert( fromUTF8( id ), menu );
1609 parentMenu->insertItem( fromUTF8( text ), menu );
1611 connect( menu, SIGNAL( activated ( int ) ),
1612 this, SLOT ( sendMenuEvent( int ) ) );
1616 y2error( "Can't find menu with ID %s", parentMenuID.c_str() );
1621 void YQWizard::addMenuEntry( const string & parentMenuID,
1622 const string & text,
1623 const string & idString )
1625 Q3PopupMenu * parentMenu = _menuIDs[ fromUTF8( parentMenuID ) ];
1629 int id = _menuEntryIDs.size();
1630 _menuEntryIDs.push_back( idString );
1631 parentMenu->insertItem( fromUTF8( text ), id );
1635 y2error( "Can't find menu with ID %s", parentMenuID.c_str() );
1640 void YQWizard::addMenuSeparator( const string & parentMenuID )
1642 Q3PopupMenu * parentMenu = _menuIDs[ fromUTF8( parentMenuID ) ];
1646 parentMenu->insertSeparator();
1650 y2error( "Can't find menu with ID %s", parentMenuID.c_str() );
1655 void YQWizard::deleteMenus()
1659 _menuBarBox->hide();
1662 _menuEntryIDs.clear();
1667 void YQWizard::sendMenuEvent( int numID )
1669 if ( numID >= 0 && numID < (int) _menuEntryIDs.size() )
1671 sendEvent( _menuEntryIDs[ numID ] );
1675 y2error( "Invalid menu ID: %d", numID );
1680 void YQWizard::sendEvent( const string & id )
1682 YQUI::ui()->sendEvent( new YMenuEvent( id ) );
1686 int YQWizard::preferredWidth()
1688 return sizeHint().width();
1692 int YQWizard::preferredHeight()
1694 return sizeHint().height();
1698 void YQWizard::setSize( int newWidth, int newHeight )
1700 resize( newWidth, newHeight );
1706 void YQWizard::resizeClientArea()
1708 // y2debug( "resizing client area" );
1709 QRect contentsRect = _clientArea->contentsRect();
1710 _contents->setSize( contentsRect.width(), contentsRect.height() );
1715 bool YQWizard::eventFilter( QObject * obj, QEvent * ev )
1717 if ( ev->type() == QEvent::Resize && obj == _contents )
1720 return true; // Event handled
1723 return QWidget::eventFilter( obj, ev );
1727 void YQWizard::setButtonLabel( YPushButton * button, const string & newLabel )
1729 button->setLabel( newLabel );
1730 YDialog::currentDialog()->checkShortcuts();
1732 YQWizardButton * wizardButton = dynamic_cast<YQWizardButton *> (button);
1736 if ( newLabel.empty() )
1738 wizardButton->hide();
1740 if ( wizardButton == _backButton && _backButtonSpacer )
1742 // Minimize _backButtonSpacer
1744 _backButtonSpacer->changeSize( 0, 0, // width, height
1745 QSizePolicy::Minimum, // horizontal
1746 QSizePolicy::Minimum ); // vertical
1751 wizardButton->show();
1753 if ( button == _backButton && _backButtonSpacer )
1755 // Restore _backButtonSpacer to normal size
1757 _backButtonSpacer->changeSize( 0, 0, // width, height
1758 QSizePolicy::Expanding, // horizontal
1759 QSizePolicy::Minimum ); // vertical
1766 void YQWizard::showReleaseNotesButton( const string & label, const string & id )
1768 if ( ! _releaseNotesButton )
1770 y2error( "NULL Release Notes button" );
1773 y2error( "This works only if there is a \"steps\" panel!" );
1778 // no way to check the shortcut, so strip it
1779 _releaseNotesButton->setText( fromUTF8( YShortcut::cleanShortcutString( label ) ) );
1780 _releaseNotesButtonId = id;
1782 if ( _releaseNotesButton->isHidden() )
1783 _releaseNotesButton->show();
1788 void YQWizard::hideReleaseNotesButton()
1790 if ( _releaseNotesButton && _releaseNotesButton->isShown() )
1791 _releaseNotesButton->hide();
1795 void YQWizard::retranslateInternalButtons()
1797 YQUI::setTextdomain( TEXTDOMAIN );
1800 // "Help" button - intentionally without keyboard shortcut
1801 _helpButton->setText( _( "Help" ) );
1804 // "Steps" button - intentionally without keyboard shortcut
1805 _stepsButton->setText( _( "Steps" ) );
1808 // "Tree" button - intentionally without keyboard shortcut
1809 _treeButton->setText( _( "Tree" ) );
1812 #include "YQWizard.moc"