2 * Copyright (C) 2003 by Unai Garro <ugarro@users.sourceforge.net>
3 * Copyright (C) 2004 by Enrico Ros <rosenric@dei.unipd.it>
4 * Copyright (C) 2004 by Stephan Kulow <coolo@kde.org>
5 * Copyright (C) 2004 by Oswald Buddenhagen <ossi@kde.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include "kdmlayout.h"
28 #include "kdmthemer.h"
30 #include <kdm_greet.h> // debug*
37 toBool( const QString
&str
)
40 int val
= str
.toInt( &ok
);
46 KdmItem::KdmItem( QObject
*parent
, const QDomNode
&node
)
51 , m_showTypeInvert( false )
57 QDomNode showNode
= node
.namedItem( "show" );
58 if (!showNode
.isNull()) {
59 QDomElement sel
= showNode
.toElement();
61 QString modes
= sel
.attribute( "modes" );
62 if (!modes
.isNull() &&
63 (modes
== "nowhere" ||
64 (modes
!= "everywhere" &&
65 !modes
.split( ",", QString::SkipEmptyParts
).contains( "console" ))))
71 m_showType
= sel
.attribute( "type" );
72 if (!m_showType
.isNull()) {
73 if (m_showType
[0] == '!') {
74 m_showType
.remove( 0, 1 );
75 m_showTypeInvert
= true;
77 if (!m_showType
.startsWith( "plugin-" ) &&
78 themer()->typeVisible( m_showType
) == m_showTypeInvert
)
85 m_minScrWidth
= sel
.attribute( "min-screen-width" ).toInt();
86 m_minScrHeight
= sel
.attribute( "min-screen-height" ).toInt();
89 // Set default layout for every item
90 currentManager
= MNone
;
91 geom
.pos
.x
.type
= geom
.pos
.y
.type
=
92 geom
.size
.x
.type
= geom
.size
.y
.type
= DTnone
;
93 geom
.minSize
.x
.type
= geom
.minSize
.y
.type
=
94 geom
.maxSize
.x
.type
= geom
.maxSize
.y
.type
= DTpixel
;
95 geom
.minSize
.x
.val
= geom
.minSize
.y
.val
= 0;
96 geom
.maxSize
.x
.val
= geom
.maxSize
.y
.val
= 1000000;
99 // Set defaults for derived item's properties
102 KdmItem
*parentItem
= qobject_cast
<KdmItem
*>( parent
);
106 style
= parentItem
->style
;
108 // Read the mandatory Pos tag. Other tags such as normal, prelighted,
109 // etc.. are read under specific implementations.
110 QDomNodeList childList
= node
.childNodes();
111 for (int nod
= 0; nod
< childList
.count(); nod
++) {
112 QDomNode child
= childList
.item( nod
);
113 QDomElement el
= child
.toElement();
114 QString tagName
= el
.tagName();
116 if (tagName
== "pos") {
117 parseSize( el
.attribute( "x", QString() ), geom
.pos
.x
);
118 parseSize( el
.attribute( "y", QString() ), geom
.pos
.y
);
119 parseSize( el
.attribute( "width", QString() ), geom
.size
.x
);
120 parseSize( el
.attribute( "height", QString() ), geom
.size
.y
);
121 parseSize( el
.attribute( "min-width", QString() ), geom
.minSize
.x
);
122 parseSize( el
.attribute( "min-height", QString() ), geom
.minSize
.y
);
123 parseSize( el
.attribute( "max-width", QString() ), geom
.maxSize
.x
);
124 parseSize( el
.attribute( "max-height", QString() ), geom
.maxSize
.y
);
125 geom
.anchor
= el
.attribute( "anchor", "nw" );
126 geom
.expand
= toBool( el
.attribute( "expand", "false" ) );
127 } else if (tagName
== "buddy")
128 buddy
= el
.attribute( "idref", "" );
129 else if (tagName
== "style")
130 parseStyle( el
, style
);
133 if (!style
.font
.present
)
134 parseFont( "Sans 14", style
.font
);
136 QDomElement el
= node
.toElement();
137 setObjectName( el
.attribute( "id", QString::number( (ulong
)this, 16 ) ) );
138 isButton
= toBool( el
.attribute( "button", "false" ) );
139 isBackground
= toBool( el
.attribute( "background", "false" ) );
142 // The "toplevel" node (the screen) is really just like a fixed node
145 // Tell 'parent' to add 'me' to its children
146 parentItem
->addChildItem( this );
163 KdmItem::needUpdate()
165 emit
needUpdate( area
.x(), area
.y(), area
.width(), area
.height() );
169 KdmItem::updateThisVisible()
172 if (show
&& (!m_showType
.isNull() || m_minScrWidth
|| m_minScrHeight
)) {
173 KdmThemer
*thm
= themer();
174 if ((!m_showType
.isNull() &&
175 !(thm
->typeVisible( m_showType
) ^ m_showTypeInvert
)) ||
177 (thm
->widget()->width() < m_minScrWidth
||
178 thm
->widget()->height() < m_minScrHeight
)))
181 if (m_visible
!= show
) {
183 emit
needPlacement();
188 KdmItem::setVisible( bool show
)
195 KdmItem::updateVisible()
199 itm
->updateVisible();
205 if (KdmThemer
*thm
= qobject_cast
<KdmThemer
*>(parent()))
207 if (KdmItem
*parentItem
= qobject_cast
<KdmItem
*>(parent()))
208 return parentItem
->themer();
213 KdmItem::setWidget( QWidget
*widget
)
215 if ((myWidget
= widget
)) {
216 myWidget
->hide(); // yes, really
217 connect( myWidget
, SIGNAL(destroyed()), SLOT(widgetGone()) );
218 setWidgetAttribs( myWidget
);
222 emit
needPlacement();
226 KdmItem::widgetGone()
231 emit
needPlacement();
235 KdmItem::setWidgetAttribs( QWidget
*widget
)
237 widget
->setPalette( style
.palette
);
238 widget
->installEventFilter( this );
239 updatePalette( myWidget
);
240 ::setWidgetAttribs( widget
, style
, style
.frame
);
244 KdmItem::updatePalette( QWidget
*w
)
246 bool set
= w
->palette().isBrushSet( w
->palette().currentColorGroup(),
247 w
->backgroundRole() );
248 bool opaque
= set
&& w
->palette().brush( w
->backgroundRole() ).isOpaque();
249 w
->setAutoFillBackground( set
);
250 w
->setAttribute( Qt::WA_OpaquePaintEvent
, opaque
);
254 KdmItem::eventFilter( QObject
*o
, QEvent
*e
)
256 if (e
->type() == QEvent::WindowActivate
||
257 e
->type() == QEvent::WindowDeactivate
||
258 e
->type() == QEvent::EnabledChange
)
260 updatePalette( (QWidget
*)o
);
266 KdmItem::showWidget( bool show
)
272 QSize
sz( area
.size().expandedTo( myWidget
->minimumSize() )
273 .boundedTo( myWidget
->maximumSize() ) );
274 QSize
off( (area
.size() - sz
) / 2 );
275 myWidget
->setGeometry(
276 area
.x() + off
.width(), area
.y() + off
.height(),
277 sz
.width(), sz
.height() );
279 myWidget
->setVisible( show
);
282 itm
->showWidget( show
);
286 KdmItem::plugActions( bool plug
)
290 doPlugActions( plug
);
292 itm
->plugActions( plug
);
296 KdmItem::doPlugActions( bool )
300 /* This is called as a result of KdmLayout::update, and directly on the root */
302 KdmItem::setGeometry( QStack
<QSize
> &parentSizes
, const QRect
&newGeometry
, bool force
)
304 enter("Item::setGeometry") << objectName() << newGeometry
;
305 // check if already 'in place'
306 if (!force
&& area
== newGeometry
) {
307 leave() << "unchanged";
313 // recurr to all boxed children
314 if (boxManager
&& !boxManager
->isEmpty())
315 boxManager
->update( parentSizes
, newGeometry
, force
);
317 // recurr to all fixed children
318 if (fixedManager
&& !fixedManager
->isEmpty())
319 fixedManager
->update( parentSizes
, newGeometry
, force
);
321 // TODO send *selective* repaint signal
327 KdmItem::paint( QPainter
*p
, const QRect
&rect
, bool background
)
332 (p
->device()->width() < m_minScrWidth
||
333 p
->device()->height() < m_minScrHeight
))
339 QRect contentsRect
= area
.intersected( rect
);
340 if (!contentsRect
.isEmpty() && (!background
|| isBackground
)) {
341 drawContents( p
, contentsRect
);
342 if (debugLevel
& DEBUG_THEMING
) {
343 // Draw bounding rect for this item
344 QPen
pen( Qt::white
);
345 pen
.setCapStyle( Qt::FlatCap
);
346 pen
.setDashPattern( QVector
<qreal
>() << 5 << 6 );
348 p
->setBackgroundMode( Qt::OpaqueMode
);
349 p
->setBackground( Qt::black
);
350 p
->drawRect( area
.x(), area
.y(), area
.width() - 1, area
.height() - 1 );
351 p
->setBackgroundMode( Qt::TransparentMode
);
355 // Dispatch paint events to children
357 itm
->paint( p
, rect
, background
);
361 KdmItem::childrenContain( int x
, int y
)
363 forEachVisibleChild (itm
) {
364 if (itm
->area
.contains( x
, y
))
366 if (itm
->childrenContain( x
, y
))
373 KdmItem::activateBuddy()
375 if (KdmItem
*itm
= themer()->findNode( buddy
))
377 itm
->myWidget
->setFocus();
378 if (QLineEdit
*le
= qobject_cast
<QLineEdit
*>(itm
->myWidget
))
383 KdmItem
*KdmItem::currentActive
= 0;
386 KdmItem::mouseEvent( int x
, int y
, bool pressed
, bool released
)
391 ItemState oldState
= state
;
392 if (area
.contains( x
, y
) || (isButton
&& childrenContain( x
, y
))) {
393 if (released
&& oldState
== Sactive
) {
395 emit
activated( objectName() );
398 } else if (pressed
&& !buddy
.isEmpty())
400 else if (pressed
|| currentActive
== this) {
402 currentActive
= this;
403 } else if (!currentActive
)
410 if (currentActive
== this)
415 if (oldState
!= state
)
416 statusChanged( isButton
);
420 itm
->mouseEvent( x
, y
, pressed
, released
);
424 KdmItem::statusChanged( bool descend
)
429 o
->statusChanged( descend
);
433 // BEGIN protected inheritable
439 return myWidget
->sizeHint();
441 geom
.size
.x
.type
== DTpixel
? geom
.size
.x
.val
: 0,
442 geom
.size
.y
.type
== DTpixel
? geom
.size
.y
.val
: 0 );
446 KdmItem::ensureHintedSize( QSize
&hintedSize
)
448 if (!hintedSize
.isValid()) {
449 hintedSize
= sizeHint();
450 debug() << "hinted" << hintedSize
;
456 KdmItem::ensureBoxHint( QSize
&boxHint
, QStack
<QSize
> &parentSizes
, QSize
&hintedSize
)
458 if (!boxHint
.isValid()) {
459 if (myWidget
|| !boxManager
)
460 boxHint
= ensureHintedSize( hintedSize
);
462 boxHint
= boxManager
->sizeHint( parentSizes
);
463 debug() << "boxHint" << boxHint
;
469 getParentSize( const QStack
<QSize
> &parentSizes
, int levels
)
471 int off
= parentSizes
.size() - 1 - levels
;
473 kError() << "Theme references element below the root.";
476 return parentSizes
[off
];
482 QStack
<QSize
> &parentSizes
, QSize
&hintedSize
, QSize
&boxHint
,
487 if (sz
.x
.type
== DTpixel
)
489 else if (sz
.x
.type
== DTnpixel
)
490 w
= io
.width() - sz
.x
.val
;
491 else if (sz
.x
.type
== DTpercent
)
492 w
= (getParentSize( parentSizes
, sz
.x
.levels
).width() * sz
.x
.val
+ 50) / 100;
493 else if (sz
.x
.type
== DTbox
)
494 w
= ensureBoxHint( boxHint
, parentSizes
, hintedSize
).width();
496 w
= ensureHintedSize( hintedSize
).width();
498 if (sz
.y
.type
== DTpixel
)
500 else if (sz
.y
.type
== DTnpixel
)
501 h
= io
.height() - sz
.y
.val
;
502 else if (sz
.y
.type
== DTpercent
)
503 h
= (getParentSize( parentSizes
, sz
.y
.levels
).height() * sz
.y
.val
+ 50) / 100;
504 else if (sz
.y
.type
== DTbox
)
505 h
= ensureBoxHint( boxHint
, parentSizes
, hintedSize
).height();
507 h
= ensureHintedSize( hintedSize
).height();
509 if (sz
.x
.type
== DTscale
&& h
&& ensureHintedSize( hintedSize
).height())
510 w
= w
* h
/ hintedSize
.height();
511 else if (sz
.y
.type
== DTscale
&& w
&& ensureHintedSize( hintedSize
).width())
512 h
= w
* h
/ hintedSize
.width();
519 KdmItem::sizingHint( QStack
<QSize
> &parentSizes
, SizeHint
&hint
)
521 enter("Item::sizingHint") << objectName() << NoSpace
<< "parentSize #"
522 << parentSizes
.size() << Space
<< parentSizes
.top();
524 QSize hintedSize
, boxHint
;
525 hint
.min
= hint
.opt
= hint
.max
= parentSizes
.top();
526 calcSize( geom
.size
, parentSizes
, hintedSize
, boxHint
, hint
.opt
);
527 calcSize( geom
.minSize
, parentSizes
, hintedSize
, boxHint
, hint
.min
);
528 calcSize( geom
.maxSize
, parentSizes
, hintedSize
, boxHint
, hint
.max
);
530 leave() << "size" << hint
.opt
<< "min" << hint
.min
<< "max" << hint
.max
;
532 hint
.max
= hint
.max
.expandedTo( hint
.min
); // if this triggers, the theme is bust
533 hint
.opt
= hint
.opt
.boundedTo( hint
.max
).expandedTo( hint
.min
);
535 // Note: no clipping to parent because this broke many themes!
539 KdmItem::placementHint( QStack
<QSize
> &sizes
, const QSize
&sz
, const QPoint
&offset
)
541 const QSize
&parentSize
= sizes
.top();
544 w
= parentSize
.width(),
545 h
= parentSize
.height();
547 enter("Item::placementHint") << objectName() << NoSpace
<< "parentSize #"
548 << sizes
.size() << Space
<< parentSize
<< "size" << sz
<< "offset" << offset
;
550 if (geom
.pos
.x
.type
== DTpixel
)
552 else if (geom
.pos
.x
.type
== DTnpixel
)
553 x
+= w
- geom
.pos
.x
.val
;
554 else if (geom
.pos
.x
.type
== DTpercent
)
555 x
+= (w
* geom
.pos
.x
.val
+ 50) / 100;
557 if (geom
.pos
.y
.type
== DTpixel
)
559 else if (geom
.pos
.y
.type
== DTnpixel
)
560 y
+= h
- geom
.pos
.y
.val
;
561 else if (geom
.pos
.y
.type
== DTpercent
)
562 y
+= (h
* geom
.pos
.y
.val
+ 50) / 100;
564 // defaults to center
565 int dx
= sz
.width() / 2, dy
= sz
.height() / 2;
567 // anchor the rect to an edge / corner
568 if (geom
.anchor
.length() > 0 && geom
.anchor
.length() < 3) {
569 if (geom
.anchor
.indexOf( 'n' ) >= 0)
571 if (geom
.anchor
.indexOf( 's' ) >= 0)
573 if (geom
.anchor
.indexOf( 'w' ) >= 0)
575 if (geom
.anchor
.indexOf( 'e' ) >= 0)
578 leave() << "x:" << x
<< "dx:" << dx
<< "y:" << y
<< "dy:" << dy
;
582 return QRect( x
, y
, sz
.width(), sz
.height() );
586 KdmItem::placementHint( QStack
<QSize
> &sizes
, const QPoint
&offset
)
589 sizingHint( sizes
, sh
);
590 return placementHint( sizes
, sh
.opt
, offset
);
593 // END protected inheritable
597 KdmItem::showStructure( const QString
&pfx
)
600 QDebug
ds( QtDebugMsg
);
602 ds
<< (qPrintable( pfx
) + 1);
603 ds
<< objectName() << qPrintable( itemType
) << area
;
605 if (!m_children
.isEmpty()) {
607 npfx
.replace( '\\', ' ' ).replace( '-', ' ' );
608 for (int i
= 0; i
< m_children
.count() - 1; i
++)
609 m_children
[i
]->showStructure( npfx
+ " |-" );
610 m_children
[m_children
.count() - 1]->showStructure( npfx
+ " \\-" );
615 KdmItem::addChildItem( KdmItem
*item
)
617 m_children
.append( item
);
618 switch (currentManager
) {
619 case MNone
: // fallback to the 'fixed' case
622 fixedManager
->addItem( item
);
625 boxManager
->addItem( item
);
631 KdmItem::setBoxLayout( const QDomNode
&node
)
634 boxManager
= new KdmLayoutBox( node
);
635 currentManager
= MBox
;
639 KdmItem::setFixedLayout( const QDomNode
&node
)
642 fixedManager
= new KdmLayoutFixed( node
);
643 currentManager
= MFixed
;
646 #include "kdmitem.moc"