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"
31 #include "abstractsortingstrategy.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"
46 class GroupManagerPrivate
49 GroupManagerPrivate(GroupManager
*manager
)
52 sortingStrategy(GroupManager::NoSorting
),
53 groupingStrategy(GroupManager::NoGrouping
),
54 lastGroupingStrategy(GroupManager::NoGrouping
),
55 abstractGroupingStrategy(0),
56 abstractSortingStrategy(0),
59 showOnlyCurrentDesktop(false),
60 showOnlyCurrentScreen(false),
61 showOnlyMinimized(false),
62 onlyGroupWhenFull(false),
63 changingGroupingStragegy(false)
67 /** reload all tasks from TaskManager */
71 * Keep track of changes in Taskmanager
73 void currentDesktopChanged(int);
74 void taskChanged(TaskPtr
, ::TaskManager::TaskChanges
);
76 void checkScreenChange();
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
;
92 QList
<TaskPtr
> geometryTasks
;
94 bool showOnlyCurrentDesktop
: 1;
95 bool showOnlyCurrentScreen
: 1;
96 bool showOnlyMinimized
: 1;
97 bool onlyGroupWhenFull
: 1;
98 bool changingGroupingStragegy
: 1;
105 GroupManager::GroupManager(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
);
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
;
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
136 q
->remove(task
); //remove what isn't needed anymore
138 taskList
.removeAll(task
);
141 foreach (const TaskPtr
& task
, taskList
) { //Remove the remaining
148 void GroupManager::add(StartupPtr task
)
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";
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()
181 << task->classClass(); */
183 if (!task
->showInTaskbar()) {
184 //kDebug() << "Do not show in taskbar";
188 if (showOnlyCurrentScreen() && !task
->isOnScreen(d
->currentScreen
)) {
189 //kDebug() << "Not on this screen and showOnlyCurrentScreen";
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(); */
203 if (showOnlyMinimized() && !task
->isMinimized()) {
204 //kDebug() << "Not minimized and only showing minimized";
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();
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!";
230 //Ok the Task should be displayed
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()) {
237 if (it
.key()->matchesWindow(task
->window())) {
238 //kDebug() << "startup task";
240 item
->setTaskPointer(task
);
247 item
= new TaskItem(this, task
);
250 connect(item
, SIGNAL(destroyed()), this, SLOT(itemDestroyed()));
251 d
->itemList
.insert(task
, item
);
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
);
260 d
->rootGroup
->add(item
);
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
);
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";
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
));
295 kDebug() << "invalid item";
298 QObject::disconnect(item
, 0, q
, 0);
302 bool GroupManager::manualGroupingRequest(AbstractGroupableItem
* item
, TaskGroup
* groupItem
)
305 if (d
->abstractGroupingStrategy
) {
306 return d
->abstractGroupingStrategy
->addItemToGroup(item
, groupItem
);
307 // kDebug() << d->abstractGroupingStrategy->type() << ManualGrouping;
308 /*if (d->abstractGroupingStrategy->type() == ManualGrouping) {
310 return (qobject_cast<ManualGroupingStrategy*>(d->abstractGroupingStrategy))->addItemToGroup(item,groupItem);
316 bool GroupManager::manualGroupingRequest(ItemList items
)
319 if (d
->abstractGroupingStrategy
) {
320 // kDebug() << d->abstractGroupingStrategy->type() << ManualGrouping;
321 if (d
->abstractGroupingStrategy
->type() == ManualGrouping
) {
323 return (qobject_cast
<ManualGroupingStrategy
*>(d
->abstractGroupingStrategy
))->groupItems(items
);
329 bool GroupManager::manualSortingRequest(AbstractGroupableItem
* taskItem
, int newIndex
)
332 if (d
->abstractSortingStrategy
) {
333 if (d
->abstractSortingStrategy
->type() == ManualSorting
) {
334 return (qobject_cast
<ManualSortingStrategy
*>(d
->abstractSortingStrategy
))->moveItem(taskItem
, newIndex
);
341 GroupPtr
GroupManager::rootGroup() const
347 void GroupManagerPrivate::currentDesktopChanged(int newDesktop
)
350 if (!showOnlyCurrentDesktop
) {
354 if (abstractSortingStrategy
) {
355 abstractSortingStrategy
->desktopChanged(newDesktop
);
358 if (abstractGroupingStrategy
) {
359 abstractGroupingStrategy
->desktopChanged(newDesktop
);
366 void GroupManagerPrivate::taskChanged(TaskPtr task
, ::TaskManager::TaskChanges changes
)
369 bool takeAction
= false;
372 if (showOnlyCurrentDesktop
&& changes
& ::TaskManager::DesktopChanged
) {
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?
381 show
= task
->isMinimized();
384 if (changes
& ::TaskManager::GeometryChanged
) {
385 if (!geometryTasks
.contains(task
)) {
386 geometryTasks
.append(task
);
389 if (!screenTimer
.isActive()) {
394 //show tasks anyway if they demand attention
395 if (changes
& ::TaskManager::StateChanged
&& task
->demandsAttention()) {
405 //kDebug() << "add(task);";
408 //kDebug() << "remove(task);";
413 void GroupManager::setScreen(int screen
)
415 //kDebug() << "new Screen: " << screen;
416 d
->currentScreen
= screen
;
420 void GroupManagerPrivate::checkScreenChange()
423 foreach (const TaskPtr
&task
, geometryTasks
) {
424 if (task
->isOnScreen(currentScreen
)) {
431 geometryTasks
.clear();
435 void GroupManager::reconnect()
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();
464 bool GroupManager::onlyGroupWhenFull() const
466 return d
->onlyGroupWhenFull
;
469 void GroupManager::setOnlyGroupWhenFull(bool state
)
472 if (d
->onlyGroupWhenFull
== state
) {
476 d
->onlyGroupWhenFull
= state
;
479 connect(d
->rootGroup
, SIGNAL(itemAdded(AbstractItemPtr
)), this, SLOT(checkIfFull()));
480 connect(d
->rootGroup
, SIGNAL(itemRemoved(AbstractItemPtr
)), this, SLOT(checkIfFull()));
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
)
491 d
->groupIsFullLimit
= limit
;
492 if (d
->onlyGroupWhenFull
) {
497 void GroupManagerPrivate::checkIfFull()
500 if (!onlyGroupWhenFull
|| groupingStrategy
!= GroupManager::ProgramGrouping
) {
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
){
563 d
->abstractSortingStrategy
->deleteLater();
568 case NoSorting
: //manual and no grouping result both in non automatic grouping
569 d
->abstractSortingStrategy
= 0;
572 d
->abstractSortingStrategy
= new ManualSortingStrategy(this);
573 d
->abstractSortingStrategy
->handleGroup(d
->rootGroup
);
577 d
->abstractSortingStrategy
= new AlphaSortingStrategy(this);
578 d
->abstractSortingStrategy
->handleGroup(d
->rootGroup
);
582 d
->abstractSortingStrategy
= new DesktopSortingStrategy(this);
583 d
->abstractSortingStrategy
->handleGroup(d
->rootGroup
);
587 kDebug() << "Invalid Strategy";
588 d
->abstractSortingStrategy
= 0;
591 d
->sortingStrategy
= sortOrder
;
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
)) {
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;
625 d
->abstractGroupingStrategy
= 0;
628 d
->abstractGroupingStrategy
= new ManualGroupingStrategy(this);
631 case ProgramGrouping
:
632 d
->abstractGroupingStrategy
= new ProgramGroupingStrategy(this);
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
*)));
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"