add more spacing
[personal-kdebase.git] / workspace / plasma / applets / pager / pager.cpp
blob65dbfd4d0fced615bf5523c611ac9f42c696a034
1 /***************************************************************************
2 * Copyright (C) 2007 by Daniel Laidig <d.laidig@gmx.de> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
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. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * 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 . *
18 ***************************************************************************/
20 #include "pager.h"
22 #include <math.h>
24 #include <QApplication>
25 #include <QPainter>
26 #include <QStyleOptionGraphicsItem>
27 #include <QFont>
28 #include <QGraphicsSceneHoverEvent>
29 #include <QTimer>
30 #include <QX11Info>
32 #include <KDialog>
33 #include <KColorScheme>
34 #include <KConfigDialog>
35 #include <KGlobalSettings>
36 #include <KSharedConfig>
37 #include <KWindowSystem>
38 #include <NETRootInfo>
39 #include <KToolInvocation>
40 #include <kmanagerselection.h>
42 #include <Plasma/Svg>
43 #include <Plasma/FrameSvg>
44 #include <Plasma/PaintUtils>
45 #include <Plasma/Theme>
46 #include <Plasma/ToolTipManager>
47 #include <Plasma/Animator>
49 #include <kephal/screens.h>
51 #include <taskmanager/task.h>
53 const int FAST_UPDATE_DELAY = 200;
54 const int UPDATE_DELAY = 500;
55 const int DRAG_SWITCH_DELAY = 1000;
57 Pager::Pager(QObject *parent, const QVariantList &args)
58 : Plasma::Applet(parent, args),
59 m_displayedText(None),
60 m_showWindowIcons(false),
61 m_showOwnBackground(false),
62 m_rows(2),
63 m_columns(0),
64 m_hoverIndex(-1),
65 m_colorScheme(0),
66 m_dragId(0),
67 m_dirtyDesktop(-1),
68 m_dragStartDesktop(-1),
69 m_dragHighlightedDesktop(-1),
70 m_dragSwitchDesktop(-1)
72 setAcceptsHoverEvents(true);
73 setAcceptDrops(true);
74 setHasConfigurationInterface(true);
76 m_background = new Plasma::FrameSvg(this);
77 m_background->setImagePath("widgets/pager");
78 m_background->setCacheAllRenderedFrames(true);
80 // initialize with a decent default
81 m_desktopCount = KWindowSystem::numberOfDesktops();
82 m_size = QSizeF(176, 88);
83 resize(m_size);
86 void Pager::init()
88 createMenu();
90 KConfigGroup cg = config();
91 m_displayedText = (DisplayedText)cg.readEntry("displayedText", (int)m_displayedText);
92 m_showWindowIcons = cg.readEntry("showWindowIcons", m_showWindowIcons);
93 m_rows = globalConfig().readEntry("rows", m_rows);
95 if (m_rows < 1) {
96 m_rows = 1;
97 } else if (m_rows > m_desktopCount) {
98 m_rows = m_desktopCount;
101 m_timer = new QTimer(this);
102 m_timer->setSingleShot(true);
103 connect(m_timer, SIGNAL(timeout()), this, SLOT(recalculateWindowRects()));
105 m_dragSwitchTimer = new QTimer(this);
106 m_dragSwitchTimer->setSingleShot(true);
107 connect(m_dragSwitchTimer, SIGNAL(timeout()), this, SLOT(dragSwitch()));
109 connect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)), this, SLOT(currentDesktopChanged(int)));
110 connect(KWindowSystem::self(), SIGNAL(windowAdded(WId)), this, SLOT(windowAdded(WId)));
111 connect(KWindowSystem::self(), SIGNAL(windowRemoved(WId)), this, SLOT(windowRemoved(WId)));
112 connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)), this, SLOT(activeWindowChanged(WId)));
113 connect(KWindowSystem::self(), SIGNAL(numberOfDesktopsChanged(int)), this, SLOT(numberOfDesktopsChanged(int)));
114 connect(KWindowSystem::self(), SIGNAL(desktopNamesChanged()), this, SLOT(desktopNamesChanged()));
115 connect(KWindowSystem::self(), SIGNAL(stackingOrderChanged()), this, SLOT(stackingOrderChanged()));
116 connect(KWindowSystem::self(), SIGNAL(windowChanged(WId,unsigned int)), this, SLOT(windowChanged(WId,unsigned int)));
117 connect(KWindowSystem::self(), SIGNAL(showingDesktopChanged(bool)), this, SLOT(showingDesktopChanged(bool)));
118 connect(Kephal::Screens::self(), SIGNAL(screenAdded(Kephal::Screen *)), SLOT(desktopsSizeChanged()));
119 connect(Kephal::Screens::self(), SIGNAL(screenRemoved(int)), SLOT(desktopsSizeChanged()));
120 connect(Kephal::Screens::self(), SIGNAL(screenResized(Kephal::Screen *, QSize, QSize)), SLOT(desktopsSizeChanged()));
121 connect(Kephal::Screens::self(), SIGNAL(screenMoved(Kephal::Screen *, QPoint, QPoint)), SLOT(desktopsSizeChanged()));
123 m_desktopLayoutOwner = new KSelectionOwner( QString( "_NET_DESKTOP_LAYOUT_S%1" )
124 .arg( QX11Info::appScreen()).toLatin1().constData(), QX11Info::appScreen(), this );
125 connect( m_desktopLayoutOwner, SIGNAL( lostOwnership()), SLOT( lostDesktopLayoutOwner()));
126 if ( !m_desktopLayoutOwner->claim( false ))
127 lostDesktopLayoutOwner();
129 connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeRefresh()));
131 recalculateGeometry();
133 m_currentDesktop = KWindowSystem::currentDesktop();
136 Pager::~Pager()
138 delete m_colorScheme;
141 void Pager::constraintsEvent(Plasma::Constraints constraints)
143 if (constraints & Plasma::SizeConstraint) {
144 recalculateGeometry();
145 recalculateWindowRects();
146 if (m_background->hasElementPrefix(QString())) {
147 m_background->setElementPrefix(QString());
148 m_background->resizeFrame(size());
151 if (constraints & Plasma::FormFactorConstraint) {
152 if (formFactor() == Plasma::Horizontal) {
153 setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
154 } else if (formFactor() == Plasma::Vertical) {
155 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
156 } else {
157 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
162 KColorScheme *Pager::colorScheme()
164 if (!m_colorScheme) {
165 m_colorScheme = new KColorScheme(QPalette::Active, KColorScheme::View, Plasma::Theme::defaultTheme()->colorScheme());
168 return m_colorScheme;
171 void Pager::createMenu()
173 QAction* configureDesktop = new QAction(SmallIcon("configure"),i18n("&Configure Desktops..."), this);
174 m_actions.append(configureDesktop);
175 connect(configureDesktop, SIGNAL(triggered(bool)), this , SLOT(slotConfigureDesktop()));
178 QList<QAction*> Pager::contextualActions()
180 return m_actions;
183 void Pager::slotConfigureDesktop()
185 QString error;
186 KToolInvocation::startServiceByDesktopName("desktop", QStringList(), &error);
189 void Pager::createConfigurationInterface(KConfigDialog *parent)
191 QWidget *widget = new QWidget();
192 ui.setupUi(widget);
193 parent->addPage(widget, i18n("General"), icon());
194 connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
195 connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
196 connect(ui.configureDesktopsButton, SIGNAL(clicked()), SLOT(slotConfigureDesktop()));
198 switch (m_displayedText){
199 case Number:
200 ui. desktopNumberRadioButton->setChecked(true);
201 break;
203 case Name:
204 ui.desktopNameRadioButton->setChecked(true);
205 break;
207 case None:
208 ui.displayNoneRadioButton->setChecked(true);
209 break;
212 ui.showWindowIconsCheckBox->setChecked(m_showWindowIcons);
213 ui.spinRows->setValue(m_rows);
214 ui.spinRows->setMaximum(m_desktopCount);
217 void Pager::recalculateGeometry()
219 if (!m_rects.isEmpty() && geometry().size() == m_size) {
220 //kDebug() << "leaving because" << !m_rects.isEmpty() << " and " << contentSize() << "==" << m_size;
221 return;
224 int padding = 2; // Space between miniatures of desktops
225 int textMargin = 3; // Space between name of desktop and border
226 int columns = m_desktopCount / m_rows + m_desktopCount % m_rows;
227 int rows = m_rows;
229 //inverse rows and columns in vertical panel
230 if (formFactor() == Plasma::Vertical) {
231 rows = columns;
232 columns = m_rows;
235 qreal leftMargin = 0;
236 qreal topMargin = 0;
237 qreal rightMargin = 0;
238 qreal bottomMargin = 0;
240 if (formFactor() == Plasma::Vertical || formFactor() == Plasma::Horizontal) {
241 m_background->setElementPrefix(QString());
242 m_background->getMargins(leftMargin, topMargin, rightMargin, bottomMargin);
244 qreal ratio = (qreal)Kephal::ScreenUtils::desktopGeometry().width() / (qreal)Kephal::ScreenUtils::desktopGeometry().height();
246 if (formFactor() == Plasma::Vertical) {
247 qreal optimalSize = (geometry().width() - KIconLoader::SizeSmall*ratio * columns + padding*(columns-1)) / 2;
249 if (optimalSize < leftMargin || optimalSize < rightMargin) {
250 leftMargin = rightMargin = qMax(qreal(0), optimalSize);
251 m_showOwnBackground = false;
253 } else if (formFactor() == Plasma::Horizontal) {
254 qreal optimalSize = (geometry().height() - KIconLoader::SizeSmall*rows + padding*(rows-1)) / 2;
256 if (optimalSize < topMargin || optimalSize < bottomMargin) {
257 topMargin = bottomMargin = qMax(qreal(0), optimalSize);
258 m_showOwnBackground = false;
260 } else {
261 m_showOwnBackground = true;
263 } else {
264 getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
267 qreal itemHeight;
268 qreal itemWidth;
270 if (formFactor() == Plasma::Vertical) {
271 itemWidth = (contentsRect().width() - leftMargin - rightMargin - padding * (columns - 1)) / columns;
272 m_widthScaleFactor = itemWidth / Kephal::ScreenUtils::desktopGeometry().width();
273 itemHeight = Kephal::ScreenUtils::desktopGeometry().height() * m_widthScaleFactor;
274 m_heightScaleFactor = m_widthScaleFactor;
275 } else {
276 itemHeight = (contentsRect().height() - topMargin - bottomMargin - padding * (rows - 1)) / rows;
277 m_heightScaleFactor = itemHeight / Kephal::ScreenUtils::desktopGeometry().height();
278 itemWidth = Kephal::ScreenUtils::desktopGeometry().width() * m_heightScaleFactor;
279 if (m_displayedText == Name) {
280 // When containment is in this position we are not limited by low width and we can
281 // afford increasing width of applet to be able to display every name of desktops
282 for (int i = 0; i < m_desktopCount; i++) {
283 QFontMetricsF metrics(KGlobalSettings::taskbarFont());
284 QSizeF textSize = metrics.size(Qt::TextSingleLine, KWindowSystem::desktopName(i+1));
285 if (textSize.width() + textMargin * 2 > itemWidth) {
286 itemWidth = textSize.width() + textMargin * 2;
290 m_widthScaleFactor = itemWidth / Kephal::ScreenUtils::desktopGeometry().width();
293 m_rects.clear();
294 m_animations.clear();
295 QRectF itemRect(QPoint(leftMargin, topMargin) , QSize(floor(itemWidth), floor(itemHeight)));
296 for (int i = 0; i < m_desktopCount; i++) {
297 itemRect.moveLeft(leftMargin + floor(i % columns * (itemWidth + padding)));
298 itemRect.moveTop(topMargin + floor(i / columns * (itemHeight + padding)));
299 m_rects.append(itemRect);
300 AnimInfo anim;
301 anim.animId = -1;
302 anim.fadeIn = true;
303 anim.alpha = 0;
304 m_animations.append(anim);
307 //Resize background svgs as needed
308 if (m_background->hasElementPrefix("normal")) {
309 m_background->setElementPrefix("normal");
310 m_background->resizeFrame(itemRect.size());
313 if (m_background->hasElementPrefix("active")) {
314 m_background->setElementPrefix("active");
315 m_background->resizeFrame(itemRect.size());
318 if (m_background->hasElementPrefix("hover")) {
319 m_background->setElementPrefix("hover");
320 m_background->resizeFrame(itemRect.size());
323 m_size = QSizeF(ceil(columns * itemWidth + padding * (columns - 1) + leftMargin + rightMargin),
324 ceil(rows * itemHeight + padding * (rows - 1) + topMargin + bottomMargin));
326 //kDebug() << "new size set" << m_size << m_rows << m_columns << columns << itemWidth;
328 resize(m_size);
329 setPreferredSize(m_size);
330 if (m_desktopLayoutOwner && columns != m_columns) {
331 // must own manager selection before setting global desktop layout
332 m_columns = columns;
333 NET::Orientation orient = NET::OrientationHorizontal;
334 NETRootInfo i( QX11Info::display(), 0 );
335 i.setDesktopLayout( orient, columns, rows, NET::DesktopLayoutCornerTopLeft );
339 void Pager::recalculateWindowRects()
341 QList<WId> windows = KWindowSystem::stackingOrder();
342 m_windowRects.clear();
343 for (int i = 0; i < m_desktopCount; i++) {
344 m_windowRects.append(QList<QPair<WId, QRect> >());
346 m_activeWindows.clear();
347 m_windowInfo.clear();
348 foreach(WId window, windows) {
349 KWindowInfo info = KWindowSystem::windowInfo(window, NET::WMGeometry | NET::WMFrameExtents | NET::WMWindowType | NET::WMDesktop | NET::WMState | NET::XAWMState | NET::WMVisibleName);
350 NET::WindowType type = info.windowType(NET::NormalMask | NET::DialogMask | NET::OverrideMask |
351 NET::UtilityMask | NET::DesktopMask | NET::DockMask |
352 NET::TopMenuMask | NET::SplashMask | NET::ToolbarMask |
353 NET::MenuMask);
355 // the reason we don't check for -1 or Net::Unknown here is that legitimate windows, such
356 // as some java application windows, may not have a type set for them.
357 // apparently sane defaults on properties is beyond the wisdom of x11.
358 if (type == NET::Desktop || type == NET::Dock || type == NET::TopMenu ||
359 type == NET::Splash || type == NET::Menu || type == NET::Toolbar ||
360 info.hasState(NET::SkipPager) || info.isMinimized()) {
361 continue;
364 for (int i = 0; i < m_desktopCount; i++) {
365 if (!info.isOnDesktop(i+1)) {
366 continue;
369 QRect windowRect = info.frameGeometry();
371 if (KWindowSystem::mapViewport()) {
372 windowRect = fixViewportPosition( windowRect );
375 windowRect = QRectF(windowRect.x() * m_widthScaleFactor,
376 windowRect.y() * m_heightScaleFactor,
377 windowRect.width() * m_widthScaleFactor,
378 windowRect.height() * m_heightScaleFactor).toRect();
379 windowRect.translate(m_rects[i].topLeft().toPoint());
380 m_windowRects[i].append(QPair<WId, QRect>(window, windowRect));
381 if (window == KWindowSystem::activeWindow()) {
382 m_activeWindows.append(windowRect);
384 m_windowInfo.append(info);
388 update();
391 void Pager::configAccepted()
393 KConfigGroup cg = config();
394 bool changed = false;
396 DisplayedText displayedText;
398 if (ui.desktopNumberRadioButton->isChecked()){
399 displayedText = Number;
401 }else if (ui.desktopNameRadioButton->isChecked()){
402 displayedText = Name;
404 }else{
405 displayedText = None;
408 if ((int)m_displayedText != (int)displayedText) {
409 m_displayedText = displayedText;
410 cg.writeEntry("displayedText", (int)m_displayedText);
411 changed = true;
414 if (m_showWindowIcons != ui.showWindowIconsCheckBox->isChecked()) {
415 m_showWindowIcons = ui.showWindowIconsCheckBox->isChecked();
416 cg.writeEntry("showWindowIcons", m_showWindowIcons);
417 changed = true;
420 // we need to keep all pager applets consistent since this affects
421 // the layout of the desktops as used by the window manager,
422 // so we store the row count in the applet global configuration
423 if (m_rows != ui.spinRows->value()) {
424 KConfigGroup globalcg = globalConfig();
425 m_rows = ui.spinRows->value();
426 if (m_rows > m_desktopCount) {
427 m_rows = m_desktopCount;
429 globalcg.writeEntry("rows", m_rows);
430 changed = true;
433 if (changed) {
434 configNeedsSaving();
435 // force an update
436 m_columns = 0;
437 m_size = QSizeF(-1, -1);
438 recalculateGeometry();
439 recalculateWindowRects();
440 update();
444 void Pager::currentDesktopChanged(int desktop)
446 if (desktop < 1) {
447 return; // bogus value, don't accept it
450 m_currentDesktop = desktop;
451 m_dirtyDesktop = -1;
453 if (!m_timer->isActive()) {
454 m_timer->start(FAST_UPDATE_DELAY);
458 void Pager::windowAdded(WId id)
460 Q_UNUSED(id)
462 KWindowInfo info = KWindowSystem::windowInfo(id, NET::WMGeometry | NET::WMFrameExtents | NET::WMWindowType | NET::WMDesktop | NET::WMState | NET::XAWMState);
463 m_dirtyDesktop = info.desktop() - 1;
465 if (!m_timer->isActive()) {
466 m_timer->start(FAST_UPDATE_DELAY);
470 void Pager::windowRemoved(WId id)
472 Q_UNUSED(id)
474 KWindowInfo info = KWindowSystem::windowInfo(id, NET::WMGeometry | NET::WMFrameExtents | NET::WMWindowType | NET::WMDesktop | NET::WMState | NET::XAWMState);
475 m_dirtyDesktop = info.desktop() - 1;
477 if (!m_timer->isActive()) {
478 m_timer->start(FAST_UPDATE_DELAY);
482 void Pager::activeWindowChanged(WId id)
484 Q_UNUSED(id)
486 KWindowInfo info = KWindowSystem::windowInfo(id, NET::WMGeometry | NET::WMFrameExtents | NET::WMWindowType | NET::WMDesktop | NET::WMState | NET::XAWMState);
487 m_dirtyDesktop = info.desktop() - 1;
489 if (!m_timer->isActive()) {
490 m_timer->start(FAST_UPDATE_DELAY);
494 void Pager::numberOfDesktopsChanged(int num)
496 if (num < 1) {
497 return; // refuse to update to zero desktops
500 m_dirtyDesktop = -1;
501 m_desktopCount = num;
503 if (m_rows > m_desktopCount) {
504 m_rows = m_desktopCount;
507 m_rects.clear();
508 recalculateGeometry();
509 recalculateWindowRects();
512 void Pager::desktopNamesChanged()
514 m_dirtyDesktop = -1;
516 m_rects.clear();
517 recalculateGeometry();
519 if (!m_timer->isActive()) {
520 m_timer->start(UPDATE_DELAY);
524 void Pager::stackingOrderChanged()
526 m_dirtyDesktop = -1;
528 if (!m_timer->isActive()) {
529 m_timer->start(FAST_UPDATE_DELAY);
533 void Pager::windowChanged(WId id, unsigned int properties)
535 Q_UNUSED(id)
537 if (properties & NET::WMGeometry) {
538 KWindowInfo info = KWindowSystem::windowInfo(id, NET::WMGeometry | NET::WMFrameExtents | NET::WMWindowType | NET::WMDesktop | NET::WMState | NET::XAWMState);
539 m_dirtyDesktop = info.desktop() - 1;
540 } else {
541 m_dirtyDesktop = -1;
544 if (properties & NET::WMGeometry ||
545 properties & NET::WMDesktop) {
546 if (!m_timer->isActive()) {
547 m_timer->start(UPDATE_DELAY);
552 void Pager::showingDesktopChanged(bool showing)
554 m_dirtyDesktop = -1;
556 Q_UNUSED(showing)
557 if (!m_timer->isActive()) {
558 m_timer->start(UPDATE_DELAY);
562 void Pager::desktopsSizeChanged()
564 m_dirtyDesktop = -1;
566 m_rects.clear();
567 recalculateGeometry();
569 if (!m_timer->isActive()) {
570 m_timer->start(UPDATE_DELAY);
574 void Pager::mousePressEvent(QGraphicsSceneMouseEvent *event)
576 if (event->buttons() != Qt::RightButton)
578 for (int i = 0; i < m_rects.count(); ++i) {
579 if (m_rects[i].contains(event->pos())) {
580 m_dragStartDesktop = m_dragHighlightedDesktop = i;
581 m_dragOriginalPos = m_dragCurrentPos = event->pos();
582 if (m_dragOriginal.isEmpty()) {
583 m_dragOriginal = m_rects[i].toRect();
586 update();
587 return;
591 Applet::mousePressEvent(event);
594 void Pager::wheelEvent(QGraphicsSceneWheelEvent *e)
596 int newDesk;
597 int desktops = KWindowSystem::numberOfDesktops();
600 if (m_kwin->numberOfViewports(0).width() * m_kwin->numberOfViewports(0).height() > 1 )
601 desktops = m_kwin->numberOfViewports(0).width() * m_kwin->numberOfViewports(0).height();
603 if (e->delta() < 0) {
604 newDesk = m_currentDesktop % desktops + 1;
605 } else {
606 newDesk = (desktops + m_currentDesktop - 2) % desktops + 1;
609 KWindowSystem::setCurrentDesktop(newDesk);
610 m_currentDesktop = newDesk;
611 update();
613 Applet::wheelEvent(e);
616 void Pager::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
618 if (m_dragId > 0) {
619 m_dragCurrentPos = event->pos();
620 m_dragHighlightedDesktop = -1;
621 m_hoverRect = QRectF();
622 int i = 0;
623 foreach (const QRectF &rect, m_rects) {
624 if (rect.contains(event->pos())) {
625 m_dragHighlightedDesktop = i;
626 m_hoverRect = rect;
627 break;
630 ++i;
632 update();
633 event->accept();
634 return;
635 } else if (m_dragStartDesktop != -1 &&
636 (event->pos() - m_dragOriginalPos).toPoint().manhattanLength() > KGlobalSettings::dndEventDelay()) {
637 m_dragId = 0; // prevent us from going through this more than once
638 for (int k = m_windowRects[m_dragStartDesktop].count() - 1; k >= 0 ; k--) {
639 if (m_windowRects[m_dragStartDesktop][k].second.contains(m_dragOriginalPos.toPoint())) {
640 m_dragOriginal = m_windowRects[m_dragStartDesktop][k].second;
641 m_dragId = m_windowRects[m_dragStartDesktop][k].first;
642 event->accept();
643 break;
648 if (m_dragOriginal.isEmpty()) {
649 Applet::mouseMoveEvent(event);
653 void Pager::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
655 if (m_dragId) {
656 if (m_dragHighlightedDesktop != -1) {
657 QPointF dest = m_dragCurrentPos - m_rects[m_dragHighlightedDesktop].topLeft() - m_dragOriginalPos + m_dragOriginal.topLeft();
658 dest = QPointF(dest.x()/m_widthScaleFactor, dest.y()/m_heightScaleFactor);
659 // don't move windows to negative positions
660 dest = QPointF(qMax(dest.x(), qreal(0.0)), qMax(dest.y(), qreal(0.0)));
661 if (!KWindowSystem::mapViewport()) {
662 KWindowInfo info = KWindowSystem::windowInfo(m_dragId, NET::WMDesktop);
664 if (!info.onAllDesktops()) {
665 KWindowSystem::setOnDesktop(m_dragId, m_dragHighlightedDesktop+1);
668 // only move the window if it is kept within the same desktop
669 // moving when dropping between desktop is too annoying due to
670 // the small drop area.
671 if (m_dragHighlightedDesktop == m_dragStartDesktop || info.onAllDesktops()) {
672 // use _NET_MOVERESIZE_WINDOW rather than plain move, so that the WM knows this is a pager request
673 NETRootInfo i( QX11Info::display(), 0 );
674 int flags = ( 0x20 << 12 ) | ( 0x03 << 8 ) | 1; // from tool, x/y, northwest gravity
675 i.moveResizeWindowRequest( m_dragId, flags, dest.toPoint().x(), dest.toPoint().y(), 0, 0 );
677 } else {
678 // setOnDesktop() with viewports is also moving a window, and since it takes a moment
679 // for the WM to do the move, there's a race condition with figuring out how much to move,
680 // so do it only as one move
681 dest += KWindowSystem::desktopToViewport( m_dragHighlightedDesktop+1, false );
682 QPoint d = KWindowSystem::constrainViewportRelativePosition( dest.toPoint());
683 NETRootInfo i( QX11Info::display(), 0 );
684 int flags = ( 0x20 << 12 ) | ( 0x03 << 8 ) | 1; // from tool, x/y, northwest gravity
685 i.moveResizeWindowRequest( m_dragId, flags, d.x(), d.y(), 0, 0 );
688 m_timer->start();
689 } else if (m_dragStartDesktop != -1 && m_dragStartDesktop < m_rects.size() &&
690 m_rects[m_dragStartDesktop].contains(event->pos()) &&
691 m_currentDesktop != m_dragStartDesktop + 1) {
692 // only change the desktop if the user presses and releases the mouse on the same desktop
693 KWindowSystem::setCurrentDesktop(m_dragStartDesktop + 1);
694 m_currentDesktop = m_dragStartDesktop + 1;
697 m_dragId = 0;
698 m_dragOriginal = QRect();
699 m_dragHighlightedDesktop = -1;
700 m_dragStartDesktop = -1;
701 m_dragOriginalPos = m_dragCurrentPos = QPointF();
703 update();
704 Applet::mouseReleaseEvent(event);
707 // If the pager is hovered in drag and drop mode, no hover events are geneated.
708 // This method provides the common implementation for hoverMoveEvent and dragMoveEvent.
709 void Pager::handleHoverMove(const QPointF& pos)
711 bool changedHover = !m_hoverRect.contains(pos);
712 Plasma::Animator *anim = Plasma::Animator::self();
714 if (changedHover && m_hoverIndex > -1) {
715 if (m_animations[m_hoverIndex].animId != -1) {
716 Plasma::Animator::self()->stopCustomAnimation(m_animations[m_hoverIndex].animId);
718 m_animations[m_hoverIndex].fadeIn = false;
719 m_animations[m_hoverIndex].alpha = 1;
720 m_animations[m_hoverIndex].animId = anim->customAnimation(40 / (1000 / s_FadeOutDuration), s_FadeOutDuration,Plasma::Animator::EaseOutCurve, this,"animationUpdate");
723 if (!changedHover) {
724 return;
727 int i = 0;
728 foreach (const QRectF &rect, m_rects) {
729 if (rect.contains(pos)) {
730 if (m_hoverRect != rect) {
731 m_hoverRect = rect;
732 m_hoverIndex = i;
733 if (m_animations[m_hoverIndex].animId != -1) {
734 anim->stopCustomAnimation(m_animations[i].animId);
736 m_animations[m_hoverIndex].fadeIn = true;
737 m_animations[m_hoverIndex].alpha = 0;
738 m_animations[m_hoverIndex].animId = anim->customAnimation(40 / (1000 / s_FadeInDuration), s_FadeInDuration,Plasma::Animator::EaseInCurve, this,"animationUpdate");
739 update();
740 updateToolTip();
742 return;
744 ++i;
746 m_hoverIndex = -1;
747 m_hoverRect = QRectF();
748 update();
751 // If the pager is hovered in drag and drop mode, no hover events are geneated.
752 // This method provides the common implementation for hoverLeaveEvent and dragLeaveEvent.
753 void Pager::handleHoverLeave()
755 if (m_hoverRect != QRectF()) {
756 m_hoverRect = QRectF();
757 update();
760 if (m_hoverIndex != -1) {
761 if (m_animations[m_hoverIndex].animId != -1) {
762 Plasma::Animator::self()->stopCustomAnimation(m_animations[m_hoverIndex].animId);
764 m_animations[m_hoverIndex].fadeIn = false;
765 m_animations[m_hoverIndex].alpha = 1;
766 m_animations[m_hoverIndex].animId = Plasma::Animator::self()->customAnimation(40 / (1000 / s_FadeOutDuration), s_FadeOutDuration,Plasma::Animator::EaseOutCurve, this,"animationUpdate");
767 m_hoverIndex = -1;
770 // The applet doesn't always get mouseReleaseEvents, for example when starting a drag
771 // on the containment and releasing the mouse on the desktop or another window. This can cause
772 // weird bugs because the pager still thinks a drag is going on.
773 // The only reliable event I found is the hoverLeaveEvent, so we just stop the drag
774 // on this event.
775 if (m_dragId || m_dragStartDesktop != -1) {
776 m_dragId = 0;
777 m_dragOriginal = QRect();
778 m_dragHighlightedDesktop = -1;
779 m_dragStartDesktop = -1;
780 m_dragOriginalPos = m_dragCurrentPos = QPointF();
781 update();
785 void Pager::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
787 handleHoverMove(event->pos());
788 Applet::hoverEnterEvent(event);
791 void Pager::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
793 handleHoverMove(event->pos());
796 void Pager::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
798 handleHoverLeave();
799 Applet::hoverLeaveEvent(event);
802 void Pager::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
804 if (event->mimeData()->hasFormat(TaskManager::Task::mimetype())) {
805 event->setAccepted(true);
806 return;
808 else event->setAccepted(false);
809 handleHoverMove(event->pos());
811 if (m_hoverIndex != -1) {
812 m_dragSwitchDesktop = m_hoverIndex;
813 m_dragSwitchTimer->start(DRAG_SWITCH_DELAY);
815 Applet::dragEnterEvent(event);
818 void Pager::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
820 handleHoverMove(event->pos());
822 if (m_dragSwitchDesktop != m_hoverIndex && m_hoverIndex != -1) {
823 m_dragSwitchDesktop = m_hoverIndex;
824 m_dragSwitchTimer->start(DRAG_SWITCH_DELAY);
825 } else if (m_hoverIndex == -1) {
826 m_dragSwitchDesktop = m_hoverIndex;
827 m_dragSwitchTimer->stop();
829 Applet::dragMoveEvent(event);
832 void Pager::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
834 handleHoverLeave();
836 m_dragSwitchDesktop = -1;
837 m_dragSwitchTimer->stop();
838 Applet::dragLeaveEvent(event);
841 void Pager::dropEvent(QGraphicsSceneDragDropEvent *event)
843 bool ok;
844 WId id = TaskManager::Task::idFromMimeData(event->mimeData(), &ok);
845 if (ok) {
846 for (int i = 0; i < m_rects.count(); ++i) {
847 if (m_rects[i].contains(event->pos().toPoint())) {
848 KWindowSystem::setOnDesktop(id, i + 1);
849 m_dragSwitchDesktop = -1;
850 break;
856 void Pager::animationUpdate(qreal progress, int animId)
858 int i = 0;
859 foreach (AnimInfo anim, m_animations) {
860 if (anim.animId == animId) {
861 break;
863 i++;
866 if (i >= m_animations.size()) {
867 return;
870 m_animations[i].alpha = m_animations[i].fadeIn ? progress : 1 - progress;
872 if (progress == 1) {
873 m_animations[i].animId = -1;
874 m_animations[i].fadeIn = true;
877 // explicit update
878 update();
881 void Pager::dragSwitch()
883 if (m_dragSwitchDesktop == -1) {
884 return;
886 KWindowSystem::setCurrentDesktop(m_dragSwitchDesktop + 1);
887 m_currentDesktop = m_dragSwitchDesktop + 1;
890 void Pager::paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRect &contentsRect)
892 Q_UNUSED( option );
893 Q_UNUSED( contentsRect );
895 KColorScheme* plasmaColorTheme = colorScheme();
896 painter->setFont(KGlobalSettings::taskbarFont());
898 // Desktop background
899 QColor defaultTextColor = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor);
900 QColor hoverColor = defaultTextColor;
901 hoverColor.setAlpha(64);
903 // Inactive windows
904 QColor drawingColor = plasmaColorTheme->foreground(KColorScheme::InactiveText).color();
905 drawingColor.setAlpha(45);
906 QBrush windowBrush(drawingColor);
907 // Inactive windows Active desktop
908 drawingColor.setAlpha(90);
909 QBrush windowBrushActiveDesk(drawingColor);
911 // Inactive window borders
912 drawingColor = defaultTextColor;
913 drawingColor.setAlpha(130);
914 QPen windowPen(drawingColor);
916 // Active window borders
917 QPen activeWindowPen(defaultTextColor);
919 // Active windows
920 drawingColor.setAlpha(130);
921 QBrush activeWindowBrush(drawingColor);
922 // Active windows Active desktop
923 drawingColor.setAlpha(155);
924 QBrush activeWindowBrushActiveDesk(drawingColor);
926 if (m_showOwnBackground && (formFactor() == Plasma::Vertical || formFactor() == Plasma::Horizontal)) {
927 m_background->setElementPrefix(QString());
928 m_background->paintFrame(painter);
931 // Draw backgrounds of desktops only when there are not the proper theme elements
932 painter->setPen(Qt::NoPen);
933 if (!m_background->hasElementPrefix("hover")) {
934 for (int i = 0; i < m_rects.count(); i++) {
935 if (m_rects[i] == m_hoverRect) {
936 QColor animHoverColor = hoverColor;
937 if (m_animations[i].animId > -1) {
938 animHoverColor.setAlpha(hoverColor.alpha()*m_animations[i].alpha);
940 painter->setBrush(animHoverColor);
941 painter->drawRect(m_rects[i]);
946 // Draw miniatures of windows from each desktop
947 painter->setPen(windowPen);
948 for (int i = 0; i < m_windowRects.count(); i++) {
949 for (int j = 0; j < m_windowRects[i].count(); j++) {
950 QRect rect = m_windowRects[i][j].second;
952 if (m_currentDesktop > 0 &&
953 m_currentDesktop <= m_rects.count() &&
954 m_rects[m_currentDesktop-1].contains(rect)) {
955 if (m_activeWindows.contains(rect)) {
956 painter->setBrush(activeWindowBrushActiveDesk);
957 painter->setPen(activeWindowPen);
958 } else {
959 painter->setBrush(windowBrushActiveDesk);
960 painter->setPen(windowPen);
962 } else {
963 if (m_activeWindows.contains(rect)) {
964 painter->setBrush(activeWindowBrush);
965 painter->setPen(activeWindowPen);
966 } else {
967 painter->setBrush(windowBrush);
968 painter->setPen(windowPen);
971 if (m_dragId == m_windowRects[i][j].first) {
972 rect.translate((m_dragCurrentPos - m_dragOriginalPos).toPoint());
973 painter->setClipRect(option->exposedRect);
974 } else if (i < m_rects.count()) {
975 painter->setClipRect(m_rects[i].adjusted(1, 1, -1, -1));
977 painter->drawRect(rect);
978 if ((rect.width() > 16) && (rect.height() > 16) && m_showWindowIcons){
979 painter->drawPixmap(rect.x() + (rect.width() - 16) / 2, rect.y() + (rect.height() - 16) / 2, 16, 16,
980 KWindowSystem::icon(m_windowRects[i][j].first, 16, 16, true));
985 // Draw desktop frame and possibly text over it
986 painter->setClipRect(option->exposedRect);
987 painter->setBrush(Qt::NoBrush);
989 QString prefix;
990 for (int i = 0; i < m_rects.count(); i++) {
991 if (i + 1 == m_currentDesktop || i == m_dragHighlightedDesktop) {
992 prefix = "active";
993 } else {
994 prefix = "normal";
997 //Paint the panel or fallback if we don't have that prefix
998 if (m_background->hasElementPrefix(prefix)) {
999 m_background->setElementPrefix(prefix);
1000 if (m_animations[i].animId > -1) {
1001 QPixmap normal = m_background->framePixmap();
1002 m_background->setElementPrefix("hover");
1003 QPixmap result = Plasma::PaintUtils::transition(normal, m_background->framePixmap(), m_animations[i].alpha);
1004 painter->drawPixmap(m_rects[i].topLeft(), result);
1005 } else {
1006 //no anims, simpler thing
1007 if (m_rects[i] == m_hoverRect) {
1008 m_background->setElementPrefix("hover");
1010 m_background->paintFrame(painter, m_rects[i].topLeft());
1012 } else {
1013 QPen drawingPen;
1015 if (i + 1 == m_currentDesktop || i == m_dragHighlightedDesktop) {
1016 defaultTextColor.setAlphaF(1);
1017 drawingPen = QPen(defaultTextColor);
1018 } else {
1019 drawingPen = QPen(plasmaColorTheme->foreground(KColorScheme::InactiveText).color());
1022 painter->setPen(drawingPen);
1023 painter->drawRect(m_rects[i]);
1026 //Draw text
1027 if (m_animations[i].animId == -1) {
1028 defaultTextColor.setAlphaF(1);
1030 defaultTextColor.setAlphaF(m_animations[i].alpha / 2 + 0.5);
1031 painter->setPen(defaultTextColor);
1033 QColor shadowColor(Qt::black);
1034 if (defaultTextColor.value() < 128) {
1035 shadowColor = Qt::white;
1038 QString desktopText = QString();
1039 if (m_displayedText == Number) { // Display number of desktop
1040 desktopText = QString::number(i + 1);
1041 } else if (m_displayedText == Name) { // Display name of desktop
1042 desktopText = KWindowSystem::desktopName(i + 1);
1045 if (!desktopText.isEmpty()) {
1046 int radius = 2;
1047 QPixmap result = Plasma::PaintUtils::shadowText(desktopText,
1048 KGlobalSettings::smallestReadableFont(),
1049 defaultTextColor,
1050 shadowColor, QPoint(0, 0), radius);
1051 QRectF target = m_rects[i];
1052 //take also shadow position and radius into account
1053 //kDebug() << target << result.height();
1054 if (target.height() >= result.height() - radius * 2) {
1055 QPointF paintPoint = target.center() - (result.rect().center() + QPoint(radius, radius));
1057 if (paintPoint.x() + radius < target.x() + 1) {
1058 paintPoint.setX(target.x() + 1 - radius);
1061 if (paintPoint.y() + radius < target.y() + 1) {
1062 paintPoint.setY(target.y() + 1 - radius);
1065 target.moveTopLeft(QPointF(0, 0));
1066 painter->drawPixmap(paintPoint, result, target);
1072 void Pager::lostDesktopLayoutOwner()
1074 delete m_desktopLayoutOwner;
1075 m_desktopLayoutOwner = NULL;
1078 // KWindowSystem does not translate position when mapping viewports
1079 // to virtual desktops (it'd probably break more things than fix),
1080 // so the offscreen coordinates need to be fixed
1081 QRect Pager::fixViewportPosition( const QRect& r )
1083 QRect desktopGeom = Kephal::ScreenUtils::desktopGeometry();
1084 int x = r.center().x() % desktopGeom.width();
1085 int y = r.center().y() % desktopGeom.height();
1086 if( x < 0 ) {
1087 x = x + desktopGeom.width();
1089 if( y < 0 ) {
1090 y = y + desktopGeom.height();
1092 return QRect( x - r.width() / 2, y - r.height() / 2, r.width(), r.height());
1095 void Pager::themeRefresh()
1097 delete m_colorScheme;
1098 m_colorScheme = 0;
1101 void Pager::updateToolTip()
1103 int hoverDesktopNumber = 0;
1105 for (int i = 0; i < m_desktopCount; i++) {
1106 if (m_rects[i] == m_hoverRect) {
1107 hoverDesktopNumber = i + 1;
1111 Plasma::ToolTipContent data;
1112 QString subtext = QString();
1113 int taskCounter = 0;
1114 int displayedTaskCounter = 0;
1116 QList<WId> windows;
1118 foreach(KWindowInfo winInfo, m_windowInfo){
1119 if (winInfo.isOnDesktop(hoverDesktopNumber) && !windows.contains(winInfo.win())) {
1120 bool active = (winInfo.win() == KWindowSystem::activeWindow());
1121 if ((taskCounter < 4) || active){
1122 QPixmap icon = KWindowSystem::icon(winInfo.win(), 16, 16, true);
1123 if (icon.isNull()) {
1124 subtext += "<br />&bull;" + winInfo.visibleName();
1125 } else {
1126 data.addResource(Plasma::ToolTipContent::ImageResource, QUrl("wicon://" + QString::number(taskCounter)), QVariant(icon));
1127 subtext += "<br /><img src=\"wicon://" + QString::number(taskCounter) + "\"/>";
1129 subtext += (active ? "<u>" : "") + winInfo.visibleName() + (active ? "</u>" : "");
1131 displayedTaskCounter++;
1132 windows.append(winInfo.win());
1134 taskCounter++;
1138 if (taskCounter) {
1139 subtext.prepend(i18np("One window:", "%1 windows:", taskCounter));
1142 if (taskCounter - displayedTaskCounter > 0) {
1143 subtext.append("<br>&bull; <i>" + i18np("and 1 other", "and %1 others", taskCounter - displayedTaskCounter) + "</i>");
1146 data.setMainText(KWindowSystem::desktopName(hoverDesktopNumber));
1147 data.setSubText(subtext);
1149 Plasma::ToolTipManager::self()->setContent(this, data);
1152 #include "pager.moc"