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

1315 lines
30 KiB

/*
kopetelistviewitem.cpp - Kopete's modular QListViewItems
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 "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: