2 * Copyright 2007 by Matt Broadstone <mbroadst@kde.org>
3 * Copyright 2007 by Robert Knight <robertknight@gmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Library General Public License version 2,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details
14 * You should have received a copy of the GNU Library General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "panelview.h"
22 #include <QApplication>
23 #include <QDesktopWidget>
24 #include <QGraphicsLinearLayout>
28 #include <X11/Xatom.h>
32 #include <KWindowSystem>
35 #include <Plasma/Containment>
36 #include <Plasma/Corona>
37 #include <Plasma/Plasma>
38 #include <Plasma/PopupApplet>
40 #include <Plasma/Theme>
42 #include "panelappletoverlay.h"
43 #include "panelcontroller.h"
44 #include "plasmaapp.h"
46 #include <kephal/screens.h>
48 class GlowBar
: public QWidget
51 GlowBar(Plasma::Direction direction
, const QRect
&triggerZone
)
54 m_svg(new Plasma::Svg(this)),
55 m_direction(direction
)
57 KWindowSystem::setOnAllDesktops(winId(), true);
58 unsigned long state
= NET::Sticky
| NET::StaysOnTop
| NET::KeepAbove
;
59 KWindowSystem::setState(winId(), state
);
60 KWindowSystem::setType(winId(), NET::Dock
);
61 m_svg
->setImagePath("widgets/glowbar");
63 QRect glowGeom
= triggerZone
;
65 switch (m_direction
) {
67 glowGeom
.setY(glowGeom
.y() - s
.height() + 1);
70 glowGeom
.setHeight(s
.height());
73 glowGeom
.setX(glowGeom
.x() - s
.width() + 1);
76 glowGeom
.setWidth(s
.width());
80 //kDebug() << "glow geom is" << glowGeom << "from" << triggerZone;
81 setGeometry(glowGeom
);
82 m_buffer
= QPixmap(size());
85 void paintEvent(QPaintEvent
* e
)
89 const QSize glowRadius
= m_svg
->elementSize("hint-glow-radius");
90 QPoint
pixmapPosition(0, 0);
92 m_buffer
.fill(QColor(0, 0, 0, int(qreal(255)*m_strength
)));
93 QPainter
p(&m_buffer
);
94 p
.setCompositionMode(QPainter::CompositionMode_SourceIn
);
96 switch (m_direction
) {
98 l
= m_svg
->pixmap("bottomleft");
99 r
= m_svg
->pixmap("bottomright");
100 c
= m_svg
->pixmap("bottom");
101 pixmapPosition
= QPoint(0, -glowRadius
.height());
104 l
= m_svg
->pixmap("topleft");
105 r
= m_svg
->pixmap("topright");
106 c
= m_svg
->pixmap("top");
109 l
= m_svg
->pixmap("topright");
110 r
= m_svg
->pixmap("bottomright");
111 c
= m_svg
->pixmap("right");
112 pixmapPosition
= QPoint(-glowRadius
.width(), 0);
115 l
= m_svg
->pixmap("topleft");
116 r
= m_svg
->pixmap("bottomleft");
117 c
= m_svg
->pixmap("left");
121 if (m_direction
== Plasma::Left
|| m_direction
== Plasma::Right
) {
122 p
.drawPixmap(QPoint(0, 0), l
);
123 p
.drawTiledPixmap(QRect(0, l
.height(), c
.width(), height() - l
.height() - r
.height()), c
);
124 p
.drawPixmap(QPoint(0, height() - r
.height()), r
);
126 p
.drawPixmap(pixmapPosition
, l
);
127 p
.drawTiledPixmap(QRect(l
.width(), pixmapPosition
.y(), width() - l
.width() - r
.width(), c
.height()), c
);
128 p
.drawPixmap(QPoint(width() - r
.width(), pixmapPosition
.y()), r
);
133 p
.drawPixmap(QPoint(0, 0), m_buffer
);
136 QSize
sizeHint() const
138 return m_svg
->elementSize("bottomright") - m_svg
->elementSize("hint-glow-radius");
141 bool event(QEvent
*event
)
143 if (event
->type() == QEvent::Paint
) {
145 p
.setCompositionMode(QPainter::CompositionMode_Source
);
146 p
.fillRect(rect(), Qt::transparent
);
148 return QWidget::event(event
);
151 void updateStrength(QPoint point
)
153 QPoint localPoint
= mapFromGlobal(point
);
156 switch (m_direction
) {
157 case Plasma::Up
: // when the panel is at the bottom.
158 newStrength
= 1 - qreal(-localPoint
.y())/m_triggerDistance
;
161 newStrength
= 1 - qreal(localPoint
.x())/m_triggerDistance
;
163 case Plasma::Left
: // when the panel is right-aligned
164 newStrength
= 1 - qreal(-localPoint
.x())/m_triggerDistance
;
168 newStrength
= 1- qreal(localPoint
.y())/m_triggerDistance
;
171 if (qAbs(newStrength
- m_strength
) > 0.01 && newStrength
>= 0 && newStrength
<= 1) {
172 m_strength
= newStrength
;
179 static const int m_triggerDistance
= 30;
182 Plasma::Direction m_direction
;
186 PanelView::PanelView(Plasma::Containment
*panel
, int id
, QWidget
*parent
)
187 : Plasma::View(panel
, id
, parent
),
188 m_panelController(0),
195 m_unhideTrigger(None
),
197 m_visibilityMode(NormalPanel
),
198 m_lastHorizontal(true),
202 m_triggerEntered(false)
204 Q_ASSERT(qobject_cast
<Plasma::Corona
*>(panel
->scene()));
206 connect(panel
, SIGNAL(destroyed(QObject
*)), this, SLOT(panelDeleted()));
207 connect(panel
, SIGNAL(toolBoxToggled()), this, SLOT(togglePanelController()));
208 kDebug() << "Panel geometry is" << panel
->geometry();
211 connect(this, SIGNAL(sceneRectAboutToChange()), this, SLOT(updatePanelGeometry()));
213 // Graphics view setup
214 setFrameStyle(QFrame::NoFrame
);
215 //setAutoFillBackground(true);
216 //setDragMode(QGraphicsView::RubberBandDrag);
217 //setCacheMode(QGraphicsView::CacheBackground);
218 setInteractive(true);
219 setAcceptDrops(true);
220 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
221 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
223 QPalette pal
= palette();
224 pal
.setBrush(backgroundRole(), Qt::transparent
);
228 registerAccessBar(true);
231 KConfigGroup viewConfig
= config();
232 KConfigGroup sizes
= KConfigGroup(&viewConfig
, "Sizes");
233 QRect screenRect
= Kephal::ScreenUtils::screenGeometry(containment()->screen());
234 m_lastSeenSize
= sizes
.readEntry("lastsize", m_lastHorizontal
? screenRect
.width() : screenRect
.height());
235 m_alignment
= alignmentFilter((Qt::Alignment
)viewConfig
.readEntry("Alignment", (int)Qt::AlignLeft
));
236 m_offset
= viewConfig
.readEntry("Offset", 0);
237 m_lastHorizontal
= isHorizontal();
239 KWindowSystem::setType(winId(), NET::Dock
);
241 // pinchContainment calls updatePanelGeometry for us
242 pinchContainment(screenRect
);
243 m_lastMin
= containment()->minimumSize();
244 m_lastMax
= containment()->maximumSize();
247 KWindowSystem::setOnAllDesktops(winId(), true);
249 QTimer::singleShot(0, this, SLOT(init()));
252 PanelView::~PanelView()
255 destroyUnhideTrigger();
257 registerAccessBar(false);
261 void PanelView::init()
263 KConfigGroup viewConfig
= config();
264 setVisibilityMode((VisibilityMode
)viewConfig
.readEntry("panelVisibility", (int)m_visibilityMode
));
269 Kephal::Screens
*screens
= Kephal::Screens::self();
270 connect(screens
, SIGNAL(screenResized(Kephal::Screen
*, QSize
, QSize
)),
271 this, SLOT(pinchContainmentToCurrentScreen()));
272 connect(screens
, SIGNAL(screenMoved(Kephal::Screen
*, QPoint
, QPoint
)),
273 this, SLOT(updatePanelGeometry()));
276 void PanelView::setLocation(Plasma::Location location
)
278 Plasma::Containment
*c
= containment();
279 QSizeF s
= c
->size();
280 QSizeF min
= c
->minimumSize();
281 QSizeF max
= c
->maximumSize();
282 qreal panelWidth
= s
.width();
283 qreal panelHeight
= s
.height();
285 Plasma::FormFactor formFactor
= c
->formFactor();
286 bool wasHorizontal
= formFactor
== Plasma::Horizontal
;
287 bool wasFullSize
= m_lastSeenSize
== (wasHorizontal
? s
.width() : s
.height());
289 if (location
== Plasma::TopEdge
|| location
== Plasma::BottomEdge
) {
290 if (!wasHorizontal
) {
291 // we're switching! swap the sizes about
292 panelHeight
= s
.width();
294 QRect screenGeom
= Kephal::ScreenUtils::screenGeometry(c
->screen());
295 panelWidth
= screenGeom
.width();
297 panelWidth
= s
.height();
299 min
= QSizeF(panelWidth
, min
.width());
300 max
= QSizeF(panelWidth
, max
.width());
303 formFactor
= Plasma::Horizontal
;
306 // we're switching! swap the sizes about
309 QRect screenGeom
= Kephal::ScreenUtils::screenGeometry(c
->screen());
310 panelHeight
= screenGeom
.height();
312 panelHeight
= s
.width();
315 panelWidth
= s
.height();
316 min
= QSizeF(min
.height(), panelHeight
);
317 max
= QSizeF(max
.height(), panelHeight
);
320 formFactor
= Plasma::Vertical
;
323 //kDebug() << "!!!!!!!!!!!!!!!!!! about to set to" << location << panelHeight << formFactor;
324 disconnect(this, SIGNAL(sceneRectAboutToChange()), this, SLOT(updatePanelGeometry()));
325 c
->setFormFactor(formFactor
);
326 c
->setLocation(location
);
328 c
->setMinimumSize(0, 0);
329 c
->setMaximumSize(QWIDGETSIZE_MAX
, QWIDGETSIZE_MAX
);
330 c
->resize(panelWidth
, panelHeight
);
331 c
->setMinimumSize(min
);
332 c
->setMaximumSize(max
);
336 QRect screenRect
= Kephal::ScreenUtils::screenGeometry(c
->screen());
337 pinchContainment(screenRect
);
338 KWindowSystem::setOnAllDesktops(winId(), true);
339 //updatePanelGeometry();
340 connect(this, SIGNAL(sceneRectAboutToChange()), this, SLOT(updatePanelGeometry()));
343 Plasma::Location
PanelView::location() const
345 return containment()->location();
348 void PanelView::setVisibilityMode(PanelView::VisibilityMode mode
)
350 unsigned long state
= NET::Sticky
;
352 delete m_mousePollTimer
;
353 m_mousePollTimer
= 0;
357 if (mode
== LetWindowsCover
) {
358 createUnhideTrigger();
359 KWindowSystem::clearState(winId(), NET::StaysOnTop
| NET::KeepAbove
);
360 state
|= NET::KeepBelow
;
362 //kDebug() << "panel shouldn't let windows cover it!";
363 state
|= NET::StaysOnTop
;
366 if (mode
== NormalPanel
) {
367 // we need to kill the input window if it exists!
368 destroyUnhideTrigger();
371 if (mode
!= AutoHide
) {
372 updatePanelGeometry();
376 //kDebug() << "panel state set to" << state << NET::Sticky;
377 KWindowSystem::setState(winId(), state
);
378 KWindowSystem::setOnAllDesktops(winId(), true);
380 m_visibilityMode
= mode
;
381 config().writeEntry("panelVisibility", (int)mode
);
384 PanelView::VisibilityMode
PanelView::visibilityMode() const
386 return m_visibilityMode
;
389 Plasma::Corona
*PanelView::corona() const
391 return qobject_cast
<Plasma::Corona
*>(scene());
394 void PanelView::updatePanelGeometry()
396 Plasma::Containment
*c
= containment();
397 kDebug() << "New panel geometry is" << c
->geometry();
399 QSize size
= c
->size().toSize();
400 QRect
geom(QPoint(0,0), size
);
401 int screen
= c
->screen();
404 //TODO: is there a valid use for -1 with a panel? floating maybe?
408 QRect screenGeom
= Kephal::ScreenUtils::screenGeometry(screen
);
410 if (m_alignment
!= Qt::AlignCenter
) {
411 m_offset
= qMax(m_offset
, 0);
415 switch (location()) {
416 case Plasma::TopEdge
:
417 case Plasma::BottomEdge
:
418 if (m_alignment
!= Qt::AlignCenter
) {
419 m_offset
= qMax(m_offset
, 0);
421 //resize the panel if is too large
422 if (geom
.width() > screenGeom
.width()) {
423 geom
.setWidth(screenGeom
.width());
426 //move the panel left/right if there is not enough room
427 if (m_alignment
== Qt::AlignLeft
) {
428 if (m_offset
+ geom
.width() > screenGeom
.width() + 1) {
429 m_offset
= screenGeom
.width() - geom
.width();
431 } else if (m_alignment
== Qt::AlignRight
) {
432 if (screenGeom
.width() - m_offset
- geom
.width() < -1 ) {
433 m_offset
= screenGeom
.width() - geom
.width();
435 } else if (m_alignment
== Qt::AlignCenter
) {
436 if (screenGeom
.center().x() - screenGeom
.x() + m_offset
+ geom
.width()/2 > screenGeom
.width() + 1) {
437 m_offset
= screenGeom
.width() - geom
.width()/2 - (screenGeom
.center().x() - screenGeom
.x());
438 } else if (screenGeom
.center().x() - screenGeom
.x() + m_offset
- geom
.width()/2 < -1) {
439 m_offset
= (screenGeom
.center().x() - screenGeom
.x()) - geom
.width()/2;
444 case Plasma::LeftEdge
:
445 case Plasma::RightEdge
:
446 if (m_alignment
!= Qt::AlignCenter
) {
447 m_offset
= qMax(m_offset
, 0);
449 //resize the panel if is too tall
450 if (geom
.height() > screenGeom
.height()) {
451 geom
.setHeight(screenGeom
.height());
454 //move the panel bottom if there is not enough room
455 //FIXME: still using alignleft/alignright is simpler and less error prone, but aligntop/alignbottom is more correct?
456 if (m_alignment
== Qt::AlignLeft
) {
457 if (m_offset
+ geom
.height() > screenGeom
.height() + 1) {
458 m_offset
= screenGeom
.height() - geom
.height();
460 } else if (m_alignment
== Qt::AlignRight
) {
461 if (screenGeom
.height() - m_offset
- geom
.height() < -1) {
462 m_offset
= screenGeom
.height() - geom
.height();
464 } else if (m_alignment
== Qt::AlignCenter
) {
465 if (screenGeom
.center().y() - screenGeom
.top() + m_offset
+ geom
.height()/2 > screenGeom
.height() + 1) {
466 m_offset
= screenGeom
.height() - geom
.height()/2 - (screenGeom
.center().y() - screenGeom
.top());
467 } else if (screenGeom
.center().y() - screenGeom
.top() + m_offset
- geom
.height()/2 < -1) {
468 m_offset
= (screenGeom
.center().y() - screenGeom
.top()) - geom
.width()/2;
473 //TODO: floating panels (probably they will save their own geometry)
479 switch (location()) {
480 case Plasma::TopEdge
:
481 if (m_alignment
== Qt::AlignLeft
) {
482 geom
.moveTopLeft(QPoint(m_offset
+ screenGeom
.left(), screenGeom
.top()));
483 } else if (m_alignment
== Qt::AlignRight
) {
484 geom
.moveTopRight(QPoint(screenGeom
.right() - m_offset
, screenGeom
.top()));
485 } else if (m_alignment
== Qt::AlignCenter
) {
486 geom
.moveTopLeft(QPoint(screenGeom
.center().x() - geom
.width()/2 + 1 - geom
.width()%2 + m_offset
, screenGeom
.top()));
489 //enable borders if needed
490 //c->setGeometry(QRect(geom.left(), c->geometry().top(), geom.width(), geom.height()));
493 case Plasma::LeftEdge
:
494 if (m_alignment
== Qt::AlignLeft
) {
495 geom
.moveTopLeft(QPoint(screenGeom
.left(), m_offset
+ screenGeom
.top()));
496 } else if (m_alignment
== Qt::AlignRight
) {
497 geom
.moveBottomLeft(QPoint(screenGeom
.left(), screenGeom
.bottom() - m_offset
));
498 } else if (m_alignment
== Qt::AlignCenter
) {
499 geom
.moveTopLeft(QPoint(screenGeom
.left(), screenGeom
.center().y() - geom
.height()/2 + 1 - geom
.height()%2 + m_offset
));
502 //enable borders if needed
503 //c->setGeometry(QRect(c->geometry().left(), geom.top(), geom.width(), geom.height()));
506 case Plasma::RightEdge
:
507 if (m_alignment
== Qt::AlignLeft
) {
508 geom
.moveTopRight(QPoint(screenGeom
.right(), m_offset
+ screenGeom
.top()));
509 } else if (m_alignment
== Qt::AlignRight
) {
510 geom
.moveBottomRight(QPoint(screenGeom
.right(), screenGeom
.bottom() - m_offset
));
511 } else if (m_alignment
== Qt::AlignCenter
) {
512 geom
.moveTopRight(QPoint(screenGeom
.right(), screenGeom
.center().y() - geom
.height()/2 + 1 - geom
.height()%2 + m_offset
));
515 //enable borders if needed
516 //c->setGeometry(QRect(c->geometry().left(), geom.top(), geom.width(), geom.height()));
519 case Plasma::BottomEdge
:
521 if (m_alignment
== Qt::AlignLeft
) {
522 geom
.moveBottomLeft(QPoint(m_offset
+ screenGeom
.left(), screenGeom
.bottom()));
523 } else if (m_alignment
== Qt::AlignRight
) {
524 geom
.moveBottomRight(QPoint(screenGeom
.right() - m_offset
, screenGeom
.bottom()));
525 } else if (m_alignment
== Qt::AlignCenter
) {
526 geom
.moveBottomLeft(QPoint(screenGeom
.center().x() - geom
.width()/2 + 1 - geom
.width()%2 + m_offset
, screenGeom
.bottom()));
529 //enable borders if needed
530 //c->setGeometry(QRect(geom.left(), c->geometry().top(), geom.width(), geom.height()));
534 kDebug() << (QObject
*)this << "thinks its panel is at " << geom
<< "was" << geometry();
535 if (geom
== geometry()) {
536 // our geometry is the same, but the panel moved around
537 // so make sure our struts are still valid
543 m_lastMin
= c
->minimumSize();
544 m_lastMax
= c
->maximumSize();
546 //update the panel controller location position and size
547 if (m_panelController
) {
548 m_panelController
->setLocation(c
->location());
550 if (m_panelController
->isVisible()) {
551 m_panelController
->resize(m_panelController
->sizeHint());
552 m_panelController
->move(m_panelController
->positionForPanelGeometry(geometry()));
555 foreach (PanelAppletOverlay
*o
, m_moveOverlays
) {
556 o
->syncOrientation();
561 bool PanelView::isHorizontal() const
563 return location() == Plasma::BottomEdge
||
564 location() == Plasma::TopEdge
;
567 void PanelView::pinchContainmentToCurrentScreen()
569 QRect screenRect
= Kephal::ScreenUtils::screenGeometry(containment()->screen());
570 pinchContainment(screenRect
);
573 void PanelView::pinchContainment(const QRect
&screenGeom
)
575 kDebug() << "**************************** pinching" << screenGeom
<< m_lastSeenSize
;
576 bool horizontal
= isHorizontal();
578 int sw
= screenGeom
.width();
579 int sh
= screenGeom
.height();
581 Plasma::Containment
*c
= containment();
582 QSizeF min
= c
->minimumSize();
583 QSizeF max
= c
->maximumSize();
585 KConfigGroup sizes
= config();
586 sizes
= KConfigGroup(&sizes
, "Sizes");
588 if (m_lastHorizontal
!= horizontal
||
589 m_lastSeenSize
!= (horizontal
? sw
: sh
)) {
590 // we're adjusting size. store the current size now
591 KConfigGroup
lastSize(&sizes
, (m_lastHorizontal
? "Horizontal" : "Vertical") +
592 QString::number(m_lastSeenSize
));
593 lastSize
.writeEntry("size", size());
594 lastSize
.writeEntry("offset", m_offset
);
595 lastSize
.writeEntry("min", m_lastMin
);
596 lastSize
.writeEntry("max", m_lastMax
);
598 QString last
= (horizontal
? "Horizontal" : "Vertical") +
599 QString::number(horizontal
? sw
: sh
);
600 if (sizes
.hasGroup(last
)) {
601 KConfigGroup
thisSize(&sizes
, last
);
604 kDebug() << "has saved properties..." << last
605 << thisSize.readEntry("min", min)
606 << thisSize.readEntry("max", max)
607 << thisSize.readEntry("size", c->geometry().size())
608 << thisSize.readEntry("offset", 0);
610 c
->setMinimumSize(0, 0);
611 c
->setMaximumSize(QWIDGETSIZE_MAX
, QWIDGETSIZE_MAX
);
612 c
->resize(thisSize
.readEntry("size", c
->geometry().size()));
613 c
->setMinimumSize(thisSize
.readEntry("min", min
));
614 c
->setMaximumSize(thisSize
.readEntry("max", max
));
615 m_offset
= thisSize
.readEntry("offset", 0);
616 } else if (m_lastSeenSize
< (horizontal
? sw
: sh
) &&
617 (horizontal
? c
->geometry().width() :
618 c
->geometry().height()) >= m_lastSeenSize
) {
619 // we are moving from a smaller space where we are 100% to a larger one
621 c
->setMaximumSize(sw
, max
.height());
622 c
->resize(sw
, c
->geometry().height());
624 c
->setMaximumSize(max
.width(), sh
);
625 c
->resize(c
->geometry().width(), sh
);
630 // Pinching strategy:
631 // if our containment is too big for the size of the screen we are now on,
632 // then we first try and limit the offset and then if that still doesn't
633 // give us enough room, we limit the size of the panel itself by setting
634 // the minimum and maximum sizes.
636 //kDebug() << "checking panel" << c->geometry() << "against" << screenGeom;
638 //kDebug() << "becoming horizontal with" << m_offset << min.width() << max.width() << sw;
639 if (m_offset
+ min
.width() > sw
) {
640 //kDebug() << "min size is too wide!";
641 if (min
.width() > sw
) {
642 c
->setMinimumSize(sw
, min
.height());
644 m_offset
= sw
- int(min
.width());
648 if (m_offset
+ max
.width() > sw
) {
649 //kDebug() << "max size is too wide!";
650 if (max
.width() > sw
) {
651 c
->setMaximumSize(sw
, max
.height());
653 m_offset
= sw
- int(max
.width());
657 if (m_offset
+ min
.height() > sh
) {
658 //kDebug() << "min size is too tall!";
659 if (min
.height() > sh
) {
660 c
->setMinimumSize(min
.width(), sh
);
662 m_offset
= sh
- int(min
.height());
666 if (m_offset
+ max
.height() > sh
) {
667 //kDebug() << "max size is too tall!";
668 if (max
.height() > sh
) {
669 c
->setMaximumSize(max
.width(), sh
);
671 m_offset
= sh
- int(max
.height());
676 if (m_lastHorizontal
!= horizontal
||
677 m_lastSeenSize
!= (horizontal
? sw
: sh
)) {
678 m_lastHorizontal
= horizontal
;
679 m_lastSeenSize
= (horizontal
? sw
: sh
);
680 sizes
.writeEntry("lastsize", m_lastSeenSize
);
683 updatePanelGeometry();
685 if (m_panelController
) {
686 m_panelController
->setContainment(c
);
687 m_panelController
->setOffset(m_offset
);
690 kDebug() << "Done pinching, containement's geom" << c
->geometry() << "own geom" << geometry();
693 void PanelView::setOffset(int newOffset
)
695 m_offset
= newOffset
;
696 KConfigGroup viewConfig
= config();
697 viewConfig
.writeEntry("Offset", m_offset
);
699 containment()->update();
701 //TODO: do we ever need to worry about pinching here, or
702 // do we just assume that the offset is always < screenSize - containmentSize?
703 updatePanelGeometry();
706 int PanelView::offset() const
711 void PanelView::setAlignment(Qt::Alignment align
)
713 m_alignment
= alignmentFilter(align
);
714 KConfigGroup viewConfig
= config();
715 viewConfig
.writeEntry("Alignment", (int)m_alignment
);
718 Qt::Alignment
PanelView::alignment() const
723 void PanelView::togglePanelController()
727 if (containment()->immutability() != Plasma::Mutable
) {
728 delete m_panelController
;
729 m_panelController
= 0;
733 if (!m_panelController
) {
734 m_panelController
= new PanelController(this);
735 m_panelController
->setContainment(containment());
736 m_panelController
->setLocation(containment()->location());
737 m_panelController
->setAlignment(m_alignment
);
738 m_panelController
->setOffset(m_offset
);
739 m_panelController
->setVisibilityMode(m_visibilityMode
);
741 connect(m_panelController
, SIGNAL(destroyed(QObject
*)), this, SLOT(edittingComplete()));
742 connect(m_panelController
, SIGNAL(offsetChanged(int)), this, SLOT(setOffset(int)));
743 connect(m_panelController
, SIGNAL(alignmentChanged(Qt::Alignment
)), this, SLOT(setAlignment(Qt::Alignment
)));
744 connect(m_panelController
, SIGNAL(locationChanged(Plasma::Location
)), this, SLOT(setLocation(Plasma::Location
)));
745 connect(m_panelController
, SIGNAL(panelVisibilityModeChanged(PanelView::VisibilityMode
)), this, SLOT(setVisibilityMode(PanelView::VisibilityMode
)));
747 if (dynamic_cast<QGraphicsLinearLayout
*>(containment()->layout())) {
748 // we only support mouse over drags for panels with linear layouts
749 QColor
overlayColor(Plasma::Theme::defaultTheme()->color(Plasma::Theme::BackgroundColor
));
750 QBrush
overlayBrush(overlayColor
);
751 QPalette
p(palette());
752 p
.setBrush(QPalette::Window
, overlayBrush
);
753 foreach (Plasma::Applet
*applet
, containment()->applets()) {
754 PanelAppletOverlay
*moveOverlay
= new PanelAppletOverlay(applet
, this);
755 moveOverlay
->setPalette(p
);
757 moveOverlay
->raise();
758 m_moveOverlays
<< moveOverlay
;
759 //kDebug() << moveOverlay << moveOverlay->geometry();
762 setTabOrder(0, m_panelController
);
763 QWidget
*prior
= m_panelController
;
764 foreach (PanelAppletOverlay
*w
, m_moveOverlays
) {
765 setTabOrder(prior
, w
);
771 if (!m_panelController
->isVisible()) {
773 m_panelController
->resize(m_panelController
->sizeHint());
774 m_panelController
->move(m_panelController
->positionForPanelGeometry(geometry()));
775 m_panelController
->show();
777 m_panelController
->close();
782 void PanelView::edittingComplete()
785 m_panelController
= 0;
787 qDeleteAll(m_moveOverlays
);
788 m_moveOverlays
.clear();
789 containment()->closeToolBox();
791 m_firstPaint
= true; // triggers autohide
793 // not overly efficient since we may not have changed any settings,
794 // but ensures that if we have, a config sync will occur
795 PlasmaApp::self()->corona()->requestConfigSync();
798 Qt::Alignment
PanelView::alignmentFilter(Qt::Alignment align
) const
800 //If it's not a supported alignment default to Qt::AlignLeft
801 if (align
== Qt::AlignLeft
|| align
== Qt::AlignRight
|| align
== Qt::AlignCenter
) {
804 return Qt::AlignLeft
;
808 void PanelView::updateStruts()
814 NETExtendedStrut strut
;
816 if (m_visibilityMode
== NormalPanel
) {
817 QRect thisScreen
= Kephal::ScreenUtils::screenGeometry(containment()->screen());
818 QRect wholeScreen
= Kephal::ScreenUtils::desktopGeometry();
820 // extended struts are to the combined screen geoms, not the single screen
821 int leftOffset
= wholeScreen
.x() - thisScreen
.x();
822 int rightOffset
= wholeScreen
.right() - thisScreen
.right();
823 int bottomOffset
= wholeScreen
.bottom() - thisScreen
.bottom();
824 int topOffset
= wholeScreen
.top() - thisScreen
.top();
825 kDebug() << "screen l/r/b/t offsets are:" << leftOffset
<< rightOffset
<< bottomOffset
<< topOffset
<< location();
829 case Plasma::TopEdge
:
830 strut
.top_width
= height() + topOffset
;
831 strut
.top_start
= x();
832 strut
.top_end
= x() + width() - 1;
835 case Plasma::BottomEdge
:
836 strut
.bottom_width
= height() + bottomOffset
;
837 strut
.bottom_start
= x();
838 strut
.bottom_end
= x() + width() - 1;
839 //kDebug() << "setting bottom edge to" << strut.bottom_width
840 // << strut.bottom_start << strut.bottom_end;
843 case Plasma::RightEdge
:
844 strut
.right_width
= width() + rightOffset
;
845 strut
.right_start
= y();
846 strut
.right_end
= y() + height() - 1;
849 case Plasma::LeftEdge
:
850 strut
.left_width
= width() + leftOffset
;
851 strut
.left_start
= y();
852 strut
.left_end
= y() + height() - 1;
856 //kDebug() << "where are we?";
861 KWindowSystem::setExtendedStrut(winId(), strut
.left_width
,
875 void PanelView::moveEvent(QMoveEvent
*event
)
878 QWidget::moveEvent(event
);
880 recreateUnhideTrigger();
883 void PanelView::resizeEvent(QResizeEvent
*event
)
885 //kDebug() << event->oldSize() << event->size();
886 QWidget::resizeEvent(event
);
887 recreateUnhideTrigger();
894 QTimeLine
*PanelView::timeLine()
897 m_timeLine
= new QTimeLine(200, this);
898 m_timeLine
->setCurveShape(QTimeLine::EaseOutCurve
);
899 m_timeLine
->setUpdateInterval(10);
900 connect(m_timeLine
, SIGNAL(valueChanged(qreal
)), this, SLOT(animateHide(qreal
)));
906 void PanelView::hideMousePoll()
908 QPoint mousePos
= QCursor::pos();
909 if (!geometry().contains(mousePos
) && !hasPopup()) {
914 void PanelView::unhideHintMousePoll()
917 QPoint mousePos
= QCursor::pos();
918 m_glowBar
->updateStrength(mousePos
);
920 if (!m_unhideTriggerGeom
.contains(mousePos
)) {
922 XMoveResizeWindow(QX11Info::display(), m_unhideTrigger
, m_unhideTriggerGeom
.x(), m_unhideTriggerGeom
.y(), m_unhideTriggerGeom
.width(), m_unhideTriggerGeom
.height());
927 QRect
PanelView::unhideHintGeometry() const
930 return m_unhideTriggerGeom
;
936 bool PanelView::hintOrUnhide(const QPoint
&point
, bool dueToDnd
)
939 if (m_visibilityMode
!= LetWindowsCover
&& isVisible()) {
943 if (!shouldHintHide()) {
944 //kDebug() << "should not hint hide";
949 //kDebug() << point << m_triggerZone;
950 // if (point == QPoint()) {
951 //kDebug() << "enter, we should start glowing!";
952 if (m_triggerZone
.contains(point
)) {
953 //kDebug() << "unhide!" << point;
956 } else if (!m_glowBar
) {
957 Plasma::Direction direction
= Plasma::locationToDirection(location());
958 m_glowBar
= new GlowBar(direction
, m_triggerZone
);
960 XMoveResizeWindow(QX11Info::display(), m_unhideTrigger
, m_triggerZone
.x(), m_triggerZone
.y(), m_triggerZone
.width(), m_triggerZone
.height());
962 //FIXME: This is ugly as hell but well, yeah
963 if (!m_mousePollTimer
) {
964 m_mousePollTimer
= new QTimer(this);
967 connect(m_mousePollTimer
, SIGNAL(timeout()), this, SLOT(unhideHintMousePoll()));
968 m_mousePollTimer
->start(200);
977 void PanelView::unhintHide()
979 //kDebug() << "hide the glow";
980 if (m_mousePollTimer
) {
981 m_mousePollTimer
->stop();
982 disconnect(m_mousePollTimer
, SIGNAL(timeout()), this, SLOT(unhideHintMousePoll()));
989 bool PanelView::hasPopup()
991 if (QApplication::activePopupWidget() || m_panelController
) {
996 foreach (Plasma::Applet
*applet
, containment()->applets()) {
997 if (applet
->isPopupShowing()) {
1006 void PanelView::unhide(bool destroyTrigger
)
1008 //kDebug() << destroyTrigger;
1010 if (destroyTrigger
) {
1011 destroyUnhideTrigger();
1013 if (!m_mousePollTimer
) {
1014 m_mousePollTimer
= new QTimer(this);
1017 disconnect(m_mousePollTimer
, SIGNAL(timeout()), this, SLOT(hideMousePoll()));
1018 connect(m_mousePollTimer
, SIGNAL(timeout()), this, SLOT(hideMousePoll()));
1019 m_mousePollTimer
->start(200);
1022 // with composite, we can quite do some nice animations with transparent
1023 // backgrounds; without it we can't so we just show/hide
1024 QTimeLine
* tl
= timeLine();
1025 tl
->setDirection(QTimeLine::Backward
);
1026 tl
->setDuration(100);
1028 if (m_visibilityMode
== AutoHide
) {
1029 // LetWindowsCover panels are always shown, so don't bother and prevent
1030 // some unsightly flickers
1034 KWindowSystem::setOnAllDesktops(winId(), true);
1035 unsigned long state
= NET::Sticky
;
1036 KWindowSystem::setState(winId(), state
);
1038 if (m_visibilityMode
== LetWindowsCover
) {
1039 m_triggerEntered
= true;
1040 KWindowSystem::raiseWindow(winId());
1041 QTimer::singleShot(0, this, SLOT(resetTriggerEnteredSuppression()));
1042 } else if (shouldHintHide()) {
1043 if (tl
->state() == QTimeLine::NotRunning
) {
1047 //if the hide before compositing was active now the view is wrong
1048 viewport()->move(0,0);
1052 void PanelView::resetTriggerEnteredSuppression()
1054 m_triggerEntered
= false;
1057 void PanelView::startAutoHide()
1059 // with composite, we can quite do some nice animations with transparent
1060 // backgrounds; without it we can't so we just show/hide
1061 if (m_mousePollTimer
) {
1062 m_mousePollTimer
->stop();
1063 disconnect(m_mousePollTimer
, SIGNAL(timeout()), this, SLOT(hideMousePoll()));
1066 QTimeLine
*tl
= timeLine();
1067 tl
->setDirection(QTimeLine::Forward
);
1068 tl
->setDuration(200);
1070 if (shouldHintHide()) {
1071 if (tl
->state() == QTimeLine::NotRunning
) {
1079 void PanelView::leaveEvent(QEvent
*event
)
1081 if (m_visibilityMode
== LetWindowsCover
) {
1082 if (m_triggerEntered
) {
1083 //kDebug() << "not creating!";
1084 m_triggerEntered
= false;
1086 createUnhideTrigger();
1088 } else if (m_visibilityMode
== AutoHide
&& !m_editting
) {
1089 // try not to hide if we have an associated popup or window about
1091 // don't hide yet, but start polling to see when we should
1092 if (!m_mousePollTimer
) {
1093 m_mousePollTimer
= new QTimer(this);
1096 disconnect(m_mousePollTimer
, SIGNAL(timeout()), this, SLOT(hideMousePoll()));
1097 connect(m_mousePollTimer
, SIGNAL(timeout()), this, SLOT(hideMousePoll()));
1098 m_mousePollTimer
->start(200);
1104 Plasma::View::leaveEvent(event
);
1107 void PanelView::drawBackground(QPainter
*painter
, const QRectF
&rect
)
1109 if (PlasmaApp::hasComposite()) {
1110 painter
->setCompositionMode(QPainter::CompositionMode_Source
);
1111 painter
->fillRect(rect
.toAlignedRect(), Qt::transparent
);
1113 Plasma::View::drawBackground(painter
, rect
);
1117 void PanelView::paintEvent(QPaintEvent
*event
)
1119 Plasma::View::paintEvent(event
);
1121 // set up our auothide system after we paint it visibly to the user
1122 if (m_visibilityMode
== AutoHide
) {
1123 QTimeLine
* tl
= timeLine();
1124 tl
->setDirection(QTimeLine::Forward
);
1125 if (tl
->state() == QTimeLine::NotRunning
) {
1130 m_firstPaint
= false;
1134 bool PanelView::event(QEvent
*event
)
1136 if (event
->type() == QEvent::Paint
) {
1138 p
.setCompositionMode(QPainter::CompositionMode_Source
);
1139 p
.fillRect(rect(), Qt::transparent
);
1142 return Plasma::View::event(event
);
1145 void PanelView::animateHide(qreal progress
)
1147 if (m_visibilityMode
== AutoHide
&& shouldHintHide()) {
1149 Plasma::Location loc
= location();
1151 if (loc
== Plasma::TopEdge
|| loc
== Plasma::BottomEdge
) {
1152 margin
= int(progress
* height());
1154 margin
= int(progress
* width());
1161 case Plasma::TopEdge
:
1164 case Plasma::BottomEdge
:
1167 case Plasma::RightEdge
:
1170 case Plasma::LeftEdge
:
1174 // no hiding unless we're on an edge.
1179 //kDebug() << progress << xtrans << ytransLetWindowsCover;
1180 viewport()->move(xtrans
, ytrans
);
1183 QTimeLine
*tl
= timeLine();
1184 if (qFuzzyCompare(qreal(1.0), progress
) && tl
->direction() == QTimeLine::Forward
) {
1185 //kDebug() << "**************** hide complete" << triggerPoint << triggerWidth << triggerHeight;
1186 createUnhideTrigger();
1188 } else if (qFuzzyCompare(qreal(1.0), qreal(progress
+ 1.0)) && tl
->direction() == QTimeLine::Backward
) {
1189 //if the show before accel was off now viewport position is wrong, so ensure it's visible
1190 //kDebug() << "show complete";
1191 viewport()->move(0,0);
1195 bool PanelView::shouldHintHide() const
1197 return m_visibilityMode
== AutoHide
&& PlasmaApp::hasComposite();
1200 void PanelView::recreateUnhideTrigger()
1203 if (m_unhideTrigger
== None
) {
1207 XDestroyWindow(QX11Info::display(), m_unhideTrigger
);
1208 m_unhideTrigger
= None
;
1209 createUnhideTrigger();
1213 void PanelView::createUnhideTrigger()
1216 //kDebug() << m_unhideTrigger << None;
1217 if (m_unhideTrigger
!= None
) {
1221 bool fancy
= shouldHintHide();
1222 int actualWidth
= 1;
1223 int actualHeight
= 1;
1224 int triggerWidth
= fancy
? 30 : 1;
1225 int triggerHeight
= fancy
? 30 : 1;
1227 QPoint actualTriggerPoint
= pos();
1228 QPoint triggerPoint
= pos();
1230 switch (location()) {
1231 case Plasma::TopEdge
:
1232 actualWidth
= triggerWidth
= width();
1236 triggerPoint
.setX(qMax(0, triggerPoint
.x() - 15));
1239 case Plasma::BottomEdge
:
1240 actualWidth
= triggerWidth
= width();
1241 actualTriggerPoint
= triggerPoint
= geometry().bottomLeft();
1245 triggerPoint
.setX(qMax(0, triggerPoint
.x() - 15));
1246 triggerPoint
.setY(qMax(0, triggerPoint
.y() - 29));
1249 case Plasma::RightEdge
:
1250 actualHeight
= triggerHeight
= height();
1251 actualTriggerPoint
= triggerPoint
= geometry().topRight();
1254 triggerHeight
+= 30;
1255 triggerPoint
.setY(qMax(0, triggerPoint
.y() - 15));
1256 triggerPoint
.setX(qMax(0, triggerPoint
.x() - 29));
1259 case Plasma::LeftEdge
:
1260 actualHeight
= triggerHeight
= height();
1263 triggerHeight
+= 30;
1264 triggerPoint
.setY(qMax(0, triggerPoint
.y() - 15));
1268 // no hiding unless we're on an edge.
1274 XSetWindowAttributes attributes
;
1275 attributes
.override_redirect
= True
;
1276 attributes
.event_mask
= EnterWindowMask
;
1279 attributes
.event_mask
= EnterWindowMask
| LeaveWindowMask
| PointerMotionMask
|
1280 KeyPressMask
| KeyPressMask
| ButtonPressMask
|
1281 ButtonReleaseMask
| ButtonMotionMask
|
1282 KeymapStateMask
| VisibilityChangeMask
|
1283 StructureNotifyMask
| ResizeRedirectMask
|
1284 SubstructureNotifyMask
|
1285 SubstructureRedirectMask
| FocusChangeMask
|
1286 PropertyChangeMask
| ColormapChangeMask
| OwnerGrabButtonMask
;
1288 unsigned long valuemask
= CWOverrideRedirect
| CWEventMask
;
1289 m_unhideTrigger
= XCreateWindow(QX11Info::display(), QX11Info::appRootWindow(),
1290 triggerPoint
.x(), triggerPoint
.y(), triggerWidth
, triggerHeight
,
1291 0, CopyFromParent
, InputOnly
, CopyFromParent
,
1292 valuemask
, &attributes
);
1294 XChangeProperty(QX11Info::display(), m_unhideTrigger
, PlasmaApp::self()->m_XdndAwareAtom
,
1295 XA_WINDOW
, 32, PropModeReplace
, (unsigned char *)&PlasmaApp::self()->m_XdndVersionAtom
, 1);
1296 XMapWindow(QX11Info::display(), m_unhideTrigger
);
1297 m_unhideTriggerGeom
= QRect(triggerPoint
, QSize(triggerWidth
, triggerHeight
));
1298 m_triggerZone
= QRect(actualTriggerPoint
, QSize(actualWidth
, actualHeight
));
1300 //kDebug() << m_unhideTrigger;
1301 PlasmaApp::self()->panelHidden(true);
1304 void PanelView::destroyUnhideTrigger()
1307 if (m_unhideTrigger
== None
) {
1312 XDestroyWindow(QX11Info::display(), m_unhideTrigger
);
1313 m_unhideTrigger
= None
;
1314 m_triggerZone
= m_unhideTriggerGeom
= QRect();
1318 PlasmaApp::self()->panelHidden(false);
1321 void PanelView::panelDeleted()
1323 if (!QApplication::closingDown()) {
1324 // the panel was removed at runtime; clean up our configuration object as well
1325 KConfigGroup c
= config();
1332 void PanelView::positionSpacer(const QPoint pos
)
1334 if (!containment()) {
1338 QGraphicsLinearLayout
*lay
= dynamic_cast<QGraphicsLinearLayout
*>(containment()->layout());
1344 Plasma::FormFactor f
= containment()->formFactor();
1345 int insertIndex
= -1;
1347 //FIXME: needed in two places, make it a function?
1348 for (int i
= 0; i
< lay
->count(); ++i
) {
1349 QRectF siblingGeometry
= lay
->itemAt(i
)->geometry();
1351 if (f
== Plasma::Horizontal
) {
1352 qreal middle
= (siblingGeometry
.left() + siblingGeometry
.right()) / 2.0;
1353 if (pos
.x() < middle
) {
1356 } else if (pos
.x() <= siblingGeometry
.right()) {
1357 insertIndex
= i
+ 1;
1360 } else { // Plasma::Vertical
1361 qreal middle
= (siblingGeometry
.top() + siblingGeometry
.bottom()) / 2.0;
1362 if (pos
.y() < middle
) {
1365 } else if (pos
.y() <= siblingGeometry
.bottom()) {
1366 insertIndex
= i
+ 1;
1372 m_spacerIndex
= insertIndex
;
1373 if (insertIndex
!= -1) {
1375 m_spacer
= new QGraphicsWidget(containment());
1376 //m_spacer->panel = this;
1378 lay
->removeItem(m_spacer
);
1380 lay
->insertItem(insertIndex
, m_spacer
);
1384 void PanelView::dragEnterEvent(QDragEnterEvent
*event
)
1386 Plasma::Containment
*c
= containment();
1387 if (c
&& c
->immutability() == Plasma::Mutable
&&
1388 (event
->mimeData()->hasFormat(static_cast<Plasma::Corona
*>(scene())->appletMimeType()) ||
1389 KUrl::List::canDecode(event
->mimeData()))) {
1390 containment()->showDropZone(event
->pos());
1393 //the containment will do the last decision whether accept it or not
1394 Plasma::View::dragEnterEvent(event
);
1397 void PanelView::dragMoveEvent(QDragMoveEvent
*event
)
1399 Plasma::Containment
*c
= containment();
1400 if (c
&& c
->immutability() == Plasma::Mutable
&&
1401 (event
->mimeData()->hasFormat(static_cast<Plasma::Corona
*>(scene())->appletMimeType()) ||
1402 KUrl::List::canDecode(event
->mimeData()))) {
1403 containment()->showDropZone(event
->pos());
1406 Plasma::View::dragMoveEvent(event
);
1409 void PanelView::dragLeaveEvent(QDragLeaveEvent
*event
)
1411 if (containment()) {
1412 containment()->showDropZone(QPoint());
1415 Plasma::View::dragLeaveEvent(event
);
1418 void PanelView::dropEvent(QDropEvent
*event
)
1420 Plasma::View::dropEvent(event
);
1422 if (containment()) {
1423 containment()->showDropZone(QPoint());
1428 #include "panelview.moc"