diff options
Diffstat (limited to 'kicker/taskmanager')
-rw-r--r-- | kicker/taskmanager/Makefile.am | 11 | ||||
-rw-r--r-- | kicker/taskmanager/configure.in.in | 25 | ||||
-rw-r--r-- | kicker/taskmanager/tasklmbmenu.cpp | 281 | ||||
-rw-r--r-- | kicker/taskmanager/tasklmbmenu.h | 85 | ||||
-rw-r--r-- | kicker/taskmanager/taskmanager.cpp | 1521 | ||||
-rw-r--r-- | kicker/taskmanager/taskmanager.h | 713 | ||||
-rw-r--r-- | kicker/taskmanager/taskrmbmenu.cpp | 328 | ||||
-rw-r--r-- | kicker/taskmanager/taskrmbmenu.h | 59 |
8 files changed, 3023 insertions, 0 deletions
diff --git a/kicker/taskmanager/Makefile.am b/kicker/taskmanager/Makefile.am new file mode 100644 index 000000000..ffe7912e7 --- /dev/null +++ b/kicker/taskmanager/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = $(all_includes) -I$(srcdir)/../libkicker + +lib_LTLIBRARIES = libtaskmanager.la +libtaskmanager_la_SOURCES = tasklmbmenu.cpp taskrmbmenu.cpp taskmanager.cpp +libtaskmanager_la_METASOURCES = AUTO + +libtaskmanager_la_LDFLAGS = $(all_libraries) -version-info 1:0:0 -no-undefined +libtaskmanager_la_LIBADD = $(LIB_KDECORE) $(LIB_XFIXES) $(LIB_XRENDER) $(LIB_XCOMPOSITE) ../libkicker/libkickermain.la + +messages: + $(XGETTEXT) *.cpp *.h -o $(podir)/libtaskmanager.pot diff --git a/kicker/taskmanager/configure.in.in b/kicker/taskmanager/configure.in.in new file mode 100644 index 000000000..0ee479dde --- /dev/null +++ b/kicker/taskmanager/configure.in.in @@ -0,0 +1,25 @@ +if test "x$with_composite" != "xno"; then + dnl XComposite check + KDE_CHECK_HEADER(X11/extensions/Xcomposite.h, [xcomposite_h=yes], [xcomposite_h=no], [#include <X11/Xlib.h>]) + if test "$xcomposite_h" = yes; then + KDE_CHECK_LIB(Xcomposite, XCompositeQueryExtension, [ + LIB_XCOMPOSITE=-lXcomposite + AC_DEFINE_UNQUOTED(HAVE_XCOMPOSITE, 1, [Define if you have the XComposite extension]) + ], [], -lXext -lX11 $X_EXTRA_LIBS) + else + LIB_XCOMPOSITE= + fi + AC_SUBST(LIB_XCOMPOSITE) +fi + +dnl XFixes check +KDE_CHECK_HEADER(X11/extensions/Xfixes.h, [xfixes_h=yes], [xfixes_h=no], [#include <X11/Xlib.h>]) +if test "$xfixes_h" = yes; then + KDE_CHECK_LIB(Xfixes, XFixesQueryExtension, [ + LIB_XFIXES=-lXfixes + AC_DEFINE_UNQUOTED(HAVE_XFIXES, 1, [Define if you have the XFixes extension]) + ], [], -lXext -lX11 $X_EXTRA_LIBS) +else + LIB_XFIXES= +fi +AC_SUBST(LIB_XFIXES) diff --git a/kicker/taskmanager/tasklmbmenu.cpp b/kicker/taskmanager/tasklmbmenu.cpp new file mode 100644 index 000000000..03278c0fe --- /dev/null +++ b/kicker/taskmanager/tasklmbmenu.cpp @@ -0,0 +1,281 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <elter@kde.org> +Copyright (c) 2002 John Firebaugh <jfirebaugh@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include "tasklmbmenu.h" +#include "tasklmbmenu.moc" + +#include <qpainter.h> +#include <qstyle.h> + +#include <kdebug.h> +#include <kglobalsettings.h> + +#include "global.h" + +TaskMenuItem::TaskMenuItem(const QString &text, + bool active, bool minimized, bool attention) + : QCustomMenuItem(), + m_text(text), + m_isActive(active), + m_isMinimized(minimized), + m_demandsAttention(attention), + m_attentionState(true) +{ +} + +TaskMenuItem::~TaskMenuItem() +{ +} + +void TaskMenuItem::paint(QPainter *p, const QColorGroup &cg, + bool highlighted, bool /*enabled*/, + int x, int y, int w, int h ) +{ + if (m_isActive) + { + QFont font = p->font(); + font.setBold(true); + p->setFont(font); + } + + if (highlighted) + { + p->setPen(cg.highlightedText()); + } + else if (m_isMinimized) + { + p->setPen(QPen(KickerLib::blendColors(cg.background(), cg.text()))); + } + else if (m_demandsAttention && !m_attentionState) + { + p->setPen(cg.mid()); + } + + p->drawText(x, y, w, h, AlignAuto|AlignVCenter|DontClip|ShowPrefix, m_text); +} + +QSize TaskMenuItem::sizeHint() +{ + QFont font = QFont(); + if (m_isActive) + { + font.setBold(true); + } + return QFontMetrics(font).size(AlignAuto|AlignVCenter|DontClip|ShowPrefix, + m_text); +} + +/*****************************************************************************/ + +TaskLMBMenu::TaskLMBMenu(const Task::List& tasks, QWidget *parent, const char *name) + : QPopupMenu(parent, name), + m_tasks(tasks), + m_lastDragId(-1), + m_attentionState(false) +{ + fillMenu(); + + setAcceptDrops(true); // Always enabled to activate task during drag&drop. + + m_dragSwitchTimer = new QTimer(this, "DragSwitchTimer"); + connect(m_dragSwitchTimer, SIGNAL(timeout()), SLOT(dragSwitch())); +} + +void TaskLMBMenu::fillMenu() +{ + setCheckable(true); + + Task::List::iterator itEnd = m_tasks.end(); + for (Task::List::iterator it = m_tasks.begin(); it != itEnd; ++it) + { + Task::Ptr t = (*it); + + QString text = t->visibleName().replace("&", "&&"); + + TaskMenuItem *menuItem = new TaskMenuItem(text, + t->isActive(), + t->isIconified(), + t->demandsAttention()); + int id = insertItem(QIconSet(t->pixmap()), menuItem); + connectItem(id, t, SLOT(activateRaiseOrIconify())); + setItemChecked(id, t->isActive()); + + if (t->demandsAttention()) + { + m_attentionState = true; + m_attentionMap.append(menuItem); + } + } + + if (m_attentionState) + { + m_attentionTimer = new QTimer(this, "AttentionTimer"); + connect(m_attentionTimer, SIGNAL(timeout()), SLOT(attentionTimeout())); + m_attentionTimer->start(500, true); + } +} + +void TaskLMBMenu::attentionTimeout() +{ + m_attentionState = !m_attentionState; + + for (QValueList<TaskMenuItem*>::const_iterator it = m_attentionMap.constBegin(); + it != m_attentionMap.constEnd(); + ++it) + { + (*it)->setAttentionState(m_attentionState); + } + + update(); + + m_attentionTimer->start(500, true); +} + +void TaskLMBMenu::dragEnterEvent( QDragEnterEvent* e ) +{ + // ignore task drags + if (TaskDrag::canDecode(e)) + { + return; + } + + int id = idAt(e->pos()); + + if (id == -1) + { + m_dragSwitchTimer->stop(); + m_lastDragId = -1; + } + else if (id != m_lastDragId) + { + m_lastDragId = id; + m_dragSwitchTimer->start(1000, true); + } + + QPopupMenu::dragEnterEvent( e ); +} + +void TaskLMBMenu::dragLeaveEvent( QDragLeaveEvent* e ) +{ + m_dragSwitchTimer->stop(); + m_lastDragId = -1; + + QPopupMenu::dragLeaveEvent(e); + + hide(); +} + +void TaskLMBMenu::dragMoveEvent( QDragMoveEvent* e ) +{ + // ignore task drags + if (TaskDrag::canDecode(e)) + { + return; + } + + int id = idAt(e->pos()); + + if (id == -1) + { + m_dragSwitchTimer->stop(); + m_lastDragId = -1; + } + else if (id != m_lastDragId) + { + m_lastDragId = id; + m_dragSwitchTimer->start(1000, true); + } + + QPopupMenu::dragMoveEvent(e); +} + +void TaskLMBMenu::dragSwitch() +{ + bool ok = false; + Task::Ptr t = m_tasks.at(indexOf(m_lastDragId), &ok); + if (ok) + { + t->activate(); + + for (unsigned int i = 0; i < count(); ++i) + { + setItemChecked(idAt(i), false ); + } + + setItemChecked( m_lastDragId, true ); + } +} + +void TaskLMBMenu::mousePressEvent( QMouseEvent* e ) +{ + if (e->button() == LeftButton) + { + m_dragStartPos = e->pos(); + } + else + { + m_dragStartPos = QPoint(); + } + + QPopupMenu::mousePressEvent(e); +} + +void TaskLMBMenu::mouseReleaseEvent(QMouseEvent* e) +{ + m_dragStartPos = QPoint(); + QPopupMenu::mouseReleaseEvent(e); +} + +void TaskLMBMenu::mouseMoveEvent(QMouseEvent* e) +{ + if (m_dragStartPos.isNull()) + { + QPopupMenu::mouseMoveEvent(e); + return; + } + + int delay = KGlobalSettings::dndEventDelay(); + QPoint newPos(e->pos()); + + if ((m_dragStartPos - newPos).manhattanLength() > delay) + { + int index = indexOf(idAt(m_dragStartPos)); + if (index != -1) + { + bool ok = false; + Task::Ptr task = m_tasks.at(index, &ok); + if (ok) + { + Task::List tasks; + tasks.append(task); + TaskDrag* drag = new TaskDrag(tasks, this); + drag->setPixmap(task->pixmap()); + drag->dragMove(); + } + } + } + + QPopupMenu::mouseMoveEvent(e); +} + diff --git a/kicker/taskmanager/tasklmbmenu.h b/kicker/taskmanager/tasklmbmenu.h new file mode 100644 index 000000000..945c3e649 --- /dev/null +++ b/kicker/taskmanager/tasklmbmenu.h @@ -0,0 +1,85 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <elter@kde.org> +Copyright (c) 2002 John Firebaugh <jfirebaugh@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef __tasklmbmenu_h__ +#define __tasklmbmenu_h__ + +#include <qpopupmenu.h> +#include <qtimer.h> + +#include "taskmanager.h" + +class TaskMenuItem : public QCustomMenuItem +{ +public: + TaskMenuItem(const QString &text, + bool active, bool minimized, bool attention); + ~TaskMenuItem(); + + void paint(QPainter*, const QColorGroup&, bool, bool, int, int, int, int); + QSize sizeHint(); + void setAttentionState(bool state) { m_attentionState = state; } + +private: + QString m_text; + bool m_isActive; + bool m_isMinimized; + bool m_demandsAttention; + bool m_attentionState; +}; + +/*****************************************************************************/ + +class KDE_EXPORT TaskLMBMenu : public QPopupMenu +{ + Q_OBJECT + +public: + TaskLMBMenu(const Task::List& list, QWidget *parent = 0, const char *name = 0); + +protected slots: + void dragSwitch(); + void attentionTimeout(); + +protected: + void dragEnterEvent(QDragEnterEvent*); + void dragLeaveEvent(QDragLeaveEvent*); + void dragMoveEvent(QDragMoveEvent*); + void mousePressEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + +private: + void fillMenu(); + + Task::List m_tasks; + int m_lastDragId; + bool m_attentionState; + QTimer* m_attentionTimer; + QTimer* m_dragSwitchTimer; + QPoint m_dragStartPos; + QValueList<TaskMenuItem*> m_attentionMap; +}; + +#endif diff --git a/kicker/taskmanager/taskmanager.cpp b/kicker/taskmanager/taskmanager.cpp new file mode 100644 index 000000000..dd9364c0e --- /dev/null +++ b/kicker/taskmanager/taskmanager.cpp @@ -0,0 +1,1521 @@ +/***************************************************************** + +Copyright (c) 2000 Matthias Elter <elter@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include <qapplication.h> +#include <qcursor.h> +#include <qimage.h> +#include <qtimer.h> + +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <klocale.h> +// #include <kpixmapio.h> +#include <kstaticdeleter.h> +#include <kwinmodule.h> +#include <kxerrorhandler.h> +#include <netwm.h> + +#include "taskmanager.h" +#include "taskmanager.moc" + +TaskManager* TaskManager::m_self = 0; +static KStaticDeleter<TaskManager> staticTaskManagerDeleter; +uint TaskManager::m_xCompositeEnabled = 0; + +TaskManager* TaskManager::the() +{ + if (!m_self) + { + staticTaskManagerDeleter.setObject(m_self, new TaskManager()); + } + return m_self; +} + +TaskManager::TaskManager() + : QObject(), + _active(0), + _startup_info(0), + m_winModule(new KWinModule()), + m_trackGeometry(false) +{ + KGlobal::locale()->insertCatalogue("libtaskmanager"); + connect(m_winModule, SIGNAL(windowAdded(WId)), + this, SLOT(windowAdded(WId))); + connect(m_winModule, SIGNAL(windowRemoved(WId)), + this, SLOT(windowRemoved(WId))); + connect(m_winModule, SIGNAL(activeWindowChanged(WId)), + this, SLOT(activeWindowChanged(WId))); + connect(m_winModule, SIGNAL(currentDesktopChanged(int)), + this, SLOT(currentDesktopChanged(int))); + connect(m_winModule, SIGNAL(windowChanged(WId,unsigned int)), + this, SLOT(windowChanged(WId,unsigned int))); + + // register existing windows + const QValueList<WId> windows = m_winModule->windows(); + QValueList<WId>::ConstIterator end(windows.end()); + for (QValueList<WId>::ConstIterator it = windows.begin(); it != end; ++it) + { + windowAdded(*it); + } + + // set active window + WId win = m_winModule->activeWindow(); + activeWindowChanged(win); + configure_startup(); +} + +TaskManager::~TaskManager() +{ + KGlobal::locale()->removeCatalogue("libtaskmanager"); +} + +void TaskManager::configure_startup() +{ + KConfig c("klaunchrc", true); + c.setGroup("FeedbackStyle"); + if (!c.readBoolEntry("TaskbarButton", true)) + return; + _startup_info = new KStartupInfo( KStartupInfo::CleanOnCantDetect, this ); + connect( _startup_info, + SIGNAL( gotNewStartup( const KStartupInfoId&, const KStartupInfoData& )), + SLOT( gotNewStartup( const KStartupInfoId&, const KStartupInfoData& ))); + connect( _startup_info, + SIGNAL( gotStartupChange( const KStartupInfoId&, const KStartupInfoData& )), + SLOT( gotStartupChange( const KStartupInfoId&, const KStartupInfoData& ))); + connect( _startup_info, + SIGNAL( gotRemoveStartup( const KStartupInfoId&, const KStartupInfoData& )), + SLOT( killStartup( const KStartupInfoId& ))); + c.setGroup( "TaskbarButtonSettings" ); + _startup_info->setTimeout( c.readUnsignedNumEntry( "Timeout", 30 )); +} + +#ifdef THUMBNAILING_POSSIBLE +void TaskManager::setXCompositeEnabled(bool state) +{ + Display *dpy = QPaintDevice::x11AppDisplay(); + + if (!state) + { + if (!--m_xCompositeEnabled) + { + // unredirecting windows + for (int i = 0; i < ScreenCount(dpy); i++) + { + XCompositeUnredirectSubwindows(dpy, RootWindow(dpy, i), + CompositeRedirectAutomatic); + } + } + return; + } + + if (m_xCompositeEnabled) + { + // we don't unlearn riding bike ;) + m_xCompositeEnabled++; + return; + } + + // XComposite extension check + int event_base, error_base; + if (!XCompositeQueryExtension(dpy, &event_base, &error_base)) + { + return; + } + + int major = 0, minor = 99; // The highest version we support + XCompositeQueryVersion(dpy, &major, &minor); + + // We use XCompositeNameWindowPixmap(), i.e. we need at least + // version 0.2. + if (major == 0 && minor < 2) + { + return; + } + + // XRender extension check + if (!XRenderQueryExtension(dpy, &event_base, &error_base)) + { + return; + } + + major = 0, minor = 99; // The highest version we support + XRenderQueryVersion(dpy, &major, &minor); + + // We use SetPictureTransform() and SetPictureFilter(), i.e. we + // need at least version 0.6. + if (major == 0 && minor < 6) + { + return; + } + + // XFixes extension check + if (!XFixesQueryExtension(dpy, &event_base, &error_base)) + { + return; + } + + major = 3, minor = 99; // The highest version we support + XFixesQueryVersion(dpy, &major, &minor); + + // We use Region objects, i.e. we need at least version 2.0. + if (major < 2) + { + return; + } + + // if we get here, we've got usable extensions + m_xCompositeEnabled++; + + // redirecting windows to backing pixmaps + for (int i = 0; i < ScreenCount(dpy); i++) + { + XCompositeRedirectSubwindows(dpy, RootWindow(dpy, i), + CompositeRedirectAutomatic); + } + + Task::Dict::iterator itEnd = m_tasksByWId.end(); + for (Task::Dict::iterator it = m_tasksByWId.begin(); it != itEnd; ++it) + { + it.data()->updateWindowPixmap(); + } +} +#else // THUMBNAILING_POSSIBLE +void TaskManager::setXCompositeEnabled(bool) +{ +} +#endif // !THUMBNAILING_POSSIBLE + +Task::Ptr TaskManager::findTask(WId w) +{ + // TODO: might be able to be made more efficient if + // we check to see if w is a transient first? + // profiling would say whether this is worth the effort + + Task::Dict::iterator it = m_tasksByWId.begin(); + Task::Dict::iterator itEnd = m_tasksByWId.end(); + + for (; it != itEnd; ++it) + { + if (it.key() == w || it.data()->hasTransient(w)) + { + return it.data(); + } + } + + return 0; +} + +Task::Ptr TaskManager::findTask(int desktop, const QPoint& p) +{ + QValueList<WId> list = winModule()->stackingOrder(); + + Task::Ptr task = 0; + int currentIndex = -1; + Task::Dict::iterator itEnd = m_tasksByWId.end(); + for (Task::Dict::iterator it = m_tasksByWId.begin(); it != itEnd; ++it) + { + Task::Ptr t = it.data(); + if (!t->isOnAllDesktops() && t->desktop() != desktop) + { + continue; + } + + if (t->isIconified() || t->isShaded()) + { + continue; + } + + if (t->geometry().contains(p)) + { + int index = list.findIndex(t->window()); + if (index > currentIndex) + { + currentIndex = index; + task = t; + } + } + } + + return task; +} + +void TaskManager::windowAdded(WId w ) +{ + NETWinInfo info(qt_xdisplay(), w, qt_xrootwin(), + NET::WMWindowType | NET::WMPid | NET::WMState); + + // ignore NET::Tool and other special window types + NET::WindowType wType = + info.windowType( NET::NormalMask | NET::DesktopMask | NET::DockMask | + NET::ToolbarMask | NET::MenuMask | NET::DialogMask | + NET::OverrideMask | NET::TopMenuMask | + NET::UtilityMask | NET::SplashMask ); + + if (wType != NET::Normal && + wType != NET::Override && + wType != NET::Unknown && + wType != NET::Dialog && + wType != NET::Utility) + { + return; + } + + // ignore windows that want to be ignored by the taskbar + if ((info.state() & NET::SkipTaskbar) != 0) + { + _skiptaskbar_windows.push_front( w ); // remember them though + return; + } + + Window transient_for_tmp; + if (XGetTransientForHint( qt_xdisplay(), (Window) w, &transient_for_tmp )) + { + WId transient_for = (WId) transient_for_tmp; + + // check if it's transient for a skiptaskbar window + if( _skiptaskbar_windows.contains( transient_for )) + return; + + // lets see if this is a transient for an existing task + if( transient_for != qt_xrootwin() + && transient_for != 0 + && wType != NET::Utility ) + { + Task::Ptr t = findTask(transient_for); + if (t) + { + if (t->window() != w) + { + t->addTransient(w, info); + // kdDebug() << "TM: Transient " << w << " added for Task: " << t->window() << endl; + } + return; + } + } + } + + Task::Ptr t = new Task(w, this); + m_tasksByWId[w] = t; + + // kdDebug() << "TM: Task added for WId: " << w << endl; + + emit taskAdded(t); +} + +void TaskManager::windowRemoved(WId w) +{ + _skiptaskbar_windows.remove(w); + + // find task + Task::Ptr t = findTask(w); + if (!t) + { + return; + } + + if (t->window() == w) + { + m_tasksByWId.remove(w); + emit taskRemoved(t); + + if (t == _active) + { + _active = 0; + } + + //kdDebug() << "TM: Task for WId " << w << " removed." << endl; + } + else + { + t->removeTransient(w); + //kdDebug() << "TM: Transient " << w << " for Task " << t->window() << " removed." << endl; + } +} + +void TaskManager::windowChanged(WId w, unsigned int dirty) +{ + if (dirty & NET::WMState) + { + NETWinInfo info (qt_xdisplay(), w, qt_xrootwin(), + NET::WMState | NET::XAWMState); + if (info.state() & NET::SkipTaskbar) + { + windowRemoved(w); + _skiptaskbar_windows.push_front(w); + return; + } + else + { + _skiptaskbar_windows.remove(w); + if (info.mappingState() != NET::Withdrawn && !findTask(w)) + { + // skipTaskBar state was removed and the window is still + // mapped, so add this window + windowAdded( w ); + } + } + } + + // check if any state we are interested in is marked dirty + if (!(dirty & (NET::WMVisibleName | NET::WMName | NET::WMIcon | + NET::WMState | NET::XAWMState | NET::WMDesktop) || + (m_trackGeometry && dirty & NET::WMGeometry))) + { + return; + } + + // find task + Task::Ptr t = findTask(w); + if (!t) + { + return; + } + + //kdDebug() << "TaskManager::windowChanged " << w << " " << dirty << endl; + + if (dirty & NET::WMState) + { + t->updateDemandsAttentionState(w); + } + + // refresh icon pixmap if necessary + if (dirty & NET::WMIcon) + { + t->refreshIcon(); + + // we're done with the icon processing, don't pass this on anymore + dirty ^= NET::WMIcon; + } + + if (dirty) + { + // only refresh this stuff if we have other changes besides icons + t->refresh(dirty); + } + + if (dirty & (NET::WMDesktop | NET::WMState | NET::XAWMState)) + { + // moved to different desktop or is on all or change in iconification/withdrawnnes + emit windowChanged(t); + + if (m_xCompositeEnabled && dirty & NET::WMState) + { + // update on restoring a minimized window + updateWindowPixmap(w); + } + + } + else if (dirty & NET::WMGeometry) + { + emit windowChangedGeometry(t); + + if (m_xCompositeEnabled) + { + // update on size changes, not on task drags + updateWindowPixmap(w); + } + + } +} + +void TaskManager::updateWindowPixmap(WId w) +{ + if (!m_xCompositeEnabled) + { + return; + } + + Task::Ptr task = findTask(w); + if (task) + { + task->updateWindowPixmap(); + } +} + +void TaskManager::activeWindowChanged(WId w ) +{ + //kdDebug() << "TaskManager::activeWindowChanged" << endl; + + Task::Ptr t = findTask( w ); + if (!t) { + if (_active) { + _active->setActive(false); + _active = 0; + } + } + else { + if (_active) + _active->setActive(false); + + _active = t; + _active->setActive(true); + } +} + +void TaskManager::currentDesktopChanged(int desktop) +{ + emit desktopChanged(desktop); +} + +void TaskManager::gotNewStartup( const KStartupInfoId& id, const KStartupInfoData& data ) +{ + Startup::Ptr s = new Startup( id, data, this ); + _startups.append(s); + + emit startupAdded(s); +} + +void TaskManager::gotStartupChange( const KStartupInfoId& id, const KStartupInfoData& data ) +{ + Startup::List::iterator itEnd = _startups.end(); + for (Startup::List::iterator sIt = _startups.begin(); sIt != itEnd; ++sIt) + { + if ((*sIt)->id() == id) + { + (*sIt)->update(data); + return; + } + } +} + +void TaskManager::killStartup( const KStartupInfoId& id ) +{ + Startup::List::iterator sIt = _startups.begin(); + Startup::List::iterator itEnd = _startups.end(); + Startup::Ptr s = 0; + for (; sIt != itEnd; ++sIt) + { + if ((*sIt)->id() == id) + { + s = *sIt; + break; + } + } + + if (!s) + { + return; + } + + _startups.erase(sIt); + emit startupRemoved(s); +} + +void TaskManager::killStartup(Startup::Ptr s) +{ + if (!s) + { + return; + } + + Startup::List::iterator sIt = _startups.begin(); + Startup::List::iterator itEnd = _startups.end(); + for (; sIt != itEnd; ++sIt) + { + if ((*sIt) == s) + { + _startups.erase(sIt); + break; + } + } + + emit startupRemoved(s); +} + +QString TaskManager::desktopName(int desk) const +{ + return m_winModule->desktopName(desk); +} + +int TaskManager::numberOfDesktops() const +{ + return m_winModule->numberOfDesktops(); +} + +bool TaskManager::isOnTop(const Task* task) +{ + if (!task) + { + return false; + } + + QValueList<WId>::ConstIterator begin(m_winModule->stackingOrder().constBegin()); + QValueList<WId>::ConstIterator it = m_winModule->stackingOrder().fromLast(); + do + { + Task::Dict::iterator taskItEnd = m_tasksByWId.end(); + for (Task::Dict::iterator taskIt = m_tasksByWId.begin(); + taskIt != taskItEnd; ++taskIt) + { + Task::Ptr t = taskIt.data(); + if ((*it) == t->window()) + { + if (t == task) + { + return true; + } + + if (!t->isIconified() && + (t->isAlwaysOnTop() == task->isAlwaysOnTop())) + { + return false; + } + + break; + } + } + } while (it-- != begin); + + return false; +} + +bool TaskManager::isOnScreen(int screen, const WId wid) +{ + if (screen == -1) + { + return true; + } + + KWin::WindowInfo wi = KWin::windowInfo(wid, NET::WMKDEFrameStrut); + + // for window decos that fudge a bit and claim to extend beyond the + // edge of the screen, we just contract a bit. + QRect window = wi.frameGeometry(); + QRect desktop = QApplication::desktop()->screenGeometry(screen); + desktop.addCoords(5, 5, -5, -5); + return window.intersects(desktop); +} + +Task::Task(WId win, QObject *parent, const char *name) + : QObject(parent, name), + _active(false), + _win(win), + m_frameId(win), + _info(KWin::windowInfo(_win, 0, NET::WM2AllowedActions)), + _lastWidth(0), + _lastHeight(0), + _lastResize(false), + _lastIcon(), + _thumbSize(0.2), + _thumb(), + _grab() +{ + // try to load icon via net_wm + _pixmap = KWin::icon(_win, 16, 16, true); + + // try to guess the icon from the classhint + if(_pixmap.isNull()) + { + KGlobal::iconLoader()->loadIcon(className().lower(), + KIcon::Small, + KIcon::Small, + KIcon::DefaultState, + 0, true); + } + + // load xapp icon + if (_pixmap.isNull()) + { + _pixmap = SmallIcon("kcmx"); + } + +#ifdef THUMBNAILING_POSSIBLE + m_windowPixmap = 0; + findWindowFrameId(); + + if (TaskManager::xCompositeEnabled()) + { + updateWindowPixmap(); + } +#endif // THUMBNAILING_POSSIBLE +} + +Task::~Task() +{ +#ifdef THUMBNAILING_POSSIBLE + if (m_windowPixmap) + { + XFreePixmap(QPaintDevice::x11AppDisplay(), m_windowPixmap); + } +#endif // THUMBNAILING_POSSIBLE +} + +// Task::findWindowFrameId() +// Code was copied from Kompose. +// Copyright (C) 2004 Hans Oischinger +// Permission granted on 2005-04-27. +void Task::findWindowFrameId() +{ +#ifdef THUMBNAILING_POSSIBLE + Window target_win, parent, root; + Window *children; + uint nchildren; + + target_win = _win; + for (;;) + { + if (!XQueryTree(QPaintDevice::x11AppDisplay(), target_win, &root, + &parent, &children, &nchildren)) + { + break; + } + + if (children) + { + XFree(children); // it's a list, that's deallocated! + } + + if (!parent || parent == root) + { + break; + } + else + { + target_win = parent; + } + } + + m_frameId = target_win; +#endif // THUMBNAILING_POSSIBLE +} + +void Task::refreshIcon() +{ + // try to load icon via net_wm + _pixmap = KWin::icon(_win, 16, 16, true); + + // try to guess the icon from the classhint + if(_pixmap.isNull()) + { + KGlobal::iconLoader()->loadIcon(className().lower(), + KIcon::Small, + KIcon::Small, + KIcon::DefaultState, + 0, true); + } + + // load xapp icon + if (_pixmap.isNull()) + { + _pixmap = SmallIcon("kcmx"); + } + + _lastIcon.resize(0,0); + emit iconChanged(); +} + +void Task::refresh(unsigned int dirty) +{ + QString name = visibleName(); + _info = KWin::windowInfo(_win, 0, NET::WM2AllowedActions); + + if (dirty != NET::WMName || name != visibleName()) + { + emit changed(dirty == NET::WMGeometry); + } +} + +void Task::setActive(bool a) +{ + _active = a; + emit changed(false); + if ( a ) + emit activated(); + else + emit deactivated(); +} + +bool Task::isMaximized() const +{ + return _info.valid() && (_info.state() & NET::Max); +} + +bool Task::isMinimized() const +{ + return _info.valid() && _info.isMinimized(); +} + +bool Task::isIconified() const +{ + return _info.valid() && _info.isMinimized(); +} + +bool Task::isAlwaysOnTop() const +{ + return _info.valid() && (_info.state() & NET::StaysOnTop); +} + +bool Task::isKeptBelowOthers() const +{ + return _info.valid() && (_info.state() & NET::KeepBelow); +} + +bool Task::isFullScreen() const +{ + return _info.valid() && (_info.state() & NET::FullScreen); +} + +bool Task::isShaded() const +{ + return _info.valid() && (_info.state() & NET::Shaded); +} + +bool Task::isOnCurrentDesktop() const +{ + return _info.valid() && _info.isOnCurrentDesktop(); +} + +bool Task::isOnAllDesktops() const +{ + return _info.valid() && _info.onAllDesktops(); +} + +bool Task::isActive() const +{ + return _active; +} + +bool Task::isOnTop() const +{ + return TaskManager::the()->isOnTop(this); +} + +bool Task::isModified() const +{ + static QString modStr = QString::fromUtf8("[") + + i18n("modified") + + QString::fromUtf8("]"); + int modStrPos = _info.visibleName().find(modStr); + + return ( modStrPos != -1 ); +} + +bool Task::demandsAttention() const +{ + return (_info.valid() && (_info.state() & NET::DemandsAttention)) || + _transients_demanding_attention.count() > 0; +} + +bool Task::isOnScreen( int screen ) const +{ + return TaskManager::isOnScreen( screen, _win ); +} + +void Task::updateDemandsAttentionState( WId w ) +{ + if (window() != w) + { + // 'w' is a transient for this task + NETWinInfo i( qt_xdisplay(), w, qt_xrootwin(), NET::WMState ); + if(i.state() & NET::DemandsAttention) + { + if (!_transients_demanding_attention.contains(w)) + { + _transients_demanding_attention.append(w); + } + } + else + { + _transients_demanding_attention.remove( w ); + } + } +} + +void Task::addTransient( WId w, const NETWinInfo& info ) +{ + _transients.append(w); + if (info.state() & NET::DemandsAttention) + { + _transients_demanding_attention.append(w); + emit changed(false); + } +} + +void Task::removeTransient(WId w) +{ + _transients.remove(w); + _transients_demanding_attention.remove(w); +} + +QString Task::className() +{ + XClassHint hint; + if(XGetClassHint(qt_xdisplay(), _win, &hint)) { + QString nh( hint.res_name ); + XFree( hint.res_name ); + XFree( hint.res_class ); + return nh; + } + return QString::null; +} + +QString Task::classClass() +{ + XClassHint hint; + if(XGetClassHint(qt_xdisplay(), _win, &hint)) { + QString ch( hint.res_class ); + XFree( hint.res_name ); + XFree( hint.res_class ); + return ch; + } + return QString::null; +} + +QPixmap Task::icon( int width, int height, bool allowResize ) +{ + if ( (width == _lastWidth) + && (height == _lastHeight) + && (allowResize == _lastResize ) + && (!_lastIcon.isNull()) ) + return _lastIcon; + + QPixmap newIcon = KWin::icon( _win, width, height, allowResize ); + if ( !newIcon.isNull() ) { + _lastIcon = newIcon; + _lastWidth = width; + _lastHeight = height; + _lastResize = allowResize; + } + + return newIcon; +} + +QPixmap Task::bestIcon( int size, bool &isStaticIcon ) +{ + QPixmap pixmap; + isStaticIcon = false; + + switch( size ) { + case KIcon::SizeSmall: + { + pixmap = icon( 16, 16, true ); + + // Icon of last resort + if( pixmap.isNull() ) { + pixmap = KGlobal::iconLoader()->loadIcon( "go", + KIcon::NoGroup, + KIcon::SizeSmall ); + isStaticIcon = true; + } + } + break; + case KIcon::SizeMedium: + { + // + // Try 34x34 first for KDE 2.1 icons with shadows, if we don't + // get one then try 32x32. + // + pixmap = icon( 34, 34, false ); + + if ( (( pixmap.width() != 34 ) || ( pixmap.height() != 34 )) && + (( pixmap.width() != 32 ) || ( pixmap.height() != 32 )) ) + { + pixmap = icon( 32, 32, true ); + } + + // Icon of last resort + if( pixmap.isNull() ) { + pixmap = KGlobal::iconLoader()->loadIcon( "go", + KIcon::NoGroup, + KIcon::SizeMedium ); + isStaticIcon = true; + } + } + break; + case KIcon::SizeLarge: + { + // If there's a 48x48 icon in the hints then use it + pixmap = icon( size, size, false ); + + // If not, try to get one from the classname + if ( pixmap.isNull() || pixmap.width() != size || pixmap.height() != size ) { + pixmap = KGlobal::iconLoader()->loadIcon( className(), + KIcon::NoGroup, + size, + KIcon::DefaultState, + 0L, + true ); + isStaticIcon = true; + } + + // If we still don't have an icon then scale the one in the hints + if ( pixmap.isNull() || ( pixmap.width() != size ) || ( pixmap.height() != size ) ) { + pixmap = icon( size, size, true ); + isStaticIcon = false; + } + + // Icon of last resort + if( pixmap.isNull() ) { + pixmap = KGlobal::iconLoader()->loadIcon( "go", + KIcon::NoGroup, + size ); + isStaticIcon = true; + } + } + } + + return pixmap; +} + +bool Task::idMatch( const QString& id1, const QString& id2 ) +{ + if ( id1.isEmpty() || id2.isEmpty() ) + return false; + + if ( id1.contains( id2 ) > 0 ) + return true; + + if ( id2.contains( id1 ) > 0 ) + return true; + + return false; +} + + +void Task::move() +{ + bool on_current = _info.isOnCurrentDesktop(); + + if (!on_current) + { + KWin::setCurrentDesktop(_info.desktop()); + KWin::forceActiveWindow(_win); + } + + if (_info.isMinimized()) + { + KWin::deIconifyWindow(_win); + } + + QRect geom = _info.geometry(); + QCursor::setPos(geom.center()); + + NETRootInfo ri(qt_xdisplay(), NET::WMMoveResize); + ri.moveResizeRequest(_win, geom.center().x(), + geom.center().y(), NET::Move); +} + +void Task::resize() +{ + bool on_current = _info.isOnCurrentDesktop(); + + if (!on_current) + { + KWin::setCurrentDesktop(_info.desktop()); + KWin::forceActiveWindow(_win); + } + + if (_info.isMinimized()) + { + KWin::deIconifyWindow(_win); + } + + QRect geom = _info.geometry(); + QCursor::setPos(geom.bottomRight()); + + NETRootInfo ri(qt_xdisplay(), NET::WMMoveResize); + ri.moveResizeRequest(_win, geom.bottomRight().x(), + geom.bottomRight().y(), NET::BottomRight); +} + +void Task::setMaximized(bool maximize) +{ + KWin::WindowInfo info = KWin::windowInfo(_win, NET::WMState | NET::XAWMState | NET::WMDesktop); + bool on_current = info.isOnCurrentDesktop(); + + if (!on_current) + { + KWin::setCurrentDesktop(info.desktop()); + } + + if (info.isMinimized()) + { + KWin::deIconifyWindow(_win); + } + + NETWinInfo ni(qt_xdisplay(), _win, qt_xrootwin(), NET::WMState); + + if (maximize) + { + ni.setState(NET::Max, NET::Max); + } + else + { + ni.setState(0, NET::Max); + } + + if (!on_current) + { + KWin::forceActiveWindow(_win); + } +} + +void Task::toggleMaximized() +{ + setMaximized(!isMaximized()); +} + +void Task::restore() +{ + KWin::WindowInfo info = KWin::windowInfo(_win, NET::WMState | NET::XAWMState | NET::WMDesktop); + bool on_current = info.isOnCurrentDesktop(); + + if (!on_current) + { + KWin::setCurrentDesktop(info.desktop()); + } + + if( info.isMinimized()) + { + KWin::deIconifyWindow(_win); + } + + NETWinInfo ni(qt_xdisplay(), _win, qt_xrootwin(), NET::WMState); + ni.setState(0, NET::Max); + + if (!on_current) + { + KWin::forceActiveWindow( _win ); + } +} + +void Task::setIconified(bool iconify) +{ + if (iconify) + { + KWin::iconifyWindow(_win); + } + else + { + KWin::WindowInfo info = KWin::windowInfo(_win, NET::WMState | NET::XAWMState | NET::WMDesktop); + bool on_current = info.isOnCurrentDesktop(); + + if (!on_current) + { + KWin::setCurrentDesktop(info.desktop()); + } + + KWin::deIconifyWindow(_win); + + if (!on_current) + { + KWin::forceActiveWindow(_win); + } + } +} + +void Task::toggleIconified() +{ + setIconified(!isIconified()); +} + +void Task::close() +{ + NETRootInfo ri( qt_xdisplay(), NET::CloseWindow ); + ri.closeWindowRequest( _win ); +} + +void Task::raise() +{ +// kdDebug(1210) << "Task::raise(): " << name() << endl; + KWin::raiseWindow( _win ); +} + +void Task::lower() +{ +// kdDebug(1210) << "Task::lower(): " << name() << endl; + KWin::lowerWindow( _win ); +} + +void Task::activate() +{ +// kdDebug(1210) << "Task::activate():" << name() << endl; + WId w = _win; + if (_transients_demanding_attention.count() > 0) + { + w = _transients_demanding_attention.last(); + } + KWin::forceActiveWindow( w ); +} + +void Task::activateRaiseOrIconify() +{ + if (!isActive() || isIconified()) + { + activate(); + } + else if (!isOnTop()) + { + raise(); + } + else + { + setIconified(true); + } +} + +void Task::toDesktop(int desk) +{ + NETWinInfo ni(qt_xdisplay(), _win, qt_xrootwin(), NET::WMDesktop); + if (desk == 0) + { + if (_info.valid() && _info.onAllDesktops()) + { + ni.setDesktop(TaskManager::the()->winModule()->currentDesktop()); + KWin::forceActiveWindow(_win); + } + else + { + ni.setDesktop(NETWinInfo::OnAllDesktops); + } + + return; + } + ni.setDesktop(desk); + if(desk == TaskManager::the()->winModule()->currentDesktop()) + KWin::forceActiveWindow(_win); +} + +void Task::toCurrentDesktop() +{ + toDesktop(TaskManager::the()->winModule()->currentDesktop()); +} + +void Task::setAlwaysOnTop(bool stay) +{ + NETWinInfo ni( qt_xdisplay(), _win, qt_xrootwin(), NET::WMState); + if(stay) + ni.setState( NET::StaysOnTop, NET::StaysOnTop ); + else + ni.setState( 0, NET::StaysOnTop ); +} + +void Task::toggleAlwaysOnTop() +{ + setAlwaysOnTop( !isAlwaysOnTop() ); +} + +void Task::setKeptBelowOthers(bool below) +{ + NETWinInfo ni(qt_xdisplay(), _win, qt_xrootwin(), NET::WMState); + + if (below) + { + ni.setState(NET::KeepBelow, NET::KeepBelow); + } + else + { + ni.setState(0, NET::KeepBelow); + } +} + +void Task::toggleKeptBelowOthers() +{ + setKeptBelowOthers(!isKeptBelowOthers()); +} + +void Task::setFullScreen(bool fullscreen) +{ + NETWinInfo ni(qt_xdisplay(), _win, qt_xrootwin(), NET::WMState); + + if (fullscreen) + { + ni.setState(NET::FullScreen, NET::FullScreen); + } + else + { + ni.setState(0, NET::FullScreen); + } +} + +void Task::toggleFullScreen() +{ + setFullScreen(!isFullScreen()); +} + +void Task::setShaded(bool shade) +{ + NETWinInfo ni( qt_xdisplay(), _win, qt_xrootwin(), NET::WMState); + if(shade) + ni.setState( NET::Shaded, NET::Shaded ); + else + ni.setState( 0, NET::Shaded ); +} + +void Task::toggleShaded() +{ + setShaded( !isShaded() ); +} + +void Task::publishIconGeometry(QRect rect) +{ + if (rect == m_iconGeometry) + { + return; + } + + m_iconGeometry = rect; + NETWinInfo ni(qt_xdisplay(), _win, qt_xrootwin(), 0); + NETRect r; + + if (rect.isValid()) + { + r.pos.x = rect.x(); + r.pos.y = rect.y(); + r.size.width = rect.width(); + r.size.height = rect.height(); + } + ni.setIconGeometry(r); +} + +void Task::updateThumbnail() +{ + if ( !_info.valid() || + !isOnCurrentDesktop() || + !isActive() || + !_grab.isNull() ) // We're already processing one... + { + return; + } + + // + // We do this as a two stage process to remove the delay caused + // by the thumbnail generation. This makes things much smoother + // on slower machines. + // + QWidget *rootWin = qApp->desktop(); + QRect geom = _info.geometry(); + _grab = QPixmap::grabWindow(rootWin->winId(), + geom.x(), geom.y(), + geom.width(), geom.height()); + + if (!_grab.isNull()) + { + QTimer::singleShot(200, this, SLOT(generateThumbnail())); + } +} + +void Task::generateThumbnail() +{ + if ( _grab.isNull() ) + return; + + QImage img = _grab.convertToImage(); + + double width = img.width(); + double height = img.height(); + width = width * _thumbSize; + height = height * _thumbSize; + + img = img.smoothScale( qRound(width), qRound(height) ); + _thumb = img; + _grab.resize( 0, 0 ); // Makes grab a null image. + + emit thumbnailChanged(); +} + +#ifdef THUMBNAILING_POSSIBLE +QPixmap Task::thumbnail(int maxDimension) +{ + if (!TaskManager::xCompositeEnabled() || !m_windowPixmap) + { + return QPixmap(); + } + + Display *dpy = QPaintDevice::x11AppDisplay(); + + XWindowAttributes winAttr; + XGetWindowAttributes(dpy, m_frameId, &winAttr); + XRenderPictFormat *format = XRenderFindVisualFormat(dpy, winAttr.visual); + + XRenderPictureAttributes picAttr; + picAttr.subwindow_mode = IncludeInferiors; // Don't clip child widgets + + Picture picture = XRenderCreatePicture(dpy, m_windowPixmap, format, + CPSubwindowMode, &picAttr); + + // Get shaped windows handled correctly. + XserverRegion region = XFixesCreateRegionFromWindow(dpy, m_frameId, + WindowRegionBounding); + XFixesSetPictureClipRegion(dpy, picture, 0, 0, region); + XFixesDestroyRegion(dpy, region); + + double factor; + if (winAttr.width > winAttr.height) + { + factor = (double)maxDimension / (double)winAttr.width; + } + else + { + factor = (double)maxDimension / (double)winAttr.height; + } + int thumbnailWidth = (int)(winAttr.width * factor); + int thumbnailHeight = (int)(winAttr.height * factor); + + QPixmap thumbnail(thumbnailWidth, thumbnailHeight); + thumbnail.fill(QApplication::palette().active().background()); + +#if 0 // QImage::smoothScale() scaling + QPixmap full(winAttr.width, winAttr.height); + full.fill(QApplication::palette().active().background()); + + bool hasAlpha = format->type == PictTypeDirect && format->direct.alphaMask; + + XRenderComposite(dpy, + hasAlpha ? PictOpOver : PictOpSrc, + picture, // src + None, // mask + full.x11RenderHandle(), // dst + 0, 0, // src offset + 0, 0, // mask offset + 0, 0, // dst offset + winAttr.width, winAttr.height); + + KPixmapIO io; + QImage image = io.convertToImage(full); + thumbnail = io.convertToPixmap(image.smoothScale(thumbnailWidth, + thumbnailHeight)); +#else // XRENDER scaling + // Scaling matrix + XTransform transformation = {{ + { XDoubleToFixed(1), XDoubleToFixed(0), XDoubleToFixed( 0) }, + { XDoubleToFixed(0), XDoubleToFixed(1), XDoubleToFixed( 0) }, + { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(factor) } + }}; + + XRenderSetPictureTransform(dpy, picture, &transformation); + XRenderSetPictureFilter(dpy, picture, FilterBest, 0, 0); + + XRenderComposite(QPaintDevice::x11AppDisplay(), + PictOpOver, // we're filtering, alpha values are probable + picture, // src + None, // mask + thumbnail.x11RenderHandle(), // dst + 0, 0, // src offset + 0, 0, // mask offset + 0, 0, // dst offset + thumbnailWidth, thumbnailHeight); +#endif + XRenderFreePicture(dpy, picture); + + return thumbnail; +} +#else // THUMBNAILING_POSSIBLE +QPixmap Task::thumbnail(int /* maxDimension */) +{ + return QPixmap(); +} +#endif // THUMBNAILING_POSSIBLE + +void Task::updateWindowPixmap() +{ +#ifdef THUMBNAILING_POSSIBLE + if (!TaskManager::xCompositeEnabled() || !isOnCurrentDesktop() || + isMinimized()) + { + return; + } + + Display *dpy = QPaintDevice::x11AppDisplay(); + + if (m_windowPixmap) + { + XFreePixmap(dpy, m_windowPixmap); + } + + KXErrorHandler err; + m_windowPixmap = XCompositeNameWindowPixmap(dpy, m_frameId); + if( err.error( true )) + m_windowPixmap = None; +#endif // THUMBNAILING_POSSIBLE +} + +Startup::Startup(const KStartupInfoId& id, const KStartupInfoData& data, + QObject * parent, const char *name) + : QObject(parent, name), _id(id), _data(data) +{ +} + +Startup::~Startup() +{ +} + +void Startup::update(const KStartupInfoData& data) +{ + _data.update(data); + emit changed(); +} + +int TaskManager::currentDesktop() const +{ + return m_winModule->currentDesktop(); +} + +TaskDrag::TaskDrag(const Task::List& tasks, QWidget* source, const char* name) + : QStoredDrag("taskbar/task", source, name) +{ + QByteArray data; + QDataStream stream(data, IO_WriteOnly); + + Task::List::const_iterator itEnd = tasks.constEnd(); + for (Task::List::const_iterator it = tasks.constBegin(); it != itEnd; ++it) + { + stream << (*it)->window(); + } + + setEncodedData(data); +} + +TaskDrag::~TaskDrag() +{ +} + +bool TaskDrag::canDecode(const QMimeSource* e) +{ + return e->provides("taskbar/task"); +} + +Task::List TaskDrag::decode( const QMimeSource* e ) +{ + QByteArray data(e->encodedData("taskbar/task")); + Task::List tasks; + + if (data.size()) + { + QDataStream stream(data, IO_ReadOnly); + while (!stream.atEnd()) + { + WId id; + stream >> id; + if (Task::Ptr task = TaskManager::the()->findTask(id)) + { + tasks.append(task); + } + } + } + + return tasks; +} + diff --git a/kicker/taskmanager/taskmanager.h b/kicker/taskmanager/taskmanager.h new file mode 100644 index 000000000..5885569d9 --- /dev/null +++ b/kicker/taskmanager/taskmanager.h @@ -0,0 +1,713 @@ +/***************************************************************** + +Copyright (c) 2000-2001 Matthias Elter <elter@kde.org> +Copyright (c) 2001 Richard Moore <rich@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef __taskmanager_h__ +#define __taskmanager_h__ + +#include <sys/types.h> + +#include <qobject.h> +#include <qpixmap.h> +#include <qpoint.h> +#include <qptrlist.h> +#include <qpixmap.h> +#include <qdragobject.h> +#include <qrect.h> +#include <qvaluelist.h> +#include <qvaluevector.h> + +#include <ksharedptr.h> +#include <kstartupinfo.h> +#include <kwin.h> + +#include <config.h> + +#if defined(HAVE_XCOMPOSITE) && \ + defined(HAVE_XRENDER) && \ + defined(HAVE_XFIXES) +#include <X11/Xlib.h> +#include <X11/extensions/Xcomposite.h> +#include <X11/extensions/Xfixes.h> +#include <X11/extensions/Xrender.h> +#if XCOMPOSITE_VERSION >= 00200 && \ + XFIXES_VERSION >= 20000 && \ + (RENDER_MAJOR > 0 || RENDER_MINOR >= 6) +#define THUMBNAILING_POSSIBLE +#endif +#endif + +class KWinModule; +class TaskManager; + +typedef QValueList<WId> WindowList; + +/** + * A dynamic interface to a task (main window). + * + * @see TaskManager + * @see KWinModule + */ +class KDE_EXPORT Task: public QObject, public KShared +{ + Q_OBJECT + Q_PROPERTY( QString visibleIconicName READ visibleIconicName ) + Q_PROPERTY( QString iconicName READ iconicName ) + Q_PROPERTY( QString visibleIconicNameWithState READ visibleIconicNameWithState ) + Q_PROPERTY( QString visibleName READ visibleName ) + Q_PROPERTY( QString name READ name ) + Q_PROPERTY( QString visibleNameWithState READ visibleNameWithState ) + Q_PROPERTY( QPixmap pixmap READ pixmap ) + Q_PROPERTY( bool maximized READ isMaximized ) + Q_PROPERTY( bool minimized READ isMinimized ) + // KDE4 deprecated + Q_PROPERTY( bool iconified READ isIconified ) + Q_PROPERTY( bool shaded READ isShaded WRITE setShaded ) + Q_PROPERTY( bool active READ isActive ) + Q_PROPERTY( bool onCurrentDesktop READ isOnCurrentDesktop ) + Q_PROPERTY( bool onAllDesktops READ isOnAllDesktops ) + Q_PROPERTY( bool alwaysOnTop READ isAlwaysOnTop WRITE setAlwaysOnTop ) + Q_PROPERTY( bool modified READ isModified ) + Q_PROPERTY( bool demandsAttention READ demandsAttention ) + Q_PROPERTY( int desktop READ desktop ) + Q_PROPERTY( double thumbnailSize READ thumbnailSize WRITE setThumbnailSize ) + Q_PROPERTY( bool hasThumbnail READ hasThumbnail ) + Q_PROPERTY( QPixmap thumbnail READ thumbnail ) + +public: + typedef KSharedPtr<Task> Ptr; + typedef QValueVector<Task::Ptr> List; + typedef QMap<WId, Task::Ptr> Dict; + + Task(WId win, QObject *parent, const char *name = 0); + virtual ~Task(); + + WId window() const { return _win; } + KWin::WindowInfo info() const { return _info; } + +#if 0 // this would use (_NET_)WM_ICON_NAME, which is shorter, but can be different from window name + QString visibleIconicName() const { return _info.visibleIconName(); } + QString visibleIconicNameWithState() const { return _info.visibleIconNameWithState(); } + QString iconicName() const { return _info.iconName(); } +#else + QString visibleIconicName() const { return _info.visibleName(); } + QString visibleIconicNameWithState() const { return _info.visibleNameWithState(); } + QString iconicName() const { return _info.name(); } +#endif + QString visibleName() const { return _info.visibleName(); } + QString visibleNameWithState() const { return _info.visibleNameWithState(); } + QString name() const { return _info.name(); } + QString className(); + QString classClass(); + + /** + * A list of the window ids of all transient windows (dialogs) associated + * with this task. + */ + WindowList transients() const { return _transients; } + + /** + * Returns a 16x16 (KIcon::Small) icon for the task. This method will + * only fall back to a static icon if there is no icon of any size in + * the WM hints. + */ + QPixmap pixmap() const { return _pixmap; } + + /** + * Returns the best icon for any of the KIcon::StdSizes. If there is no + * icon of the specified size specified in the WM hints, it will try to + * get one using KIconLoader. + * + * <pre> + * bool gotStaticIcon; + * QPixmap icon = myTask->icon( KIcon::SizeMedium, gotStaticIcon ); + * </pre> + * + * @param size Any of the constants in KIcon::StdSizes. + * @param isStaticIcon Set to true if KIconLoader was used, false otherwise. + * @see KIcon + */ + QPixmap bestIcon( int size, bool &isStaticIcon ); + + /** + * Tries to find an icon for the task with the specified size. If there + * is no icon that matches then it will either resize the closest available + * icon or return a null pixmap depending on the value of allowResize. + * + * Note that the last icon is cached, so a sequence of calls with the same + * parameters will only query the NET properties if the icon has changed or + * none was found. + */ + QPixmap icon( int width, int height, bool allowResize = false ); + + /** + * Returns true iff the windows with the specified ids should be grouped + * together in the task list. + */ + static bool idMatch(const QString &, const QString &); + + // state + + /** + * Returns true if the task's window is maximized. + */ + bool isMaximized() const; + + /** + * Returns true if the task's window is minimized. + */ + bool isMinimized() const; + + /** + * @deprecated + * Returns true if the task's window is minimized(iconified). + */ + bool isIconified() const; + + /** + * Returns true if the task's window is shaded. + */ + bool isShaded() const; + + /** + * Returns true if the task's window is the active window. + */ + bool isActive() const; + + /** + * Returns true if the task's window is the topmost non-iconified, + * non-always-on-top window. + */ + bool isOnTop() const; + + /** + * Returns true if the task's window is on the current virtual desktop. + */ + bool isOnCurrentDesktop() const; + + /** + * Returns true if the task's window is on all virtual desktops. + */ + bool isOnAllDesktops() const; + + /** + * Returns true if the task's window will remain at the top of the + * stacking order. + */ + bool isAlwaysOnTop() const; + + /** + * Returns true if the task's window will remain at the bottom of the + * stacking order. + */ + bool isKeptBelowOthers() const; + + /** + * Returns true if the task's window is in full screen mode + */ + bool isFullScreen() const; + + /** + * Returns true if the document the task is editing has been modified. + * This is currently handled heuristically by looking for the string + * '[i18n_modified]' in the window title where i18n_modified is the + * word 'modified' in the current language. + */ + bool isModified() const ; + + /** + * Returns the desktop on which this task's window resides. + */ + int desktop() const { return _info.desktop(); } + + /** + * Returns true if the task is not active but demands user's attention. + */ + bool demandsAttention() const; + + + /** + * Returns true if the window is on the specified screen of a multihead configuration + */ + bool isOnScreen( int screen ) const; + + /** + * Returns true if the task should be shown in taskbar-like apps + */ + bool showInTaskbar() const { return _info.state() ^ NET::SkipTaskbar; } + + /** + * Returns true if the task should be shown in pager-like apps + */ + bool showInPager() const { return _info.state() ^ NET::SkipPager; } + + /** + * Returns the geometry for this window + */ + QRect geometry() const { return _info.geometry(); } + + /** + * Returns the geometry for the from of this window + */ + QRect frameGeometry() const { return _info.frameGeometry(); } + + // internal + + //* @internal + void refresh(unsigned int dirty); + //* @internal + void refreshIcon(); + //* @internal + void addTransient( WId w, const NETWinInfo& info ); + //* @internal + void removeTransient( WId w ); + //* @internal + bool hasTransient(WId w) const { return _transients.find(w) != _transients.end(); } + //* @internal + void updateDemandsAttentionState( WId w ); + //* @internal + void setActive(bool a); + + // For thumbnails + + /** + * Returns the current thumbnail size. + */ + double thumbnailSize() const { return _thumbSize; } + + /** + * Sets the size for the window thumbnail. For example a size of + * 0.2 indicates the thumbnail will be 20% of the original window + * size. + */ + void setThumbnailSize( double size ) { _thumbSize = size; } + + /** + * Returns true if this task has a thumbnail. Note that this method + * can only ever return true after a call to updateThumbnail(). + */ + bool hasThumbnail() const { return !_thumb.isNull(); } + + /** + * Returns the thumbnail for this task (or a null image if there is + * none). + */ + const QPixmap &thumbnail() const { return _thumb; } + + QPixmap thumbnail(int maxDimension); + + void updateWindowPixmap(); + +public slots: + // actions + + /** + * Maximise the main window of this task. + */ + void setMaximized(bool); + void toggleMaximized(); + + /** + * Restore the main window of the task (if it was iconified). + */ + void restore(); + + /** + * Move the window of this task. + */ + void move(); + + /** + * Resize the window of this task. + */ + void resize(); + + /** + * Iconify the task. + */ + void setIconified(bool); + void toggleIconified(); + + /** + * Close the task's window. + */ + void close(); + + /** + * Raise the task's window. + */ + void raise(); + + /** + * Lower the task's window. + */ + void lower(); + + /** + * Activate the task's window. + */ + void activate(); + + /** + * Perform the action that is most appropriate for this task. If it + * is not active, activate it. Else if it is not the top window, raise + * it. Otherwise, iconify it. + */ + void activateRaiseOrIconify(); + + /** + * If true, the task's window will remain at the top of the stacking order. + */ + void setAlwaysOnTop(bool); + void toggleAlwaysOnTop(); + + /** + * If true, the task's window will remain at the bottom of the stacking order. + */ + void setKeptBelowOthers(bool); + void toggleKeptBelowOthers(); + + /** + * If true, the task's window will enter full screen mode. + */ + void setFullScreen(bool); + void toggleFullScreen(); + + /** + * If true then the task's window will be shaded. Most window managers + * represent this state by displaying on the window's title bar. + */ + void setShaded(bool); + void toggleShaded(); + + /** + * Moves the task's window to the specified virtual desktop. + */ + void toDesktop(int); + + /** + * Moves the task's window to the current virtual desktop. + */ + void toCurrentDesktop(); + + /** + * This method informs the window manager of the location at which this + * task will be displayed when iconised. It is used, for example by the + * KWin inconify animation. + */ + void publishIconGeometry(QRect); + + /** + * Tells the task to generate a new thumbnail. When the thumbnail is + * ready the thumbnailChanged() signal will be emitted. + */ + void updateThumbnail(); + +signals: + /** + * Indicates that this task has changed in some way. + */ + void changed(bool geometryChangeOnly); + + /** + * Indicates that the icon for this task has changed. + */ + void iconChanged(); + + /** + * Indicates that this task is now the active task. + */ + void activated(); + + /** + * Indicates that this task is no longer the active task. + */ + void deactivated(); + + /** + * Indicates that the thumbnail for this task has changed. + */ + void thumbnailChanged(); + +protected slots: + //* @internal + void generateThumbnail(); + +protected: + void findWindowFrameId(); + +private: + bool _active; + WId _win; + WId m_frameId; + QPixmap _pixmap; + KWin::WindowInfo _info; + WindowList _transients; + WindowList _transients_demanding_attention; + + int _lastWidth; + int _lastHeight; + bool _lastResize; + QPixmap _lastIcon; + + double _thumbSize; + QPixmap _thumb; + QPixmap _grab; + QRect m_iconGeometry; +#ifdef THUMBNAILING_POSSIBLE + Pixmap m_windowPixmap; +#endif // THUMBNAILING_POSSIBLE +}; + + +/** + * Provids a drag object for tasks across desktops. + */ +class KDE_EXPORT TaskDrag : public QStoredDrag +{ +public: + /** + * Constructs a task drag object for a task list. + */ + TaskDrag(const Task::List& tasks, QWidget* source = 0, + const char* name = 0); + ~TaskDrag(); + + /** + * Returns true if the mime source can be decoded to a TaskDrag. + */ + static bool canDecode( const QMimeSource* e ); + + /** + * Decodes the tasks from the mime source and returns them if successful. + * Otherwise an empty task list is returned. + */ + static Task::List decode( const QMimeSource* e ); +}; + + +/** + * Represents a task which is in the process of starting. + * + * @see TaskManager + */ +class KDE_EXPORT Startup: public QObject, public KShared +{ + Q_OBJECT + Q_PROPERTY( QString text READ text ) + Q_PROPERTY( QString bin READ bin ) + Q_PROPERTY( QString icon READ icon ) + +public: + typedef KSharedPtr<Startup> Ptr; + typedef QValueVector<Startup::Ptr> List; + + Startup( const KStartupInfoId& id, const KStartupInfoData& data, QObject * parent, + const char *name = 0); + virtual ~Startup(); + + /** + * The name of the starting task (if known). + */ + QString text() const { return _data.findName(); } + + /** + * The name of the executable of the starting task. + */ + QString bin() const { return _data.bin(); } + + /** + * The name of the icon to be used for the starting task. + */ + QString icon() const { return _data.findIcon(); } + void update( const KStartupInfoData& data ); + const KStartupInfoId& id() const { return _id; } + +signals: + /** + * Indicates that this startup has changed in some way. + */ + void changed(); + +private: + KStartupInfoId _id; + KStartupInfoData _data; + class StartupPrivate *d; +}; + + +/** + * A generic API for task managers. This class provides an easy way to + * build NET compliant task managers. It provides support for startup + * notification, virtual desktops and the full range of WM properties. + * + * @see Task + * @see Startup + * @see KWinModule + */ +class KDE_EXPORT TaskManager : public QObject +{ + Q_OBJECT + Q_PROPERTY( int currentDesktop READ currentDesktop ) + Q_PROPERTY( int numberOfDesktops READ numberOfDesktops ) + +public: + static TaskManager* the(); + ~TaskManager(); + + /** + * Returns the task for a given WId, or 0 if there is no such task. + */ + Task::Ptr findTask(WId w); + + /** + * Returns the task for a given location, or 0 if there is no such task. + */ + Task::Ptr findTask(int desktop, const QPoint& p); + + /** + * Returns a list of all current tasks. + */ + Task::Dict tasks() const { return m_tasksByWId; } + + /** + * Returns a list of all current startups. + */ + Startup::List startups() const { return _startups; } + + /** + * Returns the name of the nth desktop. + */ + QString desktopName(int n) const; + + /** + * Returns the number of virtual desktops. + */ + int numberOfDesktops() const; + + /** + * Returns the number of the current desktop. + */ + int currentDesktop() const; + + /** + * Returns true if the specified task is on top. + */ + bool isOnTop(const Task*); + + /** + * Tells the task manager whether or not we care about geometry + * updates. This generates a lot of activity so should only be used + * when necessary. + */ + void trackGeometry() { m_trackGeometry = true; } + void trackGeometry(bool track) { m_trackGeometry = track; } + + /** + * Returns whether the Window with WId wid is on the screen screen + */ + static bool isOnScreen( int screen, const WId wid ); + + KWinModule* winModule() const { return m_winModule; } + + void setXCompositeEnabled(bool state); + static bool xCompositeEnabled() { return m_xCompositeEnabled != 0; } + +signals: + /** + * Emitted when a new task has started. + */ + void taskAdded(Task::Ptr); + + /** + * Emitted when a task has terminated. + */ + void taskRemoved(Task::Ptr); + + /** + * Emitted when a new task is expected. + */ + void startupAdded(Startup::Ptr); + + /** + * Emitted when a startup item should be removed. This could be because + * the task has started, because it is known to have died, or simply + * as a result of a timeout. + */ + void startupRemoved(Startup::Ptr); + + /** + * Emitted when the current desktop changes. + */ + void desktopChanged(int desktop); + + /** + * Emitted when a window changes desktop. + */ + void windowChanged(Task::Ptr); + void windowChangedGeometry(Task::Ptr); + +protected slots: + //* @internal + void windowAdded(WId); + //* @internal + void windowRemoved(WId); + //* @internal + void windowChanged(WId, unsigned int); + + //* @internal + void activeWindowChanged(WId); + //* @internal + void currentDesktopChanged(int); + //* @internal + void killStartup( const KStartupInfoId& ); + //* @internal + void killStartup(Startup::Ptr); + + //* @internal + void gotNewStartup( const KStartupInfoId&, const KStartupInfoData& ); + //* @internal + void gotStartupChange( const KStartupInfoId&, const KStartupInfoData& ); + +protected: + void configure_startup(); + void updateWindowPixmap(WId); + +private: + TaskManager(); + + Task::Ptr _active; + Task::Dict m_tasksByWId; + WindowList _skiptaskbar_windows; + Startup::List _startups; + KStartupInfo* _startup_info; + KWinModule* m_winModule; + bool m_trackGeometry; + + static TaskManager* m_self; + static uint m_xCompositeEnabled; + + class TaskManagerPrivate *d; +}; + +#endif diff --git a/kicker/taskmanager/taskrmbmenu.cpp b/kicker/taskmanager/taskrmbmenu.cpp new file mode 100644 index 000000000..47260687f --- /dev/null +++ b/kicker/taskmanager/taskrmbmenu.cpp @@ -0,0 +1,328 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <elter@kde.org> +Copyright (c) 2001 John Firebaugh <jfirebaugh@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include <assert.h> + +#include <kiconloader.h> +#include <klocale.h> + +#include "taskmanager.h" + +#if defined(HAVE_XCOMPOSITE) && \ + defined(HAVE_XRENDER) && \ + defined(HAVE_XFIXES) +#include <fixx11h.h> +#endif + +#include "taskrmbmenu.h" +#include "taskrmbmenu.moc" + +TaskRMBMenu::TaskRMBMenu(const Task::List& theTasks, bool show, QWidget *parent, const char *name) + : QPopupMenu( parent, name ) + , tasks( theTasks ) + , showAll( show ) +{ + assert(tasks.count() > 0); + if (tasks.count() == 1) + { + fillMenu(tasks.first()); + } + else + { + fillMenu(); + } +} + +TaskRMBMenu::TaskRMBMenu(Task::Ptr task, bool show, QWidget *parent, const char *name) + : QPopupMenu( parent, name ) + , showAll( show ) +{ + fillMenu(task); +} + +void TaskRMBMenu::fillMenu(Task::Ptr t) +{ + int id; + setCheckable(true); + + insertItem(i18n("Ad&vanced"), makeAdvancedMenu(t)); + bool checkActions = KWin::allowedActionsSupported(); + + if (TaskManager::the()->numberOfDesktops() > 1) + { + id = insertItem(i18n("To &Desktop"), makeDesktopsMenu(t)); + + if (showAll) + { + id = insertItem(i18n("&To Current Desktop"), + t, SLOT(toCurrentDesktop())); + setItemEnabled( id, !t->isOnCurrentDesktop() ); + } + + if (checkActions) + { + setItemEnabled(id, t->info().actionSupported(NET::ActionChangeDesktop)); + } + } + + id = insertItem(SmallIconSet("move"), i18n("&Move"), t, SLOT(move())); + setItemEnabled(id, !checkActions || t->info().actionSupported(NET::ActionMove)); + + id = insertItem(i18n("Re&size"), t, SLOT(resize())); + setItemEnabled(id, !checkActions || t->info().actionSupported(NET::ActionResize)); + + id = insertItem(i18n("Mi&nimize"), t, SLOT(toggleIconified())); + setItemChecked(id, t->isIconified()); + setItemEnabled(id, !checkActions || t->info().actionSupported(NET::ActionMinimize)); + + id = insertItem(i18n("Ma&ximize"), t, SLOT(toggleMaximized())); + setItemChecked(id, t->isMaximized()); + setItemEnabled(id, !checkActions || t->info().actionSupported(NET::ActionMax)); + + id = insertItem(i18n("&Shade"), t, SLOT(toggleShaded())); + setItemChecked(id, t->isShaded()); + setItemEnabled(id, !checkActions || t->info().actionSupported(NET::ActionShade)); + + insertSeparator(); + + id = insertItem(SmallIcon("fileclose"), i18n("&Close"), t, SLOT(close())); + setItemEnabled(id, !checkActions || t->info().actionSupported(NET::ActionClose)); +} + +void TaskRMBMenu::fillMenu() +{ + int id; + setCheckable( true ); + + Task::List::iterator itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + Task::Ptr t = (*it); + + id = insertItem( QIconSet( t->pixmap() ), + t->visibleNameWithState(), + new TaskRMBMenu(t, this) ); + setItemChecked( id, t->isActive() ); + connectItem( id, t, SLOT( activateRaiseOrIconify() ) ); + } + + insertSeparator(); + + bool enable = false; + + if (TaskManager::the()->numberOfDesktops() > 1) + { + id = insertItem(i18n("All to &Desktop"), makeDesktopsMenu()); + + id = insertItem(i18n("All &to Current Desktop"), this, SLOT(slotAllToCurrentDesktop())); + Task::List::iterator itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + if (!(*it)->isOnCurrentDesktop()) + { + enable = true; + break; + } + } + setItemEnabled(id, enable); + } + + enable = false; + + id = insertItem( i18n( "Mi&nimize All" ), this, SLOT( slotMinimizeAll() ) ); + itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + if( !(*it)->isIconified() ) { + enable = true; + break; + } + } + setItemEnabled( id, enable ); + + enable = false; + + id = insertItem( i18n( "Ma&ximize All" ), this, SLOT( slotMaximizeAll() ) ); + itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + if( !(*it)->isMaximized() ) { + enable = true; + break; + } + } + setItemEnabled( id, enable ); + + enable = false; + + id = insertItem( i18n( "&Restore All" ), this, SLOT( slotRestoreAll() ) ); + itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + if( (*it)->isIconified() || (*it)->isMaximized() ) { + enable = true; + break; + } + } + setItemEnabled( id, enable ); + + insertSeparator(); + + enable = false; + + insertItem( SmallIcon( "remove" ), i18n( "&Close All" ), this, SLOT( slotCloseAll() ) ); +} + +QPopupMenu* TaskRMBMenu::makeAdvancedMenu(Task::Ptr t) +{ + int id; + QPopupMenu* menu = new QPopupMenu(this); + + menu->setCheckable(true); + + id = menu->insertItem(SmallIconSet("up"), + i18n("Keep &Above Others"), + t, SLOT(toggleAlwaysOnTop())); + menu->setItemChecked(id, t->isAlwaysOnTop()); + + id = menu->insertItem(SmallIconSet("down"), + i18n("Keep &Below Others"), + t, SLOT(toggleKeptBelowOthers())); + menu->setItemChecked(id, t->isKeptBelowOthers()); + + id = menu->insertItem(SmallIconSet("window_fullscreen"), + i18n("&Fullscreen"), + t, SLOT(toggleFullScreen())); + menu->setItemChecked(id, t->isFullScreen()); + + if (KWin::allowedActionsSupported()) + { + menu->setItemEnabled(id, t->info().actionSupported(NET::ActionFullScreen)); + } + + return menu; +} + +QPopupMenu* TaskRMBMenu::makeDesktopsMenu(Task::Ptr t) +{ + QPopupMenu* m = new QPopupMenu( this ); + m->setCheckable( true ); + + int id = m->insertItem( i18n("&All Desktops"), t, SLOT( toDesktop(int) ) ); + m->setItemParameter( id, 0 ); // 0 means all desktops + m->setItemChecked( id, t->isOnAllDesktops() ); + + m->insertSeparator(); + + for (int i = 1; i <= TaskManager::the()->numberOfDesktops(); i++) { + QString name = QString("&%1 %2").arg(i).arg(TaskManager::the()->desktopName(i).replace('&', "&&")); + id = m->insertItem( name, t, SLOT( toDesktop(int) ) ); + m->setItemParameter( id, i ); + m->setItemChecked( id, !t->isOnAllDesktops() && t->desktop() == i ); + } + + return m; +} + +QPopupMenu* TaskRMBMenu::makeDesktopsMenu() +{ + QPopupMenu* m = new QPopupMenu( this ); + m->setCheckable( true ); + + int id = m->insertItem( i18n("&All Desktops"), this, SLOT( slotAllToDesktop(int) ) ); + m->setItemParameter( id, 0 ); // 0 means all desktops + + m->insertSeparator(); + + for (int i = 1; i <= TaskManager::the()->numberOfDesktops(); i++) { + QString name = QString("&%1 %2").arg(i).arg(TaskManager::the()->desktopName(i).replace('&', "&&")); + id = m->insertItem( name, this, SLOT( slotAllToDesktop(int) ) ); + m->setItemParameter( id, i ); + } + + return m; +} + +void TaskRMBMenu::slotMinimizeAll() +{ + Task::List::iterator itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + (*it)->setIconified(true); + } +} + +void TaskRMBMenu::slotMaximizeAll() +{ + Task::List::iterator itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + (*it)->setMaximized(true); + } +} + +void TaskRMBMenu::slotRestoreAll() +{ + Task::List::iterator itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + (*it)->restore(); + } +} + +void TaskRMBMenu::slotShadeAll() +{ + Task::List::iterator itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + (*it)->setShaded( !(*it)->isShaded() ); + } +} + +void TaskRMBMenu::slotCloseAll() +{ + Task::List::iterator itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + (*it)->close(); + } +} + +void TaskRMBMenu::slotAllToDesktop( int desktop ) +{ + Task::List::iterator itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + (*it)->toDesktop( desktop ); + } +} + +void TaskRMBMenu::slotAllToCurrentDesktop() +{ + Task::List::iterator itEnd = tasks.end(); + for (Task::List::iterator it = tasks.begin(); it != itEnd; ++it) + { + (*it)->toCurrentDesktop(); + } +} diff --git a/kicker/taskmanager/taskrmbmenu.h b/kicker/taskmanager/taskrmbmenu.h new file mode 100644 index 000000000..a167d6665 --- /dev/null +++ b/kicker/taskmanager/taskrmbmenu.h @@ -0,0 +1,59 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <elter@kde.org> +Copyright (c) 2001 John Firebaugh <jfirebaugh@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#ifndef __taskrmbmenu_h__ +#define __taskrmbmenu_h__ + +#include <qpopupmenu.h> + +class KDE_EXPORT TaskRMBMenu : public QPopupMenu +{ + Q_OBJECT + +public: + TaskRMBMenu(const Task::List&, bool showAll = true, QWidget *parent = 0, const char *name = 0); + TaskRMBMenu(Task::Ptr, bool showAll = true, QWidget *parent = 0, const char *name = 0); + +private: + void fillMenu(Task::Ptr); + void fillMenu(); + QPopupMenu* makeAdvancedMenu(Task::Ptr); + QPopupMenu* makeDesktopsMenu(Task::Ptr); + QPopupMenu* makeDesktopsMenu(); + +private slots: + void slotMinimizeAll(); + void slotMaximizeAll(); + void slotRestoreAll(); + void slotShadeAll(); + void slotCloseAll(); + void slotAllToDesktop( int desktop ); + void slotAllToCurrentDesktop(); + +private: + Task::List tasks; + bool showAll; +}; + +#endif |