/* kopetelistviewitem.cpp - Kopete's modular TQListViewItems Copyright (c) 2005 by Engin AYDOGAN Copyright (c) 2004 by Richard Smith Kopete (c) 2002-2004 by the Kopete developers ************************************************************************* * * * 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 "kopeteprefs.h" #include "kopetecontact.h" #include "kopetelistviewitem.h" #include "kopeteemoticons.h" #include "kopeteonlinestatus.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_XRENDER # include # include #endif #include namespace Kopete { namespace UI { namespace ListView { // ComponentBase -------- class ComponentBase::Private { public: TQPtrList 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 TQPoint &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 ComponentBase::toolTip( const TQPoint &relativePos ) { for ( uint n = 0; n < components(); ++n ) if ( component( n )->rect().contains( relativePos ) ) return component( n )->toolTip( relativePos ); return std::make_pair( TQString(), TQRect() ); } void ComponentBase::updateAnimationPosition( int p, int s ) { for ( uint n = 0; n < components(); ++n ) { Component *comp = component( n ); TQRect start = comp->startRect(); TQRect target = comp->targetRect(); TQRect 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; TQRect rect; TQRect 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 Component::toolTip( const TQPoint &relativePos ) { if ( !d->tipSource ) return ComponentBase::toolTip( relativePos ); TQRect rc = rect(); TQString result = (*d->tipSource)( this, relativePos, rc ); return std::make_pair(result, rc); } TQRect Component::rect() { return d->rect; } TQRect Component::startRect() { return d->startRect; } TQRect 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 TQRect &newRect ) { if ( rect().isNull() ) d->startRect = TQRect( newRect.topLeft(), newRect.topLeft() ); else d->startRect = rect(); d->targetRect = newRect; //kdDebug(14000) << k_funcinfo << "At " << rect << endl; } void Component::setRect( const TQRect &rect ) { d->rect = rect; } void Component::paint( TQPainter *painter, const TQColorGroup &cg ) { /*painter->setPen( TQt::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 = 0; }; 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 !=Qt::Horizontal ) { int width = 0; for ( uint n = 0; n < components(); ++n ) width = TQMAX( 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 ==Qt::Horizontal ) { int height = 0; for ( uint n = 0; n < components(); ++n ) height = TQMAX( 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 ==Qt::Horizontal ) { max = TQMAX( max, comp->minHeight() ); sum += comp->minWidth(); } else { max = TQMAX( max, comp->minWidth() ); sum += comp->minHeight(); } } bool sizeChanged = false; if ( d->direction ==Qt::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 TQRect &rect ) { Component::layout( rect ); bool horiz = (d->direction ==Qt::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 = TQMAX( rect.width(), minWidth() ); else total = TQMAX( 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 ); TQRect 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( TQMIN( 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( TQMIN( 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(); } /*= ContactBoxComponent =====================================================*/ class ContactBoxComponent::Private { public: TQRect sub; TQPixmap back_pixmap; TQPixmap corner_tl_pixmap; TQPixmap corner_bl_pixmap; TQPixmap corner_tr_pixmap; TQPixmap corner_br_pixmap; TQPixmap top_pixmap; TQPixmap left_pixmap; TQPixmap right_pixmap; TQPixmap bottom_pixmap; }; ContactBoxComponent::ContactBoxComponent(ComponentBase *parent, Direction dir) : BoxComponent(parent, dir), d(new Private()) {} ContactBoxComponent::~ContactBoxComponent() { delete d; } void ContactBoxComponent::reloadTheme() { TQString path = KopetePrefs::prefs()->themeURL(); TQString str; str = path + "ContactBackground.png"; d->back_pixmap.load(str); str = path + "ContactTopLeft.png"; d->corner_tl_pixmap.load(str); str = path + "ContactBottomLeft.png"; d->corner_bl_pixmap.load(str); str = path + "ContactTopRight.png"; d->corner_tr_pixmap.load(str); str = path + "ContactBottomRight.png"; d->corner_br_pixmap.load(str); str = path + "ContactTop.png"; d->top_pixmap.load(str); str = path + "ContactLeft.png"; d->left_pixmap.load(str); str = path + "ContactRight.png"; d->right_pixmap.load(str); str = path + "ContactBottom.png"; d->bottom_pixmap.load(str); } void ContactBoxComponent::layout(const TQRect &rect) { d->sub.setLeft(rect.left() + d->left_pixmap.width()); d->sub.setTop(rect.top() + d->top_pixmap.height()); d->sub.setRight(rect.right() - d->right_pixmap.width()); d->sub.setBottom(rect.bottom() - d->bottom_pixmap.height()); BoxComponent::layout(d->sub); Component::layout(rect); } int ContactBoxComponent::widthForHeight(int height) { return BoxComponent::widthForHeight(height) + d->left_pixmap.width() + d->right_pixmap.width(); } int ContactBoxComponent::heightForWidth(int width) { return BoxComponent::heightForWidth(width) + d->top_pixmap.height() + d->bottom_pixmap.height(); } void ContactBoxComponent::paint(TQPainter *painter, const TQColorGroup &cg) { painter->drawPixmap(0, 0, d->corner_tl_pixmap); painter->drawPixmap(0, d->sub.bottom()+1, d->corner_bl_pixmap); painter->drawPixmap(d->sub.right()+1, 0, d->corner_tr_pixmap); painter->drawPixmap(d->sub.right()+1, d->sub.bottom()+1, d->corner_br_pixmap); painter->drawTiledPixmap(0, d->sub.top(), d->left_pixmap.width(), d->sub.height(), d->left_pixmap); painter->drawTiledPixmap(d->sub.left(), 0, d->sub.width(), d->top_pixmap.height(), d->top_pixmap); painter->drawTiledPixmap(d->sub.left(), d->sub.bottom()+1, d->sub.width(), d->bottom_pixmap.height(), d->bottom_pixmap); painter->drawTiledPixmap(d->sub.right()+1, d->sub.top(), d->right_pixmap.width(), d->sub.height(), d->right_pixmap); painter->drawTiledPixmap(d->sub, d->back_pixmap); return BoxComponent::paint(painter, cg); } /*= GroupBoxComponent =======================================================*/ class GroupBoxComponent::Private { public: TQRect sub; TQPixmap back_pixmap; TQPixmap open_pixmap; TQPixmap closed_pixmap; TQPixmap corner_tl_pixmap; TQPixmap corner_bl_pixmap; TQPixmap corner_tr_pixmap; TQPixmap corner_br_pixmap; TQPixmap top_pixmap; TQPixmap left_pixmap; TQPixmap right_pixmap; TQPixmap bottom_pixmap; }; GroupBoxComponent::GroupBoxComponent(ComponentBase *parent, Direction dir) : BoxComponent(parent, dir), d(new Private()) {} GroupBoxComponent::~GroupBoxComponent() { delete d; } void GroupBoxComponent::reloadTheme() { TQString path = KopetePrefs::prefs()->themeURL(); TQString str; str = path + "GroupBackground.png"; d->back_pixmap.load(str); str = path + "GroupOpen.png"; d->open_pixmap.load(str); str = path + "GroupClosed.png"; d->closed_pixmap.load(str); str = path + "GroupTopLeft.png"; d->corner_tl_pixmap.load(str); str = path + "GroupBottomLeft.png"; d->corner_bl_pixmap.load(str); str = path + "GroupTopRight.png"; d->corner_tr_pixmap.load(str); str = path + "GroupBottomRight.png"; d->corner_br_pixmap.load(str); str = path + "GroupTop.png"; d->top_pixmap.load(str); str = path + "GroupLeft.png"; d->left_pixmap.load(str); str = path + "GroupRight.png"; d->right_pixmap.load(str); str = path + "GroupBottom.png"; d->bottom_pixmap.load(str); } void GroupBoxComponent::layout(const TQRect &rect) { d->sub.setLeft(rect.left() + d->left_pixmap.width()); d->sub.setTop(rect.top() + d->top_pixmap.height()); d->sub.setRight(rect.right() - d->right_pixmap.width()); d->sub.setBottom(rect.bottom() - d->bottom_pixmap.height()); BoxComponent::layout(d->sub); Component::layout(rect); } int GroupBoxComponent::widthForHeight(int height) { return BoxComponent::widthForHeight(height) + d->left_pixmap.width() + d->right_pixmap.width(); } int GroupBoxComponent::heightForWidth( int width ) { return BoxComponent::heightForWidth(width) + d->top_pixmap.height() + d->bottom_pixmap.height(); } void GroupBoxComponent::paint( TQPainter *painter, const TQColorGroup &cg ) { painter->drawPixmap(0, 0, d->corner_tl_pixmap); painter->drawPixmap(0, d->sub.bottom()+1, d->corner_bl_pixmap); painter->drawPixmap(d->sub.right()+1, 0, d->corner_tr_pixmap); painter->drawPixmap(d->sub.right()+1, d->sub.bottom()+1, d->corner_br_pixmap); painter->drawTiledPixmap(0, d->sub.top(), d->left_pixmap.width(), d->sub.height(), d->left_pixmap); painter->drawTiledPixmap(d->sub.left(), 0, d->sub.width(), d->top_pixmap.height(), d->top_pixmap); painter->drawTiledPixmap(d->sub.left(), d->sub.bottom()+1, d->sub.width(), d->bottom_pixmap.height(), d->bottom_pixmap); painter->drawTiledPixmap(d->sub.right()+1, d->sub.top(), d->right_pixmap.width(), d->sub.height(), d->right_pixmap); painter->drawTiledPixmap(d->sub, d->back_pixmap); return BoxComponent::paint(painter, cg); } // ImageComponent -------- class ImageComponent::Private { public: TQPixmap 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; } TQPixmap ImageComponent::pixmap() { return d->image; } void ImageComponent::setPixmap( const TQPixmap &img, bool) { d->image = img; setMinWidth(d->image.width()); setMinHeight(d->image.height()); repaint(); } void ImageComponent::paint( TQPainter *painter, const TQColorGroup & ) { TQRect ourRc = rect(); TQRect rc = d->image.rect(); // center rc within our rect rc.moveTopLeft(ourRc.topLeft()); // paint, shrunk to be within our rect painter->drawPixmap( rc & ourRc, d->image ); } void ImageComponent::scale( int w, int h, TQ_ScaleMode mode ) { TQImage im = d->image.convertToImage(); setPixmap( TQPixmap( im.smoothScale( w, h, mode ) ) ); } /*= FaceComponent ===========================================================*/ void FaceComponent::setPixmap(const TQPixmap &img, bool) { d->image = img; setMinWidth(d->image.width()); setMinHeight(d->image.height()); if (img.width() >= 30) { d->image = TQPixmap(img.convertToImage().smoothScale(30, 30)); setMinWidth(d->image.width() + 4); setMinHeight(d->image.height() + 4); } repaint(); } static TQPoint operator+(const TQPoint &pt, const TQSize &sz) { return TQPoint(pt.x() + sz.width(), pt.y() + sz.height()); } void FaceComponent::paint(TQPainter *painter, const TQColorGroup &) { TQRect outRc = rect(); TQRect pixRc = d->image.rect(); pixRc.moveTopLeft(outRc.topLeft() + (outRc.size() - pixRc.size()) / 2); if (d->image.width() == 30) { TQPixmap pixBorder; TQString path = KopetePrefs::prefs()->themeURL(); TQString str = path + "ContactFace.png"; pixBorder.load(str); TQRect pixRc2 = pixBorder.rect(); pixRc2.moveTopLeft(outRc.topLeft() + (outRc.size() - pixRc2.size()) / 2); painter->drawPixmap(pixRc2, pixBorder); } painter->drawPixmap(pixRc, d->image); } // TextComponent class TextComponent::Private { public: Private() : customColor( false ) {} TQString text; bool customColor; TQColor color; TQFont font; }; TextComponent::TextComponent( ComponentBase *parent, const TQFont &font, const TQString &text ) : Component( parent ), d( new Private ) { setFont( font ); setText( text ); } int TextComponent::RTTI = Rtti_TextComponent; TextComponent::~TextComponent() { delete d; } TQString TextComponent::text() { return d->text; } void TextComponent::setText( const TQString &text ) { if ( text == d->text ) return; d->text = text; relayout(); calcMinSize(); } TQFont TextComponent::font() { return d->font; } void TextComponent::setFont( const TQFont &font ) { if ( font == d->font ) return; d->font = font; calcMinSize(); } void TextComponent::calcMinSize() { setMinWidth( 0 ); if ( !d->text.isEmpty() ) setMinHeight( TQFontMetrics( 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 TQFontMetrics( font() ).width( d->text ) + 2; } TQColor TextComponent::color() { return d->customColor ? d->color : TQColor(); } void TextComponent::setColor( const TQColor &color ) { d->color = color; d->customColor = true; repaint(); } void TextComponent::setDefaultColor() { d->customColor = false; repaint(); } void TextComponent::paint( TQPainter *painter, const TQColorGroup &cg ) { if ( d->customColor ) painter->setPen( d->color ); else painter->setPen( cg.text() ); TQString dispStr = KStringHandler::rPixelSqueeze( d->text, TQFontMetrics( font() ), rect().width() ); painter->setFont( font() ); painter->drawText( rect(), TQt::SingleLine, dispStr ); } // DisplayNameComponent class DisplayNameComponent::Private { public: TQString text; TQFont font; }; DisplayNameComponent::DisplayNameComponent( ComponentBase *parent ) : BoxComponent( parent ), d( new Private ) { } int DisplayNameComponent::RTTI = Rtti_DisplayNameComponent; DisplayNameComponent::~DisplayNameComponent() { delete d; } void DisplayNameComponent::layout( const TQRect &rect ) { Component::layout( rect ); // finally, lay everything out TQRect 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( TQRect( usedWidth+ rect.left(), rect.top(), totalWidth - usedWidth, comp->heightForWidth( totalWidth - usedWidth ) ) ); } else { comp->hide(); } } else { comp->show(); comp->layout( TQRect( 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 TQString& text ) { if ( d->text == text ) return; d->text = text; redraw(); } void DisplayNameComponent::redraw() { TQColor color; for ( uint n = 0; n < components(); ++n ) if( component( n )->rtti() == Rtti_TextComponent ) { ((TextComponent*)component(n))->color(); } TQValueList tokens; TQValueList::const_iterator token; clear(); // clear childs tokens = Kopete::Emoticons::tokenizeEmoticons( d->text ); ImageComponent *ic; TextComponent *t; TQFontMetrics 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( TQPixmap( (*token).picPath ) ); ic->scale( INT_MAX, fontHeight, TQ_ScaleMin ); break; default: kdDebug( 14010 ) << k_funcinfo << "This should have not happened!" << endl; } } if(color.isValid()) setColor( color ); } void DisplayNameComponent::setFont( const TQFont& 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 TQColor& 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(); } TQString 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) : ImageComponent( parent ) , d( new Private ) { d->contact = contact; d->iconSize = 12; // size of the image is fixed to 12 pixels 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 ContactComponent::toolTip( const TQPoint &/*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 TQTimer { int period; int users; public: SharedTimer( int period ) : period(period), users(0) {} void attach( TQObject *target, const char *slot ) { connect( this, TQT_SIGNAL(timeout()), target, slot ); if( users++ == 0 ) start( period ); //kdDebug(14000) << "SharedTimer::attach: users is now " << users << "\n"; } void detach( TQObject *target, const char *slot ) { disconnect( this, TQT_SIGNAL(timeout()), target, slot ); if( --users == 0 ) stop(); //kdDebug(14000) << "SharedTimer::detach: users is now " << users << "\n"; } }; class SharedTimerRef { SharedTimer &timer; TQObject * const object; const char * const slot; bool attached; public: SharedTimerRef( SharedTimer &timer, TQObject *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, TQT_SLOT( slotLayoutAnimateItems() ) ) , animateLayout( true ), opacity( 1.0 ) , visibilityTimer( theVisibilityTimer(), item, TQT_SLOT( slotUpdateVisibility() ) ) , visibilityLevel( 0 ), visibilityTarget( false ), searchMatch( true ) { } TQTimer layoutTimer; //TQTimer layoutAnimateTimer; SharedTimerRef layoutAnimateTimer; SharedTimer &theLayoutAnimateTimer() { static SharedTimer timer( 10 ); return timer; } bool animateLayout; int layoutAnimateSteps; static const int layoutAnimateStepsTotal = 10; float opacity; //TQTimer 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( TQListViewItem *parent, TQObject *owner, const char *name ) : TQObject( owner, name ), KListViewItem( parent ), d( new Private(this) ) { initLVI(); } Item::Item( TQListView *parent, TQObject *owner, const char *name ) : TQObject( 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(), TQT_SIGNAL( sizeChange( int, int, int ) ), TQT_SLOT( slotColumnResized() ) ); connect( &d->layoutTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotLayoutItems() ) ); //connect( &d->layoutAnimateTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotLayoutAnimateItems() ) ); //connect( &d->visibilityTimer, TQT_SIGNAL( timeout() ), TQT_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( TQRect( 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 = TQMIN( 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 = TQMAX( minHeight, component( n )->rect().height() ); //kdDebug(14000) << k_funcinfo << "Height is " << minHeight << endl; if ( Private::foldVisibility && d->visibilityTimer.isActive() ) { int vis = TQMIN( d->visibilityLevel, Private::visibilityFoldSteps ); minHeight = (minHeight * vis) / Private::visibilityFoldSteps; } KListViewItem::setHeight( minHeight ); } int Item::width( const TQFontMetrics &, const TQListView *lv, int c ) const { // TQt 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( TQPainter *p, const TQColorGroup &cg, int column, int width, int align ) { TQPixmap back( width, height() ); TQPainter paint( &back ); //KListViewItem::paintCell( &paint, cg, column, width, align ); // PASTED FROM KLISTVIEWITEM: // set the alternate cell background colour if necessary TQColorGroup _cg = cg; if (isAlternate()) if (listView()->viewport()->backgroundMode()==TQt::FixedColor) _cg.setColor(TQColorGroup::Background, static_cast< KListView* >(listView())->alternateBackground()); else _cg.setColor(TQColorGroup::Base, static_cast< KListView* >(listView())->alternateBackground()); // PASTED FROM TQLISTVIEWITEM { TQPainter *p = &paint; TQListView *lv = listView(); if ( !lv ) return; TQFontMetrics fm( p->fontMetrics() ); // any text we render is done by the Components, not by this class, so make sure we've nothing to write TQString t; // removed text truncating code from TQt - we do that differently, further on int marg = lv->itemMargin(); int r = marg; // const TQPixmap * icon = pixmap( column ); const BackgroundMode bgmode = lv->viewport()->backgroundMode(); const TQColorGroup::ColorRole crole = TQPalette::backgroundRoleFromMode( bgmode ); if ( _cg.brush( crole ) != lv->colorGroup().brush( crole ) ) p->fillRect( 0, 0, width, height(), _cg.brush( crole ) ); else { // all copied from TQListView::paintEmptyArea //lv->paintEmptyArea( p, TQRect( 0, 0, width, height() ) ); TQStyleOption opt( lv->sortColumn(), 0 ); // ### hack; in 3.1, add a property in TQListView and TQHeader TQStyle::SFlags how = TQStyle::Style_Default; if ( lv->isEnabled() ) how |= TQStyle::Style_Enabled; lv->style().drawComplexControl( TQStyle::CC_ListView, p, lv, TQRect( 0, 0, width, height() ), lv->colorGroup(), how, TQStyle::SC_ListView, TQStyle::SC_None, opt ); } if ( isSelected() && (column == 0 || lv->allColumnsShowFocus()) ) { p->fillRect( r - marg, 0, width - r + marg, height(), _cg.brush( TQColorGroup::Highlight ) ); // removed text pen setting code from TQt } // removed icon drawing code from TQt // draw the tree gubbins if ( multiLinesEnabled() && column == 0 && isOpen() && childCount() ) { int textheight = fm.size( align, t ).height() + 2 * lv->itemMargin(); textheight = TQMAX( textheight, TQApplication::globalStrut().height() ); if ( textheight % 2 > 0 ) textheight++; if ( textheight < height() ) { int w = lv->treeStepSize() / 2; lv->style().drawComplexControl( TQStyle::CC_ListView, p, lv, TQRect( 0, textheight, w + 1, height() - textheight + 1 ), _cg, lv->isEnabled() ? TQStyle::Style_Enabled : TQStyle::Style_Default, TQStyle::SC_ListViewExpand, (uint)TQStyle::SC_All, TQStyleOption( 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(TQColorGroup::Text , _cg.highlightedText() ); if ( Component *comp = component( column ) ) comp->paint( &paint, _cg ); paint.end(); #ifdef HAVE_XRENDER TQColor rgb = cg.base();//backgroundColor(); float opac = 1.0; if ( d->visibilityTimer.isActive() && Private::fadeVisibility ) { int vis = TQMAX( 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: