You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/libkopete/ui/kopetelistviewitem.cpp

1633 lines
38 KiB

/*
kopetelistviewitem.cpp - Kopete's modular TQListViewItems
Copyright (c) 2005 by Engin AYDOGAN <engin@bzzzt.biz>
Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
*************************************************************************
* *
* 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 <kdebug.h>
#include <kiconloader.h>
#include <kstringhandler.h>
#include <tqapplication.h>
#include <tqpixmap.h>
#include <tqpainter.h>
#include <tqptrlist.h>
#include <tqrect.h>
#include <tqtimer.h>
#include <tqheader.h>
#include <tqstyle.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:
TQPtrList<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 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<TQString,TQRect> 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<TQString,TQRect> 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::tqlayout( 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::retqlayout()
{
d->parent->retqlayout();
}
void Component::componentAdded( Component *component )
{
ComponentBase::componentAdded( component );
//update( Retqlayout );
}
void Component::componentRemoved( Component *component )
{
ComponentBase::componentRemoved( component );
//update( Retqlayout );
}
// 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
retqlayout();
}
void BoxComponent::tqlayout( const TQRect &rect )
{
Component::tqlayout( 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->tqlayout( 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::tqlayout(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::tqlayout(d->sub);
Component::tqlayout(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::tqlayout(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::tqlayout(d->sub);
Component::tqlayout(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;
retqlayout();
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::tqlayout( const TQRect &rect )
{
Component::tqlayout( 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->tqlayout( TQRect( usedWidth+ rect.left(), rect.top(),
totalWidth - usedWidth,
comp->heightForWidth( totalWidth - usedWidth ) ) );
} else {
comp->hide();
}
}
else
{
comp->show();
comp->tqlayout( 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<Kopete::Emoticons::Token> tokens;
TQValueList<Kopete::Emoticons::Token>::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<TQString,TQRect> 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 tqlayout
d->animateLayout = false;
}
void Item::scheduleLayout()
{
// perform a delayed tqlayout 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 )->tqlayout( 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 retqlayout, don't bother painting yet.
if ( d->layoutTimer.isActive() )
return;
listView()->repaintItem( this );
}
void Item::retqlayout()
{
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->tqstyle().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->tqstyle().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: