Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / kdm / kfrontend / themer / kdmitem.cpp
blob7e636ef8ede6c5adef806a27a7ff9c27b6827757
1 /*
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.
23 * Generic Kdm Item
26 #include "kdmitem.h"
27 #include "kdmlayout.h"
28 #include "kdmthemer.h"
30 #include <kdm_greet.h> // debug*
32 #include <QEvent>
33 #include <QLineEdit>
34 #include <QPainter>
36 static bool
37 toBool( const QString &str )
39 bool ok;
40 int val = str.toInt( &ok );
41 if (!ok)
42 return str == "true";
43 return val != 0;
46 KdmItem::KdmItem( QObject *parent, const QDomNode &node )
47 : QObject( parent )
48 , boxManager( 0 )
49 , fixedManager( 0 )
50 , myWidget( 0 )
51 , m_showTypeInvert( false )
52 , m_minScrWidth( 0 )
53 , m_minScrHeight( 0 )
54 , m_visible( true )
55 , m_shown( true )
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" ))))
67 m_visible = false;
68 return;
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)
80 m_visible = false;
81 return;
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;
97 geom.anchor = "nw";
99 // Set defaults for derived item's properties
100 state = Snormal;
102 KdmItem *parentItem = qobject_cast<KdmItem *>( parent );
103 if (!parentItem)
104 style.frame = false;
105 else
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" ) );
141 if (!parentItem)
142 // The "toplevel" node (the screen) is really just like a fixed node
143 setFixedLayout();
144 else
145 // Tell 'parent' to add 'me' to its children
146 parentItem->addChildItem( this );
149 KdmItem::~KdmItem()
151 delete boxManager;
152 delete fixedManager;
155 void
156 KdmItem::update()
158 forEachChild (itm)
159 itm->update();
162 void
163 KdmItem::needUpdate()
165 emit needUpdate( area.x(), area.y(), area.width(), area.height() );
168 void
169 KdmItem::updateThisVisible()
171 bool show = m_shown;
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)) ||
176 (thm->widget() &&
177 (thm->widget()->width() < m_minScrWidth ||
178 thm->widget()->height() < m_minScrHeight)))
179 show = false;
181 if (m_visible != show) {
182 m_visible = show;
183 emit needPlacement();
187 void
188 KdmItem::setVisible( bool show )
190 m_shown = show;
191 updateThisVisible();
194 void
195 KdmItem::updateVisible()
197 updateThisVisible();
198 forEachChild (itm)
199 itm->updateVisible();
202 KdmThemer *
203 KdmItem::themer()
205 if (KdmThemer *thm = qobject_cast<KdmThemer *>(parent()))
206 return thm;
207 if (KdmItem *parentItem = qobject_cast<KdmItem *>(parent()))
208 return parentItem->themer();
209 return 0;
212 void
213 KdmItem::setWidget( QWidget *widget )
215 if ((myWidget = widget)) {
216 myWidget->hide(); // yes, really
217 connect( myWidget, SIGNAL(destroyed()), SLOT(widgetGone()) );
218 setWidgetAttribs( myWidget );
221 emit needPlugging();
222 emit needPlacement();
225 void
226 KdmItem::widgetGone()
228 myWidget = 0;
230 emit needPlugging();
231 emit needPlacement();
234 void
235 KdmItem::setWidgetAttribs( QWidget *widget )
237 widget->setPalette( style.palette );
238 widget->installEventFilter( this );
239 updatePalette( myWidget );
240 ::setWidgetAttribs( widget, style, style.frame );
243 void
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 );
253 bool
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 );
262 return false;
265 void
266 KdmItem::showWidget( bool show )
268 if (!isVisible())
269 show = false;
270 if (myWidget) {
271 if (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 );
281 forEachChild (itm)
282 itm->showWidget( show );
285 void
286 KdmItem::plugActions( bool plug )
288 if (myWidget)
289 plug = false;
290 doPlugActions( plug );
291 forEachChild (itm)
292 itm->plugActions( plug );
295 void
296 KdmItem::doPlugActions( bool )
300 /* This is called as a result of KdmLayout::update, and directly on the root */
301 void
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";
308 return;
311 area = newGeometry;
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
323 leave() << "done";
326 void
327 KdmItem::paint( QPainter *p, const QRect &rect, bool background )
329 if (!isVisible())
330 return;
331 if (background &&
332 (p->device()->width() < m_minScrWidth ||
333 p->device()->height() < m_minScrHeight))
334 return;
336 if (myWidget)
337 return;
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 );
347 p->setPen( pen );
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
356 forEachChild (itm)
357 itm->paint( p, rect, background );
360 bool
361 KdmItem::childrenContain( int x, int y )
363 forEachVisibleChild (itm) {
364 if (itm->area.contains( x, y ))
365 return true;
366 if (itm->childrenContain( x, y ))
367 return true;
369 return false;
372 void
373 KdmItem::activateBuddy()
375 if (KdmItem *itm = themer()->findNode( buddy ))
376 if (itm->myWidget) {
377 itm->myWidget->setFocus();
378 if (QLineEdit *le = qobject_cast<QLineEdit *>(itm->myWidget))
379 le->selectAll();
383 KdmItem *KdmItem::currentActive = 0;
385 void
386 KdmItem::mouseEvent( int x, int y, bool pressed, bool released )
388 if (!isVisible())
389 return;
391 ItemState oldState = state;
392 if (area.contains( x, y ) || (isButton && childrenContain( x, y ))) {
393 if (released && oldState == Sactive) {
394 if (isButton)
395 emit activated( objectName() );
396 state = Sprelight;
397 currentActive = 0;
398 } else if (pressed && !buddy.isEmpty())
399 activateBuddy();
400 else if (pressed || currentActive == this) {
401 state = Sactive;
402 currentActive = this;
403 } else if (!currentActive)
404 state = Sprelight;
405 else
406 state = Snormal;
407 } else {
408 if (released)
409 currentActive = 0;
410 if (currentActive == this)
411 state = Sprelight;
412 else
413 state = Snormal;
415 if (oldState != state)
416 statusChanged( isButton );
418 if (!isButton)
419 forEachChild (itm)
420 itm->mouseEvent( x, y, pressed, released );
423 void
424 KdmItem::statusChanged( bool descend )
426 if (descend)
427 forEachChild (o) {
428 o->state = state;
429 o->statusChanged( descend );
433 // BEGIN protected inheritable
435 QSize
436 KdmItem::sizeHint()
438 if (myWidget)
439 return myWidget->sizeHint();
440 return QSize(
441 geom.size.x.type == DTpixel ? geom.size.x.val : 0,
442 geom.size.y.type == DTpixel ? geom.size.y.val : 0 );
445 const QSize &
446 KdmItem::ensureHintedSize( QSize &hintedSize )
448 if (!hintedSize.isValid()) {
449 hintedSize = sizeHint();
450 debug() << "hinted" << hintedSize;
452 return hintedSize;
455 const QSize &
456 KdmItem::ensureBoxHint( QSize &boxHint, QStack<QSize> &parentSizes, QSize &hintedSize )
458 if (!boxHint.isValid()) {
459 if (myWidget || !boxManager)
460 boxHint = ensureHintedSize( hintedSize );
461 else
462 boxHint = boxManager->sizeHint( parentSizes );
463 debug() << "boxHint" << boxHint;
465 return boxHint;
468 static const QSize &
469 getParentSize( const QStack<QSize> &parentSizes, int levels )
471 int off = parentSizes.size() - 1 - levels;
472 if (off < 0) {
473 kError() << "Theme references element below the root.";
474 off = 0;
476 return parentSizes[off];
479 void
480 KdmItem::calcSize(
481 const DataPair &sz,
482 QStack<QSize> &parentSizes, QSize &hintedSize, QSize &boxHint,
483 QSize &io )
485 int w, h;
487 if (sz.x.type == DTpixel)
488 w = sz.x.val;
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();
495 else
496 w = ensureHintedSize( hintedSize ).width();
498 if (sz.y.type == DTpixel)
499 h = sz.y.val;
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();
506 else
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();
514 io.setWidth( w );
515 io.setHeight( h );
518 void
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!
538 QRect
539 KdmItem::placementHint( QStack<QSize> &sizes, const QSize &sz, const QPoint &offset )
541 const QSize &parentSize = sizes.top();
542 int x = offset.x(),
543 y = offset.y(),
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)
551 x += geom.pos.x.val;
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)
558 y += geom.pos.y.val;
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)
570 dy = 0;
571 if (geom.anchor.indexOf( 's' ) >= 0)
572 dy = sz.height();
573 if (geom.anchor.indexOf( 'w' ) >= 0)
574 dx = 0;
575 if (geom.anchor.indexOf( 'e' ) >= 0)
576 dx = sz.width();
578 leave() << "x:" << x << "dx:" << dx << "y:" << y << "dy:" << dy;
579 y -= dy;
580 x -= dx;
582 return QRect( x, y, sz.width(), sz.height() );
585 QRect
586 KdmItem::placementHint( QStack<QSize> &sizes, const QPoint &offset )
588 SizeHint sh;
589 sizingHint( sizes, sh );
590 return placementHint( sizes, sh.opt, offset );
593 // END protected inheritable
596 void
597 KdmItem::showStructure( const QString &pfx )
600 QDebug ds( QtDebugMsg );
601 if (!pfx.isEmpty())
602 ds << (qPrintable( pfx ) + 1);
603 ds << objectName() << qPrintable( itemType ) << area;
605 if (!m_children.isEmpty()) {
606 QString npfx( pfx );
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 + " \\-" );
614 void
615 KdmItem::addChildItem( KdmItem *item )
617 m_children.append( item );
618 switch (currentManager) {
619 case MNone: // fallback to the 'fixed' case
620 setFixedLayout();
621 case MFixed:
622 fixedManager->addItem( item );
623 break;
624 case MBox:
625 boxManager->addItem( item );
626 break;
630 void
631 KdmItem::setBoxLayout( const QDomNode &node )
633 if (!boxManager)
634 boxManager = new KdmLayoutBox( node );
635 currentManager = MBox;
638 void
639 KdmItem::setFixedLayout( const QDomNode &node )
641 if (!fixedManager)
642 fixedManager = new KdmLayoutFixed( node );
643 currentManager = MFixed;
646 #include "kdmitem.moc"