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

« back to all changes in this revision

Viewing changes to libs/taskmanager/taskmanager.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
 
 
3
Copyright (c) 2000 Matthias Elter <elter@kde.org>
 
4
 
 
5
Permission is hereby granted, free of charge, to any person obtaining a copy
 
6
of this software and associated documentation files (the "Software"), to deal
 
7
in the Software without restriction, including without limitation the rights
 
8
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
9
copies of the Software, and to permit persons to whom the Software is
 
10
furnished to do so, subject to the following conditions:
 
11
 
 
12
The above copyright notice and this permission notice shall be included in
 
13
all copies or substantial portions of the Software.
 
14
 
 
15
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
16
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
17
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 
18
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 
19
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 
20
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
21
 
 
22
******************************************************************/
 
23
 
 
24
// Own
 
25
#include "taskmanager.h"
 
26
 
 
27
// Qt
 
28
#include <QApplication>
 
29
#include <QDesktopWidget>
 
30
#include <QUuid>
 
31
 
 
32
// KDE
 
33
#include <KConfig>
 
34
#include <KConfigGroup>
 
35
#include <KDebug>
 
36
#include <KDirWatch>
 
37
#include <KGlobal>
 
38
#include <KLocale>
 
39
#include <KStandardDirs>
 
40
#include <NETWinInfo>
 
41
 
 
42
#ifdef Q_WS_X11
 
43
#include <QX11Info>
 
44
#endif
 
45
 
 
46
#ifdef Q_WS_WIN
 
47
#include <windows.h>
 
48
#endif
 
49
 
 
50
#include <kephal/screens.h>
 
51
#include <kactivityconsumer.h>
 
52
 
 
53
namespace TaskManager
 
54
{
 
55
 
 
56
class TaskManagerSingleton
 
57
{
 
58
public:
 
59
   TaskManager self;
 
60
};
 
61
 
 
62
K_GLOBAL_STATIC( TaskManagerSingleton, privateTaskManagerSelf )
 
63
 
 
64
TaskManager* TaskManager::self()
 
65
{
 
66
    return &privateTaskManagerSelf->self;
 
67
}
 
68
 
 
69
class TaskManager::Private
 
70
{
 
71
public:
 
72
    Private(TaskManager *manager)
 
73
        : q(manager),
 
74
          active(0),
 
75
          startupInfo(0),
 
76
          watcher(0)
 
77
    {
 
78
    }
 
79
 
 
80
    void onAppExitCleanup()
 
81
    {
 
82
        q->disconnect(KWindowSystem::self(), 0, q, 0);
 
83
        delete watcher;
 
84
        watcher = 0;
 
85
        delete startupInfo;
 
86
        startupInfo = 0;
 
87
 
 
88
        foreach (TaskPtr task, tasksByWId) {
 
89
            task->clearPixmapData();
 
90
        }
 
91
 
 
92
        foreach (StartupPtr startup, startups) {
 
93
            startup->clearPixmapData();
 
94
        }
 
95
    }
 
96
 
 
97
    TaskManager *q;
 
98
    TaskPtr active;
 
99
    KStartupInfo* startupInfo;
 
100
    KDirWatch *watcher;
 
101
    TaskDict tasksByWId;
 
102
    StartupList startups;
 
103
    WindowList skiptaskbarWindows;
 
104
    QSet<QUuid> trackGeometryTokens;
 
105
    KActivityConsumer activityConsumer;
 
106
};
 
107
 
 
108
TaskManager::TaskManager()
 
109
    : QObject(),
 
110
      d(new Private(this))
 
111
{
 
112
    KGlobal::locale()->insertCatalog("libtaskmanager");
 
113
    connect(KWindowSystem::self(), SIGNAL(windowAdded(WId)),
 
114
            this,       SLOT(windowAdded(WId)));
 
115
    connect(KWindowSystem::self(), SIGNAL(windowRemoved(WId)),
 
116
            this,       SLOT(windowRemoved(WId)));
 
117
    connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)),
 
118
            this,       SLOT(activeWindowChanged(WId)));
 
119
    connect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)),
 
120
            this,       SLOT(currentDesktopChanged(int)));
 
121
    connect(KWindowSystem::self(), SIGNAL(windowChanged(WId,const unsigned long*)),
 
122
            this,       SLOT(windowChanged(WId,const unsigned long*)));
 
123
    connect(&d->activityConsumer, SIGNAL(currentActivityChanged(QString)),
 
124
            this,       SLOT(currentActivityChanged(QString)));
 
125
    if (QCoreApplication::instance()) {
 
126
        connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(onAppExitCleanup()));
 
127
    }
 
128
 
 
129
    currentActivityChanged(d->activityConsumer.currentActivity());
 
130
 
 
131
    // register existing windows
 
132
    const QList<WId> windows = KWindowSystem::windows();
 
133
    QList<WId>::ConstIterator end(windows.end());
 
134
    for (QList<WId>::ConstIterator it = windows.begin(); it != end; ++it)
 
135
    {
 
136
        windowAdded(*it);
 
137
    }
 
138
 
 
139
    // set active window
 
140
    WId win = KWindowSystem::activeWindow();
 
141
    activeWindowChanged(win);
 
142
 
 
143
    d->watcher = new KDirWatch(this);
 
144
    d->watcher->addFile(KGlobal::dirs()->locateLocal("config", "klaunchrc"));
 
145
    connect(d->watcher, SIGNAL(dirty(const QString&)), this, SLOT(configureStartup()));
 
146
    connect(d->watcher, SIGNAL(created(const QString&)), this, SLOT(configureStartup()));
 
147
    connect(d->watcher, SIGNAL(deleted(const QString&)), this, SLOT(configureStartup()));
 
148
 
 
149
    configureStartup();
 
150
}
 
151
 
 
152
TaskManager::~TaskManager()
 
153
{
 
154
    KGlobal::locale()->removeCatalog("libtaskmanager");
 
155
    delete d;
 
156
}
 
157
 
 
158
void TaskManager::configureStartup()
 
159
{
 
160
    KConfig _c("klaunchrc");
 
161
    KConfigGroup c(&_c, "FeedbackStyle");
 
162
    if (!c.readEntry("TaskbarButton", true)) {
 
163
        delete d->startupInfo;
 
164
        d->startupInfo = 0;
 
165
        return;
 
166
    }
 
167
 
 
168
    if (!d->startupInfo) {
 
169
        d->startupInfo = new KStartupInfo(KStartupInfo::CleanOnCantDetect, this );
 
170
        connect(d->startupInfo,
 
171
                SIGNAL(gotNewStartup(const KStartupInfoId&, const KStartupInfoData&)),
 
172
                SLOT(gotNewStartup(const KStartupInfoId&, const KStartupInfoData&)));
 
173
        connect(d->startupInfo,
 
174
                SIGNAL(gotStartupChange(const KStartupInfoId&, const KStartupInfoData&)),
 
175
                SLOT(gotStartupChange(const KStartupInfoId&, const KStartupInfoData&)));
 
176
        connect(d->startupInfo,
 
177
                SIGNAL(gotRemoveStartup(const KStartupInfoId&, const KStartupInfoData&)),
 
178
                SLOT(killStartup(const KStartupInfoId&)));
 
179
    }
 
180
 
 
181
    c = KConfigGroup(&_c, "TaskbarButtonSettings");
 
182
    d->startupInfo->setTimeout(c.readEntry("Timeout", 30));
 
183
}
 
184
 
 
185
TaskPtr TaskManager::findTask(WId w)
 
186
{
 
187
    TaskDict::const_iterator it = d->tasksByWId.constBegin();
 
188
    TaskDict::const_iterator itEnd = d->tasksByWId.constEnd();
 
189
 
 
190
    for (; it != itEnd; ++it) {
 
191
        if (it.key() == w || it.value()->hasTransient(w)) {
 
192
            return it.value();
 
193
        }
 
194
    }
 
195
 
 
196
    return TaskPtr();
 
197
}
 
198
 
 
199
TaskPtr TaskManager::findTask(int desktop, const QPoint& p)
 
200
{
 
201
    QList<WId> list = KWindowSystem::stackingOrder();
 
202
 
 
203
    TaskPtr task;
 
204
    int currentIndex = -1;
 
205
    TaskDict::iterator itEnd = d->tasksByWId.end();
 
206
    for (TaskDict::iterator it = d->tasksByWId.begin(); it != itEnd; ++it)
 
207
    {
 
208
        TaskPtr t = it.value();
 
209
        if (!t->isOnAllDesktops() && t->desktop() != desktop)
 
210
        {
 
211
            continue;
 
212
        }
 
213
        //FIXME activities?
 
214
 
 
215
        if (t->isIconified() || t->isShaded())
 
216
        {
 
217
            continue;
 
218
        }
 
219
 
 
220
        if (t->geometry().contains(p))
 
221
        {
 
222
            int index = list.indexOf(t->window());
 
223
            if (index > currentIndex)
 
224
            {
 
225
                currentIndex = index;
 
226
                task = t;
 
227
            }
 
228
        }
 
229
    }
 
230
 
 
231
    return task;
 
232
}
 
233
 
 
234
void TaskManager::windowAdded(WId w )
 
235
{
 
236
#ifdef Q_WS_X11
 
237
    NETWinInfo info(QX11Info::display(), w, QX11Info::appRootWindow(),
 
238
                    NET::WMWindowType | NET::WMPid | NET::WMState);
 
239
 
 
240
    // ignore NET::Tool and other special window types
 
241
    NET::WindowType wType = info.windowType(NET::NormalMask | NET::DesktopMask | NET::DockMask |
 
242
                                            NET::ToolbarMask | NET::MenuMask | NET::DialogMask |
 
243
                                            NET::OverrideMask | NET::TopMenuMask |
 
244
                                            NET::UtilityMask | NET::SplashMask);
 
245
 
 
246
    if (wType != NET::Normal && wType != NET::Override && wType != NET::Unknown &&
 
247
        wType != NET::Dialog && wType != NET::Utility) {
 
248
        return;
 
249
    }
 
250
 
 
251
    // ignore windows that want to be ignored by the taskbar
 
252
    if ((info.state() & NET::SkipTaskbar) != 0) {
 
253
        d->skiptaskbarWindows.insert(w); // remember them though
 
254
        return;
 
255
    }
 
256
 
 
257
    Window transient_for_tmp;
 
258
    if (XGetTransientForHint(QX11Info::display(), (Window)w, &transient_for_tmp)) {
 
259
        WId transient_for = (WId)transient_for_tmp;
 
260
 
 
261
        // check if it's transient for a skiptaskbar window
 
262
        if (d->skiptaskbarWindows.contains( transient_for )) {
 
263
            return;
 
264
        }
 
265
 
 
266
        // lets see if this is a transient for an existing task
 
267
        if (transient_for != QX11Info::appRootWindow() &&
 
268
            transient_for != 0 && wType != NET::Utility) {
 
269
            TaskPtr t = findTask(transient_for);
 
270
            if (t) {
 
271
                if (t->window() != w) {
 
272
                    t->addTransient(w, info);
 
273
                    // kDebug() << "TM: Transient " << w << " added for Task: " << t->window();
 
274
                }
 
275
                return;
 
276
            }
 
277
        }
 
278
    }
 
279
#endif
 
280
 
 
281
    TaskPtr t(new Task(w, 0));
 
282
    d->tasksByWId[w] = t;
 
283
 
 
284
    connect(t.data(), SIGNAL(changed(::TaskManager::TaskChanges)),
 
285
            this, SLOT(taskChanged(::TaskManager::TaskChanges)));
 
286
 
 
287
    if (d->startupInfo) {
 
288
        KStartupInfoId startupInfoId;
 
289
        // checkStartup modifies startupInfoId
 
290
        d->startupInfo->checkStartup(w, startupInfoId);
 
291
        foreach (StartupPtr startup, d->startups) {
 
292
            if (startup->id() == startupInfoId) {
 
293
                startup->addWindowMatch(w);
 
294
            }
 
295
        }
 
296
    }
 
297
 
 
298
    // kDebug() << "TM: Task added for WId: " << w;
 
299
    emit taskAdded(t);
 
300
}
 
301
 
 
302
void TaskManager::windowRemoved(WId w)
 
303
{
 
304
    d->skiptaskbarWindows.remove(w);
 
305
 
 
306
    // find task
 
307
    TaskPtr t = findTask(w);
 
308
    if (!t)
 
309
    {
 
310
        return;
 
311
    }
 
312
 
 
313
    if (t->window() == w)
 
314
    {
 
315
        d->tasksByWId.remove(w);
 
316
        emit taskRemoved(t);
 
317
 
 
318
        if (t == d->active)
 
319
        {
 
320
            d->active = 0;
 
321
        }
 
322
 
 
323
        //kDebug() << "TM: Task for WId " << w << " removed.";
 
324
        t->deleteLater();
 
325
    }
 
326
    else
 
327
    {
 
328
        t->removeTransient(w);
 
329
        //kDebug() << "TM: Transient " << w << " for Task " << t->window() << " removed.";
 
330
    }
 
331
}
 
332
 
 
333
void TaskManager::windowChanged(WId w, const unsigned long *dirty)
 
334
{
 
335
#ifdef Q_WS_X11
 
336
    if (dirty[NETWinInfo::PROTOCOLS] & NET::WMState) {
 
337
        NETWinInfo info (QX11Info::display(), w, QX11Info::appRootWindow(),
 
338
                         NET::WMState | NET::XAWMState);
 
339
 
 
340
        if (info.state() & NET::SkipTaskbar) {
 
341
            windowRemoved(w);
 
342
            d->skiptaskbarWindows.insert(w);
 
343
            return;
 
344
        } else {
 
345
            d->skiptaskbarWindows.remove(w);
 
346
            if (info.mappingState() != NET::Withdrawn && !findTask(w)) {
 
347
                // skipTaskBar state was removed and the window is still
 
348
                // mapped, so add this window
 
349
                windowAdded(w);
 
350
            }
 
351
        }
 
352
    }
 
353
 
 
354
    // check if any state we are interested in is marked dirty
 
355
    if (!(dirty[NETWinInfo::PROTOCOLS] & (NET::WMVisibleName | NET::WMName |
 
356
                                          NET::WMState | NET::WMIcon |
 
357
                                          NET::XAWMState | NET::WMDesktop) ||
 
358
          (trackGeometry() && dirty[NETWinInfo::PROTOCOLS] & NET::WMGeometry) ||
 
359
         (dirty[NETWinInfo::PROTOCOLS2] & NET::WM2Activities))) {
 
360
        return;
 
361
    }
 
362
 
 
363
    // find task
 
364
    TaskPtr t = findTask(w);
 
365
    if (!t) {
 
366
        return;
 
367
    }
 
368
 
 
369
    //kDebug() << "TaskManager::windowChanged " << w << " " << dirty[NETWinInfo::PROTOCOLS] << dirty[NETWinInfo::PROTOCOLS2];
 
370
 
 
371
    unsigned long propagatedChanges = 0;
 
372
    if ((dirty[NETWinInfo::PROTOCOLS] & NET::WMState) && t->updateDemandsAttentionState(w)) {
 
373
        propagatedChanges = NET::WMState;
 
374
    }
 
375
 
 
376
    //kDebug() << "got changes, but will we refresh?" << dirty[NETWinInfo::PROTOCOLS] << dirty[NETWinInfo::PROTOCOLS2];
 
377
    if (dirty[NETWinInfo::PROTOCOLS] || dirty[NETWinInfo::PROTOCOLS2]) {
 
378
        // only refresh this stuff if we have other changes besides icons
 
379
        t->refresh(Task::WindowProperties(dirty[NETWinInfo::PROTOCOLS] | propagatedChanges, dirty[NETWinInfo::PROTOCOLS2]));
 
380
    }
 
381
#endif
 
382
}
 
383
 
 
384
void TaskManager::taskChanged(::TaskManager::TaskChanges changes)
 
385
{
 
386
    Task *t = qobject_cast<Task*>(sender());
 
387
 
 
388
    if (!t || changes == TaskUnchanged || !d->tasksByWId.contains(t->info().win())) {
 
389
        return;
 
390
    }
 
391
 
 
392
    emit windowChanged(d->tasksByWId[t->info().win()], changes);
 
393
}
 
394
 
 
395
void TaskManager::activeWindowChanged(WId w)
 
396
{
 
397
    //kDebug() << "TaskManager::activeWindowChanged" << w;
 
398
    TaskPtr t = findTask(w);
 
399
    if (!t) {
 
400
        if (d->active) {
 
401
            d->active->setActive(false);
 
402
            d->active = 0;
 
403
        }
 
404
        //kDebug() << "no active window";
 
405
    } else {
 
406
        if (t->info().windowType(NET::UtilityMask) == NET::Utility) {
 
407
            // we don't want to mark utility windows as active since task managers
 
408
            // actually care about the main window and skip utility windows; utility
 
409
            // windows are hidden when their associated window loses focus anyways
 
410
            // see http://bugs.kde.org/show_bug.cgi?id=178509
 
411
            return;
 
412
        }
 
413
 
 
414
        if (d->active) {
 
415
            d->active->setActive(false);
 
416
        }
 
417
 
 
418
        d->active = t;
 
419
        d->active->setActive(true);
 
420
        //kDebug() << "active window is" << t->name();
 
421
    }
 
422
}
 
423
 
 
424
void TaskManager::currentDesktopChanged(int desktop)
 
425
{
 
426
    emit desktopChanged(desktop);
 
427
}
 
428
 
 
429
void TaskManager::currentActivityChanged(const QString &activity)
 
430
{
 
431
    emit activityChanged(activity);
 
432
}
 
433
 
 
434
void TaskManager::gotNewStartup( const KStartupInfoId& id, const KStartupInfoData& data )
 
435
{
 
436
    StartupPtr s( new Startup( id, data, 0 ) );
 
437
    d->startups.append(s);
 
438
 
 
439
    emit startupAdded(s);
 
440
}
 
441
 
 
442
void TaskManager::gotStartupChange( const KStartupInfoId& id, const KStartupInfoData& data )
 
443
{
 
444
    StartupList::iterator itEnd = d->startups.end();
 
445
    for (StartupList::iterator sIt = d->startups.begin(); sIt != itEnd; ++sIt)
 
446
    {
 
447
        if ((*sIt)->id() == id)
 
448
        {
 
449
            (*sIt)->update(data);
 
450
            return;
 
451
        }
 
452
    }
 
453
}
 
454
 
 
455
void TaskManager::killStartup( const KStartupInfoId& id )
 
456
{
 
457
    StartupList::iterator sIt = d->startups.begin();
 
458
    StartupList::iterator itEnd = d->startups.end();
 
459
    StartupPtr s;
 
460
    for (; sIt != itEnd; ++sIt)
 
461
    {
 
462
        if ((*sIt)->id() == id)
 
463
        {
 
464
            s = *sIt;
 
465
            break;
 
466
        }
 
467
    }
 
468
 
 
469
    if (!s) {
 
470
        return;
 
471
    }
 
472
 
 
473
    d->startups.erase(sIt);
 
474
    emit startupRemoved(s);
 
475
}
 
476
 
 
477
void TaskManager::killStartup(StartupPtr s)
 
478
{
 
479
    if (!s)
 
480
    {
 
481
        return;
 
482
    }
 
483
 
 
484
    StartupList::iterator sIt = d->startups.begin();
 
485
    StartupList::iterator itEnd = d->startups.end();
 
486
    for (; sIt != itEnd; ++sIt)
 
487
    {
 
488
        if ((*sIt) == s)
 
489
        {
 
490
            d->startups.erase(sIt);
 
491
            break;
 
492
        }
 
493
    }
 
494
 
 
495
    emit startupRemoved(s);
 
496
}
 
497
 
 
498
QString TaskManager::desktopName(int desk) const
 
499
{
 
500
    return KWindowSystem::desktopName(desk);
 
501
}
 
502
 
 
503
TaskDict TaskManager::tasks() const
 
504
{
 
505
    return d->tasksByWId;
 
506
}
 
507
 
 
508
StartupList TaskManager::startups() const
 
509
{
 
510
    return d->startups;
 
511
}
 
512
 
 
513
int TaskManager::numberOfDesktops() const
 
514
{
 
515
    return KWindowSystem::numberOfDesktops();
 
516
}
 
517
 
 
518
bool TaskManager::isOnTop(const Task* task) const
 
519
{
 
520
    if (!task) {
 
521
        return false;
 
522
    }
 
523
 
 
524
    QList<WId> list = KWindowSystem::stackingOrder();
 
525
    QList<WId>::const_iterator begin(list.constBegin());
 
526
    QList<WId>::const_iterator it = list.constBegin() + (list.size() - 1);
 
527
    do {
 
528
        TaskPtr t = d->tasksByWId.value(*it);
 
529
        if (t) {
 
530
            if (t == task) {
 
531
                return true;
 
532
            }
 
533
#ifndef Q_WS_WIN
 
534
            if (!t->isIconified() && (t->isAlwaysOnTop() == task->isAlwaysOnTop())) {
 
535
                return false;
 
536
            }
 
537
#endif
 
538
        }
 
539
    } while (it-- != begin);
 
540
 
 
541
    return false;
 
542
}
 
543
 
 
544
void TaskManager::setTrackGeometry(bool track, const QUuid &token)
 
545
{
 
546
    if (track) {
 
547
        if (!d->trackGeometryTokens.contains(token)) {
 
548
            d->trackGeometryTokens.insert(token);
 
549
        }
 
550
    } else {
 
551
        d->trackGeometryTokens.remove(token);
 
552
    }
 
553
}
 
554
 
 
555
bool TaskManager::trackGeometry() const
 
556
{
 
557
    return !d->trackGeometryTokens.isEmpty();
 
558
}
 
559
 
 
560
bool TaskManager::isOnScreen(int screen, const WId wid)
 
561
{
 
562
    if (screen == -1) {
 
563
        return true;
 
564
    }
 
565
 
 
566
    KWindowInfo wi = KWindowSystem::windowInfo(wid, NET::WMFrameExtents);
 
567
 
 
568
    // for window decos that fudge a bit and claim to extend beyond the
 
569
    // edge of the screen, we just contract a bit.
 
570
    QRect window = wi.frameGeometry();
 
571
    QRect desktop = Kephal::ScreenUtils::screenGeometry(screen);
 
572
    desktop.adjust(5, 5, -5, -5);
 
573
    return window.intersects(desktop);
 
574
}
 
575
 
 
576
int TaskManager::currentDesktop() const
 
577
{
 
578
    return KWindowSystem::currentDesktop();
 
579
}
 
580
 
 
581
QString TaskManager::currentActivity() const
 
582
{
 
583
    return d->activityConsumer.currentActivity(); //TODO cache
 
584
}
 
585
 
 
586
} // TaskManager namespace
 
587
 
 
588
 
 
589
#include "taskmanager.moc"