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;
100 // Set defaults for derived item's properties
103 KdmItem
*parentItem
= qobject_cast
<KdmItem
*>( parent
);
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" );
143 screen
== "greeter" ? ScrGreeter
:
144 screen
== "other" ? ScrOther
: ScrAll
;
147 // The "toplevel" node (the screen) is really just like a fixed node
150 // Tell 'parent' to add 'me' to its children
151 parentItem
->addChildItem( this );
168 KdmItem::needUpdate()
170 emit
needUpdate( area
.x(), area
.y(), area
.width(), area
.height() );
174 KdmItem::updateThisVisible()
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
)) ||
182 (thm
->widget()->width() < m_minScrWidth
||
183 thm
->widget()->height() < m_minScrHeight
)))
186 if (m_visible
!= show
) {
188 emit
needPlacement();
193 KdmItem::setVisible( bool show
)
200 KdmItem::updateVisible()
204 itm
->updateVisible();
210 if (KdmThemer
*thm
= qobject_cast
<KdmThemer
*>(parent()))
212 if (KdmItem
*parentItem
= qobject_cast
<KdmItem
*>(parent()))
213 return parentItem
->themer();
218 KdmItem::setWidget( QWidget
*widget
)
220 if ((myWidget
= widget
)) {
221 myWidget
->hide(); // yes, really
222 connect( myWidget
, SIGNAL(destroyed()), SLOT(widgetGone()) );
223 setWidgetAttribs( myWidget
);
227 emit
needPlacement();
231 KdmItem::widgetGone()
236 emit
needPlacement();
240 KdmItem::setWidgetAttribs( QWidget
*widget
)
242 widget
->setPalette( style
.palette
);
243 widget
->installEventFilter( this );
244 updatePalette( myWidget
);
245 ::setWidgetAttribs( widget
, style
, style
.frame
);
249 KdmItem::updatePalette( QWidget
*w
)
251 bool set
= w
->palette().isBrushSet( w
->palette().currentColorGroup(),
252 w
->backgroundRole() );
253 w
->setAutoFillBackground( set
);
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
);
271 KdmItem::showWidget( bool 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
);
287 itm
->showWidget( show
);
291 KdmItem::plugActions( bool plug
)
295 doPlugActions( plug
);
297 itm
->plugActions( plug
);
301 KdmItem::doPlugActions( bool )
305 /* This is called as a result of KdmLayout::update, and directly on the root */
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";
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
332 KdmItem::paint( QPainter
*p
, const QRect
&rect
, bool background
, bool primaryScreen
)
337 (p
->device()->width() < m_minScrWidth
||
338 p
->device()->height() < m_minScrHeight
))
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 );
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
366 itm
->paint( p
, rect
, background
, primaryScreen
);
370 KdmItem::childrenContain( int x
, int y
)
372 forEachVisibleChild (itm
) {
373 if (itm
->area
.contains( x
, y
))
375 if (itm
->childrenContain( x
, y
))
382 KdmItem::activateBuddy()
384 if (KdmItem
*itm
= themer()->findNode( buddy
))
386 itm
->myWidget
->setFocus();
387 if (QLineEdit
*le
= qobject_cast
<QLineEdit
*>(itm
->myWidget
))
392 KdmItem
*KdmItem::currentActive
= 0;
395 KdmItem::mouseEvent( int x
, int y
, bool pressed
, bool released
)
400 ItemState oldState
= state
;
401 if (area
.contains( x
, y
) || (isButton
&& childrenContain( x
, y
))) {
402 if (released
&& oldState
== Sactive
) {
404 emit
activated( objectName() );
407 } else if (pressed
&& !buddy
.isEmpty())
409 else if (pressed
|| currentActive
== this) {
411 currentActive
= this;
412 } else if (!currentActive
)
419 if (currentActive
== this)
424 if (oldState
!= state
)
425 statusChanged( isButton
);
429 itm
->mouseEvent( x
, y
, pressed
, released
);
433 KdmItem::statusChanged( bool descend
)
438 o
->statusChanged( descend
);
442 // BEGIN protected inheritable
448 return myWidget
->sizeHint();
450 geom
.size
.x
.type
== DTpixel
? geom
.size
.x
.val
: 0,
451 geom
.size
.y
.type
== DTpixel
? geom
.size
.y
.val
: 0 );
455 KdmItem::ensureHintedSize( QSize
&hintedSize
)
457 if (!hintedSize
.isValid()) {
458 hintedSize
= sizeHint();
459 debug() << "hinted" << hintedSize
;
465 KdmItem::ensureBoxHint( QSize
&boxHint
, QStack
<QSize
> &parentSizes
, QSize
&hintedSize
)
467 if (!boxHint
.isValid()) {
468 if (myWidget
|| !boxManager
)
469 boxHint
= ensureHintedSize( hintedSize
);
471 boxHint
= boxManager
->sizeHint( parentSizes
);
472 debug() << "boxHint" << boxHint
;
478 getParentSize( const QStack
<QSize
> &parentSizes
, int levels
)
480 int off
= parentSizes
.size() - 1 - levels
;
482 kError() << "Theme references element below the root.";
485 return parentSizes
[off
];
491 QStack
<QSize
> &parentSizes
, QSize
&hintedSize
, QSize
&boxHint
,
496 if (sz
.x
.type
== DTpixel
)
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();
505 w
= ensureHintedSize( hintedSize
).width();
507 if (sz
.y
.type
== DTpixel
)
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();
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();
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!
548 KdmItem::placementHint( QStack
<QSize
> &sizes
, const QSize
&sz
, const QPoint
&offset
)
550 const QSize
&parentSize
= sizes
.top();
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
)
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
)
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)
580 if (geom
.anchor
.indexOf( 's' ) >= 0)
582 if (geom
.anchor
.indexOf( 'w' ) >= 0)
584 if (geom
.anchor
.indexOf( 'e' ) >= 0)
587 leave() << "x:" << x
<< "dx:" << dx
<< "y:" << y
<< "dy:" << dy
;
591 return QRect( x
, y
, sz
.width(), sz
.height() );
595 KdmItem::placementHint( QStack
<QSize
> &sizes
, const QPoint
&offset
)
598 sizingHint( sizes
, sh
);
599 return placementHint( sizes
, sh
.opt
, offset
);
602 // END protected inheritable
606 KdmItem::showStructure( const QString
&pfx
)
609 QDebug
ds( QtDebugMsg
);
611 ds
<< (qPrintable( pfx
) + 1);
612 ds
<< objectName() << qPrintable( itemType
) << area
;
614 if (!m_children
.isEmpty()) {
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
+ " \\-" );
624 KdmItem::addChildItem( KdmItem
*item
)
626 m_children
.append( item
);
627 switch (currentManager
) {
628 case MNone
: // fallback to the 'fixed' case
631 fixedManager
->addItem( item
);
634 boxManager
->addItem( item
);
640 KdmItem::setBoxLayout( const QDomNode
&node
)
643 boxManager
= new KdmLayoutBox( node
);
644 currentManager
= MBox
;
648 KdmItem::setFixedLayout( const QDomNode
&node
)
651 fixedManager
= new KdmLayoutFixed( node
);
652 currentManager
= MFixed
;
655 #include "kdmitem.moc"