~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to plasma/desktop/applets/pager/pager.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 ***************************************************************************/
 
19
 
 
20
#include "pager.h"
 
21
 
 
22
#include <math.h>
 
23
 
 
24
#include <QPainter>
 
25
#include <QStyleOptionGraphicsItem>
 
26
#include <QFont>
 
27
#include <QGraphicsSceneHoverEvent>
 
28
#include <QTimer>
 
29
#include <QX11Info>
 
30
#include <QDBusInterface>
 
31
#include <QTextDocument>
 
32
#include <QPropertyAnimation>
 
33
 
 
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>
 
43
 
 
44
#include <Plasma/Svg>
 
45
#include <Plasma/FrameSvg>
 
46
#include <Plasma/PaintUtils>
 
47
#include <Plasma/Theme>
 
48
#include <Plasma/ToolTipManager>
 
49
#include <Plasma/Animator>
 
50
 
 
51
#include <kephal/screens.h>
 
52
#include <kactivityconsumer.h>
 
53
 
 
54
#include <taskmanager/task.h>
 
55
 
 
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;
 
60
 
 
61
DesktopRectangle::DesktopRectangle(QObject *parent)
 
62
    : QObject(parent),
 
63
      m_alpha(0)
 
64
{
 
65
}
 
66
 
 
67
QPropertyAnimation *DesktopRectangle::animation() const
 
68
{
 
69
    return m_animation.data();
 
70
}
 
71
 
 
72
void DesktopRectangle::setAnimation(QPropertyAnimation *animation)
 
73
{
 
74
    m_animation = animation;
 
75
}
 
76
 
 
77
qreal DesktopRectangle::alphaValue() const
 
78
{
 
79
    return m_alpha;
 
80
}
 
81
 
 
82
void DesktopRectangle::setAlphaValue(qreal value)
 
83
{
 
84
    m_alpha = value;
 
85
 
 
86
    Pager *parentItem = qobject_cast<Pager*>(parent());
 
87
    parentItem->update();
 
88
}
 
89
 
 
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),
 
96
      m_rows(2),
 
97
      m_columns(0),
 
98
      m_desktopDown(false),
 
99
      m_hoverIndex(-1),
 
100
      m_addDesktopAction(0),
 
101
      m_removeDesktopAction(0),
 
102
      m_colorScheme(0),
 
103
      m_verticalFormFactor(false),
 
104
      m_dragId(0),
 
105
      m_dragStartDesktop(-1),
 
106
      m_dragHighlightedDesktop(-1),
 
107
      m_dragSwitchDesktop(-1),
 
108
      m_ignoreNextSizeConstraint(false),
 
109
      m_configureDesktopsWidget(0)
 
110
{
 
111
    setAcceptsHoverEvents(true);
 
112
    setAcceptDrops(true);
 
113
    setHasConfigurationInterface(true);
 
114
    setAspectRatioMode(Plasma::IgnoreAspectRatio);
 
115
 
 
116
    m_background = new Plasma::FrameSvg(this);
 
117
    m_background->setImagePath("widgets/pager");
 
118
    m_background->setCacheAllRenderedFrames(true);
 
119
 
 
120
    // initialize with a decent default
 
121
    m_desktopCount = KWindowSystem::numberOfDesktops();
 
122
    m_size = QSizeF(176, 88);
 
123
    resize(m_size);
 
124
}
 
125
 
 
126
Pager::~Pager()
 
127
{
 
128
    delete m_colorScheme;
 
129
}
 
130
 
 
131
void Pager::init()
 
132
{
 
133
    createMenu();
 
134
 
 
135
    m_verticalFormFactor = (formFactor() == Plasma::Vertical);
 
136
 
 
137
    configChanged();
 
138
 
 
139
    m_timer = new QTimer(this);
 
140
    m_timer->setSingleShot(true);
 
141
    connect(m_timer, SIGNAL(timeout()), this, SLOT(recalculateWindowRects()));
 
142
 
 
143
    m_dragSwitchTimer = new QTimer(this);
 
144
    m_dragSwitchTimer->setSingleShot(true);
 
145
    connect(m_dragSwitchTimer, SIGNAL(timeout()), this, SLOT(dragSwitch()));
 
146
 
 
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()));
 
160
 
 
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();
 
166
 
 
167
    connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeRefresh()));
 
168
 
 
169
    recalculateGridSizes(m_rows);
 
170
 
 
171
    m_currentDesktop = KWindowSystem::currentDesktop();
 
172
 
 
173
    KActivityConsumer *act = new KActivityConsumer(this);
 
174
    connect(act, SIGNAL(currentActivityChanged(QString)), this, SLOT(currentActivityChanged(QString)));
 
175
    m_currentActivity = act->currentActivity();
 
176
 
 
177
    if (m_desktopCount < 2) {
 
178
        numberOfDesktopsChanged(m_desktopCount);
 
179
    }
 
180
}
 
181
 
 
182
void Pager::configChanged()
 
183
{
 
184
    KConfigGroup cg = config();
 
185
    bool changed = false;
 
186
 
 
187
    DisplayedText displayedText = (DisplayedText) cg.readEntry("displayedText", (int) m_displayedText);
 
188
    if (displayedText != m_displayedText) {
 
189
        m_displayedText = displayedText;
 
190
        changed = true;
 
191
    }
 
192
 
 
193
    bool showWindowIcons = cg.readEntry("showWindowIcons", m_showWindowIcons);
 
194
    if (showWindowIcons != m_showWindowIcons) {
 
195
        m_showWindowIcons = showWindowIcons;
 
196
        changed = true;
 
197
    }
 
198
 
 
199
    CurrentDesktopSelected currentDesktopSelected =
 
200
        (CurrentDesktopSelected) cg.readEntry("currentDesktopSelected",
 
201
                                              (int) m_currentDesktopSelected);
 
202
    if (currentDesktopSelected != m_currentDesktopSelected) {
 
203
        m_currentDesktopSelected = currentDesktopSelected;
 
204
        changed = true;
 
205
    }
 
206
 
 
207
    int rows = globalConfig().readEntry("rows", m_rows);
 
208
 
 
209
    if (changed || rows != m_rows) {
 
210
        recalculateGridSizes(rows);
 
211
        recalculateWindowRects();
 
212
        update();
 
213
    }
 
214
}
 
215
 
 
216
void Pager::constraintsEvent(Plasma::Constraints constraints)
 
217
{
 
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;
 
226
 
 
227
            recalculateWindowRects();
 
228
        }
 
229
 
 
230
        if (m_background->hasElementPrefix(QString())) {
 
231
            m_background->setElementPrefix(QString());
 
232
            m_background->resizeFrame(size());
 
233
        }
 
234
        update();
 
235
    }
 
236
 
 
237
    if (constraints & Plasma::FormFactorConstraint) {
 
238
 
 
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();
 
246
                update();
 
247
            }
 
248
        }
 
249
 
 
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());
 
256
        } else {
 
257
            setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 
258
            setMinimumSize(preferredSize());
 
259
        }
 
260
    }
 
261
}
 
262
 
 
263
KColorScheme *Pager::colorScheme()
 
264
{
 
265
    if (!m_colorScheme) {
 
266
        m_colorScheme = new KColorScheme(QPalette::Active, KColorScheme::View, Plasma::Theme::defaultTheme()->colorScheme());
 
267
    }
 
268
 
 
269
    return m_colorScheme;
 
270
}
 
271
 
 
272
void Pager::createMenu()
 
273
{
 
274
#ifdef Q_WS_X11
 
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()));
 
281
 
 
282
    if (m_desktopCount <= 1) {
 
283
        m_removeDesktopAction->setEnabled(false);
 
284
    } else if (m_desktopCount >= MAXDESKTOPS) {
 
285
        m_addDesktopAction->setEnabled(false);
 
286
    }
 
287
#endif
 
288
}
 
289
 
 
290
QList<QAction*> Pager::contextualActions()
 
291
{
 
292
  return m_actions;
 
293
}
 
294
 
 
295
#ifdef Q_WS_X11
 
296
void Pager::slotAddDesktop()
 
297
{
 
298
    NETRootInfo info(QX11Info::display(), NET::NumberOfDesktops);
 
299
    info.setNumberOfDesktops(info.numberOfDesktops() + 1);
 
300
}
 
301
 
 
302
void Pager::slotRemoveDesktop()
 
303
{
 
304
    NETRootInfo info(QX11Info::display(), NET::NumberOfDesktops);
 
305
    int desktops = info.numberOfDesktops();
 
306
    if (desktops > 1) {
 
307
        info.setNumberOfDesktops(info.numberOfDesktops() - 1);
 
308
    }
 
309
}
 
310
#endif
 
311
 
 
312
void Pager::createConfigurationInterface(KConfigDialog *parent)
 
313
{
 
314
    QWidget *widget = new QWidget();
 
315
    ui.setupUi(widget);
 
316
    m_configureDesktopsWidget = new KCModuleProxy("desktop");
 
317
 
 
318
    parent->addPage(widget, i18n("General"), icon());
 
319
    parent->addPage(m_configureDesktopsWidget, m_configureDesktopsWidget->moduleInfo().moduleName(),
 
320
                    m_configureDesktopsWidget->moduleInfo().icon());
 
321
 
 
322
    connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
 
323
    connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
 
324
 
 
325
    switch (m_displayedText){
 
326
        case Number:
 
327
            ui. desktopNumberRadioButton->setChecked(true);
 
328
            break;
 
329
 
 
330
        case Name:
 
331
            ui.desktopNameRadioButton->setChecked(true);
 
332
            break;
 
333
 
 
334
        case None:
 
335
            ui.displayNoneRadioButton->setChecked(true);
 
336
            break;
 
337
    }
 
338
 
 
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);
 
343
    } else {
 
344
        ui.spinRows->setValue(m_rows);
 
345
    }
 
346
    ui.spinRows->setMaximum(m_desktopCount);
 
347
 
 
348
    switch (m_currentDesktopSelected){
 
349
        case DoNothing:
 
350
            ui.doNothingRadioButton->setChecked(true);
 
351
            break;
 
352
 
 
353
        case ShowDesktop:
 
354
            ui.showDesktopRadioButton->setChecked(true);
 
355
            break;
 
356
 
 
357
        case ShowDashboard:
 
358
            ui.showDashboardRadioButton->setChecked(true);
 
359
            break;
 
360
    }
 
361
 
 
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()));
 
370
 
 
371
    connect(m_configureDesktopsWidget, SIGNAL(changed(bool)), parent, SLOT(settingsModified()));
 
372
}
 
373
 
 
374
void Pager::recalculateGridSizes(int rows)
 
375
{
 
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) {
 
381
        columns++;
 
382
    }
 
383
    rows = m_desktopCount / columns;
 
384
    if (m_desktopCount % columns > 0) {
 
385
        rows++;
 
386
    }
 
387
 
 
388
    // update the grid size
 
389
    if (m_rows != rows || m_columns != columns) {
 
390
        m_rows = rows;
 
391
        m_columns = columns;
 
392
 
 
393
        // write the new number of rows to the config
 
394
        globalConfig().writeEntry("rows", m_rows);
 
395
        emit configNeedsSaving();
 
396
 
 
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);
 
402
        }
 
403
    }
 
404
 
 
405
    updateSizes(true);
 
406
}
 
407
 
 
408
void Pager::updateSizes(bool allowResize)
 
409
{
 
410
    int padding = 2; // Space between miniatures of desktops
 
411
    int textMargin = 3; // Space between name of desktop and border
 
412
 
 
413
    qreal leftMargin = 0;
 
414
    qreal topMargin = 0;
 
415
    qreal rightMargin = 0;
 
416
    qreal bottomMargin = 0;
 
417
 
 
418
    qreal ratio = (qreal) Kephal::ScreenUtils::desktopGeometry().width() /
 
419
                  (qreal) Kephal::ScreenUtils::desktopGeometry().height();
 
420
 
 
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);
 
425
 
 
426
        if (formFactor() == Plasma::Vertical) {
 
427
            qreal optimalSize = (geometry().width() -
 
428
                                 KIconLoader::SizeSmall * ratio * m_columns -
 
429
                                 padding * (m_columns - 1)) / 2;
 
430
 
 
431
            if (optimalSize < leftMargin || optimalSize < rightMargin) {
 
432
                leftMargin = rightMargin = qMax(qreal(0), optimalSize);
 
433
                m_showOwnBackground = false;
 
434
            }
 
435
        } else if (formFactor() == Plasma::Horizontal) {
 
436
            qreal optimalSize = (geometry().height() -
 
437
                                 KIconLoader::SizeSmall * m_rows -
 
438
                                 padding * (m_rows - 1)) / 2;
 
439
 
 
440
            if (optimalSize < topMargin || optimalSize < bottomMargin) {
 
441
                topMargin = bottomMargin = qMax(qreal(0), optimalSize);
 
442
                m_showOwnBackground = false;
 
443
            }
 
444
        }
 
445
    } else {
 
446
        getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
 
447
    }
 
448
 
 
449
 
 
450
    qreal itemHeight;
 
451
    qreal itemWidth;
 
452
    qreal preferredItemHeight;
 
453
    qreal preferredItemWidth;
 
454
 
 
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;
 
465
        }
 
466
        itemWidth = itemHeight * ratio;
 
467
 
 
468
        m_widthScaleFactor = itemWidth / Kephal::ScreenUtils::desktopGeometry().width();
 
469
        m_heightScaleFactor = itemHeight / Kephal::ScreenUtils::desktopGeometry().height();
 
470
    } else {
 
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;
 
475
        } else {
 
476
            preferredItemHeight = (contentsRect().height() - padding * (m_rows - 1)) / m_rows;
 
477
        }
 
478
        preferredItemWidth = preferredItemHeight * ratio;
 
479
 
 
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;
 
488
                }
 
489
            }
 
490
        }
 
491
 
 
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;
 
496
        } else {
 
497
            itemWidth = (contentsRect().width() - padding * (m_columns - 1)) / m_columns;
 
498
        }
 
499
        if (itemWidth > preferredItemWidth) {
 
500
            itemWidth = preferredItemWidth;
 
501
        }
 
502
        itemHeight = preferredItemHeight;
 
503
        if (itemWidth < itemHeight * ratio) {
 
504
            itemHeight = itemWidth / ratio;
 
505
        }
 
506
 
 
507
        m_widthScaleFactor = itemWidth / Kephal::ScreenUtils::desktopGeometry().width();
 
508
        m_heightScaleFactor = itemHeight / Kephal::ScreenUtils::desktopGeometry().height();
 
509
    }
 
510
 
 
511
    m_hoverRect = QRectF();
 
512
    m_rects.clear();
 
513
    qDeleteAll(m_animations);
 
514
    m_animations.clear();
 
515
 
 
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));
 
522
    }
 
523
 
 
524
    if (m_hoverIndex >= m_animations.count()) {
 
525
        m_hoverIndex = -1;
 
526
    }
 
527
 
 
528
    //Resize background svgs as needed
 
529
    if (m_background->hasElementPrefix("normal")) {
 
530
        m_background->setElementPrefix("normal");
 
531
        m_background->resizeFrame(itemRect.size());
 
532
    }
 
533
 
 
534
    if (m_background->hasElementPrefix("active")) {
 
535
        m_background->setElementPrefix("active");
 
536
        m_background->resizeFrame(itemRect.size());
 
537
    }
 
538
 
 
539
    if (m_background->hasElementPrefix("hover")) {
 
540
        m_background->setElementPrefix("hover");
 
541
        m_background->resizeFrame(itemRect.size());
 
542
    }
 
543
 
 
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)
 
546
    if (allowResize ||
 
547
        (formFactor() != Plasma::Vertical && contentsRect().height() != m_size.height()) ||
 
548
        (formFactor() == Plasma::Vertical && contentsRect().width()  != m_size.width())) {
 
549
 
 
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));
 
555
 
 
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;
 
558
 
 
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);
 
564
    }
 
565
 
 
566
    m_size = contentsRect().size();
 
567
}
 
568
 
 
569
void Pager::recalculateWindowRects()
 
570
{
 
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> >());
 
575
    }
 
576
 
 
577
    m_activeWindows.clear();
 
578
    m_windowInfo.clear();
 
579
 
 
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 |
 
587
                                               NET::MenuMask);
 
588
 
 
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()) {
 
595
            continue;
 
596
        }
 
597
 
 
598
        //check activity
 
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)) {
 
605
                continue;
 
606
            }
 
607
        }
 
608
 
 
609
        for (int i = 0; i < m_desktopCount; i++) {
 
610
            if (!info.isOnDesktop(i+1)) {
 
611
                continue;
 
612
            }
 
613
 
 
614
            QRect windowRect = info.frameGeometry();
 
615
 
 
616
            if (KWindowSystem::mapViewport()) {
 
617
                windowRect = fixViewportPosition( windowRect );
 
618
            }
 
619
 
 
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);
 
628
            }
 
629
            m_windowInfo.append(info);
 
630
        }
 
631
    }
 
632
 
 
633
    update();
 
634
}
 
635
 
 
636
void Pager::configAccepted()
 
637
{
 
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();
 
641
 
 
642
    DisplayedText displayedText;
 
643
    if (ui.desktopNumberRadioButton->isChecked()) {
 
644
        displayedText = Number;
 
645
    } else if (ui.desktopNameRadioButton->isChecked()) {
 
646
        displayedText = Name;
 
647
    } else {
 
648
        displayedText = None;
 
649
    }
 
650
    cg.writeEntry("displayedText", (int) displayedText);
 
651
 
 
652
    cg.writeEntry("showWindowIcons", ui.showWindowIconsCheckBox->isChecked());
 
653
 
 
654
    CurrentDesktopSelected currentDesktopSelected;
 
655
    if (ui.doNothingRadioButton->isChecked()) {
 
656
        currentDesktopSelected = DoNothing;
 
657
    } else if (ui.showDesktopRadioButton->isChecked()) {
 
658
        currentDesktopSelected = ShowDesktop;
 
659
    } else {
 
660
        currentDesktopSelected = ShowDashboard;
 
661
    }
 
662
    cg.writeEntry("currentDesktopSelected", (int) currentDesktopSelected);
 
663
 
 
664
    m_configureDesktopsWidget->save();
 
665
 
 
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
 
669
    int rows = 0;
 
670
    if (formFactor() == Plasma::Vertical) {
 
671
        rows = m_desktopCount / ui.spinRows->value();
 
672
        if (m_desktopCount % ui.spinRows->value() > 0) {
 
673
            rows++;
 
674
        }
 
675
    } else {
 
676
        rows = ui.spinRows->value();
 
677
    }
 
678
    rows = qBound(1, rows, m_desktopCount);
 
679
    globalConfig().writeEntry("rows", rows);
 
680
 
 
681
    emit configNeedsSaving();
 
682
}
 
683
 
 
684
void Pager::currentDesktopChanged(int desktop)
 
685
{
 
686
    if (desktop < 1) {
 
687
        return; // bogus value, don't accept it
 
688
    }
 
689
 
 
690
    m_currentDesktop = desktop;
 
691
    m_desktopDown = false;
 
692
 
 
693
    if (!m_timer->isActive()) {
 
694
        m_timer->start(FAST_UPDATE_DELAY);
 
695
    }
 
696
}
 
697
 
 
698
void Pager::currentActivityChanged(const QString &activity)
 
699
{
 
700
    m_currentActivity = activity;
 
701
 
 
702
    if (!m_timer->isActive()) {
 
703
        m_timer->start(FAST_UPDATE_DELAY);
 
704
    }
 
705
}
 
706
 
 
707
void Pager::windowAdded(WId id)
 
708
{
 
709
    Q_UNUSED(id)
 
710
 
 
711
    if (!m_timer->isActive()) {
 
712
        m_timer->start(FAST_UPDATE_DELAY);
 
713
    }
 
714
}
 
715
 
 
716
void Pager::windowRemoved(WId id)
 
717
{
 
718
    Q_UNUSED(id)
 
719
 
 
720
    if (!m_timer->isActive()) {
 
721
        m_timer->start(FAST_UPDATE_DELAY);
 
722
    }
 
723
}
 
724
 
 
725
void Pager::activeWindowChanged(WId id)
 
726
{
 
727
    Q_UNUSED(id)
 
728
 
 
729
    if (!m_timer->isActive()) {
 
730
        m_timer->start(FAST_UPDATE_DELAY);
 
731
    }
 
732
}
 
733
 
 
734
void Pager::numberOfDesktopsChanged(int num)
 
735
{
 
736
    if (num < 1) {
 
737
        return; // refuse to update to zero desktops
 
738
    } else if (num == 1) {
 
739
        m_preHiddenSize = size();
 
740
        setMaximumSize(0, 0);
 
741
        hide();
 
742
    } else if (!isVisible()) {
 
743
        setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
 
744
        resize(m_preHiddenSize);
 
745
        show();
 
746
    }
 
747
 
 
748
#ifdef Q_WS_X11
 
749
    m_removeDesktopAction->setEnabled(num > 1);
 
750
    m_addDesktopAction->setEnabled(num < MAXDESKTOPS);
 
751
#endif
 
752
 
 
753
    m_desktopCount = num;
 
754
 
 
755
    m_rects.clear();
 
756
    recalculateGridSizes(m_rows);
 
757
    recalculateWindowRects();
 
758
}
 
759
 
 
760
void Pager::desktopNamesChanged()
 
761
{
 
762
    m_rects.clear();
 
763
    updateSizes(true);
 
764
 
 
765
    if (!m_timer->isActive()) {
 
766
        m_timer->start(UPDATE_DELAY);
 
767
    }
 
768
}
 
769
 
 
770
void Pager::stackingOrderChanged()
 
771
{
 
772
    if (!m_timer->isActive()) {
 
773
        m_timer->start(FAST_UPDATE_DELAY);
 
774
    }
 
775
}
 
776
 
 
777
void Pager::windowChanged(WId id, unsigned long* dirty)
 
778
{
 
779
    Q_UNUSED(id)
 
780
 
 
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);
 
785
        }
 
786
    }
 
787
}
 
788
 
 
789
void Pager::showingDesktopChanged(bool showing)
 
790
{
 
791
    Q_UNUSED(showing)
 
792
    if (!m_timer->isActive()) {
 
793
        m_timer->start(UPDATE_DELAY);
 
794
    }
 
795
}
 
796
 
 
797
void Pager::desktopsSizeChanged()
 
798
{
 
799
    m_rects.clear();
 
800
    updateSizes(true);
 
801
 
 
802
    if (!m_timer->isActive()) {
 
803
        m_timer->start(UPDATE_DELAY);
 
804
    }
 
805
}
 
806
 
 
807
void Pager::mousePressEvent(QGraphicsSceneMouseEvent *event)
 
808
{
 
809
    if (event->buttons() != Qt::RightButton)
 
810
    {
 
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();
 
817
                }
 
818
 
 
819
                update();
 
820
                return;
 
821
            }
 
822
        }
 
823
    }
 
824
    Applet::mousePressEvent(event);
 
825
}
 
826
 
 
827
void Pager::wheelEvent(QGraphicsSceneWheelEvent *e)
 
828
{
 
829
    int newDesk;
 
830
    int desktops = KWindowSystem::numberOfDesktops();
 
831
 
 
832
    /*
 
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();
 
835
       */
 
836
    if (e->delta() < 0) {
 
837
        newDesk = m_currentDesktop % desktops + 1;
 
838
    } else {
 
839
        newDesk = (desktops + m_currentDesktop - 2) % desktops + 1;
 
840
    }
 
841
 
 
842
    KWindowSystem::setCurrentDesktop(newDesk);
 
843
    m_currentDesktop = newDesk;
 
844
    update();
 
845
 
 
846
    Applet::wheelEvent(e);
 
847
}
 
848
 
 
849
void Pager::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
 
850
{
 
851
    if (m_dragId > 0) {
 
852
        m_dragCurrentPos = event->pos();
 
853
        m_dragHighlightedDesktop = -1;
 
854
        m_hoverRect = QRectF();
 
855
        int i = 0;
 
856
        foreach (const QRectF &rect, m_rects) {
 
857
            if (rect.contains(event->pos())) {
 
858
                m_dragHighlightedDesktop = i;
 
859
                m_hoverRect = rect;
 
860
                break;
 
861
            }
 
862
 
 
863
            ++i;
 
864
        }
 
865
        update();
 
866
        event->accept();
 
867
        return;
 
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;
 
875
                event->accept();
 
876
                break;
 
877
            }
 
878
        }
 
879
    }
 
880
 
 
881
    if (m_dragOriginal.isEmpty()) {
 
882
        Applet::mouseMoveEvent(event);
 
883
    }
 
884
}
 
885
 
 
886
void Pager::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
 
887
{
 
888
    if (m_dragId) {
 
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);
 
896
 
 
897
                if (!info.onAllDesktops()) {
 
898
                    KWindowSystem::setOnDesktop(m_dragId, m_dragHighlightedDesktop+1);
 
899
                }
 
900
 
 
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 );
 
909
                }
 
910
            } else {
 
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 );
 
919
            }
 
920
        }
 
921
        m_timer->start();
 
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) {
 
934
 
 
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");
 
941
        }
 
942
    }
 
943
 
 
944
    m_dragId = 0;
 
945
    m_dragOriginal = QRect();
 
946
    m_dragHighlightedDesktop = -1;
 
947
    m_dragStartDesktop = -1;
 
948
    m_dragOriginalPos = m_dragCurrentPos = QPointF();
 
949
 
 
950
    update();
 
951
    Applet::mouseReleaseEvent(event);
 
952
}
 
953
 
 
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)
 
957
{
 
958
    if (m_hoverRect.contains(pos)) {
 
959
        return;
 
960
    } else if (m_hoverIndex > -1) {
 
961
        QPropertyAnimation *animation = m_animations[m_hoverIndex]->animation();
 
962
        if (animation && animation->state() == QAbstractAnimation::Running) {
 
963
            animation->pause();
 
964
        } else {
 
965
            animation = new QPropertyAnimation(m_animations[m_hoverIndex], "alphaValue");
 
966
            m_animations[m_hoverIndex]->setAnimation(animation);
 
967
        }
 
968
 
 
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);
 
974
    }
 
975
 
 
976
    int i = 0;
 
977
    foreach (const QRectF &rect, m_rects) {
 
978
        if (rect.contains(pos)) {
 
979
            if (m_hoverRect != rect) {
 
980
                m_hoverRect = rect;
 
981
                m_hoverIndex = i;
 
982
 
 
983
                QPropertyAnimation *animation = m_animations[m_hoverIndex]->animation();
 
984
                if (animation && animation->state() == QAbstractAnimation::Running) {
 
985
                    animation->pause();
 
986
                } else {
 
987
                    animation = new QPropertyAnimation(m_animations[m_hoverIndex], "alphaValue");
 
988
                    m_animations[m_hoverIndex]->setAnimation(animation);
 
989
                }
 
990
 
 
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);
 
996
 
 
997
                update();
 
998
                updateToolTip();
 
999
            }
 
1000
            return;
 
1001
        }
 
1002
        ++i;
 
1003
    }
 
1004
    m_hoverRect = QRectF();
 
1005
    update();
 
1006
}
 
1007
 
 
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()
 
1011
{
 
1012
    if (m_hoverRect != QRectF()) {
 
1013
        m_hoverRect = QRectF();
 
1014
        update();
 
1015
    }
 
1016
 
 
1017
    if (m_hoverIndex != -1) {
 
1018
        QPropertyAnimation *animation = m_animations[m_hoverIndex]->animation();
 
1019
        if (animation && animation->state() == QAbstractAnimation::Running) {
 
1020
            animation->pause();
 
1021
        } else {
 
1022
            animation = new QPropertyAnimation(m_animations[m_hoverIndex], "alphaValue");
 
1023
            m_animations[m_hoverIndex]->setAnimation(animation);
 
1024
        }
 
1025
 
 
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);
 
1031
    }
 
1032
 
 
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
 
1037
    // on this event.
 
1038
    if (m_dragId || m_dragStartDesktop != -1) {
 
1039
        m_dragId = 0;
 
1040
        m_dragOriginal = QRect();
 
1041
        m_dragHighlightedDesktop = -1;
 
1042
        m_dragStartDesktop = -1;
 
1043
        m_dragOriginalPos = m_dragCurrentPos = QPointF();
 
1044
        update();
 
1045
    }
 
1046
}
 
1047
 
 
1048
void Pager::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
 
1049
{
 
1050
    handleHoverMove(event->pos());
 
1051
    Applet::hoverEnterEvent(event);
 
1052
}
 
1053
 
 
1054
void Pager::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
 
1055
{
 
1056
    handleHoverMove(event->pos());
 
1057
}
 
1058
 
 
1059
void Pager::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
 
1060
{
 
1061
    handleHoverLeave();
 
1062
    Applet::hoverLeaveEvent(event);
 
1063
}
 
1064
 
 
1065
void Pager::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
 
1066
{
 
1067
    event->setAccepted(true);
 
1068
    if (event->mimeData()->hasFormat(TaskManager::Task::mimetype())) {
 
1069
        return;
 
1070
    }
 
1071
 
 
1072
    handleHoverMove(event->pos());
 
1073
 
 
1074
    if (m_hoverIndex != -1) {
 
1075
        m_dragSwitchDesktop = m_hoverIndex;
 
1076
        m_dragSwitchTimer->start(DRAG_SWITCH_DELAY);
 
1077
    }
 
1078
    Applet::dragEnterEvent(event);
 
1079
}
 
1080
 
 
1081
void Pager::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
 
1082
{
 
1083
    handleHoverMove(event->pos());
 
1084
 
 
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();
 
1091
    }
 
1092
    Applet::dragMoveEvent(event);
 
1093
}
 
1094
 
 
1095
void Pager::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
 
1096
{
 
1097
    handleHoverLeave();
 
1098
 
 
1099
    m_dragSwitchDesktop = -1;
 
1100
    m_dragSwitchTimer->stop();
 
1101
    Applet::dragLeaveEvent(event);
 
1102
}
 
1103
 
 
1104
void Pager::dropEvent(QGraphicsSceneDragDropEvent *event)
 
1105
{
 
1106
    bool ok;
 
1107
    QList<WId> ids = TaskManager::Task::idsFromMimeData(event->mimeData(), &ok);
 
1108
    if (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);
 
1113
                }
 
1114
                m_dragSwitchDesktop = -1;
 
1115
                break;
 
1116
            }
 
1117
        }
 
1118
    }
 
1119
}
 
1120
 
 
1121
void Pager::dragSwitch()
 
1122
{
 
1123
    if (m_dragSwitchDesktop == -1) {
 
1124
        return;
 
1125
    }
 
1126
 
 
1127
    KWindowSystem::setCurrentDesktop(m_dragSwitchDesktop + 1);
 
1128
    m_currentDesktop = m_dragSwitchDesktop + 1;
 
1129
}
 
1130
 
 
1131
void Pager::paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRect &contentsRect)
 
1132
{
 
1133
    Q_UNUSED( option );
 
1134
    Q_UNUSED( contentsRect );
 
1135
 
 
1136
    KColorScheme* plasmaColorTheme = colorScheme();
 
1137
    painter->setFont(KGlobalSettings::taskbarFont());
 
1138
 
 
1139
    // Desktop background
 
1140
    QColor defaultTextColor = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor);
 
1141
    QColor hoverColor = defaultTextColor;
 
1142
    hoverColor.setAlpha(64);
 
1143
 
 
1144
    // Inactive windows
 
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);
 
1151
 
 
1152
    // Inactive window borders
 
1153
    drawingColor = defaultTextColor;
 
1154
    drawingColor.setAlpha(130);
 
1155
    QPen windowPen(drawingColor);
 
1156
 
 
1157
    // Active window borders
 
1158
    QPen activeWindowPen(defaultTextColor);
 
1159
 
 
1160
    // Active windows
 
1161
    drawingColor.setAlpha(130);
 
1162
    QBrush activeWindowBrush(drawingColor);
 
1163
    // Active windows Active desktop
 
1164
    drawingColor.setAlpha(155);
 
1165
    QBrush activeWindowBrushActiveDesk(drawingColor);
 
1166
 
 
1167
    if (m_showOwnBackground && (formFactor() == Plasma::Vertical || formFactor() == Plasma::Horizontal)) {
 
1168
        m_background->setElementPrefix(QString());
 
1169
        m_background->paintFrame(painter);
 
1170
    }
 
1171
 
 
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());
 
1180
                }
 
1181
                painter->setBrush(animHoverColor);
 
1182
                painter->drawRect(m_rects[i]);
 
1183
            }
 
1184
        }
 
1185
    }
 
1186
 
 
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;
 
1193
 
 
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);
 
1200
                    } else {
 
1201
                        painter->setBrush(windowBrushActiveDesk);
 
1202
                        painter->setPen(windowPen);
 
1203
                    }
 
1204
                } else {
 
1205
                    if (m_activeWindows.contains(rect)) {
 
1206
                        painter->setBrush(activeWindowBrush);
 
1207
                        painter->setPen(activeWindowPen);
 
1208
                    } else {
 
1209
                        painter->setBrush(windowBrush);
 
1210
                        painter->setPen(windowPen);
 
1211
                    }
 
1212
                }
 
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));
 
1218
                }
 
1219
 
 
1220
                painter->drawRect(rect);
 
1221
 
 
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));
 
1226
                }
 
1227
            }
 
1228
        }
 
1229
    }
 
1230
 
 
1231
    // Draw desktop frame and possibly text over it
 
1232
    painter->setClipRect(option->exposedRect);
 
1233
    painter->setBrush(Qt::NoBrush);
 
1234
 
 
1235
    QString prefix;
 
1236
    for (int i = 0; i < m_rects.count(); i++) {
 
1237
        if (i + 1 == m_currentDesktop || i == m_dragHighlightedDesktop) {
 
1238
            prefix = "active";
 
1239
        } else {
 
1240
            prefix = "normal";
 
1241
        }
 
1242
 
 
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);
 
1251
            } else {
 
1252
                //no anims, simpler thing
 
1253
                if (m_rects[i] == m_hoverRect) {
 
1254
                    m_background->setElementPrefix("hover");
 
1255
                }
 
1256
                m_background->paintFrame(painter, m_rects[i].topLeft());
 
1257
            }
 
1258
        } else {
 
1259
            QPen drawingPen;
 
1260
 
 
1261
            if (i + 1 == m_currentDesktop || i == m_dragHighlightedDesktop) {
 
1262
                defaultTextColor.setAlphaF(1);
 
1263
                drawingPen = QPen(defaultTextColor);
 
1264
            } else {
 
1265
                drawingPen = QPen(plasmaColorTheme->foreground(KColorScheme::InactiveText).color());
 
1266
            }
 
1267
 
 
1268
            painter->setPen(drawingPen);
 
1269
            painter->drawRect(m_rects[i]);
 
1270
        }
 
1271
 
 
1272
        //Draw text
 
1273
        if (!m_animations[i]->animation()) {
 
1274
            defaultTextColor.setAlphaF(1);
 
1275
        }
 
1276
        defaultTextColor.setAlphaF(m_animations[i]->alphaValue() / 2 + 0.5);
 
1277
        painter->setPen(defaultTextColor);
 
1278
 
 
1279
        QColor shadowColor(Qt::black);
 
1280
        if (defaultTextColor.value() < 128) {
 
1281
            shadowColor = Qt::white;
 
1282
        }
 
1283
 
 
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);
 
1289
        }
 
1290
 
 
1291
        if (!desktopText.isEmpty()) {
 
1292
            int radius = 2;
 
1293
            QPixmap result = Plasma::PaintUtils::shadowText(desktopText,
 
1294
                                                            KGlobalSettings::smallestReadableFont(),
 
1295
                                                            defaultTextColor,
 
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();
 
1300
 
 
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));
 
1307
 
 
1308
                if (paintPoint.x() + radius < target.x() + 1) {
 
1309
                    paintPoint.setX(target.x() + 1 - radius);
 
1310
                }
 
1311
 
 
1312
                if (paintPoint.y() + radius < target.y() + 1) {
 
1313
                    paintPoint.setY(target.y() + 1 - radius);
 
1314
                }
 
1315
 
 
1316
                target.moveTopLeft(QPointF(0, 0));
 
1317
                painter->drawPixmap(paintPoint, result, target);
 
1318
            }
 
1319
        }
 
1320
    }
 
1321
}
 
1322
 
 
1323
void Pager::lostDesktopLayoutOwner()
 
1324
{
 
1325
    delete m_desktopLayoutOwner;
 
1326
    m_desktopLayoutOwner = NULL;
 
1327
}
 
1328
 
 
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 )
 
1333
{
 
1334
    QRect desktopGeom = Kephal::ScreenUtils::desktopGeometry();
 
1335
    int x = r.center().x() % desktopGeom.width();
 
1336
    int y = r.center().y() % desktopGeom.height();
 
1337
    if( x < 0 ) {
 
1338
        x = x + desktopGeom.width();
 
1339
    }
 
1340
    if( y < 0 ) {
 
1341
        y = y + desktopGeom.height();
 
1342
    }
 
1343
    return QRect( x - r.width() / 2, y - r.height() / 2, r.width(), r.height());
 
1344
}
 
1345
 
 
1346
void Pager::themeRefresh()
 
1347
{
 
1348
    delete m_colorScheme;
 
1349
    m_colorScheme = 0;
 
1350
    update();
 
1351
}
 
1352
 
 
1353
void Pager::updateToolTip()
 
1354
{
 
1355
    int hoverDesktopNumber = 0;
 
1356
 
 
1357
    for (int i = 0; i < m_desktopCount; i++) {
 
1358
        if (m_rects[i] == m_hoverRect) {
 
1359
            hoverDesktopNumber = i + 1;
 
1360
        }
 
1361
    }
 
1362
 
 
1363
    Plasma::ToolTipContent data;
 
1364
    QString subtext;
 
1365
    int taskCounter = 0;
 
1366
    int displayedTaskCounter = 0;
 
1367
 
 
1368
    QList<WId> windows;
 
1369
 
 
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 />&bull;" + Qt::escape(winInfo.visibleName());
 
1377
                } else {
 
1378
                    data.addResource(Plasma::ToolTipContent::ImageResource, QUrl("wicon://" + QString::number(taskCounter)), QVariant(icon));
 
1379
                    subtext += "<br /><img src=\"wicon://" + QString::number(taskCounter) + "\"/>&nbsp;";
 
1380
                }
 
1381
                //TODO: elide text that is tooo long
 
1382
                subtext += (active ? "<u>" : "") + Qt::escape(winInfo.visibleName()).replace(' ', "&nbsp;") + (active ? "</u>" : "");
 
1383
 
 
1384
                displayedTaskCounter++; 
 
1385
                windows.append(winInfo.win());
 
1386
            }
 
1387
            taskCounter++;
 
1388
        }
 
1389
    }
 
1390
 
 
1391
    if (taskCounter) {
 
1392
        subtext.prepend(i18np("One window:", "%1 windows:", taskCounter));
 
1393
    }
 
1394
 
 
1395
    if (taskCounter - displayedTaskCounter > 0) {
 
1396
        subtext.append("<br>&bull; <i>" + i18np("and 1 other", "and %1 others", taskCounter - displayedTaskCounter) + "</i>");
 
1397
    }
 
1398
 
 
1399
    data.setMainText(KWindowSystem::desktopName(hoverDesktopNumber));
 
1400
    data.setSubText(subtext);
 
1401
 
 
1402
    Plasma::ToolTipManager::self()->setContent(this, data);
 
1403
}
 
1404
 
 
1405
#include "pager.moc"