not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / libs / taskmanager / task.cpp
blob95f95f99e29f795897d1fa3f1c9622ef8b8f1819
1 /*****************************************************************
3 Copyright (c) 2000-2001 Matthias Elter <elter@kde.org>
4 Copyright (c) 2001 Richard Moore <rich@kde.org>
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 ******************************************************************/
25 // Own
26 #include "task.h"
28 // Qt
29 #include <QMimeData>
30 #include <QTime>
31 #include <QTimer>
32 #include <QApplication>
33 #include <QDesktopWidget>
35 #ifdef Q_WS_X11
36 #include <QX11Info>
37 #endif
39 // KDE
40 #include <KDebug>
41 #include <KIconLoader>
42 #include <KLocale>
44 #include "taskmanager.h"
46 namespace TaskManager
49 static const unsigned long windowInfoFlags = NET::WMState | NET::XAWMState | NET::WMDesktop |
50 NET::WMVisibleName | NET::WMGeometry |
51 NET::WMWindowType | NET::WM2AllowedActions;
52 static const unsigned long windowInfoFlags2 = NET::WM2AllowedActions;
54 class Task::Private
56 public:
57 Private(WId w)
58 : active(false),
59 win(w),
60 frameId(w),
61 info(KWindowSystem::windowInfo(w, windowInfoFlags, windowInfoFlags2)),
62 lastWidth(0),
63 lastHeight(0),
64 lastResize(false),
65 lastIcon(),
66 cachedChanges(0),
67 cachedChangesTimerId(0)
71 bool active;
72 WId win;
73 WId frameId;
74 QPixmap pixmap;
75 KWindowInfo info;
76 WindowList transients;
77 WindowList transientsDemandingAttention;
79 int lastWidth;
80 int lastHeight;
81 bool lastResize;
82 QPixmap lastIcon;
83 QIcon icon;
85 QRect iconGeometry;
87 QTime lastUpdate;
88 unsigned int cachedChanges;
89 int cachedChangesTimerId;
92 Task::Task(WId w, QObject *parent, const char *name)
93 : QObject(parent),
94 d(new Private(w))
96 setObjectName(name);
98 // try to load icon via net_wm
99 d->pixmap = KWindowSystem::icon(d->win, 16, 16, true);
101 // try to guess the icon from the classhint
102 if (d->pixmap.isNull()) {
103 KIconLoader::global()->loadIcon(className().toLower(),
104 KIconLoader::Small,
105 KIconLoader::Small,
106 KIconLoader::DefaultState,
107 QStringList(), 0, true);
110 // load the icon for X applications
111 if (d->pixmap.isNull()) {
112 d->pixmap = SmallIcon("xorg");
116 Task::~Task()
118 delete d;
121 void Task::timerEvent(QTimerEvent *)
123 if (d->cachedChanges) {
124 d->lastUpdate = QTime();
125 refresh(d->cachedChanges);
126 d->cachedChanges = 0;
129 killTimer(d->cachedChangesTimerId);
130 d->cachedChangesTimerId = 0;
133 void Task::refreshIcon()
135 // try to load icon via net_wm
136 d->pixmap = KWindowSystem::icon(d->win, 16, 16, true);
138 // try to guess the icon from the classhint
139 if (d->pixmap.isNull()) {
140 KIconLoader::global()->loadIcon(className().toLower(),
141 KIconLoader::Small,
142 KIconLoader::Small,
143 KIconLoader::DefaultState,
144 QStringList(), 0, true);
146 // load the icon for X applications
147 if (d->pixmap.isNull()) {
148 d->pixmap = SmallIcon("xorg");
152 d->lastIcon = QPixmap();
153 d->icon = QIcon();
154 emit changed(IconChanged);
157 ::TaskManager::TaskChanges Task::refresh(unsigned int dirty)
159 if (!d->lastUpdate.isNull() && d->lastUpdate.elapsed() < 200) {
160 d->cachedChanges |= dirty;
162 if (!d->cachedChangesTimerId) {
163 d->cachedChangesTimerId = startTimer(200 - d->lastUpdate.elapsed());
166 d->lastUpdate.restart();
167 return TaskUnchanged;
170 d->lastUpdate.restart();
171 KWindowInfo info = KWindowSystem::windowInfo(d->win, windowInfoFlags, windowInfoFlags2);
172 TaskChanges changes = TaskUnchanged;
174 if (d->info.visibleName() != info.visibleName() ||
175 d->info.visibleNameWithState() != info.visibleNameWithState() ||
176 d->info.name() != info.name()) {
177 changes |= NameChanged;
180 d->info = info;
182 if (dirty & NET::WMState || dirty & NET::XAWMState) {
183 changes |= StateChanged;
186 if (dirty & NET::WMDesktop) {
187 changes |= DesktopChanged;
190 if (dirty & NET::WMGeometry) {
191 changes |= GeometryChanged;
194 if (dirty & NET::WMWindowType) {
195 changes |= WindowTypeChanged;
198 if (dirty & NET::WM2AllowedActions) {
199 changes |= ActionsChanged;
202 if (dirty & NET::WMIcon) {
203 refreshIcon();
206 if (changes != TaskUnchanged) {
207 emit changed(changes);
210 return changes;
213 void Task::setActive(bool a)
215 d->active = a;
216 emit changed(StateChanged);
217 if (a) {
218 emit activated();
219 } else {
220 emit deactivated();
224 bool Task::isMaximized() const
226 return d->info.valid(true) && (d->info.state() & NET::Max);
229 bool Task::isMinimized() const
231 return d->info.valid(true) && d->info.isMinimized();
234 bool Task::isIconified() const
236 return d->info.valid(true) && d->info.isMinimized();
239 bool Task::isAlwaysOnTop() const
241 return d->info.valid(true) && (d->info.state() & NET::StaysOnTop);
244 bool Task::isKeptBelowOthers() const
246 return d->info.valid(true) && (d->info.state() & NET::KeepBelow);
249 bool Task::isFullScreen() const
251 return d->info.valid(true) && (d->info.state() & NET::FullScreen);
254 bool Task::isShaded() const
256 return d->info.valid(true) && (d->info.state() & NET::Shaded);
259 bool Task::isOnCurrentDesktop() const
261 return d->info.valid(true) && d->info.isOnCurrentDesktop();
264 bool Task::isOnAllDesktops() const
266 return d->info.valid(true) && d->info.onAllDesktops();
269 bool Task::isActive() const
271 return d->active;
274 bool Task::isOnTop() const
276 return TaskManager::self()->isOnTop(this);
279 bool Task::isModified() const
281 static QString modStr = QString::fromUtf8("[") +
282 i18nc("marks that a task has been modified", "modified") +
283 QString::fromUtf8("]");
284 int modStrPos = d->info.visibleName().indexOf(modStr);
286 return ( modStrPos != -1 );
289 int Task::desktop() const
291 return d->info.desktop();
294 bool Task::demandsAttention() const
296 return (d->info.valid() && (d->info.state() & NET::DemandsAttention)) ||
297 !d->transientsDemandingAttention.isEmpty();
300 bool Task::isOnScreen( int screen ) const
302 return TaskManager::isOnScreen( screen, d->win );
305 bool Task::showInTaskbar() const
307 return d->info.state() ^ NET::SkipTaskbar;
310 bool Task::showInPager() const
312 return d->info.state() ^ NET::SkipPager;
315 QRect Task::geometry() const
317 return d->info.geometry();
320 void Task::updateDemandsAttentionState( WId w )
322 if (window() != w) {
323 // 'w' is a transient for this task
324 NETWinInfo i( QX11Info::display(), w, QX11Info::appRootWindow(), NET::WMState );
325 if (i.state() & NET::DemandsAttention) {
326 if (!d->transientsDemandingAttention.contains(w)) {
327 d->transientsDemandingAttention.insert(w);
329 } else {
330 d->transientsDemandingAttention.remove(w);
335 void Task::addTransient( WId w, const NETWinInfo& info )
337 d->transients.insert(w);
338 if (info.state() & NET::DemandsAttention) {
339 d->transientsDemandingAttention.insert(w);
340 emit changed(TransientsChanged);
344 void Task::removeTransient(WId w)
346 d->transients.remove(w);
347 d->transientsDemandingAttention.remove(w);
350 bool Task::hasTransient(WId w) const
352 return d->transients.contains(w);
355 WId Task::window() const
357 return d->win;
360 KWindowInfo Task::info() const
362 return d->info;
365 QString Task::visibleName() const
367 return d->info.visibleName();
370 QString Task::visibleNameWithState() const
372 return d->info.visibleNameWithState();
375 QString Task::name() const
377 return d->info.name();
380 QString Task::className() const
382 XClassHint hint;
383 if(XGetClassHint(QX11Info::display(), d->win, &hint)) {
384 QString nh( hint.res_name );
385 XFree( hint.res_name );
386 XFree( hint.res_class );
387 return nh;
389 return QString();
392 QString Task::classClass() const
394 XClassHint hint;
395 if(XGetClassHint(QX11Info::display(), d->win, &hint)) {
396 QString ch( hint.res_class );
397 XFree( hint.res_name );
398 XFree( hint.res_class );
399 return ch;
401 return QString();
404 QPixmap Task::icon( int width, int height, bool allowResize )
406 if ( (width == d->lastWidth)
407 && (height == d->lastHeight)
408 && (allowResize == d->lastResize )
409 && (!d->lastIcon.isNull()) )
410 return d->lastIcon;
412 QPixmap newIcon = KWindowSystem::icon( d->win, width, height, allowResize );
413 if ( !newIcon.isNull() ) {
414 d->lastIcon = newIcon;
415 d->lastWidth = width;
416 d->lastHeight = height;
417 d->lastResize = allowResize;
420 return newIcon;
423 QIcon Task::icon()
425 if ( !d->icon.isNull() )
426 return d->icon;
428 d->icon.addPixmap(KWindowSystem::icon( d->win, KIconLoader::SizeSmall, KIconLoader::SizeSmall, false));
429 d->icon.addPixmap(KWindowSystem::icon( d->win, KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium, false));
430 d->icon.addPixmap(KWindowSystem::icon( d->win, KIconLoader::SizeMedium, KIconLoader::SizeMedium, false));
431 d->icon.addPixmap(KWindowSystem::icon( d->win, KIconLoader::SizeLarge, KIconLoader::SizeLarge, false));
433 return d->icon;
436 WindowList Task::transients() const
438 return d->transients;
441 QPixmap Task::pixmap() const
443 return d->pixmap;
446 QPixmap Task::bestIcon( int size, bool &isStaticIcon )
448 QPixmap pixmap;
449 isStaticIcon = false;
451 switch( size ) {
452 case KIconLoader::SizeSmall:
454 pixmap = icon( 16, 16, true );
456 // Icon of last resort
457 if( pixmap.isNull() ) {
458 pixmap = KIconLoader::global()->loadIcon( "xorg",
459 KIconLoader::NoGroup,
460 KIconLoader::SizeSmall );
461 isStaticIcon = true;
464 break;
465 case KIconLoader::SizeMedium:
468 // Try 34x34 first for KDE 2.1 icons with shadows, if we don't
469 // get one then try 32x32.
471 pixmap = icon( 34, 34, false );
473 if ( (( pixmap.width() != 34 ) || ( pixmap.height() != 34 )) &&
474 (( pixmap.width() != 32 ) || ( pixmap.height() != 32 )) )
476 pixmap = icon( 32, 32, true );
479 // Icon of last resort
480 if( pixmap.isNull() ) {
481 pixmap = KIconLoader::global()->loadIcon( "xorg",
482 KIconLoader::NoGroup,
483 KIconLoader::SizeMedium );
484 isStaticIcon = true;
487 break;
488 case KIconLoader::SizeLarge:
490 // If there's a 48x48 icon in the hints then use it
491 pixmap = icon( size, size, false );
493 // If not, try to get one from the classname
494 if ( pixmap.isNull() || pixmap.width() != size || pixmap.height() != size ) {
495 pixmap = KIconLoader::global()->loadIcon( className(),
496 KIconLoader::NoGroup,
497 size,
498 KIconLoader::DefaultState,
499 QStringList(), 0L,
500 true );
501 isStaticIcon = true;
504 // If we still don't have an icon then scale the one in the hints
505 if ( pixmap.isNull() || ( pixmap.width() != size ) || ( pixmap.height() != size ) ) {
506 pixmap = icon( size, size, true );
507 isStaticIcon = false;
510 // Icon of last resort
511 if( pixmap.isNull() ) {
512 pixmap = KIconLoader::global()->loadIcon( "xorg",
513 KIconLoader::NoGroup,
514 size );
515 isStaticIcon = true;
520 return pixmap;
523 bool Task::idMatch( const QString& id1, const QString& id2 )
525 if ( id1.isEmpty() || id2.isEmpty() )
526 return false;
528 if ( id1.contains( id2 ) > 0 )
529 return true;
531 if ( id2.contains( id1 ) > 0 )
532 return true;
534 return false;
537 void Task::move()
539 bool on_current = d->info.isOnCurrentDesktop();
541 if (!on_current)
543 KWindowSystem::setCurrentDesktop(d->info.desktop());
544 KWindowSystem::forceActiveWindow(d->win);
547 if (d->info.isMinimized())
549 KWindowSystem::unminimizeWindow(d->win);
552 QRect geom = d->info.geometry();
553 QCursor::setPos(geom.center());
555 NETRootInfo ri(QX11Info::display(), NET::WMMoveResize);
556 ri.moveResizeRequest(d->win, geom.center().x(),
557 geom.center().y(), NET::Move);
560 void Task::resize()
562 bool on_current = d->info.isOnCurrentDesktop();
564 if (!on_current)
566 KWindowSystem::setCurrentDesktop(d->info.desktop());
567 KWindowSystem::forceActiveWindow(d->win);
570 if (d->info.isMinimized())
572 KWindowSystem::unminimizeWindow(d->win);
575 QRect geom = d->info.geometry();
576 QCursor::setPos(geom.bottomRight());
578 NETRootInfo ri(QX11Info::display(), NET::WMMoveResize);
579 ri.moveResizeRequest(d->win, geom.bottomRight().x(),
580 geom.bottomRight().y(), NET::BottomRight);
583 void Task::setMaximized(bool maximize)
585 KWindowInfo info = KWindowSystem::windowInfo(d->win, NET::WMState | NET::XAWMState | NET::WMDesktop);
586 bool on_current = info.isOnCurrentDesktop();
588 if (!on_current)
590 KWindowSystem::setCurrentDesktop(info.desktop());
593 if (info.isMinimized())
595 KWindowSystem::unminimizeWindow(d->win);
598 NETWinInfo ni(QX11Info::display(), d->win, QX11Info::appRootWindow(), NET::WMState);
600 if (maximize)
602 ni.setState(NET::Max, NET::Max);
604 else
606 ni.setState(0, NET::Max);
609 if (!on_current)
611 KWindowSystem::forceActiveWindow(d->win);
615 void Task::toggleMaximized()
617 setMaximized(!isMaximized());
620 void Task::restore()
622 KWindowInfo info = KWindowSystem::windowInfo(d->win, NET::WMState | NET::XAWMState | NET::WMDesktop);
623 bool on_current = info.isOnCurrentDesktop();
625 if (!on_current)
627 KWindowSystem::setCurrentDesktop(info.desktop());
630 if( info.isMinimized())
632 KWindowSystem::unminimizeWindow(d->win);
635 NETWinInfo ni(QX11Info::display(), d->win, QX11Info::appRootWindow(), NET::WMState);
636 ni.setState(0, NET::Max);
638 if (!on_current)
640 KWindowSystem::forceActiveWindow( d->win );
644 void Task::setIconified(bool iconify)
646 if (iconify)
648 KWindowSystem::minimizeWindow(d->win);
650 else
652 KWindowInfo info = KWindowSystem::windowInfo(d->win, NET::WMState | NET::XAWMState | NET::WMDesktop);
653 bool on_current = info.isOnCurrentDesktop();
655 if (!on_current)
657 KWindowSystem::setCurrentDesktop(info.desktop());
660 KWindowSystem::unminimizeWindow(d->win);
662 if (!on_current)
664 KWindowSystem::forceActiveWindow(d->win);
669 void Task::toggleIconified()
671 setIconified(!isIconified());
674 void Task::close()
676 NETRootInfo ri( QX11Info::display(), NET::CloseWindow );
677 ri.closeWindowRequest( d->win );
680 void Task::raise()
682 // kDebug(1210) << "Task::raise(): " << name();
683 KWindowSystem::raiseWindow( d->win );
686 void Task::lower()
688 // kDebug(1210) << "Task::lower(): " << name();
689 KWindowSystem::lowerWindow( d->win );
692 void Task::activate()
694 // kDebug(1210) << "Task::activate():" << name();
695 WId w = d->win;
696 if (!d->transientsDemandingAttention.isEmpty()) {
697 WindowList::const_iterator it = d->transientsDemandingAttention.end();
698 --it;
699 w = *it;
702 KWindowSystem::forceActiveWindow(w);
705 void Task::activateRaiseOrIconify()
707 //kDebug() << isActive() << isIconified() << isOnTop();
708 if (!isActive() || isIconified()) {
709 activate();
710 } else if (!isActive() && !isOnTop()) {
711 raise();
712 } else {
713 setIconified(true);
717 void Task::toDesktop(int desk)
719 NETWinInfo ni(QX11Info::display(), d->win, QX11Info::appRootWindow(), NET::WMDesktop);
720 if (desk == 0) {
721 if (isOnAllDesktops()) {
722 ni.setDesktop(KWindowSystem::currentDesktop());
723 KWindowSystem::forceActiveWindow(d->win);
724 } else {
725 ni.setDesktop(NETWinInfo::OnAllDesktops);
728 return;
731 ni.setDesktop(desk);
733 if (desk == KWindowSystem::currentDesktop()) {
734 KWindowSystem::forceActiveWindow(d->win);
738 void Task::toCurrentDesktop()
740 toDesktop(KWindowSystem::currentDesktop());
743 void Task::setAlwaysOnTop(bool stay)
745 NETWinInfo ni( QX11Info::display(), d->win, QX11Info::appRootWindow(), NET::WMState);
746 if(stay)
747 ni.setState( NET::StaysOnTop, NET::StaysOnTop );
748 else
749 ni.setState( 0, NET::StaysOnTop );
752 void Task::toggleAlwaysOnTop()
754 setAlwaysOnTop( !isAlwaysOnTop() );
757 void Task::setKeptBelowOthers(bool below)
759 NETWinInfo ni(QX11Info::display(), d->win, QX11Info::appRootWindow(), NET::WMState);
761 if (below)
763 ni.setState(NET::KeepBelow, NET::KeepBelow);
765 else
767 ni.setState(0, NET::KeepBelow);
771 void Task::toggleKeptBelowOthers()
773 setKeptBelowOthers(!isKeptBelowOthers());
776 void Task::setFullScreen(bool fullscreen)
778 NETWinInfo ni(QX11Info::display(), d->win, QX11Info::appRootWindow(), NET::WMState);
780 if (fullscreen)
782 ni.setState(NET::FullScreen, NET::FullScreen);
784 else
786 ni.setState(0, NET::FullScreen);
790 void Task::toggleFullScreen()
792 setFullScreen(!isFullScreen());
795 void Task::setShaded(bool shade)
797 NETWinInfo ni( QX11Info::display(), d->win, QX11Info::appRootWindow(), NET::WMState);
798 if(shade)
799 ni.setState( NET::Shaded, NET::Shaded );
800 else
801 ni.setState( 0, NET::Shaded );
804 void Task::toggleShaded()
806 setShaded( !isShaded() );
809 void Task::publishIconGeometry(QRect rect)
811 if (rect == d->iconGeometry)
813 return;
816 d->iconGeometry = rect;
817 NETWinInfo ni(QX11Info::display(), d->win, QX11Info::appRootWindow(), 0);
818 NETRect r;
820 if (rect.isValid())
822 r.pos.x = rect.x();
823 r.pos.y = rect.y();
824 r.size.width = rect.width();
825 r.size.height = rect.height();
827 ni.setIconGeometry(r);
830 void Task::addMimeData(QMimeData *mimeData) const
832 Q_ASSERT(mimeData);
834 QByteArray data;
835 data.resize(sizeof(WId));
836 memcpy(data.data(), &d->win, sizeof(WId));
837 mimeData->setData(mimetype(), data);
840 QString Task::mimetype()
842 return "windowsystem/winid";
845 QString Task::groupMimetype()
847 return "windowsystem/multiple-winids";
850 QList<WId> Task::idsFromMimeData(const QMimeData *mimeData, bool *ok)
852 Q_ASSERT(mimeData);
853 QList<WId> ids;
855 if (ok) {
856 *ok = false;
859 if (!mimeData->hasFormat(groupMimetype())) {
860 // try to grab a singular id if it exists
861 //kDebug() << "not group type";
862 bool singularOk;
863 WId id = idFromMimeData(mimeData, &singularOk);
865 if (ok) {
866 *ok = singularOk;
869 if (singularOk) {
870 //kDebug() << "and singular failed, too";
871 ids << id;
874 return ids;
877 QByteArray data(mimeData->data(groupMimetype()));
878 if ((unsigned int)data.size() < sizeof(int) + sizeof(WId)) {
879 //kDebug() << "wrong size" << data.size() << sizeof(int) + sizeof(WId);
880 return ids;
883 int count = 0;
884 memcpy(&count, data.data(), sizeof(int));
885 if (count < 1 || (unsigned int)data.size() < sizeof(int) + sizeof(WId) * count) {
886 //kDebug() << "wrong size, 2" << data.size() << count << sizeof(int) + sizeof(WId) * count;
887 return ids;
890 WId id;
891 for (int i = 0; i < count; ++i) {
892 memcpy(&id, data.data() + sizeof(int) + sizeof(WId) * i, sizeof(WId));
893 ids << id;
896 if (ok) {
897 *ok = true;
900 return ids;
903 WId Task::idFromMimeData(const QMimeData *mimeData, bool *ok)
905 Q_ASSERT(mimeData);
907 if (ok) {
908 *ok = false;
911 if (!mimeData->hasFormat(mimetype())) {
912 return 0;
915 QByteArray data(mimeData->data(mimetype()));
916 if (data.size() != sizeof(WId)) {
917 return 0;
920 WId id;
921 memcpy(&id, data.data(), sizeof(WId));
923 if (ok) {
924 *ok = true;
927 return id;
930 } // TaskManager namespace
932 #include "task.moc"