summaryrefslogtreecommitdiffstats
path: root/kopete/libkopete/ui/kopetelistviewitem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/libkopete/ui/kopetelistviewitem.cpp')
-rw-r--r--kopete/libkopete/ui/kopetelistviewitem.cpp1314
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: