add more spacing
[personal-kdebase.git] / workspace / libs / taskmanager / taskmanager.cpp
blob398ce1b13213cf8831f75c27b798d93e2755c6b8
1 /*****************************************************************
3 Copyright (c) 2000 Matthias Elter <elter@kde.org>
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 // Own
25 #include "taskmanager.h"
27 // Qt
28 #include <QApplication>
29 #include <QDesktopWidget>
30 #include <QUuid>
32 // KDE
33 #include <KConfig>
34 #include <KConfigGroup>
35 #include <KDebug>
36 #include <KGlobal>
37 #include <KLocale>
39 #ifdef Q_WS_X11
40 #include <QX11Info>
41 #endif
43 namespace TaskManager
46 class TaskManagerSingleton
48 public:
49 TaskManager self;
52 K_GLOBAL_STATIC( TaskManagerSingleton, privateTaskManagerSelf )
54 TaskManager* TaskManager::self()
56 return &privateTaskManagerSelf->self;
59 class TaskManager::Private
61 public:
62 Private()
63 : active(0),
64 startupInfo(0)
68 TaskPtr active;
69 KStartupInfo* startupInfo;
70 TaskDict tasksByWId;
71 StartupList startups;
72 WindowList skiptaskbarWindows;
73 QSet<QUuid> trackGeometryTokens;
76 TaskManager::TaskManager()
77 : QObject(),
78 d(new Private)
80 KGlobal::locale()->insertCatalog("libtaskmanager");
81 connect(KWindowSystem::self(), SIGNAL(windowAdded(WId)),
82 this, SLOT(windowAdded(WId)));
83 connect(KWindowSystem::self(), SIGNAL(windowRemoved(WId)),
84 this, SLOT(windowRemoved(WId)));
85 connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)),
86 this, SLOT(activeWindowChanged(WId)));
87 connect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)),
88 this, SLOT(currentDesktopChanged(int)));
89 connect(KWindowSystem::self(), SIGNAL(windowChanged(WId,unsigned int)),
90 this, SLOT(windowChanged(WId,unsigned int)));
92 // register existing windows
93 const QList<WId> windows = KWindowSystem::windows();
94 QList<WId>::ConstIterator end(windows.end());
95 for (QList<WId>::ConstIterator it = windows.begin(); it != end; ++it)
97 windowAdded(*it);
100 // set active window
101 WId win = KWindowSystem::activeWindow();
102 activeWindowChanged(win);
103 configureStartup();
106 TaskManager::~TaskManager()
108 KGlobal::locale()->removeCatalog("libtaskmanager");
109 delete d;
112 void TaskManager::configureStartup()
114 KConfig _c("klaunchrc");
115 KConfigGroup c(&_c, "FeedbackStyle");
116 if (!c.readEntry("TaskbarButton", true)) {
117 return;
120 d->startupInfo = new KStartupInfo( KStartupInfo::CleanOnCantDetect, this );
121 connect( d->startupInfo,
122 SIGNAL( gotNewStartup( const KStartupInfoId&, const KStartupInfoData& )),
123 SLOT( gotNewStartup( const KStartupInfoId&, const KStartupInfoData& )));
124 connect( d->startupInfo,
125 SIGNAL( gotStartupChange( const KStartupInfoId&, const KStartupInfoData& )),
126 SLOT( gotStartupChange( const KStartupInfoId&, const KStartupInfoData& )));
127 connect( d->startupInfo,
128 SIGNAL( gotRemoveStartup( const KStartupInfoId&, const KStartupInfoData& )),
129 SLOT( killStartup( const KStartupInfoId& )));
131 c = KConfigGroup(&_c, "TaskbarButtonSettings");
132 d->startupInfo->setTimeout(c.readEntry( "Timeout", 30 ));
135 TaskPtr TaskManager::findTask(WId w)
137 TaskDict::const_iterator it = d->tasksByWId.constBegin();
138 TaskDict::const_iterator itEnd = d->tasksByWId.constEnd();
140 for (; it != itEnd; ++it) {
141 if (it.key() == w || it.value()->hasTransient(w)) {
142 return it.value();
146 return TaskPtr();
149 TaskPtr TaskManager::findTask(int desktop, const QPoint& p)
151 QList<WId> list = KWindowSystem::stackingOrder();
153 TaskPtr task;
154 int currentIndex = -1;
155 TaskDict::iterator itEnd = d->tasksByWId.end();
156 for (TaskDict::iterator it = d->tasksByWId.begin(); it != itEnd; ++it)
158 TaskPtr t = it.value();
159 if (!t->isOnAllDesktops() && t->desktop() != desktop)
161 continue;
164 if (t->isIconified() || t->isShaded())
166 continue;
169 if (t->geometry().contains(p))
171 int index = list.indexOf(t->window());
172 if (index > currentIndex)
174 currentIndex = index;
175 task = t;
180 return task;
183 void TaskManager::windowAdded(WId w )
185 NETWinInfo info(QX11Info::display(), w, QX11Info::appRootWindow(),
186 NET::WMWindowType | NET::WMPid | NET::WMState);
188 // ignore NET::Tool and other special window types
189 NET::WindowType wType = info.windowType(NET::NormalMask | NET::DesktopMask | NET::DockMask |
190 NET::ToolbarMask | NET::MenuMask | NET::DialogMask |
191 NET::OverrideMask | NET::TopMenuMask |
192 NET::UtilityMask | NET::SplashMask);
194 if (wType != NET::Normal && wType != NET::Override && wType != NET::Unknown &&
195 wType != NET::Dialog && wType != NET::Utility) {
196 return;
199 // ignore windows that want to be ignored by the taskbar
200 if ((info.state() & NET::SkipTaskbar) != 0) {
201 d->skiptaskbarWindows.insert(w); // remember them though
202 return;
205 Window transient_for_tmp;
206 if (XGetTransientForHint(QX11Info::display(), (Window)w, &transient_for_tmp)) {
207 WId transient_for = (WId)transient_for_tmp;
209 // check if it's transient for a skiptaskbar window
210 if (d->skiptaskbarWindows.contains( transient_for )) {
211 return;
214 // lets see if this is a transient for an existing task
215 if (transient_for != QX11Info::appRootWindow() &&
216 transient_for != 0 && wType != NET::Utility) {
217 TaskPtr t = findTask(transient_for);
218 if (t) {
219 if (t->window() != w) {
220 t->addTransient(w, info);
221 // kDebug() << "TM: Transient " << w << " added for Task: " << t->window();
223 return;
228 TaskPtr t(new Task(w, 0));
229 d->tasksByWId[w] = t;
231 connect(t.data(), SIGNAL(changed(::TaskManager::TaskChanges)),
232 this, SLOT(taskChanged(::TaskManager::TaskChanges)));
234 if (d->startupInfo) {
235 KStartupInfoId startupInfoId;
236 // checkStartup modifies startupInfoId
237 d->startupInfo->checkStartup(w, startupInfoId);
238 foreach (StartupPtr startup, d->startups) {
239 if (startup->id() == startupInfoId) {
240 startup->addWindowMatch(w);
245 // kDebug() << "TM: Task added for WId: " << w;
246 emit taskAdded(t);
249 void TaskManager::windowRemoved(WId w)
251 d->skiptaskbarWindows.remove(w);
253 // find task
254 TaskPtr t = findTask(w);
255 if (!t)
257 return;
260 if (t->window() == w)
262 d->tasksByWId.remove(w);
263 emit taskRemoved(t);
265 if (t == d->active)
267 d->active = 0;
270 //kDebug() << "TM: Task for WId " << w << " removed.";
272 else
274 t->removeTransient(w);
275 //kDebug() << "TM: Transient " << w << " for Task " << t->window() << " removed.";
279 void TaskManager::windowChanged(WId w, unsigned int dirty)
281 if (dirty & NET::WMState) {
282 NETWinInfo info (QX11Info::display(), w, QX11Info::appRootWindow(),
283 NET::WMState | NET::XAWMState);
285 if (info.state() & NET::SkipTaskbar) {
286 windowRemoved(w);
287 d->skiptaskbarWindows.insert(w);
288 return;
289 } else {
290 d->skiptaskbarWindows.remove(w);
291 if (info.mappingState() != NET::Withdrawn && !findTask(w)) {
292 // skipTaskBar state was removed and the window is still
293 // mapped, so add this window
294 windowAdded(w);
299 // check if any state we are interested in is marked dirty
300 if (!(dirty & (NET::WMVisibleName | NET::WMName |
301 NET::WMState | NET::WMIcon |
302 NET::XAWMState | NET::WMDesktop) ||
303 (trackGeometry() && dirty & NET::WMGeometry))) {
304 return;
307 // find task
308 TaskPtr t = findTask(w);
309 if (!t) {
310 return;
313 //kDebug() << "TaskManager::windowChanged " << w << " " << dirty;
315 if (dirty & NET::WMState) {
316 t->updateDemandsAttentionState(w);
319 //kDebug() << "got changes, but will we refresh?" << dirty;
320 if (dirty) {
321 // only refresh this stuff if we have other changes besides icons
322 t->refresh(dirty);
326 void TaskManager::taskChanged(::TaskManager::TaskChanges changes)
328 Task *t = qobject_cast<Task*>(sender());
330 if (!t || changes == TaskUnchanged || !d->tasksByWId.contains(t->info().win())) {
331 return;
334 emit windowChanged(d->tasksByWId[t->info().win()], changes);
337 void TaskManager::activeWindowChanged(WId w)
339 //kDebug() << "TaskManager::activeWindowChanged" << w;
340 TaskPtr t = findTask(w);
341 if (!t) {
342 if (d->active) {
343 d->active->setActive(false);
344 d->active = 0;
346 //kDebug() << "no active window";
347 } else {
348 if (t->info().windowType(NET::UtilityMask) == NET::Utility) {
349 // we don't want to mark utility windows as active since task managers
350 // actually care about the main window and skip utility windows; utility
351 // windows are hidden when their associated window loses focus anyways
352 // see http://bugs.kde.org/show_bug.cgi?id=178509
353 return;
356 if (d->active) {
357 d->active->setActive(false);
360 d->active = t;
361 d->active->setActive(true);
362 //kDebug() << "active window is" << t->name();
366 void TaskManager::currentDesktopChanged(int desktop)
368 emit desktopChanged(desktop);
371 void TaskManager::gotNewStartup( const KStartupInfoId& id, const KStartupInfoData& data )
373 StartupPtr s( new Startup( id, data, 0 ) );
374 d->startups.append(s);
376 emit startupAdded(s);
379 void TaskManager::gotStartupChange( const KStartupInfoId& id, const KStartupInfoData& data )
381 StartupList::iterator itEnd = d->startups.end();
382 for (StartupList::iterator sIt = d->startups.begin(); sIt != itEnd; ++sIt)
384 if ((*sIt)->id() == id)
386 (*sIt)->update(data);
387 return;
392 void TaskManager::killStartup( const KStartupInfoId& id )
394 StartupList::iterator sIt = d->startups.begin();
395 StartupList::iterator itEnd = d->startups.end();
396 StartupPtr s;
397 for (; sIt != itEnd; ++sIt)
399 if ((*sIt)->id() == id)
401 s = *sIt;
402 break;
406 if (!s) {
407 return;
410 d->startups.erase(sIt);
411 emit startupRemoved(s);
414 void TaskManager::killStartup(StartupPtr s)
416 if (!s)
418 return;
421 StartupList::iterator sIt = d->startups.begin();
422 StartupList::iterator itEnd = d->startups.end();
423 for (; sIt != itEnd; ++sIt)
425 if ((*sIt) == s)
427 d->startups.erase(sIt);
428 break;
432 emit startupRemoved(s);
435 QString TaskManager::desktopName(int desk) const
437 return KWindowSystem::desktopName(desk);
440 TaskDict TaskManager::tasks() const
442 return d->tasksByWId;
445 StartupList TaskManager::startups() const
447 return d->startups;
450 int TaskManager::numberOfDesktops() const
452 return KWindowSystem::numberOfDesktops();
455 bool TaskManager::isOnTop(const Task* task) const
457 if (!task) {
458 return false;
461 QList<WId> list = KWindowSystem::stackingOrder();
462 QList<WId>::const_iterator begin(list.constBegin());
463 QList<WId>::const_iterator it = list.constBegin() + (list.size() - 1);
464 do {
465 TaskDict::const_iterator taskItEnd = d->tasksByWId.constEnd();
466 for (TaskDict::const_iterator taskIt = d->tasksByWId.constBegin(); taskIt != taskItEnd; ++taskIt) {
467 TaskPtr t = taskIt.value();
468 if ((*it) == t->window()) {
469 if (t == task) {
470 return true;
473 if (!t->isIconified() && (t->isAlwaysOnTop() == task->isAlwaysOnTop())) {
474 return false;
477 break;
480 } while (it-- != begin);
482 return false;
485 void TaskManager::setTrackGeometry(bool track, const QUuid &token)
487 if (track) {
488 if (!d->trackGeometryTokens.contains(token)) {
489 d->trackGeometryTokens.insert(token);
491 } else {
492 d->trackGeometryTokens.remove(token);
496 bool TaskManager::trackGeometry() const
498 return !d->trackGeometryTokens.isEmpty();
501 bool TaskManager::isOnScreen(int screen, const WId wid)
503 if (screen == -1) {
504 return true;
507 KWindowInfo wi = KWindowSystem::windowInfo(wid, NET::WMFrameExtents);
509 // for window decos that fudge a bit and claim to extend beyond the
510 // edge of the screen, we just contract a bit.
511 QRect window = wi.frameGeometry();
512 QRect desktop = QApplication::desktop()->screenGeometry(screen);
513 desktop.adjust(5, 5, -5, -5);
514 return window.intersects(desktop);
517 int TaskManager::currentDesktop() const
519 return KWindowSystem::currentDesktop();
522 } // TaskManager namespace
525 #include "taskmanager.moc"