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

« back to all changes in this revision

Viewing changes to libs/taskmanager/groupmanager.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 2008 Christian Mollekopf <chrigi_1@hotmail.com>
 
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
#include "groupmanager.h"
 
25
 
 
26
#include <QBuffer>
 
27
#include <QList>
 
28
#include <QStack>
 
29
#include <QTimer>
 
30
#include <QUuid>
 
31
 
 
32
#include <KDebug>
 
33
 
 
34
#include "abstractsortingstrategy.h"
 
35
#include "startup.h"
 
36
#include "task.h"
 
37
#include "taskitem.h"
 
38
#include "taskgroup.h"
 
39
#include "taskmanager.h"
 
40
#include "strategies/alphasortingstrategy.h"
 
41
#include "strategies/desktopsortingstrategy.h"
 
42
#include "strategies/programgroupingstrategy.h"
 
43
#include "strategies/manualgroupingstrategy.h"
 
44
#include "strategies/kustodiangroupingstrategy.h"
 
45
#include "strategies/manualsortingstrategy.h"
 
46
#include "launcheritem.h"
 
47
 
 
48
namespace TaskManager
 
49
{
 
50
 
 
51
class GroupManagerPrivate
 
52
{
 
53
public:
 
54
    GroupManagerPrivate(GroupManager *manager)
 
55
        : q(manager),
 
56
          sortingStrategy(GroupManager::NoSorting),
 
57
          groupingStrategy(GroupManager::NoGrouping),
 
58
          lastGroupingStrategy(GroupManager::NoGrouping),
 
59
          abstractGroupingStrategy(0),
 
60
          abstractSortingStrategy(0),
 
61
          currentScreen(-1),
 
62
          groupIsFullLimit(0),
 
63
          showOnlyCurrentDesktop(false),
 
64
          showOnlyCurrentActivity(false),
 
65
          showOnlyCurrentScreen(false),
 
66
          showOnlyMinimized(false),
 
67
          onlyGroupWhenFull(false),
 
68
          changingGroupingStrategy(false),
 
69
          readingLauncherConfig(false)
 
70
    {
 
71
    }
 
72
 
 
73
    /** reload all tasks from TaskManager */
 
74
    void reloadTasks();
 
75
    void actuallyReloadTasks();
 
76
 
 
77
    /**
 
78
    * Keep track of changes in Taskmanager
 
79
    */
 
80
    void currentDesktopChanged(int);
 
81
    void currentActivityChanged(QString);
 
82
    void taskChanged(TaskPtr, ::TaskManager::TaskChanges);
 
83
    void checkScreenChange();
 
84
    void taskDestroyed(QObject *item);
 
85
    void startupItemDestroyed(AbstractGroupableItem *);
 
86
    void checkIfFull();
 
87
    void actuallyCheckIfFull();
 
88
    bool addTask(TaskPtr);
 
89
    void removeTask(TaskPtr);
 
90
    void addStartup(StartupPtr);
 
91
    void removeStartup(StartupPtr);
 
92
    void launcherVisibilityChange();
 
93
    void checkLauncherVisibility(LauncherItem *launcher);
 
94
    void saveLauncher(LauncherItem *launcher);
 
95
    void saveLauncher(LauncherItem *launcher, KConfigGroup &group);
 
96
    void unsaveLauncher(LauncherItem *launcher);
 
97
    KConfigGroup launcherConfig(const KConfigGroup &config = KConfigGroup());
 
98
 
 
99
    TaskGroup *currentRootGroup();
 
100
 
 
101
    GroupManager *q;
 
102
    QHash<StartupPtr, TaskItem*> startupList;
 
103
    GroupManager::TaskSortingStrategy sortingStrategy;
 
104
    GroupManager::TaskGroupingStrategy groupingStrategy;
 
105
    GroupManager::TaskGroupingStrategy lastGroupingStrategy;
 
106
    AbstractGroupingStrategy *abstractGroupingStrategy;
 
107
    AbstractSortingStrategy *abstractSortingStrategy;
 
108
    int currentScreen;
 
109
    QTimer screenTimer;
 
110
    QTimer reloadTimer;
 
111
    QTimer checkIfFullTimer;
 
112
    QSet<Task *> geometryTasks;
 
113
    int groupIsFullLimit;
 
114
    QUuid configToken;
 
115
 
 
116
    QHash<QString, QHash<int, TaskGroup*> > rootGroups; //container for groups
 
117
    QHash<KUrl, LauncherItem *> launchers;
 
118
    int currentDesktop;
 
119
    QString currentActivity;
 
120
 
 
121
    bool showOnlyCurrentDesktop : 1;
 
122
    bool showOnlyCurrentActivity : 1;
 
123
    bool showOnlyCurrentScreen : 1;
 
124
    bool showOnlyMinimized : 1;
 
125
    bool onlyGroupWhenFull : 1;
 
126
    bool changingGroupingStrategy : 1;
 
127
    bool readingLauncherConfig : 1;
 
128
};
 
129
 
 
130
 
 
131
GroupManager::GroupManager(QObject *parent)
 
132
    : QObject(parent),
 
133
      d(new GroupManagerPrivate(this))
 
134
{
 
135
    connect(TaskManager::self(), SIGNAL(taskAdded(TaskPtr)), this, SLOT(addTask(TaskPtr)));
 
136
    connect(TaskManager::self(), SIGNAL(taskRemoved(TaskPtr)), this, SLOT(removeTask(TaskPtr)));
 
137
    connect(TaskManager::self(), SIGNAL(startupAdded(StartupPtr)), this, SLOT(addStartup(StartupPtr)));
 
138
    connect(TaskManager::self(), SIGNAL(startupRemoved(StartupPtr)), this, SLOT(removeStartup(StartupPtr)));
 
139
 
 
140
    d->currentDesktop = TaskManager::self()->currentDesktop();
 
141
    d->currentActivity = TaskManager::self()->currentActivity();
 
142
 
 
143
    d->rootGroups[d->currentActivity][d->currentDesktop] = new TaskGroup(this, "RootGroup", Qt::transparent);
 
144
 
 
145
    d->reloadTimer.setSingleShot(true);
 
146
    d->reloadTimer.setInterval(0);
 
147
    connect(&d->reloadTimer, SIGNAL(timeout()), this, SLOT(actuallyReloadTasks()));
 
148
 
 
149
    d->screenTimer.setSingleShot(true);
 
150
    d->screenTimer.setInterval(100);
 
151
    connect(&d->screenTimer, SIGNAL(timeout()), this, SLOT(checkScreenChange()));
 
152
 
 
153
    d->checkIfFullTimer.setSingleShot(true);
 
154
    d->checkIfFullTimer.setInterval(0);
 
155
    connect(&d->checkIfFullTimer, SIGNAL(timeout()), this, SLOT(actuallyCheckIfFull()));
 
156
}
 
157
 
 
158
GroupManager::~GroupManager()
 
159
{
 
160
    TaskManager::self()->setTrackGeometry(false, d->configToken);
 
161
    delete d->abstractSortingStrategy;
 
162
    delete d->abstractGroupingStrategy;
 
163
    delete d;
 
164
}
 
165
 
 
166
TaskGroup *GroupManagerPrivate::currentRootGroup()
 
167
{
 
168
    return rootGroups[currentActivity][currentDesktop];
 
169
}
 
170
 
 
171
void GroupManagerPrivate::reloadTasks()
 
172
{
 
173
    reloadTimer.start();
 
174
}
 
175
 
 
176
void GroupManagerPrivate::actuallyReloadTasks()
 
177
{
 
178
    //kDebug() << "number of tasks available " << TaskManager::self()->tasks().size();
 
179
    QHash<WId, TaskPtr> taskList = TaskManager::self()->tasks();
 
180
    QMutableHashIterator<WId, TaskPtr> it(taskList);
 
181
 
 
182
    while (it.hasNext()) {
 
183
        it.next();
 
184
 
 
185
        if (addTask(it.value())) {
 
186
            //kDebug() << "task added " << it.value()->visibleName();
 
187
            it.remove();
 
188
        }
 
189
    }
 
190
 
 
191
    // Remove what remains
 
192
    it.toFront();
 
193
    while (it.hasNext()) {
 
194
        it.next();
 
195
        removeTask(it.value());
 
196
    }
 
197
 
 
198
    emit q->reload();
 
199
}
 
200
 
 
201
void GroupManagerPrivate::addStartup(StartupPtr task)
 
202
{
 
203
    //kDebug();
 
204
    if (!startupList.contains(task)) {
 
205
        TaskItem *item = new TaskItem(q, task);
 
206
        startupList.insert(task, item);
 
207
        currentRootGroup()->add(item);
 
208
        QObject::connect(item, SIGNAL(destroyed(AbstractGroupableItem*)),
 
209
                         q, SLOT(startupItemDestroyed(AbstractGroupableItem*)));
 
210
    }
 
211
}
 
212
 
 
213
void GroupManagerPrivate::removeStartup(StartupPtr task)
 
214
{
 
215
    //kDebug();
 
216
    if (!startupList.contains(task)) {
 
217
        kDebug() << "invalid startup task";
 
218
        return;
 
219
    }
 
220
 
 
221
    AbstractGroupableItem *item = startupList.take(task);
 
222
    if (item->parentGroup()) {
 
223
        item->parentGroup()->remove(item);
 
224
    }
 
225
}
 
226
 
 
227
bool GroupManagerPrivate::addTask(TaskPtr task)
 
228
{
 
229
    //kDebug();
 
230
    /* kDebug() << task->visibleName()
 
231
             << task->visibleNameWithState()
 
232
             << task->name()
 
233
             << task->className()
 
234
             << task->classClass(); */
 
235
 
 
236
    if (!task->showInTaskbar()) {
 
237
        //kDebug() << "Do not show in taskbar";
 
238
        return false;
 
239
    }
 
240
 
 
241
    if (showOnlyCurrentScreen && !task->isOnScreen(currentScreen)) {
 
242
        //kDebug() << "Not on this screen and showOnlyCurrentScreen";
 
243
        return false;
 
244
    }
 
245
 
 
246
    // Should the Task be displayed ? We always display if attention is demaded
 
247
    if (!task->demandsAttention()) {
 
248
        // As the Task doesn't demand attention
 
249
        // go through all filters whether the task should be displayed or not
 
250
        if (showOnlyCurrentDesktop && !task->isOnCurrentDesktop()) {
 
251
            /* kDebug() << "Not on this desktop and showOnlyCurrentDesktop"
 
252
                     << KWindowSystem::currentDesktop() << task->desktop(); */
 
253
            return false;
 
254
        }
 
255
 
 
256
        if (showOnlyCurrentActivity && !task->isOnCurrentActivity()) {
 
257
            /* kDebug() << "Not on this desktop and showOnlyCurrentActivity"
 
258
                     << KWindowSystem::currentActivity() << task->desktop(); */
 
259
            return false;
 
260
        }
 
261
 
 
262
        if (showOnlyMinimized && !task->isMinimized()) {
 
263
            //kDebug() << "Not minimized and only showing minimized";
 
264
            return false;
 
265
        }
 
266
 
 
267
        NET::WindowType type = task->info().windowType(NET::NormalMask | NET::DialogMask |
 
268
                                                    NET::OverrideMask | NET::UtilityMask);
 
269
        if (type == NET::Utility) {
 
270
            //kDebug() << "skipping utility window" << task->name();
 
271
            return false;
 
272
        }
 
273
 
 
274
            //TODO: should we check for transiency? if so the following code can detect it.
 
275
        /*
 
276
            QHash <TaskPtr, TaskItem*>::iterator it = d->itemList.begin();
 
277
 
 
278
            while (it != d->itemList.end()) {
 
279
                TaskItem *item = it.value();
 
280
                if (item->task()->hasTransient(task->window())) {
 
281
                    kDebug() << "TRANSIENT TRANSIENT TRANSIENT!";
 
282
                    return flase;
 
283
                }
 
284
                ++it;
 
285
            }
 
286
        */
 
287
    }
 
288
 
 
289
    //Ok the Task should be displayed
 
290
    TaskItem *item = qobject_cast<TaskItem*>(currentRootGroup()->getMemberByWId(task->window()));
 
291
    if (!item) {
 
292
        // first search for an existing startuptask for this task
 
293
        QHash<StartupPtr, TaskItem *>::iterator it = startupList.begin();
 
294
        QHash<StartupPtr, TaskItem *>::iterator itEnd = startupList.end();
 
295
        while (it != itEnd) {
 
296
            if (it.key()->matchesWindow(task->window())) {
 
297
                //kDebug() << "startup task found";
 
298
                item = it.value();
 
299
                startupList.erase(it);
 
300
                QObject::disconnect(item, 0, q, 0);
 
301
                item->setTaskPointer(task);
 
302
                break;
 
303
            }
 
304
            ++it;
 
305
        }
 
306
 
 
307
        if (!item) {
 
308
            item = new TaskItem(q, task);
 
309
        }
 
310
 
 
311
        QObject::connect(task.data(), SIGNAL(destroyed(QObject*)),
 
312
                         q, SLOT(taskDestroyed(QObject*)));
 
313
 
 
314
        foreach (LauncherItem *launcher, launchers) {
 
315
            launcher->associateItemIfMatches(item);
 
316
        }
 
317
    }
 
318
 
 
319
    //Find a fitting group for the task with GroupingStrategies
 
320
    if (abstractGroupingStrategy && !task->demandsAttention()) { //do not group attention tasks
 
321
        abstractGroupingStrategy->handleItem(item);
 
322
    } else {
 
323
        currentRootGroup()->add(item);
 
324
    }
 
325
 
 
326
    geometryTasks.insert(task.data());
 
327
 
 
328
    return true;
 
329
}
 
330
 
 
331
 
 
332
void GroupManagerPrivate::removeTask(TaskPtr task)
 
333
{
 
334
    //kDebug() << "remove: " << task->visibleName();
 
335
    geometryTasks.remove(task.data());
 
336
 
 
337
    AbstractGroupableItem *item = currentRootGroup()->getMemberByWId(task->window());
 
338
    if (!item) {
 
339
        // this can happen if the window hasn't been caught previously,
 
340
        // of it it is an ignored type such as a NET::Utility type window
 
341
        //kDebug() << "invalid item";
 
342
        return;
 
343
    }
 
344
 
 
345
    if (item->parentGroup()) {
 
346
        item->parentGroup()->remove(item);
 
347
    }
 
348
 
 
349
    //the item must exist as long as the TaskPtr does because of activate calls so don't delete the item here, it will delete itself.
 
350
}
 
351
 
 
352
void GroupManagerPrivate::taskDestroyed(QObject *item)
 
353
{
 
354
    Task *task = static_cast<Task*>(item);
 
355
    if (task) {
 
356
        geometryTasks.remove(task);
 
357
    }
 
358
}
 
359
 
 
360
void GroupManagerPrivate::startupItemDestroyed(AbstractGroupableItem *item)
 
361
{
 
362
    TaskItem *taskItem = static_cast<TaskItem*>(item);
 
363
    startupList.remove(startupList.key(taskItem));
 
364
    geometryTasks.remove(taskItem->task().data());
 
365
}
 
366
 
 
367
bool GroupManager::manualGroupingRequest(AbstractGroupableItem* item, TaskGroup* groupItem)
 
368
{
 
369
    if (d->abstractGroupingStrategy) {
 
370
        return d->abstractGroupingStrategy->manualGroupingRequest(item, groupItem);
 
371
    }
 
372
    return false;
 
373
}
 
374
 
 
375
bool GroupManager::manualGroupingRequest(ItemList items)
 
376
{
 
377
    if (d->abstractGroupingStrategy) {
 
378
            return d->abstractGroupingStrategy->manualGroupingRequest(items);
 
379
    }
 
380
    return false;
 
381
}
 
382
 
 
383
bool GroupManager::manualSortingRequest(AbstractGroupableItem* taskItem, int newIndex)
 
384
{
 
385
    if (d->abstractSortingStrategy) {
 
386
        return d->abstractSortingStrategy->manualSortingRequest(taskItem, newIndex);
 
387
    }
 
388
    return false;
 
389
}
 
390
 
 
391
 
 
392
GroupPtr GroupManager::rootGroup() const
 
393
{
 
394
    return d->currentRootGroup();
 
395
}
 
396
 
 
397
void GroupManagerPrivate::currentActivityChanged(QString newActivity)
 
398
{
 
399
    if (!showOnlyCurrentActivity || currentActivity == newActivity) {
 
400
        return;
 
401
    }
 
402
 
 
403
    if (!rootGroups.contains(newActivity) || !rootGroups.value(newActivity).contains(currentDesktop)) {
 
404
        kDebug() << "created new desk group";
 
405
        rootGroups[newActivity][currentDesktop] = new TaskGroup(q, "RootGroup", Qt::transparent);
 
406
        if (abstractSortingStrategy) {
 
407
            abstractSortingStrategy->handleGroup(rootGroups[newActivity][currentDesktop]);
 
408
        }
 
409
    }
 
410
 
 
411
    if (onlyGroupWhenFull) {
 
412
        QObject::disconnect(currentRootGroup(), SIGNAL(itemAdded(AbstractGroupableItem *)), q, SLOT(checkIfFull()));
 
413
        QObject::disconnect(currentRootGroup(), SIGNAL(itemRemoved(AbstractGroupableItem *)), q, SLOT(checkIfFull()));
 
414
    }
 
415
 
 
416
    currentActivity = newActivity;
 
417
 
 
418
    foreach (LauncherItem *item, launchers) {
 
419
        rootGroups[currentActivity][currentDesktop]->add(item);
 
420
    }
 
421
 
 
422
    if (onlyGroupWhenFull) {
 
423
        QObject::connect(currentRootGroup(), SIGNAL(itemAdded(AbstractGroupableItem *)), q, SLOT(checkIfFull()));
 
424
        QObject::connect(currentRootGroup(), SIGNAL(itemRemoved(AbstractGroupableItem *)), q, SLOT(checkIfFull()));
 
425
    }
 
426
 
 
427
    reloadTasks();
 
428
}
 
429
 
 
430
void GroupManagerPrivate::currentDesktopChanged(int newDesktop)
 
431
{
 
432
    //kDebug();
 
433
    if (!showOnlyCurrentDesktop) {
 
434
        return;
 
435
    }
 
436
 
 
437
    if (currentDesktop == newDesktop) {
 
438
        return;
 
439
    }
 
440
 
 
441
    if (!rootGroups[currentActivity].contains(newDesktop)) {
 
442
        kDebug() << "created new desk group";
 
443
        rootGroups[currentActivity][newDesktop] = new TaskGroup(q, "RootGroup", Qt::transparent);
 
444
        if (abstractSortingStrategy) {
 
445
            abstractSortingStrategy->handleGroup(rootGroups[currentActivity][newDesktop]);
 
446
        }
 
447
    }
 
448
 
 
449
    if (onlyGroupWhenFull) {
 
450
        QObject::disconnect(currentRootGroup(), SIGNAL(itemAdded(AbstractGroupableItem *)), q, SLOT(checkIfFull()));
 
451
        QObject::disconnect(currentRootGroup(), SIGNAL(itemRemoved(AbstractGroupableItem *)), q, SLOT(checkIfFull()));
 
452
    }
 
453
 
 
454
    currentDesktop = newDesktop;
 
455
 
 
456
    foreach (LauncherItem *item, launchers) {
 
457
        rootGroups[currentActivity][currentDesktop]->add(item);
 
458
    }
 
459
 
 
460
    if (onlyGroupWhenFull) {
 
461
        QObject::connect(currentRootGroup(), SIGNAL(itemAdded(AbstractGroupableItem *)), q, SLOT(checkIfFull()));
 
462
        QObject::connect(currentRootGroup(), SIGNAL(itemRemoved(AbstractGroupableItem *)), q, SLOT(checkIfFull()));
 
463
    }
 
464
 
 
465
    reloadTasks();
 
466
}
 
467
 
 
468
 
 
469
void GroupManagerPrivate::taskChanged(TaskPtr task, ::TaskManager::TaskChanges changes)
 
470
{
 
471
    //kDebug();
 
472
    bool takeAction = false;
 
473
    bool show = true;
 
474
 
 
475
    if (showOnlyCurrentDesktop && changes & ::TaskManager::DesktopChanged) {
 
476
        takeAction = true;
 
477
        show = task->isOnCurrentDesktop();
 
478
        //kDebug() << task->visibleName() << "on" << TaskManager::self()->currentDesktop();
 
479
    }
 
480
 
 
481
    if (showOnlyCurrentActivity && changes & ::TaskManager::ActivitiesChanged) {
 
482
        takeAction = true;
 
483
        show = task->isOnCurrentActivity();
 
484
        //kDebug() << task->visibleName() << "on" << TaskManager::self()->currentDesktop();
 
485
    }
 
486
 
 
487
    if (showOnlyMinimized && changes & ::TaskManager::StateChanged) {
 
488
        //TODO: wouldn't it be nice to get notification of JUST minimization?
 
489
        takeAction = true;
 
490
        show = task->isMinimized();
 
491
    }
 
492
 
 
493
    if (showOnlyCurrentScreen && changes & ::TaskManager::GeometryChanged) {
 
494
        geometryTasks.insert(task.data());
 
495
 
 
496
        if (!screenTimer.isActive()) {
 
497
            screenTimer.start();
 
498
        }
 
499
    }
 
500
 
 
501
    if (changes & ::TaskManager::AttentionChanged) {
 
502
        // we show tasks anyway if they demand attention
 
503
        // so whenever our state changes ... try to re-adjust it
 
504
        takeAction = true;
 
505
        show = true;
 
506
    }
 
507
 
 
508
    if (!takeAction) {
 
509
        return;
 
510
    }
 
511
 
 
512
    show = show && (!showOnlyCurrentScreen || task->isOnScreen(currentScreen));
 
513
 
 
514
    if (show) {
 
515
        //kDebug() << "add(task);";
 
516
        addTask(task);
 
517
    } else {
 
518
        //kDebug() << "remove(task);";
 
519
        removeTask(task);
 
520
    }
 
521
}
 
522
 
 
523
void GroupManager::setScreen(int screen)
 
524
{
 
525
    //kDebug() << "new Screen: " << screen;
 
526
    d->currentScreen = screen;
 
527
    d->reloadTasks();
 
528
}
 
529
 
 
530
 
 
531
void GroupManagerPrivate::checkScreenChange()
 
532
{
 
533
    //kDebug();
 
534
    foreach (Task *task, geometryTasks) {
 
535
        if (task->isOnScreen(currentScreen)) {
 
536
            addTask(TaskPtr(task));
 
537
        } else {
 
538
            removeTask(TaskPtr(task));
 
539
        }
 
540
    }
 
541
}
 
542
 
 
543
void GroupManager::reconnect()
 
544
{
 
545
    //kDebug();
 
546
    disconnect(TaskManager::self(), SIGNAL(desktopChanged(int)),
 
547
               this, SLOT(currentDesktopChanged(int)));
 
548
    disconnect(TaskManager::self(), SIGNAL(activityChanged(QString)),
 
549
               this, SLOT(currentActivityChanged(QString)));
 
550
    disconnect(TaskManager::self(), SIGNAL(windowChanged(TaskPtr,::TaskManager::TaskChanges)),
 
551
               this, SLOT(taskChanged(TaskPtr,::TaskManager::TaskChanges)));
 
552
 
 
553
    if (d->showOnlyCurrentDesktop || d->showOnlyMinimized || d->showOnlyCurrentScreen || d->showOnlyCurrentActivity) {
 
554
        // listen to the relevant task manager signals
 
555
        if (d->showOnlyCurrentDesktop) {
 
556
            connect(TaskManager::self(), SIGNAL(desktopChanged(int)),
 
557
                    this, SLOT(currentDesktopChanged(int)));
 
558
        }
 
559
        if (d->showOnlyCurrentActivity) {
 
560
            connect(TaskManager::self(), SIGNAL(activityChanged(QString)),
 
561
                    this, SLOT(currentActivityChanged(QString)));
 
562
        }
 
563
 
 
564
        connect(TaskManager::self(), SIGNAL(windowChanged(TaskPtr,::TaskManager::TaskChanges)),
 
565
                this, SLOT(taskChanged(TaskPtr,::TaskManager::TaskChanges)));
 
566
    }
 
567
 
 
568
    TaskManager::self()->setTrackGeometry(d->showOnlyCurrentScreen, d->configToken);
 
569
 
 
570
    if (!d->showOnlyCurrentScreen) {
 
571
        d->geometryTasks.clear();
 
572
    }
 
573
 
 
574
    d->reloadTasks();
 
575
}
 
576
 
 
577
KConfigGroup GroupManager::config() const
 
578
{
 
579
    return KConfigGroup();
 
580
}
 
581
 
 
582
bool GroupManager::addLauncher(const KUrl &url, QIcon icon, QString name, QString genericName)
 
583
{
 
584
    if (url.isEmpty()) {
 
585
        return false;
 
586
    }
 
587
 
 
588
    LauncherItem *launcher = d->launchers.value(url); // Do not insert launchers twice
 
589
    if (!launcher) {
 
590
        launcher = new LauncherItem(d->currentRootGroup(), url);
 
591
 
 
592
        if (!launcher->isValid()) {
 
593
            delete launcher;
 
594
            return false;
 
595
        }
 
596
 
 
597
        if (!icon.isNull()) {
 
598
            launcher->setIcon(icon);
 
599
        }
 
600
 
 
601
        if (!name.isEmpty()) {
 
602
            launcher->setName(name);
 
603
        }
 
604
 
 
605
        if (!genericName.isEmpty()) {
 
606
            launcher->setGenericName(genericName);
 
607
        }
 
608
 
 
609
        QStack<TaskGroup *> groups;
 
610
        groups.push(d->currentRootGroup());
 
611
        while (!groups.isEmpty()) {
 
612
            TaskGroup *group = groups.pop();
 
613
 
 
614
            foreach (AbstractGroupableItem *item, group->members()) {
 
615
                if (item->itemType() == GroupItemType) {
 
616
                    groups.push(static_cast<TaskGroup *>(item));
 
617
                } else {
 
618
                    launcher->associateItemIfMatches(item);
 
619
                }
 
620
            }
 
621
        }
 
622
 
 
623
        d->launchers.insert(url, launcher);
 
624
        connect(launcher, SIGNAL(show(bool)), this, SLOT(launcherVisibilityChange()));
 
625
        d->checkLauncherVisibility(launcher);
 
626
        d->saveLauncher(launcher);
 
627
    }
 
628
 
 
629
    return launcher;
 
630
}
 
631
 
 
632
void GroupManager::removeLauncher(const KUrl &url)
 
633
{
 
634
    LauncherItem *launcher = d->launchers.value(url);
 
635
    if (!launcher) {
 
636
        return;
 
637
    }
 
638
 
 
639
    d->launchers.remove(url);
 
640
 
 
641
    typedef QHash<int,TaskGroup*> Metagroup;
 
642
    foreach (Metagroup metagroup, d->rootGroups) {
 
643
        foreach (TaskGroup *rootGroup, metagroup) {
 
644
            rootGroup->remove(launcher);
 
645
        }
 
646
    }
 
647
 
 
648
    d->unsaveLauncher(launcher);
 
649
    launcher->deleteLater();
 
650
}
 
651
 
 
652
void GroupManagerPrivate::launcherVisibilityChange()
 
653
{
 
654
    checkLauncherVisibility(qobject_cast<LauncherItem *>(q->sender()));
 
655
}
 
656
 
 
657
void GroupManagerPrivate::checkLauncherVisibility(LauncherItem *launcher)
 
658
{
 
659
    if (!launcher) {
 
660
        return;
 
661
    }
 
662
 
 
663
    typedef QHash<int, TaskGroup *> Metagroup;
 
664
    foreach (Metagroup metagroup, rootGroups) {
 
665
        foreach (TaskGroup *rootGroup, metagroup) {
 
666
            if (launcher->shouldShow()) {
 
667
                rootGroup->add(launcher);
 
668
            } else {
 
669
                rootGroup->remove(launcher);
 
670
            }
 
671
        }
 
672
    }
 
673
}
 
674
 
 
675
bool GroupManager::launcherExists(const KUrl &url) const
 
676
{
 
677
    return d->launchers.value(url);
 
678
}
 
679
 
 
680
void GroupManager::readLauncherConfig(const KConfigGroup &cg)
 
681
{
 
682
    KConfigGroup conf = d->launcherConfig(cg);
 
683
    if (!conf.isValid()) {
 
684
        return;
 
685
    }
 
686
 
 
687
    // prevents re-writing the results out
 
688
    d->readingLauncherConfig = true;
 
689
    QSet<KUrl> urls;
 
690
    foreach (const QString &key, conf.keyList()) {
 
691
        QStringList item = conf.readEntry(key, QStringList());
 
692
        if (item.length() >= 4) {
 
693
            KUrl url(item.at(0));
 
694
            KIcon icon;
 
695
            if (!item.at(1).isEmpty()) {
 
696
                icon = KIcon(item.at(1));
 
697
            } else if (item.length() >= 5) {
 
698
                QPixmap pixmap;
 
699
                QByteArray bytes = QByteArray::fromBase64(item.at(4).toAscii());
 
700
                pixmap.loadFromData(bytes);
 
701
                icon.addPixmap(pixmap);
 
702
            }
 
703
            QString name(item.at(2));
 
704
            QString genericName(item.at(3));
 
705
 
 
706
            if (addLauncher(url, icon, name, genericName)) {
 
707
                urls << url;
 
708
            }
 
709
        }
 
710
    }
 
711
 
 
712
    d->readingLauncherConfig = false;
 
713
 
 
714
    // a bit paranoiac, perhaps, but we check the removals first and then
 
715
    // remove the launchers after that scan because Qt's iterators operate
 
716
    // on a copy of the list and/or don't like multiple iterators messing
 
717
    // with the same list. we might get away with just calling removeLauncher
 
718
    // immediately without the removals KUrl::List, but this is known safe
 
719
    // and not a performance bottleneck
 
720
    KUrl::List removals;
 
721
    foreach (LauncherItem *launcher, d->launchers) {
 
722
        if (!urls.contains(launcher->launcherUrl())) {
 
723
            removals << launcher->launcherUrl();
 
724
        }
 
725
    }
 
726
 
 
727
    foreach (const KUrl &url, removals) {
 
728
        removeLauncher(url);
 
729
    }
 
730
}
 
731
 
 
732
void GroupManager::exportLauncherConfig(const KConfigGroup &cg)
 
733
{
 
734
    KConfigGroup conf = d->launcherConfig(cg);
 
735
    if (!conf.isValid()) {
 
736
        return;
 
737
    }
 
738
 
 
739
    foreach (LauncherItem *launcher, d->launchers) {
 
740
        d->saveLauncher(launcher, conf);
 
741
    }
 
742
}
 
743
 
 
744
KConfigGroup GroupManagerPrivate::launcherConfig(const KConfigGroup &config)
 
745
{
 
746
    KConfigGroup cg = config.isValid() ? config : q->config();
 
747
    if (!cg.isValid()) {
 
748
        return cg;
 
749
    }
 
750
 
 
751
    return KConfigGroup(&cg, "Launchers");
 
752
}
 
753
 
 
754
void GroupManagerPrivate::saveLauncher(LauncherItem *launcher)
 
755
{
 
756
    if (readingLauncherConfig) {
 
757
        return;
 
758
    }
 
759
 
 
760
    KConfigGroup cg = launcherConfig();
 
761
    if (!cg.isValid()) {
 
762
        return;
 
763
    }
 
764
 
 
765
    saveLauncher(launcher, cg);
 
766
    emit q->configChanged();
 
767
}
 
768
 
 
769
void GroupManagerPrivate::saveLauncher(LauncherItem *launcher, KConfigGroup &cg)
 
770
{
 
771
    QVariantList launcherProperties;
 
772
    launcherProperties.append(launcher->launcherUrl().url());
 
773
    launcherProperties.append(launcher->icon().name());
 
774
    launcherProperties.append(launcher->name());
 
775
    launcherProperties.append(launcher->genericName());
 
776
 
 
777
    if (launcher->icon().name().isEmpty()) {
 
778
        QPixmap pixmap = launcher->icon().pixmap(QSize(64,64));
 
779
        QByteArray bytes;
 
780
        QBuffer buffer(&bytes);
 
781
        buffer.open(QIODevice::WriteOnly);
 
782
        pixmap.save(&buffer, "PNG");
 
783
        launcherProperties.append(bytes.toBase64());
 
784
    }
 
785
 
 
786
    cg.writeEntry(launcher->name(), launcherProperties);
 
787
}
 
788
 
 
789
void GroupManagerPrivate::unsaveLauncher(LauncherItem *launcher)
 
790
{
 
791
    KConfigGroup cg = launcherConfig();
 
792
    if (!cg.isValid()) {
 
793
        return;
 
794
    }
 
795
 
 
796
    cg.deleteEntry(launcher->name());
 
797
    emit q->configChanged();
 
798
}
 
799
 
 
800
bool GroupManager::onlyGroupWhenFull() const
 
801
{
 
802
    return d->onlyGroupWhenFull;
 
803
}
 
804
 
 
805
void GroupManager::setOnlyGroupWhenFull(bool onlyGroupWhenFull)
 
806
{
 
807
    //kDebug() << onlyGroupWhenFull;
 
808
    if (d->onlyGroupWhenFull == onlyGroupWhenFull) {
 
809
        return;
 
810
    }
 
811
 
 
812
    d->onlyGroupWhenFull = onlyGroupWhenFull;
 
813
 
 
814
    disconnect(d->currentRootGroup(), SIGNAL(itemAdded(AbstractGroupableItem *)), this, SLOT(checkIfFull()));
 
815
    disconnect(d->currentRootGroup(), SIGNAL(itemRemoved(AbstractGroupableItem *)), this, SLOT(checkIfFull()));
 
816
 
 
817
    if (onlyGroupWhenFull) {
 
818
        connect(d->currentRootGroup(), SIGNAL(itemAdded(AbstractGroupableItem *)), this, SLOT(checkIfFull()));
 
819
        connect(d->currentRootGroup(), SIGNAL(itemRemoved(AbstractGroupableItem *)), this, SLOT(checkIfFull()));
 
820
        d->checkIfFull();
 
821
    }
 
822
}
 
823
 
 
824
void GroupManager::setFullLimit(int limit)
 
825
{
 
826
    //kDebug() << limit;
 
827
    d->groupIsFullLimit = limit;
 
828
    if (d->onlyGroupWhenFull) {
 
829
        d->checkIfFull();
 
830
    }
 
831
}
 
832
 
 
833
void GroupManagerPrivate::checkIfFull()
 
834
{
 
835
    // Start a timer so that if we have been triggered by a layouting
 
836
    // we give time for it to finish up instead of starting a new one
 
837
    // right away.
 
838
    checkIfFullTimer.start();
 
839
}
 
840
 
 
841
void GroupManagerPrivate::actuallyCheckIfFull()
 
842
{
 
843
    //kDebug();
 
844
    if (!onlyGroupWhenFull ||
 
845
        groupingStrategy != GroupManager::ProgramGrouping ||
 
846
        changingGroupingStrategy) {
 
847
        return;
 
848
    }
 
849
 
 
850
    if (currentRootGroup()->totalSize() >= groupIsFullLimit) {
 
851
        if (!abstractGroupingStrategy) {
 
852
            geometryTasks.clear();
 
853
            q->setGroupingStrategy(GroupManager::ProgramGrouping);
 
854
        }
 
855
    } else if (abstractGroupingStrategy) {
 
856
        geometryTasks.clear();
 
857
        q->setGroupingStrategy(GroupManager::NoGrouping);
 
858
        //let the visualization think we still use the programGrouping
 
859
        groupingStrategy = GroupManager::ProgramGrouping;
 
860
    }
 
861
}
 
862
 
 
863
bool GroupManager::showOnlyCurrentScreen() const
 
864
{
 
865
    return d->showOnlyCurrentScreen;
 
866
}
 
867
 
 
868
void GroupManager::setShowOnlyCurrentScreen(bool showOnlyCurrentScreen)
 
869
{
 
870
    d->showOnlyCurrentScreen = showOnlyCurrentScreen;
 
871
    reconnect();
 
872
}
 
873
 
 
874
bool GroupManager::showOnlyCurrentDesktop() const
 
875
{
 
876
    return d->showOnlyCurrentDesktop;
 
877
}
 
878
 
 
879
void GroupManager::setShowOnlyCurrentDesktop(bool showOnlyCurrentDesktop)
 
880
{
 
881
    d->showOnlyCurrentDesktop = showOnlyCurrentDesktop;
 
882
    reconnect();
 
883
}
 
884
 
 
885
bool GroupManager::showOnlyCurrentActivity() const
 
886
{
 
887
    return d->showOnlyCurrentActivity;
 
888
}
 
889
 
 
890
void GroupManager::setShowOnlyCurrentActivity(bool showOnlyCurrentActivity)
 
891
{
 
892
    d->showOnlyCurrentActivity = showOnlyCurrentActivity;
 
893
    reconnect();
 
894
}
 
895
 
 
896
bool GroupManager::showOnlyMinimized() const
 
897
{
 
898
    return d->showOnlyMinimized;
 
899
}
 
900
 
 
901
void GroupManager::setShowOnlyMinimized(bool showOnlyMinimized)
 
902
{
 
903
    d->showOnlyMinimized = showOnlyMinimized;
 
904
    reconnect();
 
905
}
 
906
 
 
907
GroupManager::TaskSortingStrategy GroupManager::sortingStrategy() const
 
908
{
 
909
    return d->sortingStrategy;
 
910
}
 
911
 
 
912
AbstractSortingStrategy* GroupManager::taskSorter() const
 
913
{
 
914
    return d->abstractSortingStrategy;
 
915
}
 
916
 
 
917
void GroupManager::setSortingStrategy(TaskSortingStrategy sortOrder)
 
918
{
 
919
    //kDebug() << sortOrder;
 
920
 
 
921
    if (d->abstractSortingStrategy) {
 
922
        if (d->abstractSortingStrategy->type() == sortOrder) {
 
923
            return;
 
924
        }
 
925
 
 
926
        d->abstractSortingStrategy->deleteLater();
 
927
        d->abstractSortingStrategy = 0;
 
928
    }
 
929
 
 
930
    switch (sortOrder) {
 
931
        case ManualSorting:
 
932
            d->abstractSortingStrategy = new ManualSortingStrategy(this);
 
933
            break;
 
934
 
 
935
        case AlphaSorting:
 
936
            d->abstractSortingStrategy = new AlphaSortingStrategy(this);
 
937
            break;
 
938
 
 
939
        case DesktopSorting:
 
940
            d->abstractSortingStrategy = new DesktopSortingStrategy(this);
 
941
            break;
 
942
 
 
943
        case NoSorting: //manual and no grouping result both in non automatic grouping
 
944
            break;
 
945
 
 
946
        default:
 
947
            kDebug() << "Invalid Strategy";
 
948
    }
 
949
    if (d->abstractSortingStrategy) {
 
950
        typedef QHash<int,TaskGroup*> Metagroup;
 
951
        foreach (Metagroup metagroup, d->rootGroups) {
 
952
            foreach (TaskGroup *group, metagroup) {
 
953
                d->abstractSortingStrategy->handleGroup(group);
 
954
            }
 
955
        }
 
956
    }
 
957
 
 
958
    d->sortingStrategy = sortOrder;
 
959
    reconnect();
 
960
}
 
961
 
 
962
GroupManager::TaskGroupingStrategy GroupManager::groupingStrategy() const
 
963
{
 
964
    return d->groupingStrategy;
 
965
}
 
966
 
 
967
AbstractGroupingStrategy* GroupManager::taskGrouper() const
 
968
{
 
969
    return d->abstractGroupingStrategy;
 
970
}
 
971
 
 
972
 
 
973
void GroupManager::setGroupingStrategy(TaskGroupingStrategy strategy)
 
974
{
 
975
    if (d->changingGroupingStrategy ||
 
976
        (d->abstractGroupingStrategy && d->abstractGroupingStrategy->type() == strategy)) {
 
977
        return;
 
978
    }
 
979
 
 
980
    d->changingGroupingStrategy = true;
 
981
 
 
982
    //kDebug() << strategy << kBacktrace();
 
983
    if (d->onlyGroupWhenFull) {
 
984
        disconnect(d->currentRootGroup(), SIGNAL(itemAdded(AbstractGroupableItem *)), this, SLOT(checkIfFull()));
 
985
        disconnect(d->currentRootGroup(), SIGNAL(itemRemoved(AbstractGroupableItem *)), this, SLOT(checkIfFull()));
 
986
    }
 
987
 
 
988
    if (d->abstractGroupingStrategy) {
 
989
        disconnect(d->abstractGroupingStrategy, 0, this, 0);
 
990
        d->abstractGroupingStrategy->destroy();
 
991
        d->abstractGroupingStrategy = 0;
 
992
    }
 
993
 
 
994
    switch (strategy) {
 
995
        case ManualGrouping:
 
996
            d->abstractGroupingStrategy = new ManualGroupingStrategy(this);
 
997
            break;
 
998
 
 
999
        case ProgramGrouping:
 
1000
            d->abstractGroupingStrategy = new ProgramGroupingStrategy(this);
 
1001
            break;
 
1002
 
 
1003
        case KustodianGrouping:
 
1004
            d->abstractGroupingStrategy = new KustodianGroupingStrategy(this);
 
1005
            break;
 
1006
 
 
1007
        case NoGrouping:
 
1008
            break;
 
1009
 
 
1010
        default:
 
1011
            kDebug() << "Strategy not implemented";
 
1012
    }
 
1013
 
 
1014
    d->groupingStrategy = strategy;
 
1015
 
 
1016
    d->actuallyReloadTasks();
 
1017
 
 
1018
    if (d->onlyGroupWhenFull) {
 
1019
        connect(d->currentRootGroup(), SIGNAL(itemAdded(AbstractGroupableItem *)), this, SLOT(checkIfFull()));
 
1020
        connect(d->currentRootGroup(), SIGNAL(itemRemoved(AbstractGroupableItem *)), this, SLOT(checkIfFull()));
 
1021
    }
 
1022
 
 
1023
    d->changingGroupingStrategy = false;
 
1024
}
 
1025
 
 
1026
} // TaskManager namespace
 
1027
 
 
1028
#include "groupmanager.moc"