summaryrefslogtreecommitdiffstats
path: root/krecipes/src/widgets/kremenu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'krecipes/src/widgets/kremenu.cpp')
-rw-r--r--krecipes/src/widgets/kremenu.cpp639
1 files changed, 639 insertions, 0 deletions
diff --git a/krecipes/src/widgets/kremenu.cpp b/krecipes/src/widgets/kremenu.cpp
new file mode 100644
index 0000000..892ff75
--- /dev/null
+++ b/krecipes/src/widgets/kremenu.cpp
@@ -0,0 +1,639 @@
+/***************************************************************************
+* Copyright (C) 2003 by *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* Cyril Bosselut (bosselut@b1project.com) *
+* *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as publishfed by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+#include "kremenu.h"
+
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <qfont.h>
+#include <qimage.h>
+#include <qobjectlist.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qsignalmapper.h>
+
+#include <kapplication.h>
+#include <kcursor.h>
+#include <kdebug.h>
+#include <kglobalsettings.h>
+#include <kiconloader.h>
+#include <kimageeffect.h>
+#include <klocale.h>
+#include <kpixmap.h>
+#include <kpixmapeffect.h>
+
+KreMenu::KreMenu( QWidget *parent, const char *name ) :
+#if QT_VERSION >= 0x030200
+ QWidget( parent, name, Qt::WNoAutoErase )
+#else
+ QWidget( parent, name )
+#endif
+{
+ Menu newMenu;
+
+ mainMenuId = menus.append( newMenu );
+
+ currentMenuId = mainMenuId;
+ m_currentMenu = &( *currentMenuId );
+
+ setMouseTracking( true );
+ setFocusPolicy( QWidget::StrongFocus );
+ setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred );
+}
+
+
+KreMenu::~KreMenu()
+{}
+
+void KreMenu::childEvent ( QChildEvent *e )
+{
+ if ( e->type() == QChildEvent::ChildInserted ) {
+
+ QObject * child = e->child();
+ if ( child->inherits( "KreMenuButton" ) ) {
+ KreMenuButton * button = ( KreMenuButton* ) child;
+
+ Menu *buttonMenu = &( *( button->menuId ) );
+
+
+
+ if ( !( buttonMenu->activeButton ) ) // Highlight the button if it's the first in the menu
+ {
+ button->setActive( true );
+ buttonMenu->activeButton = button;
+ }
+
+ buttonMenu->addButton( button );
+
+ if ( buttonMenu != m_currentMenu )
+ button->hide();
+ else
+ button->show();
+
+ connect ( button, SIGNAL( clicked( KreMenuButton* ) ), this, SLOT( collectClicks( KreMenuButton* ) ) );
+ }
+ }
+ else if ( e->type() == QChildEvent::ChildRemoved ) {
+ QObject * child = e->child();
+
+ KreMenuButton *button = ( KreMenuButton* ) child;
+ if ( m_currentMenu->positionList.find( button ) != m_currentMenu->positionList.end() ) // Ensure that what was removed was a button
+ {
+ // Remove the button from the list first
+ int pos = m_currentMenu->positionList[ button ]; // FIXME: this works only if the button is removed from the main menu
+ m_currentMenu->widgetList.remove( pos ); // FIXME: this works only if the button is removed from the main menu
+ m_currentMenu->positionList.remove( button ); // FIXME: this works only if the button is removed from the main menu
+
+ // Now recalculate the position of the next button
+ ( m_currentMenu->widgetNumber ) --; // FIXME: this works only if the button is removed from the main menu
+
+ KreMenuButton *lastButton = m_currentMenu->widgetList[ ( m_currentMenu->widgetNumber ) - 1 ];
+ if ( lastButton )
+ m_currentMenu->childPos = lastButton->y() + lastButton->height();
+ m_currentMenu->activeButton = 0;
+
+ setMinimumWidth( minimumSizeHint().width() + 10 ); //update the minimum width
+ }
+
+ }
+ QWidget::childEvent( e );
+}
+
+void KreMenu::collectClicks( KreMenuButton *w )
+{
+ setFocus();
+
+ highlightButton( w );
+
+ // Emit signal indicating button activation with button ID
+ KrePanel panel = w->getPanel();
+ emit clicked( panel );
+}
+
+MenuId KreMenu::createSubMenu( const QString &title, const QString &icon )
+{
+
+ // Create the new menu
+ Menu newMenu;
+ MenuId id = menus.append( newMenu );
+
+ // Add a button to the main menu for this submenu
+ KIconLoader il;
+ KreMenuButton *newMenuButton = new KreMenuButton( this );
+ newMenuButton->subMenuId = id;
+ newMenuButton->setTitle( title );
+ newMenuButton->setIconSet( il.loadIconSet( icon, KIcon::Panel ) );
+
+ // Add a button to the submenu to go back to the top menu
+ KreMenuButton *newSubMenuButton = new KreMenuButton( this );
+ newSubMenuButton->menuId = id;
+ newSubMenuButton->subMenuId = mainMenuId;
+ newSubMenuButton->setTitle( i18n( "Up" ) );
+ newSubMenuButton->setIconSet( il.loadIconSet( "1uparrow", KIcon::Panel ) );
+
+ connect( newMenuButton, SIGNAL( clicked( MenuId ) ), this, SLOT( showMenu( MenuId ) ) );
+ connect( newSubMenuButton, SIGNAL( clicked( MenuId ) ), this, SLOT( showMenu( MenuId ) ) );
+
+
+ return id;
+}
+
+void KreMenu::highlightButton( KreMenuButton *button )
+{
+ MenuId buttonMenuId = button->menuId;
+ Menu *buttonMenu = &( *buttonMenuId );
+
+ //Deactivate the old button
+ if ( buttonMenu->activeButton ) {
+ buttonMenu->activeButton->setActive( false );
+ buttonMenu->activeButton->update();
+ }
+
+ //Activate the new button
+
+ button->setActive( true );
+ button->update();
+ buttonMenu->activeButton = button;
+}
+
+void KreMenu::keyPressEvent( QKeyEvent *e )
+{
+ switch ( e->key() ) {
+ case Qt::Key_Up: {
+ int current_index = m_currentMenu->positionList[ m_currentMenu->activeButton ];
+ if ( current_index > 0 ) {
+ highlightButton( m_currentMenu->widgetList[ current_index - 1 ] );
+
+ //simulate a mouse click
+ QMouseEvent me( QEvent::MouseButtonPress, QPoint(), 0, 0 );
+ KApplication::sendEvent( m_currentMenu->activeButton, &me );
+
+ e->accept();
+ }
+ break;
+ }
+ case Qt::Key_Down: {
+ int current_index = m_currentMenu->positionList[ m_currentMenu->activeButton ];
+ if ( current_index < int( m_currentMenu->positionList.count() ) - 1 ) {
+ highlightButton( m_currentMenu->widgetList[ current_index + 1 ] );
+
+ //simulate a mouse click
+ QMouseEvent me( QEvent::MouseButtonPress, QPoint(), 0, 0 );
+ KApplication::sendEvent( m_currentMenu->activeButton, &me );
+
+ e->accept();
+ }
+ break;
+ }
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ case Qt::Key_Space: {
+ //simulate a mouse click
+ QMouseEvent me( QEvent::MouseButtonPress, QPoint(), 0, 0 );
+ KApplication::sendEvent( m_currentMenu->activeButton, &me );
+
+ e->accept();
+ break;
+ }
+ default:
+ e->ignore();
+ }
+}
+
+QSize KreMenu::sizeHint() const
+{
+ return minimumSizeHint();
+}
+
+//the minimum size hint will be the minimum size hint of the largest child
+QSize KreMenu::minimumSizeHint() const
+{
+ int width = 30;
+
+ QObjectList *childElements = queryList( 0, 0, false, false ); //only first-generation children (not recursive)
+ QObjectListIterator it( *childElements );
+
+ QObject *obj;
+ while ( ( obj = it.current() ) != 0 ) {
+ ++it;
+
+ if ( obj->isWidgetType() ) {
+ int obj_width_hint = ( ( QWidget* ) obj ) ->minimumSizeHint().width();
+
+ if ( obj_width_hint > width )
+ width = obj_width_hint;
+ }
+ }
+
+ return QSize( width, 150 );
+}
+
+void KreMenu::paintEvent( QPaintEvent * )
+{
+ // Make sure the size is bigger than the minimum necessary
+ //if (minimumWidth() <45) setMinimumWidth(45); // FIXME: can somehow setMinimumWidth be restricted? This may not be the best place to do this
+
+ // Get gradient colors
+ QColor c = colorGroup().button();
+ QColor c1 = c.dark( 130 );
+ QColor c2 = c.light( 120 );
+
+ // Draw the gradient
+ KPixmap kpm;
+ kpm.resize( size() );
+ KPixmapEffect::unbalancedGradient ( kpm, c2, c1, KPixmapEffect::HorizontalGradient, -150, -150 );
+
+ // Draw the handle
+ QPainter painter( &kpm );
+ painter.setPen( c1 );
+ painter.drawLine( width() - 5, 20, width() - 5, height() - 20 );
+ painter.end();
+
+ //Set the border transparent using a mask
+ QBitmap mask( kpm.size() );
+ mask.fill( Qt::color0 );
+ painter.begin( &mask );
+ painter.setPen( Qt::color1 );
+ painter.setBrush( Qt::color1 );
+ painter.drawRoundRect( 0, 0, width(), height(), ( int ) ( 2.0 / width() * height() ), 2 );
+ painter.end();
+ kpm.setMask( mask );
+
+ //Draw the border line
+ painter.begin( &kpm );
+ painter.setPen( c1 );
+ painter.drawRoundRect( 0, 0, width(), height(), ( int ) ( 2.0 / width() * height() ), 2 );
+
+ //Draw the top line bordering with the first button
+ if ( m_currentMenu->activeButton ) // draw only if there's a button
+ {
+ int w = m_currentMenu->activeButton->width();
+ painter.setPen( c1 );
+ painter.drawLine( w / 5, 8, w - 1, 8 );
+ painter.setPen( c2 );
+ painter.drawLine( w / 5, 9, w - 1, 9 );
+ }
+
+ painter.end();
+
+ // Copy the pixmap to the widget
+ bitBlt( this, 0, 0, &kpm );
+}
+
+void KreMenu::resizeEvent( QResizeEvent* e )
+{
+ emit resized( ( e->size() ).width(), ( e->size() ).height() );
+}
+
+void KreMenu::showMenu( MenuId id )
+{
+
+ // Hide the buttons in the current menu
+ // and show the ones in the new menu
+
+ QObjectList * childElements = queryList();
+ QObjectListIterator it( *childElements );
+
+ QObject *obj;
+ while ( ( obj = it.current() ) != 0 ) {
+ ++it;
+ if ( obj->inherits( "KreMenuButton" ) ) {
+ KreMenuButton * button = ( KreMenuButton* ) obj;
+ if ( button->menuId == currentMenuId )
+ button->hide();
+ else if ( button->menuId == id )
+ button->show();
+ }
+ }
+
+
+ // Set the new menu as current
+ currentMenuId = id;
+ m_currentMenu = &( *( currentMenuId ) );
+}
+
+
+
+
+
+KreMenuButton::KreMenuButton( KreMenu *parent, KrePanel _panel, MenuId id, const char *name ) :
+#if QT_VERSION >= 0x030200
+ QWidget( parent, name, Qt::WNoAutoErase ),
+#else
+ QWidget( parent, name ),
+#endif
+ panel( _panel )
+{
+ icon = 0;
+ highlighted = false;
+ text = QString::null;
+
+ if ( id == 0 )
+ menuId = parent->mainMenu();
+ else
+ menuId = id;
+
+ subMenuId = 0; // By default it's not a submenu button
+
+ resize( parent->size().width(), 55 );
+ connect ( parent, SIGNAL( resized( int, int ) ), this, SLOT( rescale() ) );
+ connect( this, SIGNAL( clicked() ), this, SLOT( forwardClicks() ) );
+ setCursor( QCursor( KCursor::handCursor() ) );
+}
+
+
+KreMenuButton::~KreMenuButton()
+{
+ delete icon;
+}
+
+void KreMenuButton::setTitle( const QString &s )
+{
+ text = s;
+
+#if 0 //this causes problems for the button to go back to editing a recipe
+ //adjust text to two lines if needed
+ if ( fontMetrics().width( text ) > 110 ) {
+ text.replace( ' ', "\n" );
+ }
+#endif
+
+ setMinimumWidth( minimumSizeHint().width() );
+ if ( parentWidget() ->minimumWidth() < minimumSizeHint().width() )
+ parentWidget() ->setMinimumWidth( minimumSizeHint().width() + 10 );
+
+ update();
+}
+
+void KreMenuButton::mousePressEvent ( QMouseEvent * )
+{
+ emit clicked();
+}
+
+void KreMenuButton::rescale()
+{
+ resize( parentWidget() ->width() - 10, height() );
+}
+QSize KreMenuButton::sizeHint() const
+{
+ if ( parentWidget() )
+ return ( QSize( parentWidget() ->size().width() - 10, 40 ) );
+ else
+ return QSize( 100, 30 );
+}
+
+QSize KreMenuButton::minimumSizeHint() const
+{
+ int text_width = QMAX( fontMetrics().width( text.section( '\n', 0, 0 ) ), fontMetrics().width( text.section( '\n', 1, 1 ) ) );
+
+ if ( icon )
+ return QSize( 40 + icon->width() + text_width, 30 );
+ else
+ return QSize( 40 + text_width, 30 );
+}
+
+void KreMenuButton::paintEvent( QPaintEvent * )
+{
+ if ( !isShown() )
+ return ;
+ // First draw the gradient
+ int darken = 130, lighten = 120;
+ QColor c1, c2, c1h, c2h; //non-highlighted and highlighted versions
+
+ // Set the gradient colors
+
+ c1 = colorGroup().button().dark( darken );
+ c2 = colorGroup().button().light( lighten );
+
+ if ( highlighted ) {
+ darken -= 10;
+ lighten += 10;
+ c1h = KGlobalSettings::highlightColor().dark( darken );
+ c2h = KGlobalSettings::highlightColor().light( lighten );
+ }
+
+ // draw the gradient now
+
+ QPainter painter;
+ KPixmap kpm;
+ kpm.resize( ( ( QWidget * ) parent() ) ->size() ); // use parent's same size to obtain the same gradient
+
+ if ( !highlighted ) {
+
+ // first the gradient
+ KPixmapEffect::unbalancedGradient ( kpm, c2, c1, KPixmapEffect::HorizontalGradient, -150, -150 );
+
+ }
+ else {
+
+ // top gradient (highlighted)
+ kpm.resize( width(), height() );
+ KPixmapEffect::unbalancedGradient ( kpm, c2h, c1h, KPixmapEffect::HorizontalGradient, -150, -150 );
+ // low gradient besides the line (not hightlighted)
+ KPixmap kpmb;
+ kpmb.resize( width(), 2 );
+ KPixmapEffect::unbalancedGradient ( kpmb, c2, c1, KPixmapEffect::HorizontalGradient, -150, -150 );
+ // mix the two
+ bitBlt( &kpm, 0, height() - 2, &kpmb );
+
+ }
+
+ // Draw the line
+ painter.begin( &kpm );
+ painter.setPen( colorGroup().button().dark( darken ) );
+ painter.drawLine( width() / 5, height() - 2, width() - 1, height() - 2 );
+ painter.setPen( colorGroup().button().light( lighten ) );
+ painter.drawLine( width() / 5, height() - 1, width() - 1, height() - 1 );
+ painter.end();
+
+
+ // Now Add the icon
+
+ painter.begin( &kpm );
+ int xPos, yPos;
+ if ( icon ) {
+ // Set the icon's desired horizontal position
+
+ xPos = 10;
+ yPos = 0;
+
+
+ // Make sure it fits in the area
+ // If not, resize and reposition horizontally to be centered
+
+ QPixmap scaledIcon = *icon;
+
+ if ( ( icon->height() > height() ) || ( icon->width() > width() / 3 ) ) // Nice effect, make sure you take less than half in width and fit in height (try making the menu very short in width)
+ {
+ QImage image;
+ image = ( *icon );
+ scaledIcon.convertFromImage( image.smoothScale( width() / 3, height(), QImage::ScaleMin ) );
+ }
+
+ // Calculate the icon's vertical position
+
+ yPos = ( height() - scaledIcon.height() ) / 2 - 1;
+
+
+ // Now draw it
+
+ painter.drawPixmap( xPos, yPos, scaledIcon );
+
+ xPos += scaledIcon.width(); // increase it to place the text area correctly
+ }
+
+ painter.end();
+
+ // If it's highlighted, draw a rounded area around the text
+
+ // Calculate the rounded area
+
+ int areax = xPos + 10;
+ int areah = fontMetrics().height() * ( text.contains( '\n' ) + 1 ) + fontMetrics().lineSpacing() * text.contains( '\n' ) + 6; // Make sure the area is sensible for text and adjust for multiple lines
+
+ int areaw = width() - areax - 10;
+
+ if ( areah > ( height() - 4 ) ) {
+ areah = height() - 4; // Limit to button height
+ }
+
+ int areay = ( height() - areah - 2 ) / 2 + 1; // Center the area vertically
+
+
+ // Calculate roundness
+
+ int roundy = 99, roundx = ( int ) ( ( float ) roundy * areah / areaw ); //Make corners round
+
+
+ if ( highlighted && areaw > 0 ) // If there is no space for the text area do not draw it
+ {
+
+ // Draw the gradient
+ KPixmap area;
+ area.resize( areaw, areah );
+
+
+ KPixmapEffect::gradient( area, c2h.light( 150 ), c1h.light( 150 ), KPixmapEffect::VerticalGradient );
+
+ painter.begin( &area );
+ painter.setPen( c1h );
+ painter.setBrush( Qt::NoBrush );
+ painter.drawRoundRect( 0, 0, areaw, areah, roundx, roundy );
+ painter.end();
+
+ // Make it round
+ QBitmap mask( QSize( areaw, areah ) );
+ mask.fill( Qt::color0 );
+ painter.begin( &mask );
+ painter.setPen( Qt::color1 );
+ painter.setBrush( Qt::color1 );
+ painter.drawRoundRect( 0, 0, areaw, areah, roundx, roundy );
+ painter.end();
+ area.setMask( mask );
+
+ // Copy it to the button
+ bitBlt( &kpm, areax, areay, &area );
+ }
+
+ // Finally, draw the text besides the icon
+ QRect r = rect();
+ r.setLeft( areax + 5 );
+ r.setWidth( areaw - 10 );
+
+ painter.begin( &kpm );
+ if ( highlighted )
+ painter.setPen( KGlobalSettings::highlightedTextColor() );
+ else
+ painter.setPen( KGlobalSettings::textColor() );
+ painter.setClipRect( r );
+ painter.drawText( r, Qt::AlignVCenter, text );
+ painter.end();
+
+ // Copy the offscreen button to the widget
+ bitBlt( this, 0, 0, &kpm, 0, 0, width(), height() ); // Copy the image with correct button size (button is already smaller than parent in width to leave space for the handle, so no need to use -10)
+
+}
+
+void KreMenuButton::setIconSet( const QIconSet &is )
+{
+ delete icon;
+
+ icon = new QPixmap( is.pixmap( QIconSet::Small, QIconSet::Normal, QIconSet::On ) );
+
+ setMinimumWidth( minimumSizeHint().width() );
+ if ( parentWidget() ->minimumWidth() < minimumSizeHint().width() )
+ parentWidget() ->setMinimumWidth( minimumSizeHint().width() + 10 );
+}
+
+Menu::Menu( void )
+{
+ childPos = 10; // Initial button is on top (10px), then keep scrolling down
+ widgetNumber = 0; // Initially we have no buttons
+ activeButton = 0; // Button that is highlighted
+}
+
+
+Menu::Menu( const Menu &m )
+{
+ activeButton = m.activeButton;
+ childPos = m.childPos;
+ widgetNumber = m.widgetNumber;
+
+ copyMap( positionList, m.positionList );
+ copyMap( widgetList, m.widgetList );
+}
+
+Menu::~Menu( void )
+{}
+
+Menu& Menu::operator=( const Menu &m )
+{
+
+ activeButton = m.activeButton;
+ childPos = m.childPos;
+ widgetNumber = m.widgetNumber;
+
+ copyMap( positionList, m.positionList );
+ copyMap( widgetList, m.widgetList );
+
+ return *this;
+}
+
+
+void Menu::addButton( KreMenuButton* button )
+{
+ button->move( 0, childPos );
+ button->rescale();
+ childPos += button->height();
+ positionList[ button ] = widgetNumber; // Store index for this widget, and increment number
+ widgetList[ widgetNumber ] = button; // Store the button in the list (the inverse mapping of the previous one)
+ widgetNumber++;
+}
+
+void Menu::copyMap( QMap <int, KreMenuButton*> &destMap, const QMap <int, KreMenuButton*> &origMap )
+{
+ QMap<int, KreMenuButton*>::ConstIterator it;
+ destMap.clear();
+ for ( it = origMap.begin(); it != origMap.end(); ++it ) {
+ destMap[ it.key() ] = it.data();
+ }
+}
+
+void Menu::copyMap( QMap <KreMenuButton*, int> &destMap, const QMap <KreMenuButton*, int> &origMap )
+{
+ QMap<KreMenuButton*, int>::ConstIterator it;
+ destMap.clear();
+ for ( it = origMap.begin(); it != origMap.end(); ++it ) {
+ destMap[ it.key() ] = it.data();
+ }
+}
+
+#include "kremenu.moc"