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
***************************************************************************/
25
#include <QStyleOptionGraphicsItem>
27
#include <QGraphicsSceneHoverEvent>
30
#include <QDBusInterface>
31
#include <QTextDocument>
32
#include <QPropertyAnimation>
34
#include <KColorScheme>
35
#include <KConfigDialog>
36
#include <KGlobalSettings>
37
#include <KSharedConfig>
38
#include <KCModuleProxy>
39
#include <KCModuleInfo>
40
#include <KWindowSystem>
41
#include <NETRootInfo>
42
#include <kmanagerselection.h>
45
#include <Plasma/FrameSvg>
46
#include <Plasma/PaintUtils>
47
#include <Plasma/Theme>
48
#include <Plasma/ToolTipManager>
49
#include <Plasma/Animator>
51
#include <kephal/screens.h>
52
#include <kactivityconsumer.h>
54
#include <taskmanager/task.h>
56
const int FAST_UPDATE_DELAY = 100;
57
const int UPDATE_DELAY = 500;
58
const int DRAG_SWITCH_DELAY = 1000;
59
const int MAXDESKTOPS = 20;
61
DesktopRectangle::DesktopRectangle(QObject *parent)
67
QPropertyAnimation *DesktopRectangle::animation() const
69
return m_animation.data();
72
void DesktopRectangle::setAnimation(QPropertyAnimation *animation)
74
m_animation = animation;
77
qreal DesktopRectangle::alphaValue() const
82
void DesktopRectangle::setAlphaValue(qreal value)
86
Pager *parentItem = qobject_cast<Pager*>(parent());
90
Pager::Pager(QObject *parent, const QVariantList &args)
91
: Plasma::Applet(parent, args),
92
m_displayedText(None),
93
m_currentDesktopSelected(DoNothing),
94
m_showWindowIcons(false),
95
m_showOwnBackground(false),
100
m_addDesktopAction(0),
101
m_removeDesktopAction(0),
103
m_verticalFormFactor(false),
105
m_dragStartDesktop(-1),
106
m_dragHighlightedDesktop(-1),
107
m_dragSwitchDesktop(-1),
108
m_ignoreNextSizeConstraint(false),
109
m_configureDesktopsWidget(0)
111
setAcceptsHoverEvents(true);
112
setAcceptDrops(true);
113
setHasConfigurationInterface(true);
114
setAspectRatioMode(Plasma::IgnoreAspectRatio);
116
m_background = new Plasma::FrameSvg(this);
117
m_background->setImagePath("widgets/pager");
118
m_background->setCacheAllRenderedFrames(true);
120
// initialize with a decent default
121
m_desktopCount = KWindowSystem::numberOfDesktops();
122
m_size = QSizeF(176, 88);
128
delete m_colorScheme;
135
m_verticalFormFactor = (formFactor() == Plasma::Vertical);
139
m_timer = new QTimer(this);
140
m_timer->setSingleShot(true);
141
connect(m_timer, SIGNAL(timeout()), this, SLOT(recalculateWindowRects()));
143
m_dragSwitchTimer = new QTimer(this);
144
m_dragSwitchTimer->setSingleShot(true);
145
connect(m_dragSwitchTimer, SIGNAL(timeout()), this, SLOT(dragSwitch()));
147
connect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)), this, SLOT(currentDesktopChanged(int)));
148
connect(KWindowSystem::self(), SIGNAL(windowAdded(WId)), this, SLOT(windowAdded(WId)));
149
connect(KWindowSystem::self(), SIGNAL(windowRemoved(WId)), this, SLOT(windowRemoved(WId)));
150
connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)), this, SLOT(activeWindowChanged(WId)));
151
connect(KWindowSystem::self(), SIGNAL(numberOfDesktopsChanged(int)), this, SLOT(numberOfDesktopsChanged(int)));
152
connect(KWindowSystem::self(), SIGNAL(desktopNamesChanged()), this, SLOT(desktopNamesChanged()));
153
connect(KWindowSystem::self(), SIGNAL(stackingOrderChanged()), this, SLOT(stackingOrderChanged()));
154
connect(KWindowSystem::self(), SIGNAL(windowChanged(WId,unsigned long*)), this, SLOT(windowChanged(WId,unsigned long*)));
155
connect(KWindowSystem::self(), SIGNAL(showingDesktopChanged(bool)), this, SLOT(showingDesktopChanged(bool)));
156
connect(Kephal::Screens::self(), SIGNAL(screenAdded(Kephal::Screen *)), SLOT(desktopsSizeChanged()));
157
connect(Kephal::Screens::self(), SIGNAL(screenRemoved(int)), SLOT(desktopsSizeChanged()));
158
connect(Kephal::Screens::self(), SIGNAL(screenResized(Kephal::Screen *, QSize, QSize)), SLOT(desktopsSizeChanged()));
159
connect(Kephal::Screens::self(), SIGNAL(screenMoved(Kephal::Screen *, QPoint, QPoint)), SLOT(desktopsSizeChanged()));
161
m_desktopLayoutOwner = new KSelectionOwner( QString( "_NET_DESKTOP_LAYOUT_S%1" )
162
.arg( QX11Info::appScreen()).toLatin1().constData(), QX11Info::appScreen(), this );
163
connect( m_desktopLayoutOwner, SIGNAL( lostOwnership()), SLOT( lostDesktopLayoutOwner()));
164
if ( !m_desktopLayoutOwner->claim( false ))
165
lostDesktopLayoutOwner();
167
connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeRefresh()));
169
recalculateGridSizes(m_rows);
171
m_currentDesktop = KWindowSystem::currentDesktop();
173
KActivityConsumer *act = new KActivityConsumer(this);
174
connect(act, SIGNAL(currentActivityChanged(QString)), this, SLOT(currentActivityChanged(QString)));
175
m_currentActivity = act->currentActivity();
177
if (m_desktopCount < 2) {
178
numberOfDesktopsChanged(m_desktopCount);
182
void Pager::configChanged()
184
KConfigGroup cg = config();
185
bool changed = false;
187
DisplayedText displayedText = (DisplayedText) cg.readEntry("displayedText", (int) m_displayedText);
188
if (displayedText != m_displayedText) {
189
m_displayedText = displayedText;
193
bool showWindowIcons = cg.readEntry("showWindowIcons", m_showWindowIcons);
194
if (showWindowIcons != m_showWindowIcons) {
195
m_showWindowIcons = showWindowIcons;
199
CurrentDesktopSelected currentDesktopSelected =
200
(CurrentDesktopSelected) cg.readEntry("currentDesktopSelected",
201
(int) m_currentDesktopSelected);
202
if (currentDesktopSelected != m_currentDesktopSelected) {
203
m_currentDesktopSelected = currentDesktopSelected;
207
int rows = globalConfig().readEntry("rows", m_rows);
209
if (changed || rows != m_rows) {
210
recalculateGridSizes(rows);
211
recalculateWindowRects();
216
void Pager::constraintsEvent(Plasma::Constraints constraints)
218
if (constraints & Plasma::SizeConstraint) {
219
// no need to update everything twice (if we are going to flip rows and columns later)
220
if (!(constraints & Plasma::FormFactorConstraint) ||
221
m_verticalFormFactor == (formFactor() == Plasma::Vertical) ||
222
m_columns == m_rows) {
223
// use m_ignoreNextSizeConstraint to decide whether to try to resize the plasmoid again
224
updateSizes(!m_ignoreNextSizeConstraint);
225
m_ignoreNextSizeConstraint = !m_ignoreNextSizeConstraint;
227
recalculateWindowRects();
230
if (m_background->hasElementPrefix(QString())) {
231
m_background->setElementPrefix(QString());
232
m_background->resizeFrame(size());
237
if (constraints & Plasma::FormFactorConstraint) {
239
if (m_verticalFormFactor != (formFactor() == Plasma::Vertical)) {
240
m_verticalFormFactor = (formFactor() == Plasma::Vertical);
241
// whenever we switch to/from vertical form factor, swap the rows and columns around
242
if (m_columns != m_rows) {
243
// pass in columns as the new rows
244
recalculateGridSizes(m_columns);
245
recalculateWindowRects();
250
if (formFactor() == Plasma::Horizontal) {
251
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
252
setMinimumSize(preferredSize().width(), 0);
253
} else if (formFactor() == Plasma::Vertical) {
254
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
255
setMinimumSize(0, preferredSize().height());
257
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
258
setMinimumSize(preferredSize());
263
KColorScheme *Pager::colorScheme()
265
if (!m_colorScheme) {
266
m_colorScheme = new KColorScheme(QPalette::Active, KColorScheme::View, Plasma::Theme::defaultTheme()->colorScheme());
269
return m_colorScheme;
272
void Pager::createMenu()
275
m_addDesktopAction = new QAction(SmallIcon("list-add"),i18n("&Add Virtual Desktop"), this);
276
m_actions.append(m_addDesktopAction);
277
connect(m_addDesktopAction, SIGNAL(triggered(bool)), this , SLOT(slotAddDesktop()));
278
m_removeDesktopAction = new QAction(SmallIcon("list-remove"),i18n("&Remove Last Virtual Desktop"), this);
279
m_actions.append(m_removeDesktopAction);
280
connect(m_removeDesktopAction, SIGNAL(triggered(bool)), this , SLOT(slotRemoveDesktop()));
282
if (m_desktopCount <= 1) {
283
m_removeDesktopAction->setEnabled(false);
284
} else if (m_desktopCount >= MAXDESKTOPS) {
285
m_addDesktopAction->setEnabled(false);
290
QList<QAction*> Pager::contextualActions()
296
void Pager::slotAddDesktop()
298
NETRootInfo info(QX11Info::display(), NET::NumberOfDesktops);
299
info.setNumberOfDesktops(info.numberOfDesktops() + 1);
302
void Pager::slotRemoveDesktop()
304
NETRootInfo info(QX11Info::display(), NET::NumberOfDesktops);
305
int desktops = info.numberOfDesktops();
307
info.setNumberOfDesktops(info.numberOfDesktops() - 1);
312
void Pager::createConfigurationInterface(KConfigDialog *parent)
314
QWidget *widget = new QWidget();
316
m_configureDesktopsWidget = new KCModuleProxy("desktop");
318
parent->addPage(widget, i18n("General"), icon());
319
parent->addPage(m_configureDesktopsWidget, m_configureDesktopsWidget->moduleInfo().moduleName(),
320
m_configureDesktopsWidget->moduleInfo().icon());
322
connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
323
connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
325
switch (m_displayedText){
327
ui. desktopNumberRadioButton->setChecked(true);
331
ui.desktopNameRadioButton->setChecked(true);
335
ui.displayNoneRadioButton->setChecked(true);
339
ui.showWindowIconsCheckBox->setChecked(m_showWindowIcons);
340
if (formFactor() == Plasma::Vertical) {
341
ui.labelRows->setText(i18n("Number of columns:"));
342
ui.spinRows->setValue(m_columns);
344
ui.spinRows->setValue(m_rows);
346
ui.spinRows->setMaximum(m_desktopCount);
348
switch (m_currentDesktopSelected){
350
ui.doNothingRadioButton->setChecked(true);
354
ui.showDesktopRadioButton->setChecked(true);
358
ui.showDashboardRadioButton->setChecked(true);
362
connect(ui.desktopNumberRadioButton, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
363
connect(ui.desktopNameRadioButton, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
364
connect(ui.displayNoneRadioButton, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
365
connect(ui.showWindowIconsCheckBox, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
366
connect(ui.spinRows, SIGNAL(valueChanged(int)), parent, SLOT(settingsModified()));
367
connect(ui.doNothingRadioButton, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
368
connect(ui.showDesktopRadioButton, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
369
connect(ui.showDashboardRadioButton, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
371
connect(m_configureDesktopsWidget, SIGNAL(changed(bool)), parent, SLOT(settingsModified()));
374
void Pager::recalculateGridSizes(int rows)
376
// recalculate the number of rows and columns in the grid
377
rows = qBound(1, rows, m_desktopCount);
378
// avoid weird cases like having 3 rows for 4 desktops, where the last row is unused
379
int columns = m_desktopCount / rows;
380
if (m_desktopCount % rows > 0) {
383
rows = m_desktopCount / columns;
384
if (m_desktopCount % columns > 0) {
388
// update the grid size
389
if (m_rows != rows || m_columns != columns) {
393
// write the new number of rows to the config
394
globalConfig().writeEntry("rows", m_rows);
395
emit configNeedsSaving();
397
if (m_desktopLayoutOwner) {
398
// must own manager selection before setting global desktop layout
399
NET::Orientation orient = NET::OrientationHorizontal;
400
NETRootInfo info(QX11Info::display(), 0);
401
info.setDesktopLayout(orient, columns, rows, NET::DesktopLayoutCornerTopLeft);
408
void Pager::updateSizes(bool allowResize)
410
int padding = 2; // Space between miniatures of desktops
411
int textMargin = 3; // Space between name of desktop and border
413
qreal leftMargin = 0;
415
qreal rightMargin = 0;
416
qreal bottomMargin = 0;
418
qreal ratio = (qreal) Kephal::ScreenUtils::desktopGeometry().width() /
419
(qreal) Kephal::ScreenUtils::desktopGeometry().height();
421
// calculate the margins
422
if (formFactor() == Plasma::Vertical || formFactor() == Plasma::Horizontal) {
423
m_background->setElementPrefix(QString());
424
m_background->getMargins(leftMargin, topMargin, rightMargin, bottomMargin);
426
if (formFactor() == Plasma::Vertical) {
427
qreal optimalSize = (geometry().width() -
428
KIconLoader::SizeSmall * ratio * m_columns -
429
padding * (m_columns - 1)) / 2;
431
if (optimalSize < leftMargin || optimalSize < rightMargin) {
432
leftMargin = rightMargin = qMax(qreal(0), optimalSize);
433
m_showOwnBackground = false;
435
} else if (formFactor() == Plasma::Horizontal) {
436
qreal optimalSize = (geometry().height() -
437
KIconLoader::SizeSmall * m_rows -
438
padding * (m_rows - 1)) / 2;
440
if (optimalSize < topMargin || optimalSize < bottomMargin) {
441
topMargin = bottomMargin = qMax(qreal(0), optimalSize);
442
m_showOwnBackground = false;
446
getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
452
qreal preferredItemHeight;
453
qreal preferredItemWidth;
455
if (formFactor() == Plasma::Vertical) {
456
// work out the preferred size based on the width of the contentsRect
457
preferredItemWidth = (contentsRect().width() - leftMargin - rightMargin -
458
padding * (m_columns - 1)) / m_columns;
459
preferredItemHeight = preferredItemWidth / ratio;
460
// make sure items of the new size actually fit in the current contentsRect
461
itemHeight = (contentsRect().height() - topMargin - bottomMargin -
462
padding * (m_rows - 1)) / m_rows;
463
if (itemHeight > preferredItemHeight) {
464
itemHeight = preferredItemHeight;
466
itemWidth = itemHeight * ratio;
468
m_widthScaleFactor = itemWidth / Kephal::ScreenUtils::desktopGeometry().width();
469
m_heightScaleFactor = itemHeight / Kephal::ScreenUtils::desktopGeometry().height();
471
// work out the preferred size based on the height of the contentsRect
472
if (formFactor() == Plasma::Horizontal) {
473
preferredItemHeight = (contentsRect().height() - topMargin - bottomMargin -
474
padding * (m_rows - 1)) / m_rows;
476
preferredItemHeight = (contentsRect().height() - padding * (m_rows - 1)) / m_rows;
478
preferredItemWidth = preferredItemHeight * ratio;
480
if (m_displayedText == Name) {
481
// When containment is in this position we are not limited by low width and we can
482
// afford increasing width of applet to be able to display every name of desktops
483
for (int i = 0; i < m_desktopCount; i++) {
484
QFontMetricsF metrics(KGlobalSettings::taskbarFont());
485
QSizeF textSize = metrics.size(Qt::TextSingleLine, KWindowSystem::desktopName(i+1));
486
if (textSize.width() + textMargin * 2 > preferredItemWidth) {
487
preferredItemWidth = textSize.width() + textMargin * 2;
492
// make sure items of the new size actually fit in the current contentsRect
493
if (formFactor() == Plasma::Horizontal) {
494
itemWidth = (contentsRect().width() - leftMargin - rightMargin -
495
padding * (m_columns - 1)) / m_columns;
497
itemWidth = (contentsRect().width() - padding * (m_columns - 1)) / m_columns;
499
if (itemWidth > preferredItemWidth) {
500
itemWidth = preferredItemWidth;
502
itemHeight = preferredItemHeight;
503
if (itemWidth < itemHeight * ratio) {
504
itemHeight = itemWidth / ratio;
507
m_widthScaleFactor = itemWidth / Kephal::ScreenUtils::desktopGeometry().width();
508
m_heightScaleFactor = itemHeight / Kephal::ScreenUtils::desktopGeometry().height();
511
m_hoverRect = QRectF();
513
qDeleteAll(m_animations);
514
m_animations.clear();
516
QRectF itemRect(QPoint(leftMargin, topMargin) , QSize(floor(itemWidth), floor(itemHeight)));
517
for (int i = 0; i < m_desktopCount; i++) {
518
itemRect.moveLeft(leftMargin + floor((i % m_columns) * (itemWidth + padding)));
519
itemRect.moveTop(topMargin + floor((i / m_columns) * (itemHeight + padding)));
520
m_rects.append(itemRect);
521
m_animations.append(new DesktopRectangle(this));
524
if (m_hoverIndex >= m_animations.count()) {
528
//Resize background svgs as needed
529
if (m_background->hasElementPrefix("normal")) {
530
m_background->setElementPrefix("normal");
531
m_background->resizeFrame(itemRect.size());
534
if (m_background->hasElementPrefix("active")) {
535
m_background->setElementPrefix("active");
536
m_background->resizeFrame(itemRect.size());
539
if (m_background->hasElementPrefix("hover")) {
540
m_background->setElementPrefix("hover");
541
m_background->resizeFrame(itemRect.size());
544
// do not try to resize unless the caller has allowed it,
545
// or the height has changed (or the width has changed in a vertical panel)
547
(formFactor() != Plasma::Vertical && contentsRect().height() != m_size.height()) ||
548
(formFactor() == Plasma::Vertical && contentsRect().width() != m_size.width())) {
550
// this new size will have the same height/width as the horizontal/vertical panel has given it
551
QSizeF preferred = QSizeF(ceil(m_columns * preferredItemWidth + padding * (m_columns - 1) +
552
leftMargin + rightMargin),
553
ceil(m_rows * preferredItemHeight + padding * (m_rows - 1) +
554
topMargin + bottomMargin));
556
//kDebug() << "current size:" << contentsRect() << " new preferred size: " << preferred << " form factor:" << formFactor() << " grid:" << m_rows << "x" << m_columns <<
557
// " actual grid:" << rows << "x" << columns << " item size:" << itemWidth << "x" << itemHeight << " preferred item size:" << preferredItemWidth << "x" << preferredItemHeight;
559
// make sure the minimum size is smaller than preferred
560
setMinimumSize(qMin(preferred.width(), minimumSize().width()),
561
qMin(preferred.height(), minimumSize().height()));
562
setPreferredSize(preferred);
563
emit sizeHintChanged(Qt::PreferredSize);
566
m_size = contentsRect().size();
569
void Pager::recalculateWindowRects()
571
QList<WId> windows = KWindowSystem::stackingOrder();
572
m_windowRects.clear();
573
for (int i = 0; i < m_desktopCount; i++) {
574
m_windowRects.append(QList<QPair<WId, QRect> >());
577
m_activeWindows.clear();
578
m_windowInfo.clear();
580
foreach (WId window, windows) {
581
KWindowInfo info = KWindowSystem::windowInfo(window, NET::WMGeometry | NET::WMFrameExtents |
582
NET::WMWindowType | NET::WMDesktop |
583
NET::WMState | NET::XAWMState | NET::WMVisibleName);
584
NET::WindowType type = info.windowType(NET::NormalMask | NET::DialogMask | NET::OverrideMask |
585
NET::UtilityMask | NET::DesktopMask | NET::DockMask |
586
NET::TopMenuMask | NET::SplashMask | NET::ToolbarMask |
589
// the reason we don't check for -1 or Net::Unknown here is that legitimate windows, such
590
// as some java application windows, may not have a type set for them.
591
// apparently sane defaults on properties is beyond the wisdom of x11.
592
if (type == NET::Desktop || type == NET::Dock || type == NET::TopMenu ||
593
type == NET::Splash || type == NET::Menu || type == NET::Toolbar ||
594
info.hasState(NET::SkipPager) || info.isMinimized()) {
599
unsigned long properties[] = { 0, NET::WM2Activities };
600
NETWinInfo netInfo(QX11Info::display(), window, QX11Info::appRootWindow(), properties, 2);
601
QString result(netInfo.activities());
602
if (!result.isEmpty()) {
603
QStringList activities = result.split(',');
604
if (!activities.contains(m_currentActivity)) {
609
for (int i = 0; i < m_desktopCount; i++) {
610
if (!info.isOnDesktop(i+1)) {
614
QRect windowRect = info.frameGeometry();
616
if (KWindowSystem::mapViewport()) {
617
windowRect = fixViewportPosition( windowRect );
620
windowRect = QRectF(windowRect.x() * m_widthScaleFactor,
621
windowRect.y() * m_heightScaleFactor,
622
windowRect.width() * m_widthScaleFactor,
623
windowRect.height() * m_heightScaleFactor).toRect();
624
windowRect.translate(m_rects[i].topLeft().toPoint());
625
m_windowRects[i].append(QPair<WId, QRect>(window, windowRect));
626
if (window == KWindowSystem::activeWindow()) {
627
m_activeWindows.append(windowRect);
629
m_windowInfo.append(info);
636
void Pager::configAccepted()
638
// only write the config here, it will be loaded in by configChanged(),
639
// which is called after this when the config dialog is accepted
640
KConfigGroup cg = config();
642
DisplayedText displayedText;
643
if (ui.desktopNumberRadioButton->isChecked()) {
644
displayedText = Number;
645
} else if (ui.desktopNameRadioButton->isChecked()) {
646
displayedText = Name;
648
displayedText = None;
650
cg.writeEntry("displayedText", (int) displayedText);
652
cg.writeEntry("showWindowIcons", ui.showWindowIconsCheckBox->isChecked());
654
CurrentDesktopSelected currentDesktopSelected;
655
if (ui.doNothingRadioButton->isChecked()) {
656
currentDesktopSelected = DoNothing;
657
} else if (ui.showDesktopRadioButton->isChecked()) {
658
currentDesktopSelected = ShowDesktop;
660
currentDesktopSelected = ShowDashboard;
662
cg.writeEntry("currentDesktopSelected", (int) currentDesktopSelected);
664
m_configureDesktopsWidget->save();
666
// we need to keep all pager applets consistent since this affects
667
// the layout of the desktops as used by the window manager,
668
// so we store the row count in the applet global configuration
670
if (formFactor() == Plasma::Vertical) {
671
rows = m_desktopCount / ui.spinRows->value();
672
if (m_desktopCount % ui.spinRows->value() > 0) {
676
rows = ui.spinRows->value();
678
rows = qBound(1, rows, m_desktopCount);
679
globalConfig().writeEntry("rows", rows);
681
emit configNeedsSaving();
684
void Pager::currentDesktopChanged(int desktop)
687
return; // bogus value, don't accept it
690
m_currentDesktop = desktop;
691
m_desktopDown = false;
693
if (!m_timer->isActive()) {
694
m_timer->start(FAST_UPDATE_DELAY);
698
void Pager::currentActivityChanged(const QString &activity)
700
m_currentActivity = activity;
702
if (!m_timer->isActive()) {
703
m_timer->start(FAST_UPDATE_DELAY);
707
void Pager::windowAdded(WId id)
711
if (!m_timer->isActive()) {
712
m_timer->start(FAST_UPDATE_DELAY);
716
void Pager::windowRemoved(WId id)
720
if (!m_timer->isActive()) {
721
m_timer->start(FAST_UPDATE_DELAY);
725
void Pager::activeWindowChanged(WId id)
729
if (!m_timer->isActive()) {
730
m_timer->start(FAST_UPDATE_DELAY);
734
void Pager::numberOfDesktopsChanged(int num)
737
return; // refuse to update to zero desktops
738
} else if (num == 1) {
739
m_preHiddenSize = size();
740
setMaximumSize(0, 0);
742
} else if (!isVisible()) {
743
setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
744
resize(m_preHiddenSize);
749
m_removeDesktopAction->setEnabled(num > 1);
750
m_addDesktopAction->setEnabled(num < MAXDESKTOPS);
753
m_desktopCount = num;
756
recalculateGridSizes(m_rows);
757
recalculateWindowRects();
760
void Pager::desktopNamesChanged()
765
if (!m_timer->isActive()) {
766
m_timer->start(UPDATE_DELAY);
770
void Pager::stackingOrderChanged()
772
if (!m_timer->isActive()) {
773
m_timer->start(FAST_UPDATE_DELAY);
777
void Pager::windowChanged(WId id, unsigned long* dirty)
781
if (dirty[NETWinInfo::PROTOCOLS] & (NET::WMGeometry | NET::WMDesktop) ||
782
dirty[NETWinInfo::PROTOCOLS2] & NET::WM2Activities) {
783
if (!m_timer->isActive()) {
784
m_timer->start(UPDATE_DELAY);
789
void Pager::showingDesktopChanged(bool showing)
792
if (!m_timer->isActive()) {
793
m_timer->start(UPDATE_DELAY);
797
void Pager::desktopsSizeChanged()
802
if (!m_timer->isActive()) {
803
m_timer->start(UPDATE_DELAY);
807
void Pager::mousePressEvent(QGraphicsSceneMouseEvent *event)
809
if (event->buttons() != Qt::RightButton)
811
for (int i = 0; i < m_rects.count(); ++i) {
812
if (m_rects[i].contains(event->pos())) {
813
m_dragStartDesktop = m_dragHighlightedDesktop = i;
814
m_dragOriginalPos = m_dragCurrentPos = event->pos();
815
if (m_dragOriginal.isEmpty()) {
816
m_dragOriginal = m_rects[i].toRect();
824
Applet::mousePressEvent(event);
827
void Pager::wheelEvent(QGraphicsSceneWheelEvent *e)
830
int desktops = KWindowSystem::numberOfDesktops();
833
if (m_kwin->numberOfViewports(0).width() * m_kwin->numberOfViewports(0).height() > 1 )
834
desktops = m_kwin->numberOfViewports(0).width() * m_kwin->numberOfViewports(0).height();
836
if (e->delta() < 0) {
837
newDesk = m_currentDesktop % desktops + 1;
839
newDesk = (desktops + m_currentDesktop - 2) % desktops + 1;
842
KWindowSystem::setCurrentDesktop(newDesk);
843
m_currentDesktop = newDesk;
846
Applet::wheelEvent(e);
849
void Pager::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
852
m_dragCurrentPos = event->pos();
853
m_dragHighlightedDesktop = -1;
854
m_hoverRect = QRectF();
856
foreach (const QRectF &rect, m_rects) {
857
if (rect.contains(event->pos())) {
858
m_dragHighlightedDesktop = i;
868
} else if (m_dragStartDesktop != -1 &&
869
(event->pos() - m_dragOriginalPos).toPoint().manhattanLength() > KGlobalSettings::dndEventDelay()) {
870
m_dragId = 0; // prevent us from going through this more than once
871
for (int k = m_windowRects[m_dragStartDesktop].count() - 1; k >= 0 ; k--) {
872
if (m_windowRects[m_dragStartDesktop][k].second.contains(m_dragOriginalPos.toPoint())) {
873
m_dragOriginal = m_windowRects[m_dragStartDesktop][k].second;
874
m_dragId = m_windowRects[m_dragStartDesktop][k].first;
881
if (m_dragOriginal.isEmpty()) {
882
Applet::mouseMoveEvent(event);
886
void Pager::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
889
if (m_dragHighlightedDesktop != -1) {
890
QPointF dest = m_dragCurrentPos - m_rects[m_dragHighlightedDesktop].topLeft() - m_dragOriginalPos + m_dragOriginal.topLeft();
891
dest = QPointF(dest.x()/m_widthScaleFactor, dest.y()/m_heightScaleFactor);
892
// don't move windows to negative positions
893
dest = QPointF(qMax(dest.x(), qreal(0.0)), qMax(dest.y(), qreal(0.0)));
894
if (!KWindowSystem::mapViewport()) {
895
KWindowInfo info = KWindowSystem::windowInfo(m_dragId, NET::WMDesktop | NET::WMState);
897
if (!info.onAllDesktops()) {
898
KWindowSystem::setOnDesktop(m_dragId, m_dragHighlightedDesktop+1);
901
// only move the window if it is not full screen and if it is kept within the same desktop
902
// moving when dropping between desktop is too annoying due to the small drop area.
903
if (!(info.state() & NET::FullScreen) &&
904
(m_dragHighlightedDesktop == m_dragStartDesktop || info.onAllDesktops())) {
905
// use _NET_MOVERESIZE_WINDOW rather than plain move, so that the WM knows this is a pager request
906
NETRootInfo i( QX11Info::display(), 0 );
907
int flags = ( 0x20 << 12 ) | ( 0x03 << 8 ) | 1; // from tool, x/y, northwest gravity
908
i.moveResizeWindowRequest( m_dragId, flags, dest.toPoint().x(), dest.toPoint().y(), 0, 0 );
911
// setOnDesktop() with viewports is also moving a window, and since it takes a moment
912
// for the WM to do the move, there's a race condition with figuring out how much to move,
913
// so do it only as one move
914
dest += KWindowSystem::desktopToViewport( m_dragHighlightedDesktop+1, false );
915
QPoint d = KWindowSystem::constrainViewportRelativePosition( dest.toPoint());
916
NETRootInfo i( QX11Info::display(), 0 );
917
int flags = ( 0x20 << 12 ) | ( 0x03 << 8 ) | 1; // from tool, x/y, northwest gravity
918
i.moveResizeWindowRequest( m_dragId, flags, d.x(), d.y(), 0, 0 );
922
} else if (m_dragStartDesktop != -1 && m_dragStartDesktop < m_rects.size() &&
923
m_rects[m_dragStartDesktop].contains(event->pos()) &&
924
m_currentDesktop != m_dragStartDesktop + 1) {
925
// only change the desktop if the user presses and releases the mouse on the same desktop
926
KWindowSystem::setCurrentDesktop(m_dragStartDesktop + 1);
927
m_currentDesktop = m_dragStartDesktop + 1;
928
} else if (m_dragStartDesktop != -1 && m_dragStartDesktop < m_rects.size() &&
929
m_rects[m_dragStartDesktop].contains(event->pos()) &&
930
m_currentDesktop == m_dragStartDesktop + 1) {
931
// toogle the desktop or the dashboard
932
// if the user presses and releases the mouse on the current desktop, default option is do nothing
933
if (m_currentDesktopSelected == ShowDesktop) {
935
NETRootInfo info(QX11Info::display(), 0);
936
m_desktopDown = !m_desktopDown;
937
info.setShowingDesktop(m_desktopDown);
938
} else if (m_currentDesktopSelected == ShowDashboard) {
939
QDBusInterface plasmaApp("org.kde.plasma-desktop", "/App");
940
plasmaApp.call("toggleDashboard");
945
m_dragOriginal = QRect();
946
m_dragHighlightedDesktop = -1;
947
m_dragStartDesktop = -1;
948
m_dragOriginalPos = m_dragCurrentPos = QPointF();
951
Applet::mouseReleaseEvent(event);
954
// If the pager is hovered in drag and drop mode, no hover events are geneated.
955
// This method provides the common implementation for hoverMoveEvent and dragMoveEvent.
956
void Pager::handleHoverMove(const QPointF& pos)
958
if (m_hoverRect.contains(pos)) {
960
} else if (m_hoverIndex > -1) {
961
QPropertyAnimation *animation = m_animations[m_hoverIndex]->animation();
962
if (animation && animation->state() == QAbstractAnimation::Running) {
965
animation = new QPropertyAnimation(m_animations[m_hoverIndex], "alphaValue");
966
m_animations[m_hoverIndex]->setAnimation(animation);
969
animation->setProperty("duration", s_FadeOutDuration);
970
animation->setProperty("easingCurve", QEasingCurve::OutQuad);
971
animation->setProperty("startValue", 1.0);
972
animation->setProperty("endValue", 0.0);
973
animation->start(QAbstractAnimation::DeleteWhenStopped);
977
foreach (const QRectF &rect, m_rects) {
978
if (rect.contains(pos)) {
979
if (m_hoverRect != rect) {
983
QPropertyAnimation *animation = m_animations[m_hoverIndex]->animation();
984
if (animation && animation->state() == QAbstractAnimation::Running) {
987
animation = new QPropertyAnimation(m_animations[m_hoverIndex], "alphaValue");
988
m_animations[m_hoverIndex]->setAnimation(animation);
991
animation->setProperty("duration", s_FadeInDuration);
992
animation->setProperty("easingCurve", QEasingCurve::InQuad);
993
animation->setProperty("startValue", 0.0);
994
animation->setProperty("endValue", 1.0);
995
animation->start(QAbstractAnimation::DeleteWhenStopped);
1004
m_hoverRect = QRectF();
1008
// If the pager is hovered in drag and drop mode, no hover events are geneated.
1009
// This method provides the common implementation for hoverLeaveEvent and dragLeaveEvent.
1010
void Pager::handleHoverLeave()
1012
if (m_hoverRect != QRectF()) {
1013
m_hoverRect = QRectF();
1017
if (m_hoverIndex != -1) {
1018
QPropertyAnimation *animation = m_animations[m_hoverIndex]->animation();
1019
if (animation && animation->state() == QAbstractAnimation::Running) {
1022
animation = new QPropertyAnimation(m_animations[m_hoverIndex], "alphaValue");
1023
m_animations[m_hoverIndex]->setAnimation(animation);
1026
animation->setProperty("duration", s_FadeOutDuration);
1027
animation->setProperty("easingCurve", QEasingCurve::OutQuad);
1028
animation->setProperty("startValue", 1.0);
1029
animation->setProperty("endValue", 0.0);
1030
animation->start(QAbstractAnimation::DeleteWhenStopped);
1033
// The applet doesn't always get mouseReleaseEvents, for example when starting a drag
1034
// on the containment and releasing the mouse on the desktop or another window. This can cause
1035
// weird bugs because the pager still thinks a drag is going on.
1036
// The only reliable event I found is the hoverLeaveEvent, so we just stop the drag
1038
if (m_dragId || m_dragStartDesktop != -1) {
1040
m_dragOriginal = QRect();
1041
m_dragHighlightedDesktop = -1;
1042
m_dragStartDesktop = -1;
1043
m_dragOriginalPos = m_dragCurrentPos = QPointF();
1048
void Pager::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
1050
handleHoverMove(event->pos());
1051
Applet::hoverEnterEvent(event);
1054
void Pager::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
1056
handleHoverMove(event->pos());
1059
void Pager::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
1062
Applet::hoverLeaveEvent(event);
1065
void Pager::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
1067
event->setAccepted(true);
1068
if (event->mimeData()->hasFormat(TaskManager::Task::mimetype())) {
1072
handleHoverMove(event->pos());
1074
if (m_hoverIndex != -1) {
1075
m_dragSwitchDesktop = m_hoverIndex;
1076
m_dragSwitchTimer->start(DRAG_SWITCH_DELAY);
1078
Applet::dragEnterEvent(event);
1081
void Pager::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
1083
handleHoverMove(event->pos());
1085
if (m_dragSwitchDesktop != m_hoverIndex && m_hoverIndex != -1) {
1086
m_dragSwitchDesktop = m_hoverIndex;
1087
m_dragSwitchTimer->start(DRAG_SWITCH_DELAY);
1088
} else if (m_hoverIndex == -1) {
1089
m_dragSwitchDesktop = m_hoverIndex;
1090
m_dragSwitchTimer->stop();
1092
Applet::dragMoveEvent(event);
1095
void Pager::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
1099
m_dragSwitchDesktop = -1;
1100
m_dragSwitchTimer->stop();
1101
Applet::dragLeaveEvent(event);
1104
void Pager::dropEvent(QGraphicsSceneDragDropEvent *event)
1107
QList<WId> ids = TaskManager::Task::idsFromMimeData(event->mimeData(), &ok);
1109
for (int i = 0; i < m_rects.count(); ++i) {
1110
if (m_rects[i].contains(event->pos().toPoint())) {
1111
foreach (const WId &id, ids) {
1112
KWindowSystem::setOnDesktop(id, i + 1);
1114
m_dragSwitchDesktop = -1;
1121
void Pager::dragSwitch()
1123
if (m_dragSwitchDesktop == -1) {
1127
KWindowSystem::setCurrentDesktop(m_dragSwitchDesktop + 1);
1128
m_currentDesktop = m_dragSwitchDesktop + 1;
1131
void Pager::paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRect &contentsRect)
1134
Q_UNUSED( contentsRect );
1136
KColorScheme* plasmaColorTheme = colorScheme();
1137
painter->setFont(KGlobalSettings::taskbarFont());
1139
// Desktop background
1140
QColor defaultTextColor = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor);
1141
QColor hoverColor = defaultTextColor;
1142
hoverColor.setAlpha(64);
1145
QColor drawingColor = plasmaColorTheme->foreground(KColorScheme::InactiveText).color();
1146
drawingColor.setAlpha(45);
1147
QBrush windowBrush(drawingColor);
1148
// Inactive windows Active desktop
1149
drawingColor.setAlpha(90);
1150
QBrush windowBrushActiveDesk(drawingColor);
1152
// Inactive window borders
1153
drawingColor = defaultTextColor;
1154
drawingColor.setAlpha(130);
1155
QPen windowPen(drawingColor);
1157
// Active window borders
1158
QPen activeWindowPen(defaultTextColor);
1161
drawingColor.setAlpha(130);
1162
QBrush activeWindowBrush(drawingColor);
1163
// Active windows Active desktop
1164
drawingColor.setAlpha(155);
1165
QBrush activeWindowBrushActiveDesk(drawingColor);
1167
if (m_showOwnBackground && (formFactor() == Plasma::Vertical || formFactor() == Plasma::Horizontal)) {
1168
m_background->setElementPrefix(QString());
1169
m_background->paintFrame(painter);
1172
// Draw backgrounds of desktops only when there are not the proper theme elements
1173
painter->setPen(Qt::NoPen);
1174
if (!m_background->hasElementPrefix("hover")) {
1175
for (int i = 0; i < m_rects.count(); i++) {
1176
if (m_rects[i] == m_hoverRect) {
1177
QColor animHoverColor = hoverColor;
1178
if (m_animations[i]->animation()) {
1179
animHoverColor.setAlpha(hoverColor.alpha() * m_animations[i]->alphaValue());
1181
painter->setBrush(animHoverColor);
1182
painter->drawRect(m_rects[i]);
1187
// Draw miniatures of windows from each desktop
1188
if (!m_rects.isEmpty() && m_rects[0].width() > 12 && m_rects[0].height() > 12) {
1189
painter->setPen(windowPen);
1190
for (int i = 0; i < m_windowRects.count(); i++) {
1191
for (int j = 0; j < m_windowRects[i].count(); j++) {
1192
QRect rect = m_windowRects[i][j].second;
1194
if (m_currentDesktop > 0 &&
1195
m_currentDesktop <= m_rects.count() &&
1196
m_rects[m_currentDesktop-1].contains(rect)) {
1197
if (m_activeWindows.contains(rect)) {
1198
painter->setBrush(activeWindowBrushActiveDesk);
1199
painter->setPen(activeWindowPen);
1201
painter->setBrush(windowBrushActiveDesk);
1202
painter->setPen(windowPen);
1205
if (m_activeWindows.contains(rect)) {
1206
painter->setBrush(activeWindowBrush);
1207
painter->setPen(activeWindowPen);
1209
painter->setBrush(windowBrush);
1210
painter->setPen(windowPen);
1213
if (m_dragId == m_windowRects[i][j].first) {
1214
rect.translate((m_dragCurrentPos - m_dragOriginalPos).toPoint());
1215
painter->setClipRect(option->exposedRect);
1216
} else if (i < m_rects.count()) {
1217
painter->setClipRect(m_rects[i].adjusted(1, 1, -1, -1));
1220
painter->drawRect(rect);
1222
int size = qMin(16, qMin(rect.width(), rect.height()));
1223
if (size >= 12 && m_showWindowIcons) {
1224
painter->drawPixmap(rect.x() + (rect.width() - size) / 2, rect.y() + (rect.height() - size) / 2, size, size,
1225
KWindowSystem::icon(m_windowRects[i][j].first, size, size, true));
1231
// Draw desktop frame and possibly text over it
1232
painter->setClipRect(option->exposedRect);
1233
painter->setBrush(Qt::NoBrush);
1236
for (int i = 0; i < m_rects.count(); i++) {
1237
if (i + 1 == m_currentDesktop || i == m_dragHighlightedDesktop) {
1243
//Paint the panel or fallback if we don't have that prefix
1244
if (m_background->hasElementPrefix(prefix)) {
1245
m_background->setElementPrefix(prefix);
1246
if (m_animations[i]->animation()) {
1247
QPixmap normal = m_background->framePixmap();
1248
m_background->setElementPrefix("hover");
1249
QPixmap result = Plasma::PaintUtils::transition(normal, m_background->framePixmap(), m_animations[i]->alphaValue());
1250
painter->drawPixmap(m_rects[i].topLeft(), result);
1252
//no anims, simpler thing
1253
if (m_rects[i] == m_hoverRect) {
1254
m_background->setElementPrefix("hover");
1256
m_background->paintFrame(painter, m_rects[i].topLeft());
1261
if (i + 1 == m_currentDesktop || i == m_dragHighlightedDesktop) {
1262
defaultTextColor.setAlphaF(1);
1263
drawingPen = QPen(defaultTextColor);
1265
drawingPen = QPen(plasmaColorTheme->foreground(KColorScheme::InactiveText).color());
1268
painter->setPen(drawingPen);
1269
painter->drawRect(m_rects[i]);
1273
if (!m_animations[i]->animation()) {
1274
defaultTextColor.setAlphaF(1);
1276
defaultTextColor.setAlphaF(m_animations[i]->alphaValue() / 2 + 0.5);
1277
painter->setPen(defaultTextColor);
1279
QColor shadowColor(Qt::black);
1280
if (defaultTextColor.value() < 128) {
1281
shadowColor = Qt::white;
1284
QString desktopText;
1285
if (m_displayedText == Number) { // Display number of desktop
1286
desktopText = QString::number(i + 1);
1287
} else if (m_displayedText == Name) { // Display name of desktop
1288
desktopText = KWindowSystem::desktopName(i + 1);
1291
if (!desktopText.isEmpty()) {
1293
QPixmap result = Plasma::PaintUtils::shadowText(desktopText,
1294
KGlobalSettings::smallestReadableFont(),
1296
shadowColor, QPoint(0, 0), radius);
1297
QRectF target = m_rects[i];
1298
//take also shadow position and radius into account
1299
//kDebug() << target << result.height();
1301
// for the default size of the panel we can allow this "one pixel"
1302
// offset on the bottom. the applet is so small that you almost
1303
// can't see the offset and this brings back the labels for the
1304
// panel's default size.
1305
if (target.height() + 1 >= result.height() - radius * 2) {
1306
QPointF paintPoint = target.center() - (result.rect().center() + QPoint(radius, radius));
1308
if (paintPoint.x() + radius < target.x() + 1) {
1309
paintPoint.setX(target.x() + 1 - radius);
1312
if (paintPoint.y() + radius < target.y() + 1) {
1313
paintPoint.setY(target.y() + 1 - radius);
1316
target.moveTopLeft(QPointF(0, 0));
1317
painter->drawPixmap(paintPoint, result, target);
1323
void Pager::lostDesktopLayoutOwner()
1325
delete m_desktopLayoutOwner;
1326
m_desktopLayoutOwner = NULL;
1329
// KWindowSystem does not translate position when mapping viewports
1330
// to virtual desktops (it'd probably break more things than fix),
1331
// so the offscreen coordinates need to be fixed
1332
QRect Pager::fixViewportPosition( const QRect& r )
1334
QRect desktopGeom = Kephal::ScreenUtils::desktopGeometry();
1335
int x = r.center().x() % desktopGeom.width();
1336
int y = r.center().y() % desktopGeom.height();
1338
x = x + desktopGeom.width();
1341
y = y + desktopGeom.height();
1343
return QRect( x - r.width() / 2, y - r.height() / 2, r.width(), r.height());
1346
void Pager::themeRefresh()
1348
delete m_colorScheme;
1353
void Pager::updateToolTip()
1355
int hoverDesktopNumber = 0;
1357
for (int i = 0; i < m_desktopCount; i++) {
1358
if (m_rects[i] == m_hoverRect) {
1359
hoverDesktopNumber = i + 1;
1363
Plasma::ToolTipContent data;
1365
int taskCounter = 0;
1366
int displayedTaskCounter = 0;
1370
foreach(const KWindowInfo &winInfo, m_windowInfo){
1371
if (winInfo.isOnDesktop(hoverDesktopNumber) && !windows.contains(winInfo.win())) {
1372
bool active = (winInfo.win() == KWindowSystem::activeWindow());
1373
if ((taskCounter < 4) || active){
1374
QPixmap icon = KWindowSystem::icon(winInfo.win(), 16, 16, true);
1375
if (icon.isNull()) {
1376
subtext += "<br />•" + Qt::escape(winInfo.visibleName());
1378
data.addResource(Plasma::ToolTipContent::ImageResource, QUrl("wicon://" + QString::number(taskCounter)), QVariant(icon));
1379
subtext += "<br /><img src=\"wicon://" + QString::number(taskCounter) + "\"/> ";
1381
//TODO: elide text that is tooo long
1382
subtext += (active ? "<u>" : "") + Qt::escape(winInfo.visibleName()).replace(' ', " ") + (active ? "</u>" : "");
1384
displayedTaskCounter++;
1385
windows.append(winInfo.win());
1392
subtext.prepend(i18np("One window:", "%1 windows:", taskCounter));
1395
if (taskCounter - displayedTaskCounter > 0) {
1396
subtext.append("<br>• <i>" + i18np("and 1 other", "and %1 others", taskCounter - displayedTaskCounter) + "</i>");
1399
data.setMainText(KWindowSystem::desktopName(hoverDesktopNumber));
1400
data.setSubText(subtext);
1402
Plasma::ToolTipManager::self()->setContent(this, data);
1405
#include "pager.moc"