not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / libs / taskmanager / groupmanager.cpp
blob1ca0c42a7af90e908634664a02134cc7bc9f2ce8
1 /*****************************************************************
3 Copyright 2008 Christian Mollekopf <chrigi_1@hotmail.com>
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:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
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.
22 ******************************************************************/
24 #include "groupmanager.h"
26 #include <QList>
27 #include <KDebug>
28 #include <QTimer>
29 #include <QUuid>
31 #include "abstractsortingstrategy.h"
32 #include "startup.h"
33 #include "task.h"
34 #include "taskitem.h"
35 #include "taskgroup.h"
36 #include "taskmanager.h"
37 #include "strategies/alphasortingstrategy.h"
38 #include "strategies/desktopsortingstrategy.h"
39 #include "strategies/programgroupingstrategy.h"
40 #include "strategies/manualgroupingstrategy.h"
41 #include "strategies/manualsortingstrategy.h"
43 namespace TaskManager
46 class GroupManagerPrivate
48 public:
49 GroupManagerPrivate(GroupManager *manager)
50 : q(manager),
51 rootGroup(0),
52 sortingStrategy(GroupManager::NoSorting),
53 groupingStrategy(GroupManager::NoGrouping),
54 lastGroupingStrategy(GroupManager::NoGrouping),
55 abstractGroupingStrategy(0),
56 abstractSortingStrategy(0),
57 currentScreen(-1),
58 groupIsFullLimit(0),
59 showOnlyCurrentDesktop(false),
60 showOnlyCurrentScreen(false),
61 showOnlyMinimized(false),
62 onlyGroupWhenFull(false),
63 changingGroupingStragegy(false)
67 /** reload all tasks from TaskManager */
68 void reloadTasks();
70 /**
71 * Keep track of changes in Taskmanager
73 void currentDesktopChanged(int);
74 void taskChanged(TaskPtr, ::TaskManager::TaskChanges);
76 void checkScreenChange();
78 void itemDestroyed();
79 void checkIfFull();
81 GroupManager *q;
82 QHash<TaskPtr, TaskItem*> itemList; //holds all tasks of the Taskmanager
83 QHash<StartupPtr, TaskItem*> startupList;
84 TaskGroup *rootGroup; //the current layout
85 GroupManager::TaskSortingStrategy sortingStrategy;
86 GroupManager::TaskGroupingStrategy groupingStrategy;
87 GroupManager::TaskGroupingStrategy lastGroupingStrategy;
88 AbstractGroupingStrategy *abstractGroupingStrategy;
89 AbstractSortingStrategy *abstractSortingStrategy;
90 int currentScreen;
91 QTimer screenTimer;
92 QList<TaskPtr> geometryTasks;
93 int groupIsFullLimit;
94 bool showOnlyCurrentDesktop : 1;
95 bool showOnlyCurrentScreen : 1;
96 bool showOnlyMinimized : 1;
97 bool onlyGroupWhenFull : 1;
98 bool changingGroupingStragegy : 1;
99 QUuid configToken;
105 GroupManager::GroupManager(QObject *parent)
106 : QObject(parent),
107 d(new GroupManagerPrivate(this))
109 connect(TaskManager::self(), SIGNAL(taskAdded(TaskPtr)), this, SLOT(add(TaskPtr)));
110 connect(TaskManager::self(), SIGNAL(taskRemoved(TaskPtr)), this, SLOT(remove(TaskPtr)));
111 connect(TaskManager::self(), SIGNAL(startupAdded(StartupPtr)), this, SLOT(add(StartupPtr)));
112 connect(TaskManager::self(), SIGNAL(startupRemoved(StartupPtr)), this, SLOT(remove(StartupPtr)));
113 d->rootGroup = new TaskGroup(this, "RootGroup", Qt::transparent);
114 //reloadTasks();
115 d->screenTimer.setSingleShot(true);
116 d->screenTimer.setInterval(100);
117 connect(&d->screenTimer, SIGNAL(timeout()), this, SLOT(checkScreenChange()));
120 GroupManager::~GroupManager()
122 TaskManager::TaskManager::self()->setTrackGeometry(false, d->configToken);
123 delete d->abstractSortingStrategy;
124 delete d->abstractGroupingStrategy;
125 delete d->rootGroup;
126 delete d;
129 void GroupManagerPrivate::reloadTasks()
131 //kDebug() << "number of tasks available " << TaskManager::self()->tasks().size();
133 QList <TaskPtr> taskList = TaskManager::self()->tasks().values();
134 foreach (const TaskPtr& task, taskList) { //Add all existing tasks
135 if (!q->add(task)) {
136 q->remove(task); //remove what isn't needed anymore
138 taskList.removeAll(task);
141 foreach (const TaskPtr& task, taskList) { //Remove the remaining
142 q->remove(task);
145 emit q->reload();
148 void GroupManager::add(StartupPtr task)
150 //kDebug();
151 TaskItem *item;
152 if (!d->startupList.contains(task)) {
153 item = new TaskItem(this, task);
154 d->startupList.insert(task, item);
155 d->rootGroup->add(item);
159 void GroupManager::remove(StartupPtr task)
161 //kDebug() << "startup";
162 if (!d->startupList.contains(task)) {
163 kDebug() << "invalid startup task";
164 return;
167 AbstractItemPtr item = d->startupList.take(task);
168 if (item->parentGroup()) {
169 item->parentGroup()->remove(item);
172 emit itemRemoved(item);
175 bool GroupManager::add(TaskPtr task)
177 /* kDebug() << task->visibleName()
178 << task->visibleNameWithState()
179 << task->name()
180 << task->className()
181 << task->classClass(); */
183 if (!task->showInTaskbar()) {
184 //kDebug() << "Do not show in taskbar";
185 return false;
188 if (showOnlyCurrentScreen() && !task->isOnScreen(d->currentScreen)) {
189 //kDebug() << "Not on this screen and showOnlyCurrentScreen";
190 return false;
193 // Should the Task be displayed ? We always display if attention is demaded
194 if (!task->demandsAttention()) {
195 // As the Task doesn't demand attention
196 // go through all filters whether the task should be displayed or not
197 if (showOnlyCurrentDesktop() && !task->isOnCurrentDesktop()) {
198 /* kDebug() << "Not on this desktop and showOnlyCurrentDesktop"
199 << KWindowSystem::currentDesktop() << task->desktop(); */
200 return false;
203 if (showOnlyMinimized() && !task->isMinimized()) {
204 //kDebug() << "Not minimized and only showing minimized";
205 return false;
208 NET::WindowType type = task->info().windowType(NET::NormalMask | NET::DialogMask |
209 NET::OverrideMask | NET::UtilityMask);
210 if (type == NET::Utility) {
211 //kDebug() << "skipping utility window" << task->name();
212 return false;
215 //TODO: should we check for transiency? if so the following code can detect it.
217 QHash <TaskPtr, TaskItem*>::iterator it = d->itemList.begin();
219 while (it != d->itemList.end()) {
220 TaskItem *item = it.value();
221 if (item->task()->hasTransient(task->window())) {
222 kDebug() << "TRANSIENT TRANSIENT TRANSIENT!";
223 return flase;
225 ++it;
230 //Ok the Task should be displayed
231 TaskItem *item = 0;
232 if (!d->itemList.contains(task)) {
233 //Lookout for existing startuptask of this task
234 QMutableHashIterator<StartupPtr, TaskItem*> it(d->startupList);
235 while (it.hasNext()) {
236 it.next();
237 if (it.key()->matchesWindow(task->window())) {
238 //kDebug() << "startup task";
239 item = it.value();
240 item->setTaskPointer(task);
241 it.remove();
242 break;
246 if (!item) {
247 item = new TaskItem(this, task);
250 connect(item, SIGNAL(destroyed()), this, SLOT(itemDestroyed()));
251 d->itemList.insert(task, item);
252 } else {
253 item = d->itemList.value(task); //we add it again so the group is evaluated again
256 //Find a fitting group for the task with GroupingStrategies
257 if (d->abstractGroupingStrategy && !task->demandsAttention()) { //do not group attetion tasks
258 d->abstractGroupingStrategy->handleItem(item);
259 } else {
260 d->rootGroup->add(item);
263 return true;
267 void GroupManager::remove(TaskPtr task)
269 //kDebug() << "remove: " << task->visibleName();
270 if (!d->geometryTasks.isEmpty()) {
271 d->geometryTasks.removeAll(task);
274 TaskItem *item = d->itemList.value(task);
275 if (!item) {
276 // this can happen if the window hasn't been caught previously,
277 // of it its an ignored type such as a NET::Utility type window
278 //kDebug() << "invalid item";
279 return;
282 if (item->parentGroup()) {
283 item->parentGroup()->remove(item);
286 emit itemRemoved(item);
287 //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. We keep it in the itemlist because it may return
290 void GroupManagerPrivate::itemDestroyed()
292 TaskItem *taskItem = qobject_cast<TaskItem*>(q->sender());
293 TaskItem *item = itemList.take(itemList.key(taskItem));
294 if (!item) {
295 kDebug() << "invalid item";
296 return;
298 QObject::disconnect(item, 0, q, 0);
302 bool GroupManager::manualGroupingRequest(AbstractGroupableItem* item, TaskGroup* groupItem)
304 //kDebug();
305 if (d->abstractGroupingStrategy) {
306 return d->abstractGroupingStrategy->addItemToGroup(item, groupItem);
307 // kDebug() << d->abstractGroupingStrategy->type() << ManualGrouping;
308 /*if (d->abstractGroupingStrategy->type() == ManualGrouping) {
309 // kDebug();
310 return (qobject_cast<ManualGroupingStrategy*>(d->abstractGroupingStrategy))->addItemToGroup(item,groupItem);
313 return false;
316 bool GroupManager::manualGroupingRequest(ItemList items)
318 // kDebug();
319 if (d->abstractGroupingStrategy) {
320 // kDebug() << d->abstractGroupingStrategy->type() << ManualGrouping;
321 if (d->abstractGroupingStrategy->type() == ManualGrouping) {
322 // kDebug();
323 return (qobject_cast<ManualGroupingStrategy*>(d->abstractGroupingStrategy))->groupItems(items);
326 return false;
329 bool GroupManager::manualSortingRequest(AbstractGroupableItem* taskItem, int newIndex)
331 //kDebug();
332 if (d->abstractSortingStrategy) {
333 if (d->abstractSortingStrategy->type() == ManualSorting) {
334 return (qobject_cast<ManualSortingStrategy*>(d->abstractSortingStrategy))->moveItem(taskItem, newIndex);
337 return false;
341 GroupPtr GroupManager::rootGroup() const
343 return d->rootGroup;
347 void GroupManagerPrivate::currentDesktopChanged(int newDesktop)
349 //kDebug();
350 if (!showOnlyCurrentDesktop) {
351 return;
354 if (abstractSortingStrategy) {
355 abstractSortingStrategy->desktopChanged(newDesktop);
358 if (abstractGroupingStrategy) {
359 abstractGroupingStrategy->desktopChanged(newDesktop);
362 reloadTasks();
366 void GroupManagerPrivate::taskChanged(TaskPtr task, ::TaskManager::TaskChanges changes)
368 //kDebug();
369 bool takeAction = false;
370 bool show = true;
372 if (showOnlyCurrentDesktop && changes & ::TaskManager::DesktopChanged) {
373 takeAction = true;
374 show = task->isOnCurrentDesktop();
375 //kDebug() << task->visibleName() << "on" << TaskManager::self()->currentDesktop();
378 if (showOnlyMinimized && changes & ::TaskManager::StateChanged) {
379 //TODO: wouldn't it be nice to get notification of JUST minimization?
380 takeAction = true;
381 show = task->isMinimized();
384 if (changes & ::TaskManager::GeometryChanged) {
385 if (!geometryTasks.contains(task)) {
386 geometryTasks.append(task);
389 if (!screenTimer.isActive()) {
390 screenTimer.start();
394 //show tasks anyway if they demand attention
395 if (changes & ::TaskManager::StateChanged && task->demandsAttention()) {
396 takeAction = true;
397 show = true;
400 if (!takeAction) {
401 return;
404 if (show) {
405 //kDebug() << "add(task);";
406 q->add(task);
407 } else {
408 //kDebug() << "remove(task);";
409 q->remove(task);
413 void GroupManager::setScreen(int screen)
415 //kDebug() << "new Screen: " << screen;
416 d->currentScreen = screen;
420 void GroupManagerPrivate::checkScreenChange()
422 //kDebug();
423 foreach (const TaskPtr &task, geometryTasks) {
424 if (task->isOnScreen(currentScreen)) {
425 q->add(task);
426 } else {
427 q->remove(task);
431 geometryTasks.clear();
435 void GroupManager::reconnect()
437 //kDebug();
438 disconnect(TaskManager::self(), SIGNAL(desktopChanged(int)),
439 this, SLOT(currentDesktopChanged(int)));
440 disconnect(TaskManager::self(), SIGNAL(windowChanged(TaskPtr,::TaskManager::TaskChanges)),
441 this, SLOT(taskChanged(TaskPtr,::TaskManager::TaskChanges)));
443 if (d->showOnlyCurrentDesktop || d->showOnlyMinimized || d->showOnlyCurrentScreen) {
444 // listen to the relevant task manager signals
445 if (d->showOnlyCurrentDesktop) {
446 connect(TaskManager::TaskManager::self(), SIGNAL(desktopChanged(int)),
447 this, SLOT(currentDesktopChanged(int)));
450 connect(TaskManager::self(), SIGNAL(windowChanged(TaskPtr,::TaskManager::TaskChanges)),
451 this, SLOT(taskChanged(TaskPtr,::TaskManager::TaskChanges)));
454 TaskManager::TaskManager::self()->setTrackGeometry(d->showOnlyCurrentScreen, d->configToken);
456 if (!d->showOnlyCurrentScreen) {
457 d->geometryTasks.clear();
460 d->reloadTasks();
464 bool GroupManager::onlyGroupWhenFull() const
466 return d->onlyGroupWhenFull;
469 void GroupManager::setOnlyGroupWhenFull(bool state)
471 //kDebug() << state;
472 if (d->onlyGroupWhenFull == state) {
473 return;
476 d->onlyGroupWhenFull = state;
478 if (state) {
479 connect(d->rootGroup, SIGNAL(itemAdded(AbstractItemPtr)), this, SLOT(checkIfFull()));
480 connect(d->rootGroup, SIGNAL(itemRemoved(AbstractItemPtr)), this, SLOT(checkIfFull()));
481 d->checkIfFull();
482 } else {
483 disconnect(d->rootGroup, SIGNAL(itemAdded(AbstractItemPtr)), this, SLOT(checkIfFull()));
484 disconnect(d->rootGroup, SIGNAL(itemRemoved(AbstractItemPtr)), this, SLOT(checkIfFull()));
488 void GroupManager::setFullLimit(int limit)
490 //kDebug() << limit;
491 d->groupIsFullLimit = limit;
492 if (d->onlyGroupWhenFull) {
493 d->checkIfFull();
497 void GroupManagerPrivate::checkIfFull()
499 //kDebug();
500 if (!onlyGroupWhenFull || groupingStrategy != GroupManager::ProgramGrouping) {
501 return;
504 if (itemList.size() >= groupIsFullLimit) {
505 if (!abstractGroupingStrategy) {
506 q->setGroupingStrategy(GroupManager::ProgramGrouping);
508 } else if (abstractGroupingStrategy) {
509 q->setGroupingStrategy(GroupManager::NoGrouping);
510 //let the visualization thing we still use the programGrouping
511 groupingStrategy = GroupManager::ProgramGrouping;
515 bool GroupManager::showOnlyCurrentScreen() const
517 return d->showOnlyCurrentScreen;
520 void GroupManager::setShowOnlyCurrentScreen(bool showOnlyCurrentScreen)
522 d->showOnlyCurrentScreen = showOnlyCurrentScreen;
525 bool GroupManager::showOnlyCurrentDesktop() const
527 return d->showOnlyCurrentDesktop;
530 void GroupManager::setShowOnlyCurrentDesktop(bool showOnlyCurrentDesktop)
532 d->showOnlyCurrentDesktop = showOnlyCurrentDesktop;
535 bool GroupManager::showOnlyMinimized() const
537 return d->showOnlyMinimized;
540 void GroupManager::setShowOnlyMinimized(bool showOnlyMinimized)
542 d->showOnlyMinimized = showOnlyMinimized;
545 GroupManager::TaskSortingStrategy GroupManager::sortingStrategy() const
547 return d->sortingStrategy;
550 AbstractSortingStrategy* GroupManager::taskSorter() const
552 return d->abstractSortingStrategy;
555 void GroupManager::setSortingStrategy(TaskSortingStrategy sortOrder)
557 //kDebug() << sortOrder;
559 if (d->abstractSortingStrategy) {
560 if (d->abstractSortingStrategy->type() == sortOrder){
561 return;
562 } else {
563 d->abstractSortingStrategy->deleteLater();
567 switch (sortOrder) {
568 case NoSorting: //manual and no grouping result both in non automatic grouping
569 d->abstractSortingStrategy = 0;
570 break;
571 case ManualSorting:
572 d->abstractSortingStrategy = new ManualSortingStrategy(this);
573 d->abstractSortingStrategy->handleGroup(d->rootGroup);
574 break;
576 case AlphaSorting:
577 d->abstractSortingStrategy = new AlphaSortingStrategy(this);
578 d->abstractSortingStrategy->handleGroup(d->rootGroup);
579 break;
581 case DesktopSorting:
582 d->abstractSortingStrategy = new DesktopSortingStrategy(this);
583 d->abstractSortingStrategy->handleGroup(d->rootGroup);
584 break;
586 default:
587 kDebug() << "Invalid Strategy";
588 d->abstractSortingStrategy = 0;
591 d->sortingStrategy = sortOrder;
592 d->reloadTasks();
595 GroupManager::TaskGroupingStrategy GroupManager::groupingStrategy() const
597 return d->groupingStrategy;
600 AbstractGroupingStrategy* GroupManager::taskGrouper() const
602 return d->abstractGroupingStrategy;
605 void GroupManager::setGroupingStrategy(TaskGroupingStrategy strategy)
607 if (d->changingGroupingStragegy ||
608 (d->abstractGroupingStrategy && d->abstractGroupingStrategy->type() == strategy)) {
609 return;
612 d->changingGroupingStragegy = true;
614 //kDebug() << strategy << kBacktrace();
615 if (d->onlyGroupWhenFull) {
616 disconnect(d->rootGroup, SIGNAL(itemAdded(AbstractItemPtr)), this, SLOT(checkIfFull()));
617 disconnect(d->rootGroup, SIGNAL(itemRemoved(AbstractItemPtr)), this, SLOT(checkIfFull()));
620 delete d->abstractGroupingStrategy;
621 d->abstractGroupingStrategy = 0;
623 switch (strategy) {
624 case NoGrouping:
625 d->abstractGroupingStrategy = 0;
626 break;
627 case ManualGrouping:
628 d->abstractGroupingStrategy = new ManualGroupingStrategy(this);
629 break;
631 case ProgramGrouping:
632 d->abstractGroupingStrategy = new ProgramGroupingStrategy(this);
633 break;
635 default:
636 kDebug() << "Strategy not implemented";
637 d->abstractGroupingStrategy = 0;
640 d->groupingStrategy = strategy;
642 if (d->groupingStrategy) {
643 connect(d->abstractGroupingStrategy, SIGNAL(groupRemoved(TaskGroup*)),
644 this, SIGNAL(groupRemoved(TaskGroup*)));
647 d->reloadTasks();
649 if (d->onlyGroupWhenFull) {
650 connect(d->rootGroup, SIGNAL(itemAdded(AbstractItemPtr)), this, SLOT(checkIfFull()));
651 connect(d->rootGroup, SIGNAL(itemRemoved(AbstractItemPtr)), this, SLOT(checkIfFull()));
654 d->changingGroupingStragegy = false;
657 } // TaskManager namespace
659 #include "groupmanager.moc"