You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
krecipes/krecipes/src/widgets/kremenu.cpp

640 lines
17 KiB

/***************************************************************************
* 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 <ntqbitmap.h>
#include <ntqcursor.h>
#include <ntqfont.h>
#include <ntqimage.h>
#include <ntqobjectlist.h>
#include <ntqpainter.h>
#include <ntqpixmap.h>
#include <ntqsignalmapper.h>
#include <tdeapplication.h>
#include <kcursor.h>
#include <kdebug.h>
#include <tdeglobalsettings.h>
#include <kiconloader.h>
#include <kimageeffect.h>
#include <tdelocale.h>
#include <kpixmap.h>
#include <kpixmapeffect.h>
KreMenu::KreMenu( TQWidget *parent, const char *name ) :
#if TQT_VERSION >= 0x030200
TQWidget( parent, name, TQt::WNoAutoErase )
#else
TQWidget( parent, name )
#endif
{
Menu newMenu;
mainMenuId = menus.append( newMenu );
currentMenuId = mainMenuId;
m_currentMenu = &( *currentMenuId );
setMouseTracking( true );
setFocusPolicy( TQWidget::StrongFocus );
setSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Preferred );
}
KreMenu::~KreMenu()
{}
void KreMenu::childEvent ( TQChildEvent *e )
{
if ( e->type() == TQChildEvent::ChildInserted ) {
TQObject * 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, TQ_SIGNAL( clicked( KreMenuButton* ) ), this, TQ_SLOT( collectClicks( KreMenuButton* ) ) );
}
}
else if ( e->type() == TQChildEvent::ChildRemoved ) {
TQObject * 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
}
}
TQWidget::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 TQString &title, const TQString &icon )
{
// Create the new menu
Menu newMenu;
MenuId id = menus.append( newMenu );
// Add a button to the main menu for this submenu
TDEIconLoader il;
KreMenuButton *newMenuButton = new KreMenuButton( this );
newMenuButton->subMenuId = id;
newMenuButton->setTitle( title );
newMenuButton->setIconSet( il.loadIconSet( icon, TDEIcon::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", TDEIcon::Panel ) );
connect( newMenuButton, TQ_SIGNAL( clicked( MenuId ) ), this, TQ_SLOT( showMenu( MenuId ) ) );
connect( newSubMenuButton, TQ_SIGNAL( clicked( MenuId ) ), this, TQ_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( TQKeyEvent *e )
{
switch ( e->key() ) {
case TQt::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
TQMouseEvent me( TQEvent::MouseButtonPress, TQPoint(), 0, 0 );
TDEApplication::sendEvent( m_currentMenu->activeButton, &me );
e->accept();
}
break;
}
case TQt::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
TQMouseEvent me( TQEvent::MouseButtonPress, TQPoint(), 0, 0 );
TDEApplication::sendEvent( m_currentMenu->activeButton, &me );
e->accept();
}
break;
}
case TQt::Key_Enter:
case TQt::Key_Return:
case TQt::Key_Space: {
//simulate a mouse click
TQMouseEvent me( TQEvent::MouseButtonPress, TQPoint(), 0, 0 );
TDEApplication::sendEvent( m_currentMenu->activeButton, &me );
e->accept();
break;
}
default:
e->ignore();
}
}
TQSize KreMenu::sizeHint() const
{
return minimumSizeHint();
}
//the minimum size hint will be the minimum size hint of the largest child
TQSize KreMenu::minimumSizeHint() const
{
int width = 30;
TQObjectList *childElements = queryList( 0, 0, false, false ); //only first-generation children (not recursive)
TQObjectListIterator it( *childElements );
TQObject *obj;
while ( ( obj = it.current() ) != 0 ) {
++it;
if ( obj->isWidgetType() ) {
int obj_width_hint = ( ( TQWidget* ) obj ) ->minimumSizeHint().width();
if ( obj_width_hint > width )
width = obj_width_hint;
}
}
return TQSize( width, 150 );
}
void KreMenu::paintEvent( TQPaintEvent * )
{
// 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
TQColor c = colorGroup().button();
TQColor c1 = c.dark( 130 );
TQColor c2 = c.light( 120 );
// Draw the gradient
KPixmap kpm;
kpm.resize( size() );
KPixmapEffect::unbalancedGradient ( kpm, c2, c1, KPixmapEffect::HorizontalGradient, -150, -150 );
// Draw the handle
TQPainter painter( &kpm );
painter.setPen( c1 );
painter.drawLine( width() - 5, 20, width() - 5, height() - 20 );
painter.end();
//Set the border transparent using a mask
TQBitmap mask( kpm.size() );
mask.fill( TQt::color0 );
painter.begin( &mask );
painter.setPen( TQt::color1 );
painter.setBrush( TQt::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( TQResizeEvent* 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
TQObjectList * childElements = queryList();
TQObjectListIterator it( *childElements );
TQObject *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 TQT_VERSION >= 0x030200
TQWidget( parent, name, TQt::WNoAutoErase ),
#else
TQWidget( parent, name ),
#endif
panel( _panel )
{
icon = 0;
highlighted = false;
text = TQString::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, TQ_SIGNAL( resized( int, int ) ), this, TQ_SLOT( rescale() ) );
connect( this, TQ_SIGNAL( clicked() ), this, TQ_SLOT( forwardClicks() ) );
setCursor( TQCursor( KCursor::handCursor() ) );
}
KreMenuButton::~KreMenuButton()
{
delete icon;
}
void KreMenuButton::setTitle( const TQString &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 ( TQMouseEvent * )
{
emit clicked();
}
void KreMenuButton::rescale()
{
resize( parentWidget() ->width() - 10, height() );
}
TQSize KreMenuButton::sizeHint() const
{
if ( parentWidget() )
return ( TQSize( parentWidget() ->size().width() - 10, 40 ) );
else
return TQSize( 100, 30 );
}
TQSize KreMenuButton::minimumSizeHint() const
{
int text_width = TQMAX( fontMetrics().width( text.section( '\n', 0, 0 ) ), fontMetrics().width( text.section( '\n', 1, 1 ) ) );
if ( icon )
return TQSize( 40 + icon->width() + text_width, 30 );
else
return TQSize( 40 + text_width, 30 );
}
void KreMenuButton::paintEvent( TQPaintEvent * )
{
if ( !isShown() )
return ;
// First draw the gradient
int darken = 130, lighten = 120;
TQColor 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 = TDEGlobalSettings::highlightColor().dark( darken );
c2h = TDEGlobalSettings::highlightColor().light( lighten );
}
// draw the gradient now
TQPainter painter;
KPixmap kpm;
kpm.resize( ( ( TQWidget * ) 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
TQPixmap 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)
{
TQImage image;
image = ( *icon );
scaledIcon.convertFromImage( image.smoothScale( width() / 3, height(), TQImage::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( TQt::NoBrush );
painter.drawRoundRect( 0, 0, areaw, areah, roundx, roundy );
painter.end();
// Make it round
TQBitmap mask( TQSize( areaw, areah ) );
mask.fill( TQt::color0 );
painter.begin( &mask );
painter.setPen( TQt::color1 );
painter.setBrush( TQt::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
TQRect r = rect();
r.setLeft( areax + 5 );
r.setWidth( areaw - 10 );
painter.begin( &kpm );
if ( highlighted )
painter.setPen( TDEGlobalSettings::highlightedTextColor() );
else
painter.setPen( TDEGlobalSettings::textColor() );
painter.setClipRect( r );
painter.drawText( r, TQt::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 TQIconSet &is )
{
delete icon;
icon = new TQPixmap( is.pixmap( TQIconSet::Small, TQIconSet::Normal, TQIconSet::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( TQMap <int, KreMenuButton*> &destMap, const TQMap <int, KreMenuButton*> &origMap )
{
TQMap<int, KreMenuButton*>::ConstIterator it;
destMap.clear();
for ( it = origMap.begin(); it != origMap.end(); ++it ) {
destMap[ it.key() ] = it.data();
}
}
void Menu::copyMap( TQMap <KreMenuButton*, int> &destMap, const TQMap <KreMenuButton*, int> &origMap )
{
TQMap<KreMenuButton*, int>::ConstIterator it;
destMap.clear();
for ( it = origMap.begin(); it != origMap.end(); ++it ) {
destMap[ it.key() ] = it.data();
}
}
#include "kremenu.moc"