add more spacing
[personal-kdebase.git] / workspace / kdm / kfrontend / themer / kdmitem.cpp
blob1537ed757a27972b2ed936c72402355b4d9bd63c
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";
98 geom.expand = 0;
100 // Set defaults for derived item's properties
101 state = Snormal;
103 KdmItem *parentItem = qobject_cast<KdmItem *>( parent );
104 if (!parentItem)
105 style.frame = false;
106 else
107 style = parentItem->style;
109 // Read the mandatory Pos tag. Other tags such as normal, prelighted,
110 // etc.. are read under specific implementations.
111 QDomNodeList childList = node.childNodes();
112 for (int nod = 0; nod < childList.count(); nod++) {
113 QDomNode child = childList.item( nod );
114 QDomElement el = child.toElement();
115 QString tagName = el.tagName();
117 if (tagName == "pos") {
118 parseSize( el.attribute( "x", QString() ), geom.pos.x );
119 parseSize( el.attribute( "y", QString() ), geom.pos.y );
120 parseSize( el.attribute( "width", QString() ), geom.size.x );
121 parseSize( el.attribute( "height", QString() ), geom.size.y );
122 parseSize( el.attribute( "min-width", QString() ), geom.minSize.x );
123 parseSize( el.attribute( "min-height", QString() ), geom.minSize.y );
124 parseSize( el.attribute( "max-width", QString() ), geom.maxSize.x );
125 parseSize( el.attribute( "max-height", QString() ), geom.maxSize.y );
126 geom.anchor = el.attribute( "anchor", "nw" );
127 geom.expand = toBool( el.attribute( "expand", "false" ) );
128 } else if (tagName == "buddy")
129 buddy = el.attribute( "idref", "" );
130 else if (tagName == "style")
131 parseStyle( el, style );
134 if (!style.font.present)
135 parseFont( "Sans 14", style.font );
137 QDomElement el = node.toElement();
138 setObjectName( el.attribute( "id", QString::number( (ulong)this, 16 ) ) );
139 isButton = toBool( el.attribute( "button", "false" ) );
140 isBackground = toBool( el.attribute( "background", "false" ) );
141 QString screen = el.attribute( "screen", isBackground ? "all" : "greeter" );
142 paintOnScreen =
143 screen == "greeter" ? ScrGreeter :
144 screen == "other" ? ScrOther : ScrAll;
146 if (!parentItem)
147 // The "toplevel" node (the screen) is really just like a fixed node
148 setFixedLayout();
149 else
150 // Tell 'parent' to add 'me' to its children
151 parentItem->addChildItem( this );
154 KdmItem::~KdmItem()
156 delete boxManager;
157 delete fixedManager;
160 void
161 KdmItem::update()
163 forEachChild (itm)
164 itm->update();
167 void
168 KdmItem::needUpdate()
170 emit needUpdate( area.x(), area.y(), area.width(), area.height() );
173 void
174 KdmItem::updateThisVisible()
176 bool show = m_shown;
177 if (show && (!m_showType.isNull() || m_minScrWidth || m_minScrHeight)) {
178 KdmThemer *thm = themer();
179 if ((!m_showType.isNull() &&
180 !(thm->typeVisible( m_showType ) ^ m_showTypeInvert)) ||
181 (thm->widget() &&
182 (thm->widget()->width() < m_minScrWidth ||
183 thm->widget()->height() < m_minScrHeight)))
184 show = false;
186 if (m_visible != show) {
187 m_visible = show;
188 emit needPlacement();
192 void
193 KdmItem::setVisible( bool show )
195 m_shown = show;
196 updateThisVisible();
199 void
200 KdmItem::updateVisible()
202 updateThisVisible();
203 forEachChild (itm)
204 itm->updateVisible();
207 KdmThemer *
208 KdmItem::themer()
210 if (KdmThemer *thm = qobject_cast<KdmThemer *>(parent()))
211 return thm;
212 if (KdmItem *parentItem = qobject_cast<KdmItem *>(parent()))
213 return parentItem->themer();
214 return 0;
217 void
218 KdmItem::setWidget( QWidget *widget )
220 if ((myWidget = widget)) {
221 myWidget->hide(); // yes, really
222 connect( myWidget, SIGNAL(destroyed()), SLOT(widgetGone()) );
223 setWidgetAttribs( myWidget );
226 emit needPlugging();
227 emit needPlacement();
230 void
231 KdmItem::widgetGone()
233 myWidget = 0;
235 emit needPlugging();
236 emit needPlacement();
239 void
240 KdmItem::setWidgetAttribs( QWidget *widget )
242 widget->setPalette( style.palette );
243 widget->installEventFilter( this );
244 updatePalette( myWidget );
245 ::setWidgetAttribs( widget, style, style.frame );
248 void
249 KdmItem::updatePalette( QWidget *w )
251 bool set = w->palette().isBrushSet( w->palette().currentColorGroup(),
252 w->backgroundRole() );
253 w->setAutoFillBackground( set );
256 bool
257 KdmItem::eventFilter( QObject *o, QEvent *e )
259 if (e->type() == QEvent::WindowActivate ||
260 e->type() == QEvent::WindowDeactivate ||
261 e->type() == QEvent::EnabledChange)
263 updatePalette( (QWidget *)o );
264 } else if (e->type() == QEvent::ChildAdded) {
265 ::setWidgetAttribs( myWidget, style, style.frame );
267 return false;
270 void
271 KdmItem::showWidget( bool show )
273 if (!isVisible())
274 show = false;
275 if (myWidget) {
276 if (show) {
277 QSize sz( area.size().expandedTo( myWidget->minimumSize() )
278 .boundedTo( myWidget->maximumSize() ) );
279 QSize off( (area.size() - sz) / 2 );
280 myWidget->setGeometry(
281 area.x() + off.width(), area.y() + off.height(),
282 sz.width(), sz.height() );
284 myWidget->setVisible( show );
286 forEachChild (itm)
287 itm->showWidget( show );
290 void
291 KdmItem::plugActions( bool plug )
293 if (myWidget)
294 plug = false;
295 doPlugActions( plug );
296 forEachChild (itm)
297 itm->plugActions( plug );
300 void
301 KdmItem::doPlugActions( bool )
305 /* This is called as a result of KdmLayout::update, and directly on the root */
306 void
307 KdmItem::setGeometry( QStack<QSize> &parentSizes, const QRect &newGeometry, bool force )
309 enter("Item::setGeometry") << objectName() << newGeometry;
310 // check if already 'in place'
311 if (!force && area == newGeometry) {
312 leave() << "unchanged";
313 return;
316 area = newGeometry;
318 // recurr to all boxed children
319 if (boxManager && !boxManager->isEmpty())
320 boxManager->update( parentSizes, newGeometry, force );
322 // recurr to all fixed children
323 if (fixedManager && !fixedManager->isEmpty())
324 fixedManager->update( parentSizes, newGeometry, force );
326 // TODO send *selective* repaint signal
328 leave() << "done";
331 void
332 KdmItem::paint( QPainter *p, const QRect &rect, bool background, bool primaryScreen )
334 if (!isVisible())
335 return;
336 if (background &&
337 (p->device()->width() < m_minScrWidth ||
338 p->device()->height() < m_minScrHeight))
339 return;
341 if (myWidget)
342 return;
344 QRect contentsRect = area.intersected( rect );
345 if (!contentsRect.isEmpty() &&
346 (!background || isBackground) &&
347 (paintOnScreen == ScrAll ||
348 ((paintOnScreen == ScrGreeter) == primaryScreen)))
350 drawContents( p, contentsRect );
351 if (debugLevel & DEBUG_THEMING) {
352 // Draw bounding rect for this item
353 QPen pen( Qt::white );
354 pen.setCapStyle( Qt::FlatCap );
355 pen.setDashPattern( QVector<qreal>() << 5 << 6 );
356 p->setPen( pen );
357 p->setBackgroundMode( Qt::OpaqueMode );
358 p->setBackground( Qt::black );
359 p->drawRect( area.x(), area.y(), area.width() - 1, area.height() - 1 );
360 p->setBackgroundMode( Qt::TransparentMode );
364 // Dispatch paint events to children
365 forEachChild (itm)
366 itm->paint( p, rect, background, primaryScreen );
369 bool
370 KdmItem::childrenContain( int x, int y )
372 forEachVisibleChild (itm) {
373 if (itm->area.contains( x, y ))
374 return true;
375 if (itm->childrenContain( x, y ))
376 return true;
378 return false;
381 void
382 KdmItem::activateBuddy()
384 if (KdmItem *itm = themer()->findNode( buddy ))
385 if (itm->myWidget) {
386 itm->myWidget->setFocus();
387 if (QLineEdit *le = qobject_cast<QLineEdit *>(itm->myWidget))
388 le->selectAll();
392 KdmItem *KdmItem::currentActive = 0;
394 void
395 KdmItem::mouseEvent( int x, int y, bool pressed, bool released )
397 if (!isVisible())
398 return;
400 ItemState oldState = state;
401 if (area.contains( x, y ) || (isButton && childrenContain( x, y ))) {
402 if (released && oldState == Sactive) {
403 if (isButton)
404 emit activated( objectName() );
405 state = Sprelight;
406 currentActive = 0;
407 } else if (pressed && !buddy.isEmpty())
408 activateBuddy();
409 else if (pressed || currentActive == this) {
410 state = Sactive;
411 currentActive = this;
412 } else if (!currentActive)
413 state = Sprelight;
414 else
415 state = Snormal;
416 } else {
417 if (released)
418 currentActive = 0;
419 if (currentActive == this)
420 state = Sprelight;
421 else
422 state = Snormal;
424 if (oldState != state)
425 statusChanged( isButton );
427 if (!isButton)
428 forEachChild (itm)
429 itm->mouseEvent( x, y, pressed, released );
432 void
433 KdmItem::statusChanged( bool descend )
435 if (descend)
436 forEachChild (o) {
437 o->state = state;
438 o->statusChanged( descend );
442 // BEGIN protected inheritable
444 QSize
445 KdmItem::sizeHint()
447 if (myWidget)
448 return myWidget->sizeHint();
449 return QSize(
450 geom.size.x.type == DTpixel ? geom.size.x.val : 0,
451 geom.size.y.type == DTpixel ? geom.size.y.val : 0 );
454 const QSize &
455 KdmItem::ensureHintedSize( QSize &hintedSize )
457 if (!hintedSize.isValid()) {
458 hintedSize = sizeHint();
459 debug() << "hinted" << hintedSize;
461 return hintedSize;
464 const QSize &
465 KdmItem::ensureBoxHint( QSize &boxHint, QStack<QSize> &parentSizes, QSize &hintedSize )
467 if (!boxHint.isValid()) {
468 if (myWidget || !boxManager)
469 boxHint = ensureHintedSize( hintedSize );
470 else
471 boxHint = boxManager->sizeHint( parentSizes );
472 debug() << "boxHint" << boxHint;
474 return boxHint;
477 static const QSize &
478 getParentSize( const QStack<QSize> &parentSizes, int levels )
480 int off = parentSizes.size() - 1 - levels;
481 if (off < 0) {
482 kError() << "Theme references element below the root.";
483 off = 0;
485 return parentSizes[off];
488 void
489 KdmItem::calcSize(
490 const DataPair &sz,
491 QStack<QSize> &parentSizes, QSize &hintedSize, QSize &boxHint,
492 QSize &io )
494 int w, h;
496 if (sz.x.type == DTpixel)
497 w = sz.x.val;
498 else if (sz.x.type == DTnpixel)
499 w = io.width() - sz.x.val;
500 else if (sz.x.type == DTpercent)
501 w = (getParentSize( parentSizes, sz.x.levels ).width() * sz.x.val + 50) / 100;
502 else if (sz.x.type == DTbox)
503 w = ensureBoxHint( boxHint, parentSizes, hintedSize ).width();
504 else
505 w = ensureHintedSize( hintedSize ).width();
507 if (sz.y.type == DTpixel)
508 h = sz.y.val;
509 else if (sz.y.type == DTnpixel)
510 h = io.height() - sz.y.val;
511 else if (sz.y.type == DTpercent)
512 h = (getParentSize( parentSizes, sz.y.levels ).height() * sz.y.val + 50) / 100;
513 else if (sz.y.type == DTbox)
514 h = ensureBoxHint( boxHint, parentSizes, hintedSize ).height();
515 else
516 h = ensureHintedSize( hintedSize ).height();
518 if (sz.x.type == DTscale && h && ensureHintedSize( hintedSize ).height())
519 w = w * h / hintedSize.height();
520 else if (sz.y.type == DTscale && w && ensureHintedSize( hintedSize ).width())
521 h = w * h / hintedSize.width();
523 io.setWidth( w );
524 io.setHeight( h );
527 void
528 KdmItem::sizingHint( QStack<QSize> &parentSizes, SizeHint &hint )
530 enter("Item::sizingHint") << objectName() << NoSpace << "parentSize #"
531 << parentSizes.size() << Space << parentSizes.top();
533 QSize hintedSize, boxHint;
534 hint.min = hint.opt = hint.max = parentSizes.top();
535 calcSize( geom.size, parentSizes, hintedSize, boxHint, hint.opt );
536 calcSize( geom.minSize, parentSizes, hintedSize, boxHint, hint.min );
537 calcSize( geom.maxSize, parentSizes, hintedSize, boxHint, hint.max );
539 leave() << "size" << hint.opt << "min" << hint.min << "max" << hint.max;
541 hint.max = hint.max.expandedTo( hint.min ); // if this triggers, the theme is bust
542 hint.opt = hint.opt.boundedTo( hint.max ).expandedTo( hint.min );
544 // Note: no clipping to parent because this broke many themes!
547 QRect
548 KdmItem::placementHint( QStack<QSize> &sizes, const QSize &sz, const QPoint &offset )
550 const QSize &parentSize = sizes.top();
551 int x = offset.x(),
552 y = offset.y(),
553 w = parentSize.width(),
554 h = parentSize.height();
556 enter("Item::placementHint") << objectName() << NoSpace << "parentSize #"
557 << sizes.size() << Space << parentSize << "size" << sz << "offset" << offset;
559 if (geom.pos.x.type == DTpixel)
560 x += geom.pos.x.val;
561 else if (geom.pos.x.type == DTnpixel)
562 x += w - geom.pos.x.val;
563 else if (geom.pos.x.type == DTpercent)
564 x += (w * geom.pos.x.val + 50) / 100;
566 if (geom.pos.y.type == DTpixel)
567 y += geom.pos.y.val;
568 else if (geom.pos.y.type == DTnpixel)
569 y += h - geom.pos.y.val;
570 else if (geom.pos.y.type == DTpercent)
571 y += (h * geom.pos.y.val + 50) / 100;
573 // defaults to center
574 int dx = sz.width() / 2, dy = sz.height() / 2;
576 // anchor the rect to an edge / corner
577 if (geom.anchor.length() > 0 && geom.anchor.length() < 3) {
578 if (geom.anchor.indexOf( 'n' ) >= 0)
579 dy = 0;
580 if (geom.anchor.indexOf( 's' ) >= 0)
581 dy = sz.height();
582 if (geom.anchor.indexOf( 'w' ) >= 0)
583 dx = 0;
584 if (geom.anchor.indexOf( 'e' ) >= 0)
585 dx = sz.width();
587 leave() << "x:" << x << "dx:" << dx << "y:" << y << "dy:" << dy;
588 y -= dy;
589 x -= dx;
591 return QRect( x, y, sz.width(), sz.height() );
594 QRect
595 KdmItem::placementHint( QStack<QSize> &sizes, const QPoint &offset )
597 SizeHint sh;
598 sizingHint( sizes, sh );
599 return placementHint( sizes, sh.opt, offset );
602 // END protected inheritable
605 void
606 KdmItem::showStructure( const QString &pfx )
609 QDebug ds( QtDebugMsg );
610 if (!pfx.isEmpty())
611 ds << (qPrintable( pfx ) + 1);
612 ds << objectName() << qPrintable( itemType ) << area;
614 if (!m_children.isEmpty()) {
615 QString npfx( pfx );
616 npfx.replace( '\\', ' ' ).replace( '-', ' ' );
617 for (int i = 0; i < m_children.count() - 1; i++)
618 m_children[i]->showStructure( npfx + " |-" );
619 m_children[m_children.count() - 1]->showStructure( npfx + " \\-" );
623 void
624 KdmItem::addChildItem( KdmItem *item )
626 m_children.append( item );
627 switch (currentManager) {
628 case MNone: // fallback to the 'fixed' case
629 setFixedLayout();
630 case MFixed:
631 fixedManager->addItem( item );
632 break;
633 case MBox:
634 boxManager->addItem( item );
635 break;
639 void
640 KdmItem::setBoxLayout( const QDomNode &node )
642 if (!boxManager)
643 boxManager = new KdmLayoutBox( node );
644 currentManager = MBox;
647 void
648 KdmItem::setFixedLayout( const QDomNode &node )
650 if (!fixedManager)
651 fixedManager = new KdmLayoutFixed( node );
652 currentManager = MFixed;
655 #include "kdmitem.moc"