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.
tdebase/tdm/kfrontend/themer/tdmitem.cpp

672 lines
17 KiB

/*
* Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
* Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
* Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
* Copyright (C) 2004 by Oswald Buddenhagen <ossi@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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*
* Generic Kdm Item
*/
// #define DRAW_OUTLINE 1 // for debugging only
#include "tdmitem.h"
#include "tdmlayout.h"
#include "tdmconfig.h"
#include <tdeglobal.h>
#include <kdebug.h>
#include <tqframe.h>
#include <tqwidget.h>
#include <tqlayout.h>
#include <tqimage.h>
#include <tqpainter.h>
extern bool argb_visual_available;
KdmItem::KdmItem( KdmItem *parent, const TQDomNode &node, const char *name )
: TQObject( parent, name )
, boxManager( 0 )
, fixedManager( 0 )
, image( 0 )
, myWidget( 0 )
, myLayoutItem( 0 )
, buttonParent( 0 )
{
init(node, name);
}
KdmItem::KdmItem( TQWidget *parent, const TQDomNode &node, const char *name )
: TQObject( parent, name )
, boxManager( 0 )
, fixedManager( 0 )
, image( 0 )
, myWidget( 0 )
, myLayoutItem( 0 )
, buttonParent( 0 )
{
init(node, name);
}
void
KdmItem::init( const TQDomNode &node, const char * )
{
// Set default layout for every item
currentManager = MNone;
pos.x = pos.y = 0;
pos.width = pos.height = 1;
pos.xType = pos.yType = pos.wType = pos.hType = DTnone;
pos.anchor = "nw";
m_backgroundModifier = 255;
isShown = InitialHidden;
// Set defaults for derived item's properties
properties.incrementalPaint = false;
state = Snormal;
// The "toplevel" node (the screen) is really just like a fixed node
if (!parent() || !parent()->inherits( "KdmItem" )) {
setFixedLayout();
return;
}
// Read the mandatory Pos tag. Other tags such as normal, prelighted,
// etc.. are read under specific implementations.
TQDomNodeList childList = node.childNodes();
for (uint nod = 0; nod < childList.count(); nod++) {
TQDomNode child = childList.item( nod );
TQDomElement el = child.toElement();
TQString tagName = el.tagName(), attr;
if (tagName == "pos") {
parseAttribute( el.attribute( "x", TQString::null ), pos.x, pos.xType );
parseAttribute( el.attribute( "y", TQString::null ), pos.y, pos.yType );
parseAttribute( el.attribute( "width", TQString::null ), pos.width, pos.wType );
parseAttribute( el.attribute( "height", TQString::null ), pos.height, pos.hType );
pos.anchor = el.attribute( "anchor", "nw" );
}
if (tagName == "bgmodifier") {
m_backgroundModifier = TQString(el.attribute( "value", "255" )).toInt();
}
}
TQDomNode tnode = node;
id = tnode.toElement().attribute( "id", TQString::number( (ulong)this, 16 ) );
// Tell 'parent' to add 'me' to its children
KdmItem *parentItem = static_cast<KdmItem *>( parent() );
parentItem->addChildItem( this );
}
KdmItem::~KdmItem()
{
delete boxManager;
delete fixedManager;
delete image;
}
void
KdmItem::update()
{
}
void
KdmItem::needUpdate()
{
emit needUpdate( area.x(), area.y(), area.width(), area.height() );
}
void
KdmItem::show( bool force )
{
if (isShown != InitialHidden && !force)
return;
TQValueList<KdmItem *>::Iterator it;
for (it = m_children.begin(); it != m_children.end(); ++it)
(*it)->show();
isShown = Shown;
if (myWidget)
myWidget->show();
// XXX showing of layouts not implemented, prolly pointless anyway
needUpdate();
}
void
KdmItem::hide( bool force )
{
if (isShown == ExplicitlyHidden)
return;
if (isShown == InitialHidden && force) {
isShown = ExplicitlyHidden;
return; // no need for further action
}
TQValueList<KdmItem *>::Iterator it;
for (it = m_children.begin(); it != m_children.end(); ++it)
(*it)->hide();
isShown = force ? ExplicitlyHidden : InitialHidden;
if (myWidget)
myWidget->hide();
// XXX hiding of layouts not implemented, prolly pointless anyway
needUpdate();
}
void
KdmItem::inheritFromButton( KdmItem *button )
{
if (button)
buttonParent = button;
TQValueList<KdmItem *>::Iterator it;
for (it = m_children.begin(); it != m_children.end(); ++it)
(*it)->inheritFromButton( button );
}
KdmItem *
KdmItem::findNode( const TQString &_id ) const
{
if (id == _id)
return const_cast<KdmItem *>( this );
TQValueList<KdmItem *>::ConstIterator it;
for (it = m_children.begin(); it != m_children.end(); ++it) {
KdmItem *t = (*it)->findNode( _id );
if (t)
return t;
}
return 0;
}
KdmItem *
KdmItem::findNodeByType( const TQString &_type ) const
{
if (itemType == _type)
return const_cast<KdmItem *>( this );
TQValueList<KdmItem *>::ConstIterator it;
for (it = m_children.begin(); it != m_children.end(); ++it) {
KdmItem *t = (*it)->findNodeByType( _type );
if (t)
return t;
}
return 0;
}
void
KdmItem::setWidget( TQWidget *widget )
{
// delete myWidget; -- themer->widget() owns the widgets
myWidget = widget;
if (isHidden())
myWidget->hide();
else
myWidget->show();
// Remove borders so that it blends nicely with the theme background
TQFrame* frame = ::tqt_cast<TQFrame *>( widget );
if (frame)
frame->setFrameStyle( TQFrame::NoFrame );
setGeometry(area, true);
connect( myWidget, TQ_SIGNAL(destroyed()), TQ_SLOT(widgetGone()) );
}
void
KdmItem::widgetGone()
{
myWidget = 0;
}
void
KdmItem::setLayoutItem( TQLayoutItem *item )
{
myLayoutItem = item;
// XXX hiding not supported - it think it's pointless here
if (myLayoutItem->widget())
connect( myLayoutItem->widget(), TQ_SIGNAL(destroyed()),
TQ_SLOT(layoutItemGone()) );
else if (myLayoutItem->layout())
connect( myLayoutItem->layout(), TQ_SIGNAL(destroyed()),
TQ_SLOT(layoutItemGone()) );
}
void
KdmItem::layoutItemGone()
{
myLayoutItem = 0;
}
/* This is called as a result of KdmLayout::update, and directly on the root */
void
KdmItem::setGeometry( const TQRect &newGeometry, bool force )
{
kdDebug() << " KdmItem::setGeometry " << id << newGeometry << endl;
// check if already 'in place'
if (!force && area == newGeometry)
return;
area = newGeometry;
if (myWidget) {
TQRect widGeo = newGeometry;
if ( widGeo.height() > myWidget->maximumHeight() ) {
widGeo.moveTop( widGeo.top() + ( widGeo.height() - myWidget->maximumHeight() ) / 2 );
widGeo.setHeight( myWidget->maximumHeight() );
}
myWidget->setGeometry( widGeo );
}
if (myLayoutItem)
myLayoutItem->setGeometry( newGeometry );
// recurr to all boxed children
if (boxManager && !boxManager->isEmpty())
boxManager->update( newGeometry, force );
// recurr to all fixed children
if (fixedManager && !fixedManager->isEmpty())
fixedManager->update( newGeometry, force );
// TODO send *selective* repaint signal
}
void
KdmItem::paint( TQPainter *p, const TQRect &rect )
{
if (isHidden()) {
return;
}
if (myWidget || (myLayoutItem && myLayoutItem->widget())) {
// TDEListView because it's missing a TQ_OBJECT
// FIXME: This is a nice idea in theory, but in practice it is
// very confusing for the user not to see the empty list box
// delineated from the rest of the greeter.
// Maybe set a darker version of the background instead of an exact copy?
if ( myWidget && myWidget->isA( "TDEListView" ) ) {
if (!argb_visual_available) {
// Software blend only (no compositing support)
TQPixmap copy( myWidget->size() );
kdDebug() << myWidget->geometry() << " " << area << " " << myWidget->size() << endl;
bitBlt( &copy, TQPoint( 0, 0), p->device(), myWidget->geometry(), TQt::CopyROP );
// Lighten it slightly
TQImage lightVersion;
lightVersion = copy.convertToImage();
uchar * r = lightVersion.bits();
uchar * g = lightVersion.bits() + 1;
uchar * b = lightVersion.bits() + 2;
uchar * end = lightVersion.bits() + lightVersion.numBytes();
while ( r != end ) {
if ((*r) < (255-m_backgroundModifier))
*r = (uchar) (*r) + m_backgroundModifier;
else
*r = 255;
if ((*g) < (255-m_backgroundModifier))
*g = (uchar) (*g) + m_backgroundModifier;
else
*g = 255;
if ((*b) < (255-m_backgroundModifier))
*b = (uchar) (*b) + m_backgroundModifier;
else
*b = 255;
r += 4;
g += 4;
b += 4;
}
copy = lightVersion;
// Set it
myWidget->setPaletteBackgroundPixmap( copy );
}
else {
// We have compositing support!
TQRgb blend_color = tqRgba(m_backgroundModifier, m_backgroundModifier, m_backgroundModifier, 0); // RGBA overlay
TQImage img( myWidget->size(), 32 );
img = img.convertDepth(32);
img.setAlphaBuffer(true);
uchar * rd = img.bits();
for( int y = 0; y < img.height(); ++y )
{
for( short int x = 0; x < img.width(); ++x )
{
*reinterpret_cast<TQRgb*>(rd) = blend_color;
rd += 4;
}
}
myWidget->setPaletteBackgroundPixmap( img );
}
}
return;
}
if (area.intersects( rect )) {
TQRect contentsRect = area.intersect( rect );
contentsRect.moveBy( TQMIN( 0, -area.x() ), TQMIN( 0, -area.y() ) );
drawContents( p, contentsRect );
}
#ifdef DRAW_OUTLINE
// Draw bounding rect for this item
p->setPen( TQt::white );
p->drawRect( area );
#endif
if (myLayoutItem) {
return;
}
// Dispatch paint events to children
TQValueList<KdmItem *>::Iterator it;
for (it = m_children.begin(); it != m_children.end(); ++it) {
(*it)->paint( p, rect );
}
}
KdmItem *KdmItem::currentActive = 0;
void
KdmItem::mouseEvent( int x, int y, bool pressed, bool released )
{
if (isShown == ExplicitlyHidden)
return;
if (buttonParent && buttonParent != this) {
buttonParent->mouseEvent( x, y, pressed, released );
return;
}
ItemState oldState = state;
if (area.contains( x, y )) {
if (released && oldState == Sactive) {
if (buttonParent)
emit activated( id );
state = Sprelight;
currentActive = 0;
} else if (pressed || currentActive == this) {
state = Sactive;
currentActive = this;
} else if (!currentActive)
state = Sprelight;
else
state = Snormal;
} else {
if (released)
currentActive = 0;
if (currentActive == this)
state = Sprelight;
else
state = Snormal;
}
if (!buttonParent) {
TQValueList<KdmItem *>::Iterator it;
for (it = m_children.begin(); it != m_children.end(); ++it)
(*it)->mouseEvent( x, y, pressed, released );
}
if (oldState != state)
statusChanged();
}
void
KdmItem::statusChanged()
{
if (buttonParent == this) {
TQValueList<KdmItem *>::Iterator it;
for (it = m_children.begin(); it != m_children.end(); ++it) {
(*it)->state = state;
(*it)->statusChanged();
}
}
}
// BEGIN protected inheritable
TQSize
KdmItem::sizeHint()
{
if (myWidget)
return myWidget->size();
if (myLayoutItem)
return myLayoutItem->sizeHint();
int w = pos.wType == DTpixel ? kAbs( pos.width ) : -1,
h = pos.hType == DTpixel ? kAbs( pos.height ) : -1;
return TQSize( w, h );
}
TQRect
KdmItem::placementHint( const TQRect &parentRect )
{
TQSize hintedSize = sizeHint();
TQSize boxHint;
int x = parentRect.left(),
y = parentRect.top(),
w = parentRect.width(),
h = parentRect.height();
kdDebug() << timestamp() << " KdmItem::placementHint parentRect=" << parentRect << " hintedSize=" << hintedSize << endl;
// check if width or height are set to "box"
if (pos.wType == DTbox || pos.hType == DTbox) {
if (myLayoutItem || myWidget)
boxHint = hintedSize;
else {
if (!boxManager)
return parentRect;
boxHint = boxManager->sizeHint();
}
kdDebug() << timestamp() << " boxHint " << boxHint << endl;
}
if (pos.xType == DTpixel)
x += pos.x;
else if (pos.xType == DTnpixel)
x = parentRect.right() - pos.x;
else if (pos.xType == DTpercent)
x += tqRound( parentRect.width() / 100.0 * pos.x );
if (pos.yType == DTpixel)
y += pos.y;
else if (pos.yType == DTnpixel)
y = parentRect.bottom() - pos.y;
else if (pos.yType == DTpercent)
y += tqRound( parentRect.height() / 100.0 * pos.y );
if (pos.wType == DTpixel)
w = pos.width;
else if (pos.wType == DTnpixel)
w -= pos.width;
else if (pos.wType == DTpercent)
w = tqRound( parentRect.width() / 100.0 * pos.width );
else if (pos.wType == DTbox)
w = boxHint.width();
else if (hintedSize.width() > 0)
w = hintedSize.width();
else
w = 0;
if (pos.hType == DTpixel)
h = pos.height;
else if (pos.hType == DTnpixel)
h -= pos.height;
else if (pos.hType == DTpercent)
h = tqRound( parentRect.height() / 100.0 * pos.height );
else if (pos.hType == DTbox)
h = boxHint.height();
else if (hintedSize.height() > 0) {
if (w && pos.wType != DTnone)
h = (hintedSize.height() * w) / hintedSize.width();
else
h = hintedSize.height();
} else
h = 0;
// we choose to take the hinted size, but it's better to listen to the aspect ratio
if (pos.wType == DTnone && pos.hType != DTnone && h && w) {
w = tqRound(float(hintedSize.width() * h) / hintedSize.height());
}
// defaults to center
int dx = -w / 2, dy = -h / 2;
// anchor the rect to an edge / corner
if (pos.anchor.length() > 0 && pos.anchor.length() < 3) {
if (pos.anchor.find( 'n' ) >= 0)
dy = 0;
if (pos.anchor.find( 's' ) >= 0)
dy = -h;
if (pos.anchor.find( 'w' ) >= 0)
dx = 0;
if (pos.anchor.find( 'e' ) >= 0)
dx = -w;
}
// KdmItem *p = static_cast<KdmItem*>( parent() );
kdDebug() << timestamp() << " placementHint " << this << " x=" << x << " dx=" << dx << " w=" << w << " y=" << y << " dy=" << dy << " h=" << h << " " << parentRect << endl;
y += dy;
x += dx;
// Note: no clipping to parent because this broke many themes!
return TQRect( x, y, w, h );
}
// END protected inheritable
void
KdmItem::addChildItem( KdmItem *item )
{
m_children.append( item );
switch (currentManager) {
case MNone: // fallback to the 'fixed' case
setFixedLayout();
case MFixed:
fixedManager->addItem( item );
break;
case MBox:
boxManager->addItem( item );
break;
}
// signal bounce from child to parent
connect( item, TQ_SIGNAL(needUpdate( int, int, int, int )), TQ_SIGNAL(needUpdate( int, int, int, int )) );
connect( item, TQ_SIGNAL(activated( const TQString & )), TQ_SIGNAL(activated( const TQString & )) );
}
void
KdmItem::parseAttribute( const TQString &s, int &val, enum DataType &dType )
{
if (s.isEmpty())
return;
int p;
if (s == "box") { // box value
dType = DTbox;
val = 0;
} else if ((p = s.find( '%' )) >= 0) { // percent value
dType = DTpercent;
TQString sCopy = s;
sCopy.remove( p, 1 );
sCopy.replace( ',', '.' );
val = (int)sCopy.toDouble();
} else { // int value
dType = DTpixel;
TQString sCopy = s;
if (sCopy.at( 0 ) == '-') {
sCopy.remove( 0, 1 );
dType = DTnpixel;
}
sCopy.replace( ',', '.' );
val = (int)sCopy.toDouble();
}
}
void
KdmItem::parseFont( const TQString &s, TQFont &font )
{
int splitAt = s.findRev( ' ' );
if (splitAt < 1)
return;
font.setFamily( s.left( splitAt ) );
int fontSize = s.mid( splitAt + 1 ).toInt();
if (fontSize > 1)
font.setPointSize( fontSize );
}
void
KdmItem::parseColor( const TQString &s, TQColor &color )
{
if (s.at( 0 ) != '#')
return;
bool ok;
TQString sCopy = s;
int hexColor = sCopy.remove( 0, 1 ).toInt( &ok, 16 );
if (ok)
color.setRgb( hexColor );
}
void
KdmItem::setBoxLayout( const TQDomNode &node )
{
if (!boxManager)
boxManager = new KdmLayoutBox( node );
currentManager = MBox;
}
void
KdmItem::setFixedLayout( const TQDomNode &node )
{
if (!fixedManager)
fixedManager = new KdmLayoutFixed( node );
currentManager = MFixed;
}
TQWidget *
KdmItem::parentWidget() const
{
if (myWidget)
return myWidget;
if (!this->parent())
return 0;
if (parent()->tqt_cast("TQWidget"))
return (TQWidget*)parent();
return ((KdmItem*)parent())->parentWidget();
}
#include "tdmitem.moc"