diff --git a/libtaskmanager/groupmanager.cpp b/libtaskmanager/groupmanager.cpp index 002a14def..4dee90be3 100644 --- a/libtaskmanager/groupmanager.cpp +++ b/libtaskmanager/groupmanager.cpp @@ -93,12 +93,14 @@ public: void checkScreenChange(); void taskDestroyed(QObject *item); void startupItemDestroyed(AbstractGroupableItem *); + void startupDestroyed(QObject *); void checkIfFull(); void actuallyCheckIfFull(); bool addTask(::TaskManager::Task *); void removeTask(::TaskManager::Task *); void addStartup(::TaskManager::Startup *); void removeStartup(::TaskManager::Startup *); + void actuallyRemoveStartup(); void sycocaChanged(const QStringList &types); void launcherVisibilityChange(); void checkLauncherVisibility(LauncherItem *launcher); @@ -124,6 +126,7 @@ public: QHash > rootGroups; //container for groups QList launchers; + QList< ::TaskManager::Startup *> startupRemoveList; int currentDesktop; QString currentActivity; @@ -218,32 +221,80 @@ void GroupManagerPrivate::actuallyReloadTasks() emit q->reload(); } -void GroupManagerPrivate::addStartup(::TaskManager::Startup *task) +void GroupManagerPrivate::addStartup(::TaskManager::Startup *startup) { - //kDebug(); - if (!startupList.contains(task)) { - TaskItem *item = new TaskItem(q, task); - startupList.insert(task, item); - currentRootGroup()->add(item); - QObject::connect(item, SIGNAL(destroyed(AbstractGroupableItem*)), - q, SLOT(startupItemDestroyed(AbstractGroupableItem*))); + if (startupList.contains(startup)) { + return; + } + + QHash::iterator it = startupList.begin(); + QHash::iterator itEnd = startupList.end(); + + while (it != itEnd) { + if (it.key()->desktopId() == startup->desktopId() && it.key()->bin() == startup->bin()) { + return; + } + ++it; + } + + foreach(const AbstractGroupableItem *item, currentRootGroup()->members()) { + if (item->itemType() == TaskItemType) + { + const TaskItem *task = static_cast(item); + + if (task->launcherUrl().toLocalFile() == startup->desktopId() + || task->taskName().toLower() == startup->bin()) { + return; + } + } } + + TaskItem *item = new TaskItem(q, startup); + + foreach (LauncherItem * launcher, launchers) { + if (launcher->associateItemIfMatches(item)) { + // Task demands attention, so is to be shown, therefore hide the launcher... + currentRootGroup()->remove(launcher); + } + } + + startupList.insert(startup, item); + currentRootGroup()->add(item); + QObject::connect(startup, SIGNAL(destroyed(QObject*)), q, SLOT(startupDestroyed(QObject*))); + QObject::connect(item, SIGNAL(destroyed(AbstractGroupableItem*)), + q, SLOT(startupItemDestroyed(AbstractGroupableItem*))); } -void GroupManagerPrivate::removeStartup(::TaskManager::Startup *task) +void GroupManagerPrivate::removeStartup(::TaskManager::Startup *startup) { - //kDebug(); - if (!startupList.contains(task)) { - qWarning() << "invalid startup task"; + startupRemoveList.append(startup); + QTimer::singleShot(2000, q, SLOT(actuallyRemoveStartup())); +} + +void GroupManagerPrivate::actuallyRemoveStartup() +{ + if (startupRemoveList.isEmpty()) { return; } - TaskItem *item = startupList.take(task); + ::TaskManager::Startup *startup = startupRemoveList.takeFirst(); + + if (!startupList.contains(startup)) { + return; + } + + TaskItem *item = startupList.take(startup); if (item->parentGroup()) { item->parentGroup()->remove(item); } item->setTaskPointer(0); + + foreach (LauncherItem * launcher, launchers) { + launcher->removeItemIfAssociated(item); + } + + delete startup; } bool GroupManagerPrivate::addTask(::TaskManager::Task *task) @@ -320,15 +371,19 @@ bool GroupManagerPrivate::addTask(::TaskManager::Task *task) TaskItem *startupItem = 0; QHash::iterator it = startupList.begin(); QHash::iterator itEnd = startupList.end(); + const QString desktopId = TaskItem::launcherUrlFromTask(q, task).toLocalFile(); while (it != itEnd) { - if (it.key()->matchesWindow(task->window())) { + if (it.key()->matchesWindow(task->window()) || it.key()->desktopId() == desktopId || it.key()->bin() == task->className()) { //kDebug() << "startup task found"; item = startupItem = it.value(); + ::TaskManager::Startup *startup = it.key(); + startupRemoveList.removeAll(startup); startupList.erase(it); QObject::disconnect(item, 0, q, 0); if (!skip) { item->setTaskPointer(task); } + delete startup; break; } ++it; @@ -409,6 +464,28 @@ void GroupManagerPrivate::taskDestroyed(QObject *item) } } +void GroupManagerPrivate::startupDestroyed(QObject *obj) +{ + ::TaskManager::Startup *startup = static_cast< ::TaskManager::Startup *>(obj); + + if (!startupList.contains(startup)) { + return; + } + + TaskItem *item = startupList.take(startup); + startupRemoveList.removeAll(startup); + + if (item->parentGroup()) { + item->parentGroup()->remove(item); + } + + item->setTaskPointer(0); + + foreach (LauncherItem * launcher, launchers) { + launcher->removeItemIfAssociated(item); + } +} + void GroupManagerPrivate::startupItemDestroyed(AbstractGroupableItem *item) { TaskItem *taskItem = static_cast(item); diff --git a/libtaskmanager/groupmanager.h b/libtaskmanager/groupmanager.h index 604b4f051..4e84deb6b 100644 --- a/libtaskmanager/groupmanager.h +++ b/libtaskmanager/groupmanager.h @@ -228,12 +228,14 @@ private: Q_PRIVATE_SLOT(d, void taskChanged(::TaskManager::Task *, ::TaskManager::TaskChanges)) Q_PRIVATE_SLOT(d, void checkScreenChange()) Q_PRIVATE_SLOT(d, void startupItemDestroyed(AbstractGroupableItem *)) + Q_PRIVATE_SLOT(d, void startupDestroyed(QObject *)) Q_PRIVATE_SLOT(d, void checkIfFull()) Q_PRIVATE_SLOT(d, void actuallyCheckIfFull()) Q_PRIVATE_SLOT(d, bool addTask(::TaskManager::Task *)) Q_PRIVATE_SLOT(d, void removeTask(::TaskManager::Task *)) Q_PRIVATE_SLOT(d, void addStartup(::TaskManager::Startup *)) Q_PRIVATE_SLOT(d, void removeStartup(::TaskManager::Startup *)) + Q_PRIVATE_SLOT(d, void actuallyRemoveStartup()) Q_PRIVATE_SLOT(d, void actuallyReloadTasks()) Q_PRIVATE_SLOT(d, void taskDestroyed(QObject *)) Q_PRIVATE_SLOT(d, void sycocaChanged(const QStringList &)) diff --git a/libtaskmanager/taskitem.cpp b/libtaskmanager/taskitem.cpp index 189244b69..38c06f4dd 100644 --- a/libtaskmanager/taskitem.cpp +++ b/libtaskmanager/taskitem.cpp @@ -109,6 +109,8 @@ void TaskItem::setTaskPointer(Task *task) d->launcherUrl.clear(); } + resetLauncherCheck(); + if (differentTask) { if (d->task) { disconnect(d->task.data(), 0, this, 0); @@ -559,24 +561,35 @@ QUrl TaskItem::launcherUrl() const return d->launcherUrl; } + // Set a flag so that we remeber that we have already checked for a launcher. This is becasue if we fail, then + // we will keep on failing so the isEmpty() check above is not enough. + d->checkedForLauncher = true; + + if (d->task || d->startupTask) { + d->launcherUrl = launcherUrlFromTask(static_cast(parent()), d->task.data(), d->startupTask.data()); + } + + return d->launcherUrl; +} + +QUrl TaskItem::launcherUrlFromTask(GroupManager *groupManager, Task *task, Startup *startup) +{ + QUrl launcherUrl; + // Search for applications which are executable and case-insensitively match the windowclass of the task and // See http://techbase.kde.org/Development/Tutorials/Services/Traders#The_KTrader_Query_Language KService::List services; bool triedPid = false; - // Set a flag so that we remeber that we have already checked for a launcher. This is becasue if we fail, then - // we will keep on failing so the isEmpty() check above is not enough. - d->checkedForLauncher = true; - - if (d->task && !(d->task.data()->classClass().isEmpty() && d->task.data()->className().isEmpty())) { + if (task && !(task->classClass().isEmpty() && task->className().isEmpty())) { // For KCModules, if we matched on window class, etc, we would end up matching to kcmshell4 - but we are more than likely // interested in the actual control module. Therefore we obtain this via the commandline. This commandline may contain // "kdeinit4:" or "[kdeinit]", so we remove these first. - if ("Kcmshell4" == d->task.data()->classClass()) { - d->launcherUrl = getServiceLauncherUrl(d->task.data()->pid(), "KCModule", QStringList() << "kdeinit4:" << "[kdeinit]"); - if (!d->launcherUrl.isEmpty()) { - return d->launcherUrl; + if ("Kcmshell4" == task->classClass()) { + launcherUrl = getServiceLauncherUrl(task->pid(), "KCModule", QStringList() << "kdeinit4:" << "[kdeinit]"); + if (!launcherUrl.isEmpty()) { + return launcherUrl; } } @@ -587,31 +600,31 @@ QUrl TaskItem::launcherUrl() const // Some apps have different launchers depending upon commandline... QStringList matchCommandLineFirst = set.readEntry("MatchCommandLineFirst", QStringList()); - if (!d->task.data()->classClass().isEmpty() && matchCommandLineFirst.contains(d->task.data()->classClass())) { + if (!task->classClass().isEmpty() && matchCommandLineFirst.contains(task->classClass())) { triedPid = true; - services = getServicesViaPid(d->task.data()->pid()); + services = getServicesViaPid(task->pid()); } // Try to match using className also - if (!d->task.data()->className().isEmpty() && matchCommandLineFirst.contains("::"+d->task.data()->className())) { + if (!task->className().isEmpty() && matchCommandLineFirst.contains("::"+task->className())) { triedPid = true; - services = getServicesViaPid(d->task.data()->pid()); + services = getServicesViaPid(task->pid()); } // If the user has manualy set a mapping, respect this first... - QString mapped(grp.readEntry(d->task.data()->classClass() + "::" + d->task.data()->className(), QString())); + QString mapped(grp.readEntry(task->classClass() + "::" + task->className(), QString())); if (mapped.endsWith(".desktop")) { - d->launcherUrl = QUrl(mapped); - return d->launcherUrl; + launcherUrl = mapped; + return launcherUrl; } - if (!d->task.data()->classClass().isEmpty()) { + if (!task->classClass().isEmpty()) { if (mapped.isEmpty()) { - mapped = grp.readEntry(d->task.data()->classClass(), QString()); + mapped = grp.readEntry(task->classClass(), QString()); if (mapped.endsWith(".desktop")) { - d->launcherUrl = QUrl(mapped); - return d->launcherUrl; + launcherUrl = mapped; + return launcherUrl; } } @@ -619,8 +632,8 @@ QUrl TaskItem::launcherUrl() const // So, Settings/ManualOnly lists window classes where the user will always have to manualy set the launcher... QStringList manualOnly = set.readEntry("ManualOnly", QStringList()); - if (!d->task.data()->classClass().isEmpty() && manualOnly.contains(d->task.data()->classClass())) { - return d->launcherUrl; + if (!task->classClass().isEmpty() && manualOnly.contains(task->classClass())) { + return launcherUrl; } if (!mapped.isEmpty()) { @@ -631,44 +644,44 @@ QUrl TaskItem::launcherUrl() const services = KServiceTypeTrader::self()->query("Application", QString("exist Exec and ('%1' =~ Name)").arg(mapped)); } - if (services.empty() && qobject_cast(parent())) { - QUrl savedUrl = static_cast(parent())->launcherForWmClass(d->task.data()->classClass()); + if (services.empty() && groupManager) { + QUrl savedUrl = groupManager->launcherForWmClass(task->classClass()); if (savedUrl.isValid()) { - d->launcherUrl = savedUrl; - return d->launcherUrl; + launcherUrl = savedUrl; + return launcherUrl; } } // To match other docks (docky, unity, etc.) attempt to match on DesktopEntryName first... if (services.empty()) { - services = KServiceTypeTrader::self()->query("Application", QString("exist Exec and ('%1' =~ DesktopEntryName)").arg(d->task.data()->classClass())); + services = KServiceTypeTrader::self()->query("Application", QString("exist Exec and ('%1' =~ DesktopEntryName)").arg(task->classClass())); } // Try StartupWMClass if (services.empty()) { - services = KServiceTypeTrader::self()->query("Application", QString("exist Exec and ('%1' =~ StartupWMClass)").arg(d->task.data()->classClass())); + services = KServiceTypeTrader::self()->query("Application", QString("exist Exec and ('%1' =~ StartupWMClass)").arg(task->classClass())); } // Try 'Name' - unfortunately this can be translated, so has a good chance of failing! (As it does for KDE's own "System Settings" (even in English!!)) if (services.empty()) { - services = KServiceTypeTrader::self()->query("Application", QString("exist Exec and ('%1' =~ Name)").arg(d->task.data()->classClass())); + services = KServiceTypeTrader::self()->query("Application", QString("exist Exec and ('%1' =~ Name)").arg(task->classClass())); } } // Ok, absolute *last* chance, try matching via pid (but only if we have not already tried this!)... if (services.empty() && !triedPid) { - services = getServicesViaPid(d->task.data()->pid()); + services = getServicesViaPid(task->pid()); } } - if (services.empty() && isStartupItem()) { + if (services.empty() && startup) { // Try to match via desktop filename... - if (!startup()->desktopId().isNull() && startup()->desktopId().endsWith(".desktop")) { - if (startup()->desktopId().startsWith("/")) { - d->launcherUrl = QUrl::fromLocalFile(startup()->desktopId()); - return d->launcherUrl; + if (!startup->desktopId().isNull() && startup->desktopId().endsWith(".desktop")) { + if (startup->desktopId().startsWith("/")) { + launcherUrl = QUrl::fromLocalFile(startup->desktopId()); + return launcherUrl; } else { - QString desktopName = startup()->desktopId(); + QString desktopName = startup->desktopId(); if (desktopName.endsWith(".desktop")) { desktopName = desktopName.mid(desktopName.length() - 8); @@ -679,13 +692,13 @@ QUrl TaskItem::launcherUrl() const } // Try StartupWMClass - if (services.empty() && !startup()->wmClass().isNull()) { - services = KServiceTypeTrader::self()->query("Application", QString("exist Exec and ('%1' =~ StartupWMClass)").arg(startup()->wmClass())); + if (services.empty() && !startup->wmClass().isNull()) { + services = KServiceTypeTrader::self()->query("Application", QString("exist Exec and ('%1' =~ StartupWMClass)").arg(startup->wmClass())); } // Try via name... - if (services.empty() && !startup()->text().isNull()) { - services = KServiceTypeTrader::self()->query("Application", QString("exist Exec and ('%1' =~ Name)").arg(startup()->text())); + if (services.empty() && !startup->text().isNull()) { + services = KServiceTypeTrader::self()->query("Application", QString("exist Exec and ('%1' =~ Name)").arg(startup->text())); } } @@ -696,11 +709,11 @@ QUrl TaskItem::launcherUrl() const } if (!path.isEmpty()) { - d->launcherUrl = QUrl::fromLocalFile(path); + launcherUrl = QUrl::fromLocalFile(path); } } - return d->launcherUrl; + return launcherUrl; } void TaskItem::resetLauncherCheck() diff --git a/libtaskmanager/taskitem.h b/libtaskmanager/taskitem.h index 2c6acb8a6..5209b6aa4 100644 --- a/libtaskmanager/taskitem.h +++ b/libtaskmanager/taskitem.h @@ -34,6 +34,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace TaskManager { +class GroupManager; /** * Wrapper class so we do not have to use the Task class directly and the Task* remains guarded @@ -85,6 +86,7 @@ public: void setLauncherUrl(const QUrl &url); void setLauncherUrl(const AbstractGroupableItem *item); QUrl launcherUrl() const; + static QUrl launcherUrlFromTask(GroupManager *groupManager, Task *task, Startup *startup = 0); void resetLauncherCheck(); public Q_SLOTS: diff --git a/libtaskmanager/taskmanager.cpp b/libtaskmanager/taskmanager.cpp index 6b3cd120c..54ce4466c 100644 --- a/libtaskmanager/taskmanager.cpp +++ b/libtaskmanager/taskmanager.cpp @@ -422,6 +422,7 @@ void TaskManager::currentDesktopChanged(int desktop) void TaskManager::gotNewStartup(const KStartupInfoId& id, const KStartupInfoData& data) { Startup *s = new Startup(id, data, 0); + connect(s, SIGNAL(destroyed(QObject*)), this, SLOT(startupDestroyed(QObject*))); d->startups.append(s); emit startupAdded(s); } @@ -442,11 +443,15 @@ void TaskManager::killStartup(const KStartupInfoId& id) if (startup->id() == id) { d->startups.removeAll(startup); emit startupRemoved(startup); - delete startup; } } } +void TaskManager::startupDestroyed(QObject* obj) +{ + d->startups.removeAll(static_cast(obj)); +} + QString TaskManager::desktopName(int desk) const { return KWindowSystem::desktopName(desk); diff --git a/libtaskmanager/taskmanager.h b/libtaskmanager/taskmanager.h index 704e675b3..b795e6afb 100644 --- a/libtaskmanager/taskmanager.h +++ b/libtaskmanager/taskmanager.h @@ -206,6 +206,8 @@ protected Q_SLOTS: void gotNewStartup(const KStartupInfoId&, const KStartupInfoData&); //* @internal void gotStartupChange(const KStartupInfoId&, const KStartupInfoData&); + //* @internal + void startupDestroyed(QObject*); //* @internal void taskChanged(::TaskManager::TaskChanges changes);