1 /***************************************************************************
2 * Copyright (C) 2007 by Daniel Laidig <d.laidig@gmx.de> *
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. *
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 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 ***************************************************************************/
24 #include <QApplication>
26 #include <QStyleOptionGraphicsItem>
28 #include <QGraphicsSceneHoverEvent>
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>
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),
68 m_dragStartDesktop(-1),
69 m_dragHighlightedDesktop(-1),
70 m_dragSwitchDesktop(-1)
72 setAcceptsHoverEvents(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);
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
);
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();
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
);
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()
183 void Pager::slotConfigureDesktop()
186 KToolInvocation::startServiceByDesktopName("desktop", QStringList(), &error
);
189 void Pager::createConfigurationInterface(KConfigDialog
*parent
)
191 QWidget
*widget
= new QWidget();
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
){
200 ui
. desktopNumberRadioButton
->setChecked(true);
204 ui
.desktopNameRadioButton
->setChecked(true);
208 ui
.displayNoneRadioButton
->setChecked(true);
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;
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
;
229 //inverse rows and columns in vertical panel
230 if (formFactor() == Plasma::Vertical
) {
235 qreal leftMargin
= 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;
261 m_showOwnBackground
= true;
264 getContentsMargins(&leftMargin
, &topMargin
, &rightMargin
, &bottomMargin
);
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
;
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();
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
);
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;
329 setPreferredSize(m_size
);
330 if (m_desktopLayoutOwner
&& columns
!= m_columns
) {
331 // must own manager selection before setting global desktop layout
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
|
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()) {
364 for (int i
= 0; i
< m_desktopCount
; i
++) {
365 if (!info
.isOnDesktop(i
+1)) {
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
);
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
;
405 displayedText
= None
;
408 if ((int)m_displayedText
!= (int)displayedText
) {
409 m_displayedText
= displayedText
;
410 cg
.writeEntry("displayedText", (int)m_displayedText
);
414 if (m_showWindowIcons
!= ui
.showWindowIconsCheckBox
->isChecked()) {
415 m_showWindowIcons
= ui
.showWindowIconsCheckBox
->isChecked();
416 cg
.writeEntry("showWindowIcons", m_showWindowIcons
);
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
);
437 m_size
= QSizeF(-1, -1);
438 recalculateGeometry();
439 recalculateWindowRects();
444 void Pager::currentDesktopChanged(int desktop
)
447 return; // bogus value, don't accept it
450 m_currentDesktop
= desktop
;
453 if (!m_timer
->isActive()) {
454 m_timer
->start(FAST_UPDATE_DELAY
);
458 void Pager::windowAdded(WId 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
)
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
)
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
)
497 return; // refuse to update to zero desktops
501 m_desktopCount
= num
;
503 if (m_rows
> m_desktopCount
) {
504 m_rows
= m_desktopCount
;
508 recalculateGeometry();
509 recalculateWindowRects();
512 void Pager::desktopNamesChanged()
517 recalculateGeometry();
519 if (!m_timer
->isActive()) {
520 m_timer
->start(UPDATE_DELAY
);
524 void Pager::stackingOrderChanged()
528 if (!m_timer
->isActive()) {
529 m_timer
->start(FAST_UPDATE_DELAY
);
533 void Pager::windowChanged(WId id
, unsigned int properties
)
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;
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
)
557 if (!m_timer
->isActive()) {
558 m_timer
->start(UPDATE_DELAY
);
562 void Pager::desktopsSizeChanged()
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();
591 Applet::mousePressEvent(event
);
594 void Pager::wheelEvent(QGraphicsSceneWheelEvent
*e
)
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;
606 newDesk
= (desktops
+ m_currentDesktop
- 2) % desktops
+ 1;
609 KWindowSystem::setCurrentDesktop(newDesk
);
610 m_currentDesktop
= newDesk
;
613 Applet::wheelEvent(e
);
616 void Pager::mouseMoveEvent(QGraphicsSceneMouseEvent
*event
)
619 m_dragCurrentPos
= event
->pos();
620 m_dragHighlightedDesktop
= -1;
621 m_hoverRect
= QRectF();
623 foreach (const QRectF
&rect
, m_rects
) {
624 if (rect
.contains(event
->pos())) {
625 m_dragHighlightedDesktop
= i
;
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
;
648 if (m_dragOriginal
.isEmpty()) {
649 Applet::mouseMoveEvent(event
);
653 void Pager::mouseReleaseEvent(QGraphicsSceneMouseEvent
*event
)
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 );
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 );
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;
698 m_dragOriginal
= QRect();
699 m_dragHighlightedDesktop
= -1;
700 m_dragStartDesktop
= -1;
701 m_dragOriginalPos
= m_dragCurrentPos
= QPointF();
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");
728 foreach (const QRectF
&rect
, m_rects
) {
729 if (rect
.contains(pos
)) {
730 if (m_hoverRect
!= rect
) {
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");
747 m_hoverRect
= QRectF();
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();
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");
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
775 if (m_dragId
|| m_dragStartDesktop
!= -1) {
777 m_dragOriginal
= QRect();
778 m_dragHighlightedDesktop
= -1;
779 m_dragStartDesktop
= -1;
780 m_dragOriginalPos
= m_dragCurrentPos
= QPointF();
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
)
799 Applet::hoverLeaveEvent(event
);
802 void Pager::dragEnterEvent(QGraphicsSceneDragDropEvent
*event
)
804 if (event
->mimeData()->hasFormat(TaskManager::Task::mimetype())) {
805 event
->setAccepted(true);
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
)
836 m_dragSwitchDesktop
= -1;
837 m_dragSwitchTimer
->stop();
838 Applet::dragLeaveEvent(event
);
841 void Pager::dropEvent(QGraphicsSceneDragDropEvent
*event
)
844 WId id
= TaskManager::Task::idFromMimeData(event
->mimeData(), &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;
856 void Pager::animationUpdate(qreal progress
, int animId
)
859 foreach (AnimInfo anim
, m_animations
) {
860 if (anim
.animId
== animId
) {
866 if (i
>= m_animations
.size()) {
870 m_animations
[i
].alpha
= m_animations
[i
].fadeIn
? progress
: 1 - progress
;
873 m_animations
[i
].animId
= -1;
874 m_animations
[i
].fadeIn
= true;
881 void Pager::dragSwitch()
883 if (m_dragSwitchDesktop
== -1) {
886 KWindowSystem::setCurrentDesktop(m_dragSwitchDesktop
+ 1);
887 m_currentDesktop
= m_dragSwitchDesktop
+ 1;
890 void Pager::paintInterface(QPainter
*painter
, const QStyleOptionGraphicsItem
*option
, const QRect
&contentsRect
)
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);
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
);
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
);
959 painter
->setBrush(windowBrushActiveDesk
);
960 painter
->setPen(windowPen
);
963 if (m_activeWindows
.contains(rect
)) {
964 painter
->setBrush(activeWindowBrush
);
965 painter
->setPen(activeWindowPen
);
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
);
990 for (int i
= 0; i
< m_rects
.count(); i
++) {
991 if (i
+ 1 == m_currentDesktop
|| i
== m_dragHighlightedDesktop
) {
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
);
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());
1015 if (i
+ 1 == m_currentDesktop
|| i
== m_dragHighlightedDesktop
) {
1016 defaultTextColor
.setAlphaF(1);
1017 drawingPen
= QPen(defaultTextColor
);
1019 drawingPen
= QPen(plasmaColorTheme
->foreground(KColorScheme::InactiveText
).color());
1022 painter
->setPen(drawingPen
);
1023 painter
->drawRect(m_rects
[i
]);
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()) {
1047 QPixmap result
= Plasma::PaintUtils::shadowText(desktopText
,
1048 KGlobalSettings::smallestReadableFont(),
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();
1087 x
= x
+ desktopGeom
.width();
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
;
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;
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 />•" + winInfo
.visibleName();
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());
1139 subtext
.prepend(i18np("One window:", "%1 windows:", taskCounter
));
1142 if (taskCounter
- displayedTaskCounter
> 0) {
1143 subtext
.append("<br>• <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"