dtor first
[personal-kdebase.git] / workspace / plasma / shells / desktop / panelview.cpp
bloba2cddc2177324e926579ef6c44835ddc63a58e94
1 /*
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>
25 #include <QTimeLine>
26 #include <QTimer>
27 #ifdef Q_WS_X11
28 #include <X11/Xatom.h>
29 #include <QX11Info>
30 #endif
32 #include <KWindowSystem>
33 #include <KDebug>
35 #include <Plasma/Containment>
36 #include <Plasma/Corona>
37 #include <Plasma/Plasma>
38 #include <Plasma/PopupApplet>
39 #include <Plasma/Svg>
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
50 public:
51 GlowBar(Plasma::Direction direction, const QRect &triggerZone)
52 : QWidget(0),
53 m_strength(0.3),
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;
64 QSize s = sizeHint();
65 switch (m_direction) {
66 case Plasma::Up:
67 glowGeom.setY(glowGeom.y() - s.height() + 1);
68 // fallthrough
69 case Plasma::Down:
70 glowGeom.setHeight(s.height());
71 break;
72 case Plasma::Left:
73 glowGeom.setX(glowGeom.x() - s.width() + 1);
74 // fallthrough
75 case Plasma::Right:
76 glowGeom.setWidth(s.width());
77 break;
80 //kDebug() << "glow geom is" << glowGeom << "from" << triggerZone;
81 setGeometry(glowGeom);
82 m_buffer = QPixmap(size());
85 void paintEvent(QPaintEvent* e)
87 Q_UNUSED(e)
88 QPixmap l, r, c;
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) {
97 case Plasma::Down:
98 l = m_svg->pixmap("bottomleft");
99 r = m_svg->pixmap("bottomright");
100 c = m_svg->pixmap("bottom");
101 pixmapPosition = QPoint(0, -glowRadius.height());
102 break;
103 case Plasma::Up:
104 l = m_svg->pixmap("topleft");
105 r = m_svg->pixmap("topright");
106 c = m_svg->pixmap("top");
107 break;
108 case Plasma::Right:
109 l = m_svg->pixmap("topright");
110 r = m_svg->pixmap("bottomright");
111 c = m_svg->pixmap("right");
112 pixmapPosition = QPoint(-glowRadius.width(), 0);
113 break;
114 case Plasma::Left:
115 l = m_svg->pixmap("topleft");
116 r = m_svg->pixmap("bottomleft");
117 c = m_svg->pixmap("left");
118 break;
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);
125 } else {
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);
131 p.end();
132 p.begin(this);
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) {
144 QPainter p(this);
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);
155 qreal newStrength;
156 switch (m_direction) {
157 case Plasma::Up: // when the panel is at the bottom.
158 newStrength = 1 - qreal(-localPoint.y())/m_triggerDistance;
159 break;
160 case Plasma::Right:
161 newStrength = 1 - qreal(localPoint.x())/m_triggerDistance;
162 break;
163 case Plasma::Left: // when the panel is right-aligned
164 newStrength = 1 - qreal(-localPoint.x())/m_triggerDistance;
165 break;
166 case Plasma::Down:
167 default:
168 newStrength = 1- qreal(localPoint.y())/m_triggerDistance;
169 break;
171 if (qAbs(newStrength - m_strength) > 0.01 && newStrength >= 0 && newStrength <= 1) {
172 m_strength = newStrength;
173 update();
178 private:
179 static const int m_triggerDistance = 30;
180 qreal m_strength;
181 Plasma::Svg *m_svg;
182 Plasma::Direction m_direction;
183 QPixmap m_buffer;
186 PanelView::PanelView(Plasma::Containment *panel, int id, QWidget *parent)
187 : Plasma::View(panel, id, parent),
188 m_panelController(0),
189 m_glowBar(0),
190 m_mousePollTimer(0),
191 m_timeLine(0),
192 m_spacer(0),
193 m_spacerIndex(-1),
194 #ifdef Q_WS_X11
195 m_unhideTrigger(None),
196 #endif
197 m_visibilityMode(NormalPanel),
198 m_lastHorizontal(true),
199 m_init(false),
200 m_editting(false),
201 m_firstPaint(true),
202 m_triggerEntered(false)
204 Q_ASSERT(qobject_cast<Plasma::Corona*>(panel->scene()));
205 if (panel) {
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);
225 setPalette(pal);
227 #ifdef Q_WS_WIN
228 registerAccessBar(true);
229 #endif
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();
246 // KWin setup
247 KWindowSystem::setOnAllDesktops(winId(), true);
249 QTimer::singleShot(0, this, SLOT(init()));
252 PanelView::~PanelView()
254 delete m_glowBar;
255 destroyUnhideTrigger();
256 #ifdef Q_WS_WIN
257 registerAccessBar(false);
258 #endif
261 void PanelView::init()
263 KConfigGroup viewConfig = config();
264 setVisibilityMode((VisibilityMode)viewConfig.readEntry("panelVisibility", (int)m_visibilityMode));
266 m_init = true;
267 updateStruts();
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();
293 if (wasFullSize) {
294 QRect screenGeom = Kephal::ScreenUtils::screenGeometry(c->screen());
295 panelWidth = screenGeom.width();
296 } else {
297 panelWidth = s.height();
299 min = QSizeF(panelWidth, min.width());
300 max = QSizeF(panelWidth, max.width());
303 formFactor = Plasma::Horizontal;
304 } else {
305 if (wasHorizontal) {
306 // we're switching! swap the sizes about
308 if (wasFullSize) {
309 QRect screenGeom = Kephal::ScreenUtils::screenGeometry(c->screen());
310 panelHeight = screenGeom.height();
311 } else {
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);
333 #ifdef Q_WS_WIN
334 appBarPosChanged();
335 #endif
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;
354 delete m_glowBar;
355 m_glowBar = 0;
357 if (mode == LetWindowsCover) {
358 createUnhideTrigger();
359 KWindowSystem::clearState(winId(), NET::StaysOnTop | NET::KeepAbove);
360 state |= NET::KeepBelow;
361 } else {
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();
373 show();
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();
403 if (screen < 0) {
404 //TODO: is there a valid use for -1 with a panel? floating maybe?
405 screen = 0;
408 QRect screenGeom = Kephal::ScreenUtils::screenGeometry(screen);
410 if (m_alignment != Qt::AlignCenter) {
411 m_offset = qMax(m_offset, 0);
414 //Sanity controls
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;
442 break;
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;
471 break;
473 //TODO: floating panels (probably they will save their own geometry)
474 default:
475 break;
478 //Actual movement
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()));
491 break;
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()));
504 break;
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()));
517 break;
519 case Plasma::BottomEdge:
520 default:
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()));
531 break;
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
538 updateStruts();
539 } else {
540 setGeometry(geom);
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
620 if (horizontal) {
621 c->setMaximumSize(sw, max.height());
622 c->resize(sw, c->geometry().height());
623 } else {
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;
637 if (horizontal) {
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());
643 } else {
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());
652 } else {
653 m_offset = sw - int(max.width());
656 } else {
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);
661 } else {
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);
670 } else {
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
708 return m_offset;
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
720 return m_alignment;
723 void PanelView::togglePanelController()
725 //kDebug();
726 m_editting = false;
727 if (containment()->immutability() != Plasma::Mutable) {
728 delete m_panelController;
729 m_panelController = 0;
730 return;
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);
756 moveOverlay->show();
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);
766 prior = w;
771 if (!m_panelController->isVisible()) {
772 m_editting = true;
773 m_panelController->resize(m_panelController->sizeHint());
774 m_panelController->move(m_panelController->positionForPanelGeometry(geometry()));
775 m_panelController->show();
776 } else {
777 m_panelController->close();
778 updateStruts();
782 void PanelView::edittingComplete()
784 //kDebug();
785 m_panelController = 0;
786 m_editting = false;
787 qDeleteAll(m_moveOverlays);
788 m_moveOverlays.clear();
789 containment()->closeToolBox();
790 updateStruts();
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) {
802 return align;
803 } else {
804 return Qt::AlignLeft;
808 void PanelView::updateStruts()
810 if (!m_init) {
811 return;
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();
827 switch (location())
829 case Plasma::TopEdge:
830 strut.top_width = height() + topOffset;
831 strut.top_start = x();
832 strut.top_end = x() + width() - 1;
833 break;
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;
841 break;
843 case Plasma::RightEdge:
844 strut.right_width = width() + rightOffset;
845 strut.right_start = y();
846 strut.right_end = y() + height() - 1;
847 break;
849 case Plasma::LeftEdge:
850 strut.left_width = width() + leftOffset;
851 strut.left_start = y();
852 strut.left_end = y() + height() - 1;
853 break;
855 default:
856 //kDebug() << "where are we?";
857 break;
861 KWindowSystem::setExtendedStrut(winId(), strut.left_width,
862 strut.left_start,
863 strut.left_end,
864 strut.right_width,
865 strut.right_start,
866 strut.right_end,
867 strut.top_width,
868 strut.top_start,
869 strut.top_end,
870 strut.bottom_width,
871 strut.bottom_start,
872 strut.bottom_end);
875 void PanelView::moveEvent(QMoveEvent *event)
877 //kDebug();
878 QWidget::moveEvent(event);
879 updateStruts();
880 recreateUnhideTrigger();
883 void PanelView::resizeEvent(QResizeEvent *event)
885 //kDebug() << event->oldSize() << event->size();
886 QWidget::resizeEvent(event);
887 recreateUnhideTrigger();
888 updateStruts();
889 #ifdef Q_WS_WIN
890 appBarPosChanged();
891 #endif
894 QTimeLine *PanelView::timeLine()
896 if (!m_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)));
903 return m_timeLine;
906 void PanelView::hideMousePoll()
908 QPoint mousePos = QCursor::pos();
909 if (!geometry().contains(mousePos) && !hasPopup()) {
910 startAutoHide();
914 void PanelView::unhideHintMousePoll()
916 #ifdef Q_WS_X11
917 QPoint mousePos = QCursor::pos();
918 m_glowBar->updateStrength(mousePos);
920 if (!m_unhideTriggerGeom.contains(mousePos)) {
921 unhintHide();
922 XMoveResizeWindow(QX11Info::display(), m_unhideTrigger, m_unhideTriggerGeom.x(), m_unhideTriggerGeom.y(), m_unhideTriggerGeom.width(), m_unhideTriggerGeom.height());
924 #endif
927 QRect PanelView::unhideHintGeometry() const
929 #ifdef Q_WS_X11
930 return m_unhideTriggerGeom;
931 #else
932 return QRect();
933 #endif
936 bool PanelView::hintOrUnhide(const QPoint &point, bool dueToDnd)
938 #ifdef Q_WS_X11
939 if (m_visibilityMode != LetWindowsCover && isVisible()) {
940 return false;
943 if (!shouldHintHide()) {
944 //kDebug() << "should not hint hide";
945 unhide(!dueToDnd);
946 return true;
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;
954 unhide(!dueToDnd);
955 return true;
956 } else if (!m_glowBar) {
957 Plasma::Direction direction = Plasma::locationToDirection(location());
958 m_glowBar = new GlowBar(direction, m_triggerZone);
959 m_glowBar->show();
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);
971 return false;
972 #else
973 return false;
974 #endif
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()));
985 delete m_glowBar;
986 m_glowBar = 0;
989 bool PanelView::hasPopup()
991 if (QApplication::activePopupWidget() || m_panelController) {
992 return true;
995 if (containment()) {
996 foreach (Plasma::Applet *applet, containment()->applets()) {
997 if (applet->isPopupShowing()) {
998 return true;
1003 return false;
1006 void PanelView::unhide(bool destroyTrigger)
1008 //kDebug() << destroyTrigger;
1009 unhintHide();
1010 if (destroyTrigger) {
1011 destroyUnhideTrigger();
1012 } else {
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
1031 show();
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) {
1044 tl->start();
1046 } else {
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) {
1072 tl->start();
1074 } else {
1075 animateHide(1.0);
1079 void PanelView::leaveEvent(QEvent *event)
1081 if (m_visibilityMode == LetWindowsCover) {
1082 if (m_triggerEntered) {
1083 //kDebug() << "not creating!";
1084 m_triggerEntered = false;
1085 } else {
1086 createUnhideTrigger();
1088 } else if (m_visibilityMode == AutoHide && !m_editting) {
1089 // try not to hide if we have an associated popup or window about
1090 if (hasPopup()) {
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);
1099 } else {
1100 startAutoHide();
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);
1112 } else {
1113 Plasma::View::drawBackground(painter, rect);
1117 void PanelView::paintEvent(QPaintEvent *event)
1119 Plasma::View::paintEvent(event);
1120 if (m_firstPaint) {
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) {
1126 tl->start();
1130 m_firstPaint = false;
1134 bool PanelView::event(QEvent *event)
1136 if (event->type() == QEvent::Paint) {
1137 QPainter p(this);
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()) {
1148 int margin = 0;
1149 Plasma::Location loc = location();
1151 if (loc == Plasma::TopEdge || loc == Plasma::BottomEdge) {
1152 margin = int(progress * height());
1153 } else {
1154 margin = int(progress * width());
1157 int xtrans = 0;
1158 int ytrans = 0;
1160 switch (loc) {
1161 case Plasma::TopEdge:
1162 ytrans = -margin;
1163 break;
1164 case Plasma::BottomEdge:
1165 ytrans = margin;
1166 break;
1167 case Plasma::RightEdge:
1168 xtrans = margin;
1169 break;
1170 case Plasma::LeftEdge:
1171 xtrans = -margin;
1172 break;
1173 default:
1174 // no hiding unless we're on an edge.
1175 return;
1176 break;
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();
1187 hide();
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()
1202 #ifdef Q_WS_X11
1203 if (m_unhideTrigger == None) {
1204 return;
1207 XDestroyWindow(QX11Info::display(), m_unhideTrigger);
1208 m_unhideTrigger = None;
1209 createUnhideTrigger();
1210 #endif
1213 void PanelView::createUnhideTrigger()
1215 #ifdef Q_WS_X11
1216 //kDebug() << m_unhideTrigger << None;
1217 if (m_unhideTrigger != None) {
1218 return;
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();
1234 if (fancy) {
1235 triggerWidth += 30;
1236 triggerPoint.setX(qMax(0, triggerPoint.x() - 15));
1238 break;
1239 case Plasma::BottomEdge:
1240 actualWidth = triggerWidth = width();
1241 actualTriggerPoint = triggerPoint = geometry().bottomLeft();
1243 if (fancy) {
1244 triggerWidth += 30;
1245 triggerPoint.setX(qMax(0, triggerPoint.x() - 15));
1246 triggerPoint.setY(qMax(0, triggerPoint.y() - 29));
1248 break;
1249 case Plasma::RightEdge:
1250 actualHeight = triggerHeight = height();
1251 actualTriggerPoint = triggerPoint = geometry().topRight();
1253 if (fancy) {
1254 triggerHeight += 30;
1255 triggerPoint.setY(qMax(0, triggerPoint.y() - 15));
1256 triggerPoint.setX(qMax(0, triggerPoint.x() - 29));
1258 break;
1259 case Plasma::LeftEdge:
1260 actualHeight = triggerHeight = height();
1262 if (fancy) {
1263 triggerHeight += 30;
1264 triggerPoint.setY(qMax(0, triggerPoint.y() - 15));
1266 break;
1267 default:
1268 // no hiding unless we're on an edge.
1269 return;
1270 break;
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));
1299 #endif
1300 //kDebug() << m_unhideTrigger;
1301 PlasmaApp::self()->panelHidden(true);
1304 void PanelView::destroyUnhideTrigger()
1306 #ifdef Q_WS_X11
1307 if (m_unhideTrigger == None) {
1308 return;
1311 //kDebug();
1312 XDestroyWindow(QX11Info::display(), m_unhideTrigger);
1313 m_unhideTrigger = None;
1314 m_triggerZone = m_unhideTriggerGeom = QRect();
1315 #endif
1317 //kDebug();
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();
1326 c.deleteGroup();
1329 deleteLater();
1332 void PanelView::positionSpacer(const QPoint pos)
1334 if (!containment()) {
1335 return;
1338 QGraphicsLinearLayout *lay = dynamic_cast<QGraphicsLinearLayout*>(containment()->layout());
1340 if (!lay) {
1341 return;
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) {
1354 insertIndex = i;
1355 break;
1356 } else if (pos.x() <= siblingGeometry.right()) {
1357 insertIndex = i + 1;
1358 break;
1360 } else { // Plasma::Vertical
1361 qreal middle = (siblingGeometry.top() + siblingGeometry.bottom()) / 2.0;
1362 if (pos.y() < middle) {
1363 insertIndex = i;
1364 break;
1365 } else if (pos.y() <= siblingGeometry.bottom()) {
1366 insertIndex = i + 1;
1367 break;
1372 m_spacerIndex = insertIndex;
1373 if (insertIndex != -1) {
1374 if (!m_spacer) {
1375 m_spacer = new QGraphicsWidget(containment());
1376 //m_spacer->panel = this;
1378 lay->removeItem(m_spacer);
1379 m_spacer->show();
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"