/*************************************************************************** * Copyright (C) 2007 by Robert Knight * * Copyright (C) 2008 by Alexis Ménard * * Copyright (C) 2012-2013 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "backend.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KAStats = KActivities::Experimental::Stats; using namespace KAStats; using namespace KAStats::Terms; Backend::Backend(QObject* parent) : QObject(parent), m_groupManager(new TaskManager::GroupManager(this)), m_tasksModel(new TaskManager::TasksModel(m_groupManager, this)), m_contextMenu(0), m_taskManagerItem(0), m_toolTipItem(0), m_panelWinId(0), m_highlightWindows(false) { connect(m_groupManager, SIGNAL(launcherListChanged()), this, SLOT(updateLaunchersCache())); } Backend::~Backend() { delete m_contextMenu; } TaskManager::GroupManager* Backend::groupManager() const { return m_groupManager; } TaskManager::TasksModel* Backend::tasksModel() const { return m_tasksModel; } QQuickItem* Backend::taskManagerItem() const { return m_taskManagerItem; } void Backend::setTaskManagerItem(QQuickItem* item) { if (item != m_taskManagerItem) { m_taskManagerItem = item; emit taskManagerItemChanged(item); } } QQuickItem* Backend::toolTipItem() const { return m_toolTipItem; } void Backend::setToolTipItem(QQuickItem* item) { if (item != m_toolTipItem) { m_toolTipItem = item; connect(item, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(toolTipWindowChanged(QQuickWindow*))); emit toolTipItemChanged(item); } } void Backend::toolTipWindowChanged(QQuickWindow *window) { Q_UNUSED(window) updateWindowHighlight(); } bool Backend::anyTaskNeedsAttention() const { return groupManager()->rootGroup()->demandsAttention(); } bool Backend::highlightWindows() const { return m_highlightWindows; } void Backend::setHighlightWindows(bool highlight) { if (highlight != m_highlightWindows) { m_highlightWindows = highlight; updateWindowHighlight(); emit highlightWindowsChanged(highlight); } } int Backend::groupingStrategy() const { return m_groupManager->groupingStrategy(); } void Backend::setGroupingStrategy(int groupingStrategy) { // FIXME TODO: This is fucking terrible. m_groupManager->setGroupingStrategy(static_cast(groupingStrategy)); } int Backend::sortingStrategy() const { return m_groupManager->sortingStrategy(); } void Backend::setSortingStrategy(int sortingStrategy) { // FIXME TODO: This is fucking terrible. m_groupManager->setSortingStrategy(static_cast(sortingStrategy)); } void Backend::updateLaunchersCache() { const QList launcherList = m_groupManager->launcherList(); QStringList launchers; foreach(const QUrl& launcher, launcherList) { launchers.append(launcher.toString()); } QString joined(launchers.join(',')); if (joined != m_launchers) { m_launchers = joined; emit launchersChanged(); } } QString Backend::launchers() const { return m_launchers; } void Backend::setLaunchers(const QString& launchers) { if (launchers == m_launchers) { return; } m_launchers = launchers; QList launcherList; foreach(const QString& launcher, launchers.split(',')) { launcherList.append(launcher); } disconnect(m_groupManager, SIGNAL(launcherListChanged()), this, SLOT(updateLaunchersCache())); m_groupManager->setLauncherList(launcherList); connect(m_groupManager, SIGNAL(launcherListChanged()), this, SLOT(updateLaunchersCache())); emit launchersChanged(); } void Backend::activateItem(int id, bool toggle) { TaskManager::AbstractGroupableItem* item = m_groupManager->rootGroup()->getMemberById(id); if (!item) { return; } if (item->itemType() == TaskManager::TaskItemType && !item->isStartupItem()) { TaskManager::TaskItem* taskItem = static_cast(item); if (toggle) { taskItem->task()->activateRaiseOrIconify(); } else { taskItem->task()->activate(); } } else if (item->itemType() == TaskManager::LauncherItemType) { static_cast(item)->launch(); } } void Backend::activateWindow(int winId) { KWindowSystem::forceActiveWindow(winId); } void Backend::closeByWinId(int winId) { TaskManager::AbstractGroupableItem *item = m_groupManager->rootGroup()->getMemberByWId(winId); if (item) { item->close(); } } void Backend::closeByItemId(int itemId) { TaskManager::AbstractGroupableItem *item = m_groupManager->rootGroup()->getMemberById(itemId); if (item) { item->close(); } } void Backend::launchNewInstance(int id) { TaskManager::AbstractGroupableItem* item = m_groupManager->rootGroup()->getMemberById(id); if (item) { item->launchNewInstance(); } } void Backend::itemContextMenu(QQuickItem *item, QObject *configAction) { TaskManager::AbstractGroupableItem* agItem = m_groupManager->rootGroup()->getMemberById(item->property("itemId").toInt()); if (!KAuthorized::authorizeKAction("kwin_rmb") || !item || !item->window() || !agItem) { return; } QList actionList; QAction *action = static_cast(configAction); if (action && action->isEnabled()) { actionList << action; } if (agItem->itemType() == TaskManager::TaskItemType && !agItem->isStartupItem()) { TaskManager::TaskItem* taskItem = static_cast(agItem); m_contextMenu = new TaskManager::BasicMenu(0, taskItem, m_groupManager, actionList); } else if (agItem->itemType() == TaskManager::GroupItemType) { TaskManager::TaskGroup* taskGroup = static_cast(agItem); const int maxWidth = 0.8 * item->window()->screen()->size().width(); m_contextMenu = new TaskManager::BasicMenu(0, taskGroup, m_groupManager, actionList, QList (), maxWidth); } else if (agItem->itemType() == TaskManager::LauncherItemType) { TaskManager::LauncherItem *launcher = static_cast(agItem); m_contextMenu = new TaskManager::BasicMenu(0, launcher, m_groupManager, actionList); addRecentDocumentActions(launcher, m_contextMenu); } if (!m_contextMenu) { return; } m_contextMenu->adjustSize(); if (m_taskManagerItem && !m_taskManagerItem->property("vertical").toBool()) { m_contextMenu->setMinimumWidth(item->width()); } // Close menu when the delegate is destroyed. connect(item, &QQuickItem::destroyed, m_contextMenu.data(), &TaskManager::BasicMenu::deleteLater); // Interrupt the call chain from the delegate in case it's destroyed while // we're in QMenu::exec(); // FIXME TODO: Just use a lambda on Qt 5.4+. QMetaObject::invokeMethod(this, "actuallyOpenContextMenu", Qt::QueuedConnection); } void Backend::addRecentDocumentActions(TaskManager::LauncherItem *launcher, TaskManager::BasicMenu* menu) const { if (!launcher || !menu || !launcher->launcherUrl().isLocalFile()) { return; } QAction *firstAction = menu->actions().at(0); if (!firstAction) { return; } QString desktopName = launcher->launcherUrl().fileName(); QString storageId = desktopName; if (storageId.startsWith("org.kde.")) { storageId = storageId.right(storageId.length() - 8); } if (storageId.endsWith(".desktop")) { storageId = storageId.left(storageId.length() - 8); } auto query = UsedResources | RecentlyUsedFirst | Agent(storageId) | Type::any() | Activity::current() | Url::file(); ResultSet results(query); ResultSet::const_iterator resultIt; resultIt = results.begin(); int actions = 0; while (actions < 5 && resultIt != results.end()) { const QString resource = (*resultIt).resource(); const QUrl url(resource); if (!url.isValid()) { continue; } const KFileItem fileItem(url); if (!fileItem.isFile()) { continue; } if (actions == 0) { menu->insertSection(firstAction, i18n("Recent Documents for %1", launcher->name())); } QAction *action = new QAction(menu); action->setText(url.fileName()); action->setIcon(QIcon::fromTheme(fileItem.iconName(), QIcon::fromTheme("unknown"))); action->setProperty("agent", storageId); action->setProperty("entryPath", launcher->launcherUrl()); action->setData(resource); connect(action, &QAction::triggered, this, &Backend::handleRecentDocumentAction); menu->insertAction(firstAction, action); ++resultIt; ++actions; } if (actions > 0) { QAction *action = new QAction(menu); action->setText(i18n("Forget Recent Documents")); action->setProperty("agent", storageId); connect(action, &QAction::triggered, this, &Backend::handleRecentDocumentAction); menu->insertAction(firstAction, action); menu->insertSeparator(firstAction); } } void Backend::handleRecentDocumentAction() const { const QAction *action = qobject_cast(sender()); if (!action) { return; } const QString agent = action->property("agent").toString(); if (agent.isEmpty()) { return; } const QString desktopPath = action->property("entryPath").toUrl().toLocalFile(); const QString resource = action->data().toString(); if (desktopPath.isEmpty() || resource.isEmpty()) { auto query = UsedResources | Agent(agent) | Type::any() | Activity::current() | Url::file(); KAStats::forgetResources(query); return; } KService::Ptr service = KService::serviceByDesktopPath(desktopPath); qDebug() << service; if (!service) { return; } KRun::runService(*service, QList() << QUrl(resource), QApplication::activeWindow()); } void Backend::actuallyOpenContextMenu() { if (!m_contextMenu) { return; } m_contextMenu->exec(QCursor::pos()); m_contextMenu->deleteLater(); } void Backend::itemHovered(int id, bool hovered) { m_windowsToHighlight.clear(); TaskManager::AbstractGroupableItem* item = m_groupManager->rootGroup()->getMemberById(id); if (item && hovered) { m_windowsToHighlight = QList::fromSet(item->winIds()); } updateWindowHighlight(); } void Backend::windowHovered(int winId, bool hovered) { m_windowsToHighlight.clear(); if (hovered) { m_windowsToHighlight.append(winId); } updateWindowHighlight(); } void Backend::updateWindowHighlight() { if (!m_highlightWindows) { if (m_panelWinId) { KWindowEffects::highlightWindows(m_panelWinId, QList()); m_panelWinId = 0; } return; } if (m_taskManagerItem && m_taskManagerItem->window()) { m_panelWinId = m_taskManagerItem->window()->winId(); } else { return; } QList windows = m_windowsToHighlight; if (windows.count() && m_toolTipItem && m_toolTipItem->window()) { windows.append(m_toolTipItem->window()->winId()); } KWindowEffects::highlightWindows(m_panelWinId, windows); } void Backend::itemMove(int id, int newIndex) { m_groupManager->manualSortingRequest(m_groupManager->rootGroup()->getMemberById(id), newIndex); } void Backend::itemGeometryChanged(QQuickItem *item, int id) { TaskManager:: AbstractGroupableItem *agItem = m_groupManager->rootGroup()->getMemberById(id); if (!item || !item->window() || !agItem || agItem->itemType() != TaskManager::TaskItemType) { return; } TaskManager::TaskItem *taskItem = static_cast(agItem); if (!taskItem->task()) { return; } QRect iconRect(item->x(), item->y(), item->width(), item->height()); iconRect.moveTopLeft(item->parentItem()->mapToScene(iconRect.topLeft()).toPoint()); iconRect.moveTopLeft(item->window()->mapToGlobal(iconRect.topLeft())); taskItem->task()->publishIconGeometry(iconRect); } bool Backend::canPresentWindows() const { return (KWindowSystem::compositingActive() && KWindowEffects::isEffectAvailable(KWindowEffects::PresentWindowsGroup)); } void Backend::presentWindows(int groupParentId) { TaskManager::AbstractGroupableItem *item = m_groupManager->rootGroup()->getMemberById(groupParentId); if (item && m_taskManagerItem && m_taskManagerItem->window()) { if (m_windowsToHighlight.count()) { m_windowsToHighlight.clear(); updateWindowHighlight(); } KWindowEffects::presentWindows(m_taskManagerItem->window()->winId(), QList::fromSet(item->winIds())); } } void Backend::urlDropped(const QUrl& url) { if (!url.isValid() || !url.isLocalFile()) { return; } KDesktopFile desktopFile(url.toLocalFile()); if (desktopFile.hasApplicationType()) { m_groupManager->addLauncher(url); } }