diff options
Diffstat (limited to 'kopete/libkopete/ui/kopetelistviewitem.cpp')
-rw-r--r-- | kopete/libkopete/ui/kopetelistviewitem.cpp | 1314 |
1 files changed, 1314 insertions, 0 deletions
diff --git a/kopete/libkopete/ui/kopetelistviewitem.cpp b/kopete/libkopete/ui/kopetelistviewitem.cpp new file mode 100644 index 00000000..fda2ff4c --- /dev/null +++ b/kopete/libkopete/ui/kopetelistviewitem.cpp @@ -0,0 +1,1314 @@ +/* + kopetelistviewitem.cpp - Kopete's modular QListViewItems + + Copyright (c) 2005 by Engin AYDOGAN <[email protected]> + Copyright (c) 2004 by Richard Smith <[email protected]> + + Kopete (c) 2002-2004 by the Kopete developers <[email protected]> + + ************************************************************************* + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ************************************************************************* +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "kopetecontact.h" +#include "kopetelistviewitem.h" +#include "kopeteemoticons.h" +#include "kopeteonlinestatus.h" + +#include <kdebug.h> +#include <kiconloader.h> +#include <kstringhandler.h> + +#include <qapplication.h> +#include <qpixmap.h> +#include <qpainter.h> +#include <qptrlist.h> +#include <qrect.h> +#include <qtimer.h> +#include <qheader.h> +#include <qstyle.h> + +#ifdef HAVE_XRENDER +# include <X11/Xlib.h> +# include <X11/extensions/Xrender.h> +#endif + +#include <limits.h> + +namespace Kopete { +namespace UI { +namespace ListView { + +// ComponentBase -------- + +class ComponentBase::Private +{ +public: + QPtrList<Component> components; +}; + +ComponentBase::ComponentBase() + : d( new Private ) +{ +} + +ComponentBase::~ComponentBase() +{ + d->components.setAutoDelete( true ); + delete d; +} + +uint ComponentBase::components() { return d->components.count(); } +Component *ComponentBase::component( uint n ) { return d->components.at( n ); } + +Component *ComponentBase::componentAt( const QPoint &pt ) +{ + for ( uint n = 0; n < components(); ++n ) + { + if ( component( n )->rect().contains( pt ) ) + { + if ( Component *comp = component( n )->componentAt( pt ) ) + return comp; + return component( n ); + } + } + return 0; +} + +void ComponentBase::componentAdded( Component *component ) +{ + d->components.append( component ); +} + +void ComponentBase::componentRemoved( Component *component ) +{ + //TODO: make sure the component is in d->components once and only once. + // if not, the situation is best referred to as 'very very broken indeed'. + d->components.remove( component ); +} + +void ComponentBase::clear() +{ + /* I'm switching setAutoDelete back and forth instead of turning it + * on permenantly, because original author of this class set it to + * auto delete in the dtor, that might have a reason that I can't + * imagine right now */ + bool tmp = d->components.autoDelete(); + d->components.setAutoDelete( true ); + d->components.clear(); + d->components.setAutoDelete( tmp ); +} + +void ComponentBase::componentResized( Component * ) +{ +} + +std::pair<QString,QRect> ComponentBase::toolTip( const QPoint &relativePos ) +{ + for ( uint n = 0; n < components(); ++n ) + if ( component( n )->rect().contains( relativePos ) ) + return component( n )->toolTip( relativePos ); + + return std::make_pair( QString::null, QRect() ); +} + +void ComponentBase::updateAnimationPosition( int p, int s ) +{ + for ( uint n = 0; n < components(); ++n ) + { + Component *comp = component( n ); + QRect start = comp->startRect(); + QRect target = comp->targetRect(); + QRect rc( start.left() + ((target.left() - start.left()) * p) / s, + start.top() + ((target.top() - start.top()) * p) / s, + start.width() + ((target.width() - start.width()) * p) / s, + start.height() + ((target.height() - start.height()) * p) / s ); + comp->setRect( rc ); + comp->updateAnimationPosition( p, s ); + } +} + +// Component -------- + +class Component::Private +{ +public: + Private( ComponentBase *parent ) + : parent( parent ), minWidth( 0 ), minHeight( 0 ) + , growHoriz( false ), growVert( false ) + , tipSource( 0 ) + { + } + ComponentBase *parent; + QRect rect; + QRect startRect, targetRect; + int minWidth, minHeight; + bool growHoriz, growVert; + bool show; /** @since 23-03-2005 */ + ToolTipSource *tipSource; +}; + +Component::Component( ComponentBase *parent ) + : d( new Private( parent ) ) +{ + d->parent->componentAdded( this ); + d->show = true; +} + +int Component::RTTI = Rtti_Component; + +Component::~Component() +{ + d->parent->componentRemoved( this ); + delete d; +} + + +void Component::hide() +{ + d->show = false; +} + +void Component::show() +{ + d->show = true; +} + +bool Component::isShown() +{ + return d->show; +} + +bool Component::isHidden() +{ + return !d->show; +} + +void Component::setToolTipSource( ToolTipSource *source ) +{ + d->tipSource = source; +} + +std::pair<QString,QRect> Component::toolTip( const QPoint &relativePos ) +{ + if ( !d->tipSource ) + return ComponentBase::toolTip( relativePos ); + + QRect rc = rect(); + QString result = (*d->tipSource)( this, relativePos, rc ); + return std::make_pair(result, rc); +} + +QRect Component::rect() { return d->rect; } +QRect Component::startRect() { return d->startRect; } +QRect Component::targetRect() { return d->targetRect; } + +int Component::minWidth() { return d->minWidth; } +int Component::minHeight() { return d->minHeight; } +int Component::widthForHeight( int ) { return minWidth(); } +int Component::heightForWidth( int ) { return minHeight(); } + +bool Component::setMinWidth( int width ) +{ + if ( d->minWidth == width ) return false; + d->minWidth = width; + + d->parent->componentResized( this ); + return true; +} +bool Component::setMinHeight( int height ) +{ + if ( d->minHeight == height ) return false; + d->minHeight = height; + + d->parent->componentResized( this ); + return true; +} + +void Component::layout( const QRect &newRect ) +{ + if ( rect().isNull() ) + d->startRect = QRect( newRect.topLeft(), newRect.topLeft() ); + else + d->startRect = rect(); + d->targetRect = newRect; + //kdDebug(14000) << k_funcinfo << "At " << rect << endl; +} + +void Component::setRect( const QRect &rect ) +{ + d->rect = rect; +} + +void Component::paint( QPainter *painter, const QColorGroup &cg ) +{ + /*painter->setPen( Qt::red ); + painter->drawRect( rect() );*/ + for ( uint n = 0; n < components(); ++n ) + { + if( component( n )->isShown() ) + component( n )->paint( painter, cg ); + } +} + +void Component::repaint() +{ + d->parent->repaint(); +} + +void Component::relayout() +{ + d->parent->relayout(); +} + +void Component::componentAdded( Component *component ) +{ + ComponentBase::componentAdded( component ); + //update( Relayout ); +} + +void Component::componentRemoved( Component *component ) +{ + ComponentBase::componentRemoved( component ); + //update( Relayout ); +} + +// BoxComponent -------- + +class BoxComponent::Private +{ +public: + Private( BoxComponent::Direction dir ) : direction( dir ) {} + BoxComponent::Direction direction; + + static const int padding = 2; +}; + +BoxComponent::BoxComponent( ComponentBase *parent, Direction dir ) + : Component( parent ), d( new Private( dir ) ) +{ +} + +int BoxComponent::RTTI = Rtti_BoxComponent; + +BoxComponent::~BoxComponent() +{ + delete d; +} + +int BoxComponent::widthForHeight( int height ) +{ + if ( d->direction != Horizontal ) + { + int width = 0; + for ( uint n = 0; n < components(); ++n ) + width = QMAX( width, component( n )->widthForHeight( height ) ); + return width; + } + else + { + int width = (components() - 1) * Private::padding; + for ( uint n = 0; n < components(); ++n ) + width += component( n )->widthForHeight( height ); + return width; + } +} + +int BoxComponent::heightForWidth( int width ) +{ + if ( d->direction == Horizontal ) + { + int height = 0; + for ( uint n = 0; n < components(); ++n ) + height = QMAX( height, component( n )->heightForWidth( width ) ); + return height; + } + else + { + int height = (components() - 1) * Private::padding; + for ( uint n = 0; n < components(); ++n ) + height += component( n )->heightForWidth( width ); + return height; + } +} + +void BoxComponent::calcMinSize() +{ + int sum = (components() - 1) * Private::padding, max = 0; + for ( uint n = 0; n < components(); ++n ) + { + Component *comp = component( n ); + if ( d->direction == Horizontal ) + { + max = QMAX( max, comp->minHeight() ); + sum += comp->minWidth(); + } + else + { + max = QMAX( max, comp->minWidth() ); + sum += comp->minHeight(); + } + } + + bool sizeChanged = false; + if ( d->direction == Horizontal ) + { + if ( setMinWidth( sum ) ) sizeChanged = true; + if ( setMinHeight( max ) ) sizeChanged = true; + } + else + { + if ( setMinWidth( max ) ) sizeChanged = true; + if ( setMinHeight( sum ) ) sizeChanged = true; + } + + if ( sizeChanged ) + repaint(); + else + relayout(); +} + +void BoxComponent::layout( const QRect &rect ) +{ + Component::layout( rect ); + + bool horiz = (d->direction == Horizontal); + int fixedSize = 0; + for ( uint n = 0; n < components(); ++n ) + { + Component *comp = component( n ); + if ( horiz ) + fixedSize += comp->minWidth(); + else + fixedSize += comp->minHeight(); + } + + // remaining space after all fixed items have been allocated + int padding = Private::padding; + + // ensure total is at least minXXX. the only time the rect + // will be smaller than that is when we don't fit, and in + // that cases we should pretend that we're wide/high enough. + int total; + if ( horiz ) + total = QMAX( rect.width(), minWidth() ); + else + total = QMAX( rect.height(), minHeight() ); + + int remaining = total - fixedSize - padding * (components() - 1); + + // finally, lay everything out + int pos = 0; + for ( uint n = 0; n < components(); ++n ) + { + Component *comp = component( n ); + + QRect rc; + if ( horiz ) + { + rc.setLeft( rect.left() + pos ); + rc.setTop( rect.top() ); + rc.setHeight( rect.height() ); + int minWidth = comp->minWidth(); + int desiredWidth = comp->widthForHeight( rect.height() ); + rc.setWidth( QMIN( remaining + minWidth, desiredWidth ) ); + pos += rc.width(); + remaining -= rc.width() - minWidth; + } + else + { + rc.setLeft( rect.left() ); + rc.setTop( rect.top() + pos ); + rc.setWidth( rect.width() ); + int minHeight = comp->minHeight(); + int desiredHeight = comp->heightForWidth( rect.width() ); + rc.setHeight( QMIN( remaining + minHeight, desiredHeight ) ); + pos += rc.height(); + remaining -= rc.height() - minHeight; + } + comp->layout( rc & rect ); + pos += padding; + } +} + +void BoxComponent::componentAdded( Component *component ) +{ + Component::componentAdded( component ); + calcMinSize(); +} + +void BoxComponent::componentRemoved( Component *component ) +{ + Component::componentRemoved( component ); + calcMinSize(); +} + +void BoxComponent::componentResized( Component *component ) +{ + Component::componentResized( component ); + calcMinSize(); +} + +// ImageComponent -------- + +class ImageComponent::Private +{ +public: + QPixmap image; +}; + +ImageComponent::ImageComponent( ComponentBase *parent ) + : Component( parent ), d( new Private ) +{ +} + +int ImageComponent::RTTI = Rtti_ImageComponent; + +ImageComponent::ImageComponent( ComponentBase *parent, int minW, int minH ) + : Component( parent ), d( new Private ) +{ + setMinWidth( minW ); + setMinHeight( minH ); + repaint(); +} + +ImageComponent::~ImageComponent() +{ + delete d; +} + +QPixmap ImageComponent::pixmap() +{ + return d->image; +} + +void ImageComponent::setPixmap( const QPixmap &img, bool adjustSize) +{ + d->image = img; + if ( adjustSize ) + { + setMinWidth( img.width() ); + setMinHeight( img.height() ); + } + repaint(); +} + +static QPoint operator+( const QPoint &pt, const QSize &sz ) +{ + return QPoint( pt.x() + sz.width(), pt.y() + sz.height() ); +} + +/*static QPoint operator+( const QSize &sz, const QPoint &pt ) +{ + return pt + sz; +}*/ + +void ImageComponent::paint( QPainter *painter, const QColorGroup & ) +{ + QRect ourRc = rect(); + QRect rc = d->image.rect(); + // center rc within our rect + rc.moveTopLeft( ourRc.topLeft() + (ourRc.size() - rc.size()) / 2 ); + // paint, shrunk to be within our rect + painter->drawPixmap( rc & ourRc, d->image ); +} + +void ImageComponent::scale( int w, int h, QImage::ScaleMode mode ) +{ + QImage im = d->image.convertToImage(); + setPixmap( QPixmap( im.smoothScale( w, h, mode ) ) ); +} +// TextComponent + +class TextComponent::Private +{ +public: + Private() : customColor( false ) {} + QString text; + bool customColor; + QColor color; + QFont font; +}; + +TextComponent::TextComponent( ComponentBase *parent, const QFont &font, const QString &text ) + : Component( parent ), d( new Private ) +{ + setFont( font ); + setText( text ); +} + +int TextComponent::RTTI = Rtti_TextComponent; + +TextComponent::~TextComponent() +{ + delete d; +} + +QString TextComponent::text() +{ + return d->text; +} + +void TextComponent::setText( const QString &text ) +{ + if ( text == d->text ) return; + d->text = text; + relayout(); + calcMinSize(); +} + +QFont TextComponent::font() +{ + return d->font; +} + +void TextComponent::setFont( const QFont &font ) +{ + if ( font == d->font ) return; + d->font = font; + calcMinSize(); +} + +void TextComponent::calcMinSize() +{ + setMinWidth( 0 ); + + if ( !d->text.isEmpty() ) + setMinHeight( QFontMetrics( font() ).height() ); + else + setMinHeight( 0 ); + + repaint(); +} + +int TextComponent::widthForHeight( int ) +{ + // add 2 to place an extra gap between the text and things to its right. + // allegedly if this is not done the protocol icons overlap the text. + // i however have never seen this problem (which would almost certainly + // be a bug somewhere else). + return QFontMetrics( font() ).width( d->text ) + 2; +} + +QColor TextComponent::color() +{ + return d->customColor ? d->color : QColor(); +} + +void TextComponent::setColor( const QColor &color ) +{ + d->color = color; + d->customColor = true; + repaint(); +} + +void TextComponent::setDefaultColor() +{ + d->customColor = false; + repaint(); +} + +void TextComponent::paint( QPainter *painter, const QColorGroup &cg ) +{ + if ( d->customColor ) + painter->setPen( d->color ); + else + painter->setPen( cg.text() ); + QString dispStr = KStringHandler::rPixelSqueeze( d->text, QFontMetrics( font() ), rect().width() ); + painter->setFont( font() ); + painter->drawText( rect(), Qt::SingleLine, dispStr ); +} + +// DisplayNameComponent + +class DisplayNameComponent::Private +{ +public: + QString text; + QFont font; +}; + +DisplayNameComponent::DisplayNameComponent( ComponentBase *parent ) + : BoxComponent( parent ), d( new Private ) +{ +} + +int DisplayNameComponent::RTTI = Rtti_DisplayNameComponent; + +DisplayNameComponent::~DisplayNameComponent() +{ + delete d; +} + +void DisplayNameComponent::layout( const QRect &rect ) +{ + Component::layout( rect ); + + // finally, lay everything out + QRect rc; + int totalWidth = rect.width(); + int usedWidth = 0; + bool exceeded = false; + for ( uint n = 0; n < components(); ++n ) + { + Component *comp = component( n ); + if ( !exceeded ) + { + if ( ( usedWidth + comp->widthForHeight( rect.height() ) ) > totalWidth ) + { + exceeded = true; + // TextComponents can squeeze themselves + if ( comp->rtti() == Rtti_TextComponent ) + { + comp->show(); + comp->layout( QRect( usedWidth+ rect.left(), rect.top(), + totalWidth - usedWidth, + comp->heightForWidth( totalWidth - usedWidth ) ) ); + } else { + comp->hide(); + } + } + else + { + comp->show(); + comp->layout( QRect( usedWidth+ rect.left(), rect.top(), + comp->widthForHeight( rect.height() ), + comp->heightForWidth( rect.width() ) ) ); + } + usedWidth+= comp->widthForHeight( rect.height() ); + } + else + { + // Shall we implement a hide()/show() in Component class ? + comp->hide(); + } + } +} + +void DisplayNameComponent::setText( const QString& text ) +{ + if ( d->text == text ) + return; + d->text = text; + + redraw(); +} + +void DisplayNameComponent::redraw() +{ + QColor color; + for ( uint n = 0; n < components(); ++n ) + if( component( n )->rtti() == Rtti_TextComponent ) + { + ((TextComponent*)component(n))->color(); + } + + QValueList<Kopete::Emoticons::Token> tokens; + QValueList<Kopete::Emoticons::Token>::const_iterator token; + + clear(); // clear childs + + tokens = Kopete::Emoticons::tokenizeEmoticons( d->text ); + ImageComponent *ic; + TextComponent *t; + + QFontMetrics fontMetrics( d->font ); + int fontHeight = fontMetrics.height(); + for ( token = tokens.begin(); token != tokens.end(); ++token ) + { + switch ( (*token).type ) + { + case Kopete::Emoticons::Text: + t = new TextComponent( this, d->font, (*token).text ); + break; + case Kopete::Emoticons::Image: + ic = new ImageComponent( this ); + ic->setPixmap( QPixmap( (*token).picPath ) ); + ic->scale( INT_MAX, fontHeight, QImage::ScaleMin ); + break; + default: + kdDebug( 14010 ) << k_funcinfo << "This should have not happened!" << endl; + } + } + + if(color.isValid()) + setColor( color ); +} + +void DisplayNameComponent::setFont( const QFont& font ) +{ + for ( uint n = 0; n < components(); ++n ) + if( component( n )->rtti() == Rtti_TextComponent ) + ((TextComponent*)component(n))->setFont( font ); + d->font = font; +} + +void DisplayNameComponent::setColor( const QColor& color ) +{ + for ( uint n = 0; n < components(); ++n ) + if( component( n )->rtti() == Rtti_TextComponent ) + ((TextComponent*)component(n))->setColor( color ); +} + +void DisplayNameComponent::setDefaultColor() +{ + for ( uint n = 0; n < components(); ++n ) + if( component( n )->rtti() == Rtti_TextComponent ) + ((TextComponent*)component(n))->setDefaultColor(); +} + +QString DisplayNameComponent::text() +{ + return d->text; +} + +// HSpacerComponent -------- + +HSpacerComponent::HSpacerComponent( ComponentBase *parent ) + : Component( parent ) +{ + setMinWidth( 0 ); + setMinHeight( 0 ); +} + +int HSpacerComponent::RTTI = Rtti_HSpacerComponent; + +int HSpacerComponent::widthForHeight( int ) +{ + return INT_MAX; +} + +// VSpacerComponent -------- + +VSpacerComponent::VSpacerComponent( ComponentBase *parent ) + : Component( parent ) +{ + setMinWidth( 0 ); + setMinHeight( 0 ); +} + +int VSpacerComponent::RTTI = Rtti_VSpacerComponent; + +int VSpacerComponent::heightForWidth( int ) +{ + return INT_MAX; +} + +////////////////// ContactComponent ///////////////////////// + +class ContactComponent::Private +{ +public: + Kopete::Contact *contact; + int iconSize; +}; + +ContactComponent::ContactComponent( ComponentBase *parent, Kopete::Contact *contact, int iconSize) : ImageComponent( parent ) , d( new Private ) +{ + d->contact = contact; + d->iconSize = iconSize; + updatePixmap(); +} + +ContactComponent::~ContactComponent() +{ + delete d; +} + +void ContactComponent::updatePixmap() +{ + setPixmap( contact()->onlineStatus().iconFor( contact(), d->iconSize ) ); +} +Kopete::Contact *ContactComponent::contact() +{ + return d->contact; +} + +// we don't need to use a tooltip source here - this way is simpler +std::pair<QString,QRect> ContactComponent::toolTip( const QPoint &/*relativePos*/ ) +{ + return std::make_pair(d->contact->toolTip(),rect()); +} + +////////////////// SpacerComponent ///////////////////////// + +SpacerComponent::SpacerComponent( ComponentBase *parent, int w, int h ) : Component( parent ) +{ + setMinWidth(w); + setMinHeight(h); +} + +// Item -------- + +/** + * A periodic timer intended to be shared amongst multiple objects. Will run only + * if an object is attached to it. + */ +class SharedTimer : private QTimer +{ + int period; + int users; +public: + SharedTimer( int period ) : period(period), users(0) {} + void attach( QObject *target, const char *slot ) + { + connect( this, SIGNAL(timeout()), target, slot ); + if( users++ == 0 ) + start( period ); + //kdDebug(14000) << "SharedTimer::attach: users is now " << users << "\n"; + } + void detach( QObject *target, const char *slot ) + { + disconnect( this, SIGNAL(timeout()), target, slot ); + if( --users == 0 ) + stop(); + //kdDebug(14000) << "SharedTimer::detach: users is now " << users << "\n"; + } +}; + +class SharedTimerRef +{ + SharedTimer &timer; + QObject * const object; + const char * const slot; + bool attached; +public: + SharedTimerRef( SharedTimer &timer, QObject *obj, const char *slot ) + : timer(timer), object(obj), slot(slot), attached(false) + { + } + void start() + { + if( attached ) return; + timer.attach( object, slot ); + attached = true; + } + void stop() + { + if( !attached ) return; + timer.detach( object, slot ); + attached = false; + } + bool isActive() + { + return attached; + } +}; + +class Item::Private +{ +public: + Private( Item *item ) + : layoutAnimateTimer( theLayoutAnimateTimer(), item, SLOT( slotLayoutAnimateItems() ) ) + , animateLayout( true ), opacity( 1.0 ) + , visibilityTimer( theVisibilityTimer(), item, SLOT( slotUpdateVisibility() ) ) + , visibilityLevel( 0 ), visibilityTarget( false ), searchMatch( true ) + { + } + + QTimer layoutTimer; + + //QTimer layoutAnimateTimer; + SharedTimerRef layoutAnimateTimer; + SharedTimer &theLayoutAnimateTimer() + { + static SharedTimer timer( 10 ); + return timer; + } + + bool animateLayout; + int layoutAnimateSteps; + static const int layoutAnimateStepsTotal = 10; + + float opacity; + + //QTimer visibilityTimer; + SharedTimerRef visibilityTimer; + SharedTimer &theVisibilityTimer() + { + static SharedTimer timer( 40 ); + return timer; + } + + int visibilityLevel; + bool visibilityTarget; + static const int visibilityFoldSteps = 7; +#ifdef HAVE_XRENDER + static const int visibilityFadeSteps = 7; +#else + static const int visibilityFadeSteps = 0; +#endif + static const int visibilityStepsTotal = visibilityFoldSteps + visibilityFadeSteps; + + bool searchMatch; + + static bool animateChanges; + static bool fadeVisibility; + static bool foldVisibility; +}; + +bool Item::Private::animateChanges = true; +bool Item::Private::fadeVisibility = true; +bool Item::Private::foldVisibility = true; + +Item::Item( QListViewItem *parent, QObject *owner, const char *name ) + : QObject( owner, name ), KListViewItem( parent ), d( new Private(this) ) +{ + initLVI(); +} + +Item::Item( QListView *parent, QObject *owner, const char *name ) + : QObject( owner, name ), KListViewItem( parent ), d( new Private(this) ) +{ + initLVI(); +} + +Item::~Item() +{ + delete d; +} + +void Item::setEffects( bool animation, bool fading, bool folding ) +{ + Private::animateChanges = animation; + Private::fadeVisibility = fading; + Private::foldVisibility = folding; +} + +void Item::initLVI() +{ + connect( listView()->header(), SIGNAL( sizeChange( int, int, int ) ), SLOT( slotColumnResized() ) ); + connect( &d->layoutTimer, SIGNAL( timeout() ), SLOT( slotLayoutItems() ) ); + //connect( &d->layoutAnimateTimer, SIGNAL( timeout() ), SLOT( slotLayoutAnimateItems() ) ); + //connect( &d->visibilityTimer, SIGNAL( timeout() ), SLOT( slotUpdateVisibility() ) ); + setVisible( false ); + setTargetVisibility( true ); +} + +void Item::slotColumnResized() +{ + scheduleLayout(); + // if we've been resized, don't animate the layout + d->animateLayout = false; +} + +void Item::scheduleLayout() +{ + // perform a delayed layout in order to speed it all up + if ( ! d->layoutTimer.isActive() ) + d->layoutTimer.start( 30, true ); +} + +void Item::slotLayoutItems() +{ + d->layoutTimer.stop(); + + for ( uint n = 0; n < components(); ++n ) + { + int width = listView()->columnWidth(n); + if ( n == 0 ) + { + int d = depth() + (listView()->rootIsDecorated() ? 1 : 0); + width -= d * listView()->treeStepSize(); + } + + int height = component( n )->heightForWidth( width ); + component( n )->layout( QRect( 0, 0, width, height ) ); + //kdDebug(14000) << k_funcinfo << "Component " << n << " is " << width << " x " << height << endl; + } + + if ( Private::animateChanges && d->animateLayout && !d->visibilityTimer.isActive() ) + { + d->layoutAnimateTimer.start(); + //if ( !d->layoutAnimateTimer.isActive() ) + // d->layoutAnimateTimer.start( 10 ); + d->layoutAnimateSteps = 0; + } + else + { + d->layoutAnimateSteps = Private::layoutAnimateStepsTotal; + d->animateLayout = true; + } + slotLayoutAnimateItems(); +} + +void Item::slotLayoutAnimateItems() +{ + if ( ++d->layoutAnimateSteps >= Private::layoutAnimateStepsTotal ) + d->layoutAnimateTimer.stop(); + + const int s = Private::layoutAnimateStepsTotal; + const int p = QMIN( d->layoutAnimateSteps, s ); + + updateAnimationPosition( p, s ); + setHeight(0); + repaint(); +} + +float Item::opacity() +{ + return d->opacity; +} + +void Item::setOpacity( float opacity ) +{ + if ( d->opacity == opacity ) return; + d->opacity = opacity; + repaint(); +} + +void Item::setSearchMatch( bool match ) +{ + d->searchMatch = match; + + if ( !match ) + setVisible( false ); + else + { + kdDebug(14000) << k_funcinfo << " match: " << match << ", vis timer active: " << d->visibilityTimer.isActive() + << ", target visibility: " << targetVisibility() << endl; + if ( d->visibilityTimer.isActive() ) + setVisible( true ); + else + setVisible( targetVisibility() ); + } +} + +bool Item::targetVisibility() +{ + return d->visibilityTarget; +} + +void Item::setTargetVisibility( bool vis ) +{ + if ( d->visibilityTarget == vis ) + { + // in case we're getting called because our parent was shown and + // we need to be rehidden + if ( !d->visibilityTimer.isActive() ) + setVisible( vis && d->searchMatch ); + return; + } + d->visibilityTarget = vis; + d->visibilityTimer.start(); + //d->visibilityTimer.start( 40 ); + if ( targetVisibility() ) + setVisible( d->searchMatch ); + slotUpdateVisibility(); +} + +void Item::slotUpdateVisibility() +{ + if ( targetVisibility() ) + ++d->visibilityLevel; + else + --d->visibilityLevel; + + if ( !Private::foldVisibility && !Private::fadeVisibility ) + d->visibilityLevel = targetVisibility() ? Private::visibilityStepsTotal : 0; + else if ( !Private::fadeVisibility && d->visibilityLevel >= Private::visibilityFoldSteps ) + d->visibilityLevel = targetVisibility() ? Private::visibilityStepsTotal : Private::visibilityFoldSteps - 1; + else if ( !Private::foldVisibility && d->visibilityLevel <= Private::visibilityFoldSteps ) + d->visibilityLevel = targetVisibility() ? Private::visibilityFoldSteps + 1 : 0; + + if ( d->visibilityLevel >= Private::visibilityStepsTotal ) + { + d->visibilityLevel = Private::visibilityStepsTotal; + d->visibilityTimer.stop(); + } + else if ( d->visibilityLevel <= 0 ) + { + d->visibilityLevel = 0; + d->visibilityTimer.stop(); + setVisible( false ); + } + setHeight( 0 ); + repaint(); +} + +void Item::repaint() +{ + // if we're about to relayout, don't bother painting yet. + if ( d->layoutTimer.isActive() ) + return; + listView()->repaintItem( this ); +} + +void Item::relayout() +{ + scheduleLayout(); +} + +void Item::setup() +{ + KListViewItem::setup(); + slotLayoutItems(); +} + +void Item::setHeight( int ) +{ + int minHeight = 0; + for ( uint n = 0; n < components(); ++n ) + minHeight = QMAX( minHeight, component( n )->rect().height() ); + //kdDebug(14000) << k_funcinfo << "Height is " << minHeight << endl; + if ( Private::foldVisibility && d->visibilityTimer.isActive() ) + { + int vis = QMIN( d->visibilityLevel, Private::visibilityFoldSteps ); + minHeight = (minHeight * vis) / Private::visibilityFoldSteps; + } + KListViewItem::setHeight( minHeight ); +} + +int Item::width( const QFontMetrics &, const QListView *lv, int c ) const +{ + // Qt computes the itemRect from this. we want the whole item to be + // clickable, so we return the widest we could possibly be. + return lv->header()->sectionSize( c ); +} + +void Item::paintCell( QPainter *p, const QColorGroup &cg, int column, int width, int align ) +{ + QPixmap back( width, height() ); + QPainter paint( &back ); + //KListViewItem::paintCell( &paint, cg, column, width, align ); + // PASTED FROM KLISTVIEWITEM: + // set the alternate cell background colour if necessary + QColorGroup _cg = cg; + if (isAlternate()) + if (listView()->viewport()->backgroundMode()==Qt::FixedColor) + _cg.setColor(QColorGroup::Background, static_cast< KListView* >(listView())->alternateBackground()); + else + _cg.setColor(QColorGroup::Base, static_cast< KListView* >(listView())->alternateBackground()); + // PASTED FROM QLISTVIEWITEM + { + QPainter *p = &paint; + + QListView *lv = listView(); + if ( !lv ) + return; + QFontMetrics fm( p->fontMetrics() ); + + // any text we render is done by the Components, not by this class, so make sure we've nothing to write + QString t; + + // removed text truncating code from Qt - we do that differently, further on + + int marg = lv->itemMargin(); + int r = marg; + // const QPixmap * icon = pixmap( column ); + + const BackgroundMode bgmode = lv->viewport()->backgroundMode(); + const QColorGroup::ColorRole crole = QPalette::backgroundRoleFromMode( bgmode ); + + if ( _cg.brush( crole ) != lv->colorGroup().brush( crole ) ) + p->fillRect( 0, 0, width, height(), _cg.brush( crole ) ); + else + { + // all copied from QListView::paintEmptyArea + + //lv->paintEmptyArea( p, QRect( 0, 0, width, height() ) ); + QStyleOption opt( lv->sortColumn(), 0 ); // ### hack; in 3.1, add a property in QListView and QHeader + QStyle::SFlags how = QStyle::Style_Default; + if ( lv->isEnabled() ) + how |= QStyle::Style_Enabled; + + lv->style().drawComplexControl( QStyle::CC_ListView, + p, lv, QRect( 0, 0, width, height() ), lv->colorGroup(), + how, QStyle::SC_ListView, QStyle::SC_None, + opt ); + } + + + + if ( isSelected() && + (column == 0 || lv->allColumnsShowFocus()) ) { + p->fillRect( r - marg, 0, width - r + marg, height(), + _cg.brush( QColorGroup::Highlight ) ); + // removed text pen setting code from Qt + } + + // removed icon drawing code from Qt + + // draw the tree gubbins + if ( multiLinesEnabled() && column == 0 && isOpen() && childCount() ) { + int textheight = fm.size( align, t ).height() + 2 * lv->itemMargin(); + textheight = QMAX( textheight, QApplication::globalStrut().height() ); + if ( textheight % 2 > 0 ) + textheight++; + if ( textheight < height() ) { + int w = lv->treeStepSize() / 2; + lv->style().drawComplexControl( QStyle::CC_ListView, p, lv, + QRect( 0, textheight, w + 1, height() - textheight + 1 ), _cg, + lv->isEnabled() ? QStyle::Style_Enabled : QStyle::Style_Default, + QStyle::SC_ListViewExpand, + (uint)QStyle::SC_All, QStyleOption( this ) ); + } + } + } + // END OF PASTE + + + //do you see a better way to tell the TextComponent we are selected ? - Olivier 2004-09-02 + if ( isSelected() ) + _cg.setColor(QColorGroup::Text , _cg.highlightedText() ); + + if ( Component *comp = component( column ) ) + comp->paint( &paint, _cg ); + paint.end(); + +#ifdef HAVE_XRENDER + QColor rgb = cg.base();//backgroundColor(); + float opac = 1.0; + if ( d->visibilityTimer.isActive() && Private::fadeVisibility ) + { + int vis = QMAX( d->visibilityLevel - Private::visibilityFoldSteps, 0 ); + opac = float(vis) / Private::visibilityFadeSteps; + } + opac *= opacity(); + const int alpha = 257 - int(opac * 257); + if ( alpha != 0 ) + { + XRenderColor clr = { alpha * rgb.red(), alpha * rgb.green(), alpha * rgb.blue(), alpha * 0xff }; + XRenderFillRectangle( back.x11Display(), PictOpOver, back.x11RenderHandle(), + &clr, 0, 0, width, height() ); + } +#endif + + p->drawPixmap( 0, 0, back ); +} + +void Item::componentAdded( Component *component ) +{ + ComponentBase::componentAdded( component ); + scheduleLayout(); +} + +void Item::componentRemoved( Component *component ) +{ + ComponentBase::componentRemoved( component ); + scheduleLayout(); +} + +void Item::componentResized( Component *component ) +{ + ComponentBase::componentResized( component ); + scheduleLayout(); +} + +} // END namespace ListView +} // END namespace UI +} // END namespace Kopete + +#include "kopetelistviewitem.moc" + +// vim: set noet ts=4 sts=4 sw=4: |