diff --git a/abstractmodel.cpp b/abstractmodel.cpp index 334731604..18eed0357 100644 --- a/abstractmodel.cpp +++ b/abstractmodel.cpp @@ -1,130 +1,135 @@ /*************************************************************************** * Copyright (C) 2014-2015 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 "abstractmodel.h" #include "actionlist.h" AbstractModel::AbstractModel(QObject *parent) : QAbstractListModel(parent) , m_iconSize(32) { } AbstractModel::~AbstractModel() { } QHash AbstractModel::roleNames() const { QHash roles; roles.insert(Qt::DisplayRole, "display"); roles.insert(Qt::DecorationRole, "decoration"); roles.insert(Kicker::FavoriteIdRole, "favoriteId"); roles.insert(Kicker::IsParentRole, "isParent"); roles.insert(Kicker::IsSeparatorRole, "isSeparator"); roles.insert(Kicker::HasChildrenRole, "hasChildren"); roles.insert(Kicker::HasActionListRole, "hasActionList"); roles.insert(Kicker::ActionListRole, "actionList"); roles.insert(Kicker::UrlRole, "url"); return roles; } int AbstractModel::count() const { return rowCount(); } int AbstractModel::separatorCount() const { return 0; } int AbstractModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return 1; } int AbstractModel::iconSize() const { return m_iconSize; } void AbstractModel::setIconSize(int iconSize) { if (m_iconSize != iconSize) { m_iconSize = iconSize; refresh(); } } void AbstractModel::refresh() { } +QString AbstractModel::labelForRow(int row) +{ + return data(index(row, 0), Qt::DisplayRole).toString(); +} + AbstractModel *AbstractModel::modelForRow(int row) { Q_UNUSED(row) return 0; } bool AbstractModel::hasActions() const { return false; } QVariantList AbstractModel::actions() const { return QVariantList(); } AbstractModel* AbstractModel::favoritesModel() { return rootModel()->favoritesModel(); } AbstractModel* AbstractModel::rootModel() { if (!parent()) { return nullptr; } QObject *p = this; AbstractModel *rootModel = nullptr; while (p) { if (qobject_cast(p)) { rootModel = qobject_cast(p); } else { return rootModel; } p = p->parent(); } return rootModel; } void AbstractModel::entryChanged(AbstractEntry *entry) { Q_UNUSED(entry) } diff --git a/abstractmodel.h b/abstractmodel.h index 66dce9ebe..142de27db 100644 --- a/abstractmodel.h +++ b/abstractmodel.h @@ -1,73 +1,80 @@ /*************************************************************************** * Copyright (C) 2014-2015 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 . * ***************************************************************************/ #ifndef ABSTRACTMODEL_H #define ABSTRACTMODEL_H #include class AbstractEntry; class AbstractModel : public QAbstractListModel { Q_OBJECT + Q_PROPERTY(QString description READ description NOTIFY descriptionChanged) + Q_PROPERTY(int count READ count NOTIFY countChanged) Q_PROPERTY(int separatorCount READ separatorCount NOTIFY separatorCountChanged) Q_PROPERTY(int iconSize READ iconSize WRITE setIconSize NOTIFY iconSizeChanged) Q_PROPERTY(QObject* favoritesModel READ favoritesModel CONSTANT) public: explicit AbstractModel(QObject *parent = 0); ~AbstractModel(); virtual QHash roleNames() const; + virtual QString description() const = 0; + int count() const; virtual int separatorCount() const; int columnCount(const QModelIndex &parent = QModelIndex()) const; int iconSize() const; void setIconSize(int size); Q_INVOKABLE virtual bool trigger(int row, const QString &actionId, const QVariant &argument) = 0; Q_INVOKABLE virtual void refresh(); + Q_INVOKABLE virtual QString labelForRow(int row); + Q_INVOKABLE virtual AbstractModel *modelForRow(int row); virtual bool hasActions() const; virtual QVariantList actions() const; virtual AbstractModel* favoritesModel(); AbstractModel* rootModel(); virtual void entryChanged(AbstractEntry *entry); Q_SIGNALS: + void descriptionChanged() const; void countChanged() const; void separatorCountChanged() const; void iconSizeChanged() const; private: int m_iconSize; }; #endif diff --git a/appentry.cpp b/appentry.cpp index 7448a7a4c..45644d4f8 100644 --- a/appentry.cpp +++ b/appentry.cpp @@ -1,311 +1,311 @@ /*************************************************************************** * Copyright (C) 201 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 "appentry.h" #include "actionlist.h" #include "appsmodel.h" #include "containmentinterface.h" #include "menuentryeditor.h" #ifdef PackageKitQt5_FOUND #include "findpackagenamejob.h" #endif #include #include #include #include #if HAVE_X11 #include #endif #include #include #include #include #include #include #include #include #include #include QObject *AppEntry::m_appletInterface = nullptr; QQmlPropertyMap *AppEntry::m_appletConfig = nullptr; MenuEntryEditor *AppEntry::m_menuEntryEditor = nullptr; AppEntry::AppEntry(AbstractModel *owner, KService::Ptr service, NameFormat nameFormat) : AbstractEntry(owner) , m_service(service) { if (m_service) { init(nameFormat); } } AppEntry::AppEntry(AbstractModel *owner, const QString &id) : AbstractEntry(owner) { const QUrl url(id); if (url.scheme() == QStringLiteral("preferred")) { m_service = defaultAppByName(url.host()); m_id = id; } else { m_service = KService::serviceByStorageId(id); } if (m_service) { init((NameFormat)qobject_cast(owner->rootModel())->appNameFormat()); } } void AppEntry::init(NameFormat nameFormat) { m_name = nameFromService(m_service, nameFormat); m_icon = QIcon::fromTheme(m_service->icon(), QIcon::fromTheme("unknown")); if (!m_appletInterface) { AbstractModel *rootModel = m_owner->rootModel(); m_appletInterface = rootModel->property("appletInterface").value(); m_appletConfig = qobject_cast(m_appletInterface->property("configuration").value()); } if (!m_menuEntryEditor) { m_menuEntryEditor = new MenuEntryEditor(); } } bool AppEntry::isValid() const { return m_service; } QIcon AppEntry::icon() const { return m_icon; } QString AppEntry::name() const { return m_name; } KService::Ptr AppEntry::service() const { return m_service; } QString AppEntry::id() const { if (!m_id.isEmpty()) { return m_id; } return m_service->storageId(); } QUrl AppEntry::url() const { return QUrl::fromLocalFile(m_service->entryPath()); } bool AppEntry::hasActions() const { return true; } QVariantList AppEntry::actions() const { QVariantList actionList; actionList << Kicker::recentDocumentActions(m_service); if (actionList.count()) { actionList << Kicker::createSeparatorActionItem(); } if (ContainmentInterface::mayAddLauncher(m_appletInterface, ContainmentInterface::Desktop)) { actionList << Kicker::createActionItem(i18n("Add to Desktop"), "addToDesktop"); } if (ContainmentInterface::mayAddLauncher(m_appletInterface, ContainmentInterface::Panel)) { actionList << Kicker::createActionItem(i18n("Add to Panel"), "addToPanel"); } if (ContainmentInterface::mayAddLauncher(m_appletInterface, ContainmentInterface::TaskManager, m_service->entryPath())) { actionList << Kicker::createActionItem(i18n("Add as Launcher"), "addToTaskManager"); } if (m_menuEntryEditor->canEdit(m_service->entryPath())) { actionList << Kicker::createSeparatorActionItem(); QVariantMap editAction = Kicker::createActionItem(i18n("Edit Application..."), "editApplication"); editAction["icon"] = "kmenuedit"; // TODO: Using the KMenuEdit icon might be misleading. actionList << editAction; } #ifdef PackageKitQt5_FOUND QStringList files(m_service->entryPath()); if (m_service->isApplication()) { files += QStandardPaths::findExecutable(KShell::splitArgs(m_service->exec()).first()); } FindPackageJob* job = new FindPackageJob(files); // TODO: Would be great to make this async. if (job->exec() && !job->packageNames().isEmpty()) { QString packageName = job->packageNames().first(); QVariantMap removeAction = Kicker::createActionItem(i18n("Remove '%1'...", packageName), "removeApplication", packageName); removeAction["icon"] = "applications-other"; actionList << removeAction; } #endif if (m_appletConfig && m_appletConfig->contains("hiddenApplications") && qobject_cast(m_owner)) { const QStringList &hiddenApps = m_appletConfig->value("hiddenApplications").toStringList(); if (!hiddenApps.contains(m_service->menuId())) { actionList << Kicker::createActionItem(i18n("Hide Application"), "hideApplication"); } } return actionList; } bool AppEntry::run(const QString& actionId, const QVariant &argument) { if (actionId.isEmpty()) { quint32 timeStamp = 0; #if HAVE_X11 if (QX11Info::isPlatformX11()) { timeStamp = QX11Info::appUserTime(); } #endif new KRun(QUrl::fromLocalFile(m_service->entryPath()), 0, true, KStartupInfo::createNewStartupIdForTimestamp(timeStamp)); KActivities::ResourceInstance::notifyAccessed(QUrl("applications:" + m_service->storageId()), "org.kde.plasma.kicker"); return true; } else if (actionId == "addToDesktop" && ContainmentInterface::mayAddLauncher(m_appletInterface, ContainmentInterface::Desktop)) { ContainmentInterface::addLauncher(m_appletInterface, ContainmentInterface::Desktop, m_service->entryPath()); } else if (actionId == "addToPanel" && ContainmentInterface::mayAddLauncher(m_appletInterface, ContainmentInterface::Panel)) { ContainmentInterface::addLauncher(m_appletInterface, ContainmentInterface::Panel, m_service->entryPath()); } else if (actionId == "addToTaskManager" && ContainmentInterface::mayAddLauncher(m_appletInterface, ContainmentInterface::TaskManager, m_service->entryPath())) { ContainmentInterface::addLauncher(m_appletInterface, ContainmentInterface::TaskManager, m_service->entryPath()); } else if (actionId == "editApplication" && m_menuEntryEditor->canEdit(m_service->entryPath())) { QMetaObject::invokeMethod(m_menuEntryEditor, "edit", Qt::QueuedConnection, Q_ARG(QString, m_service->entryPath()), Q_ARG(QString, m_service->menuId())); return true; } else if (actionId == "removeApplication") { if (m_appletConfig && m_appletConfig->contains("removeApplicationCommand")) { const QStringList &removeAppCmd = KShell::splitArgs(m_appletConfig->value("removeApplicationCommand").toString()); if (!removeAppCmd.isEmpty()) { return QProcess::startDetached(removeAppCmd.first(), removeAppCmd.mid(1) << argument.toString()); } } } return Kicker::handleRecentDocumentAction(m_service, actionId, argument); } QString AppEntry::nameFromService(const KService::Ptr service, NameFormat nameFormat) { const QString &name = service->name(); QString genericName = service->genericName(); if (genericName.isEmpty()) { genericName = service->comment(); } if (nameFormat == NameOnly || genericName.isEmpty() || name == genericName) { return name; } else if (nameFormat == GenericNameOnly) { return genericName; } else if (nameFormat == NameAndGenericName) { return i18nc("App name (Generic name)", "%1 (%2)", name, genericName); } else { return i18nc("Generic name (App name)", "%1 (%2)", genericName, name); } } KService::Ptr AppEntry::defaultAppByName(const QString& name) { if (name == QLatin1String("browser")) { KConfigGroup config(KSharedConfig::openConfig(), "General"); QString browser = config.readPathEntry("BrowserApplication", QString()); if (browser.isEmpty()) { return KMimeTypeTrader::self()->preferredService(QLatin1String("text/html")); } else if (browser.startsWith('!')) { browser = browser.mid(1); } return KService::serviceByStorageId(browser); } return KService::Ptr(); } AppGroupEntry::AppGroupEntry(AppsModel *parentModel, KServiceGroup::Ptr group, - bool flat, int appNameFormat) : AbstractGroupEntry(parentModel) + bool flat, bool separators, int appNameFormat) : AbstractGroupEntry(parentModel) { m_name = group->caption(); m_icon = QIcon::fromTheme(group->icon(), QIcon::fromTheme("unknown")); - AppsModel* model = new AppsModel(group->entryPath(), flat, parentModel); + AppsModel* model = new AppsModel(group->entryPath(), flat, separators, parentModel); model->setAppNameFormat(appNameFormat); m_childModel = model; - QObject::connect(parentModel, &AppsModel::refreshing, model, &AppsModel::deleteLater); + QObject::connect(parentModel, &AppsModel::cleared, model, &AppsModel::deleteLater); QObject::connect(model, &AppsModel::countChanged, [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } } ); QObject::connect(model, &AppsModel::hiddenEntriesChanged, [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } } ); } QIcon AppGroupEntry::icon() const { return m_icon; } QString AppGroupEntry::name() const { return m_name; } bool AppGroupEntry::hasChildren() const { return m_childModel && m_childModel->count() > 0; } AbstractModel *AppGroupEntry::childModel() const { return m_childModel; } diff --git a/appentry.h b/appentry.h index 9695e18ef..3d0b1bdbf 100644 --- a/appentry.h +++ b/appentry.h @@ -1,95 +1,95 @@ /*************************************************************************** * Copyright (C) 201 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 . * ***************************************************************************/ #ifndef APPENTRY_H #define APPENTRY_H #include "abstractentry.h" #include #include class AppsModel; class MenuEntryEditor; class QQmlPropertyMap; class AppEntry : public AbstractEntry { public: enum NameFormat { NameOnly = 0, GenericNameOnly, NameAndGenericName, GenericNameAndName }; explicit AppEntry(AbstractModel *owner, KService::Ptr service, NameFormat nameFormat); explicit AppEntry(AbstractModel *owner, const QString &id); EntryType type() const { return RunnableType; } bool isValid() const; QIcon icon() const; QString name() const; KService::Ptr service() const; QString id() const; QUrl url() const; bool hasActions() const; QVariantList actions() const; bool run(const QString& actionId = QString(), const QVariant &argument = QVariant()); static QString nameFromService(const KService::Ptr service, NameFormat nameFormat); static KService::Ptr defaultAppByName(const QString &name); private: void init(NameFormat nameFormat); QString m_id; QString m_name; QIcon m_icon; KService::Ptr m_service; static QObject *m_appletInterface; static QQmlPropertyMap *m_appletConfig; static MenuEntryEditor *m_menuEntryEditor; }; class AppGroupEntry : public AbstractGroupEntry { public: AppGroupEntry(AppsModel *parentModel, KServiceGroup::Ptr group, - bool flat, int appNameFormat); + bool flat, bool separators, int appNameFormat); QIcon icon() const; QString name() const; bool hasChildren() const; AbstractModel *childModel() const; private: QString m_name; QIcon m_icon; QPointer m_childModel; }; #endif diff --git a/appsmodel.cpp b/appsmodel.cpp index 7508a2b95..100ac5aa1 100644 --- a/appsmodel.cpp +++ b/appsmodel.cpp @@ -1,417 +1,510 @@ /*************************************************************************** * Copyright (C) 2012 Aurélien Gâteau * * Copyright (C) 2013-2015 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 "appsmodel.h" #include "actionlist.h" #include #include #include #include #include -AppsModel::AppsModel(const QString &entryPath, bool flat, QObject *parent) +AppsModel::AppsModel(const QString &entryPath, bool flat, bool separators, QObject *parent) : AbstractModel(parent) +, m_deleteEntriesOnDestruction(true) , m_separatorCount(0) +, m_showSeparators(separators) +, m_description(i18n("Applications")) , m_entryPath(entryPath) +, m_staticEntryList(false) , m_changeTimer(0) , m_flat(flat) , m_sortNeeded(false) , m_appNameFormat(AppEntry::NameOnly) { if (!m_entryPath.isEmpty()) { refresh(); } } +AppsModel::AppsModel(const QList entryList, bool deleteEntriesOnDestruction, QObject *parent) +: AbstractModel(parent) +, m_deleteEntriesOnDestruction(deleteEntriesOnDestruction) +, m_separatorCount(0) +, m_showSeparators(false) +, m_description(i18n("Applications")) +, m_entryPath(QString()) +, m_staticEntryList(true) +, m_changeTimer(0) +, m_flat(true) +, m_sortNeeded(false) +, m_appNameFormat(AppEntry::NameOnly) +{ + foreach(AbstractEntry *suggestedEntry, entryList) { + bool found = false; + + foreach (const AbstractEntry *entry, m_entryList) { + if (entry->type() == AbstractEntry::RunnableType + && static_cast(entry)->service()->storageId() + == static_cast(suggestedEntry)->service()->storageId()) { + found = true; + } + } + + if (!found) { + m_entryList << suggestedEntry; + } + } + + sortEntries(); +} + AppsModel::~AppsModel() { - qDeleteAll(m_entryList); + if (m_deleteEntriesOnDestruction) { + qDeleteAll(m_entryList); + } +} + +QString AppsModel::description() const +{ + return m_description; +} + +void AppsModel::setDescription(const QString &text) +{ + if (m_description != text) { + m_description = text; + + emit descriptionChanged(); + } } QVariant AppsModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= m_entryList.count()) { return QVariant(); } const AbstractEntry *entry = m_entryList.at(index.row()); if (role == Qt::DisplayRole) { return entry->name(); } else if (role == Qt::DecorationRole) { return entry->icon(); } else if (role == Kicker::FavoriteIdRole && entry->type() == AbstractEntry::RunnableType) { return entry->id(); } else if (role == Kicker::IsParentRole) { return (entry->type() == AbstractEntry::GroupType); } else if (role == Kicker::IsSeparatorRole) { return (entry->type() == AbstractEntry::SeparatorType); } else if (role == Kicker::HasChildrenRole) { return entry->hasChildren(); } else if (role == Kicker::HasActionListRole) { const AppsModel *appsModel = qobject_cast(entry->childModel()); return entry->hasActions() || (appsModel && !appsModel->hiddenEntries().isEmpty()); } else if (role == Kicker::ActionListRole) { QVariantList actionList = entry->actions(); if (!m_hiddenEntries.isEmpty()) { actionList << Kicker::createSeparatorActionItem(); actionList << Kicker::createActionItem(i18n("Unhide Applications in this Submenu"), "unhideSiblingApplications"); } const AppsModel *appsModel = qobject_cast(entry->childModel()); if (appsModel && !appsModel->hiddenEntries().isEmpty()) { actionList << Kicker::createActionItem(i18n("Unhide Applications in '%1'", entry->name()), "unhideChildApplications"); } return actionList; } return QVariant(); } +QModelIndex AppsModel::index(int row, int column, const QModelIndex &parent) const +{ + return hasIndex(row, column, parent) ? createIndex(row, column, m_entryList.at(row)) : QModelIndex(); +} + + int AppsModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : m_entryList.count(); } bool AppsModel::trigger(int row, const QString &actionId, const QVariant &argument) { if (row < 0 || row >= m_entryList.count()) { return false; } AbstractEntry *entry = m_entryList.at(row); if (actionId == "hideApplication" && entry->type() == AbstractEntry::RunnableType) { QObject *appletInterface = rootModel()->property("appletInterface").value(); QQmlPropertyMap *appletConfig = qobject_cast(appletInterface->property("configuration").value()); if (appletConfig && appletConfig->contains("hiddenApplications")) { QStringList hiddenApps = appletConfig->value("hiddenApplications").toStringList(); KService::Ptr service = static_cast(entry)->service(); if (!hiddenApps.contains(service->menuId())) { hiddenApps << service->menuId(); appletConfig->insert("hiddenApplications", hiddenApps); QMetaObject::invokeMethod(appletConfig, "valueChanged", Qt::DirectConnection, Q_ARG(QString, "hiddenApplications"), Q_ARG(QVariant, hiddenApps)); refresh(); emit hiddenEntriesChanged(); } } return false; } else if (actionId == "unhideSiblingApplications") { QObject *appletInterface = rootModel()->property("appletInterface").value(); QQmlPropertyMap *appletConfig = qobject_cast(appletInterface->property("configuration").value()); if (appletConfig && appletConfig->contains("hiddenApplications")) { QStringList hiddenApps = appletConfig->value("hiddenApplications").toStringList(); foreach(const QString& app, m_hiddenEntries) { hiddenApps.removeOne(app); } appletConfig->insert("hiddenApplications", hiddenApps); QMetaObject::invokeMethod(appletConfig, "valueChanged", Qt::DirectConnection, Q_ARG(QString, "hiddenApplications"), Q_ARG(QVariant, hiddenApps)); m_hiddenEntries.clear(); refresh(); emit hiddenEntriesChanged(); } return false; } else if (actionId == "unhideChildApplications") { QObject *appletInterface = rootModel()->property("appletInterface").value(); QQmlPropertyMap *appletConfig = qobject_cast(appletInterface->property("configuration").value()); if (entry->type() == AbstractEntry::GroupType && appletConfig && appletConfig->contains("hiddenApplications")) { const AppsModel *appsModel = qobject_cast(entry->childModel()); if (!appsModel) { return false; } QStringList hiddenApps = appletConfig->value("hiddenApplications").toStringList(); foreach(const QString& app, appsModel->hiddenEntries()) { hiddenApps.removeOne(app); } appletConfig->insert("hiddenApplications", hiddenApps); QMetaObject::invokeMethod(appletConfig, "valueChanged", Qt::DirectConnection, Q_ARG(QString, "hiddenApplications"), Q_ARG(QVariant, hiddenApps)); refresh(); emit hiddenEntriesChanged(); } return false; } return entry->run(actionId, argument); } AbstractModel *AppsModel::modelForRow(int row) { if (row < 0 || row >= m_entryList.count()) { return 0; } return m_entryList.at(row)->childModel(); } int AppsModel::separatorCount() const { return m_separatorCount; } bool AppsModel::flat() const { return m_flat; } void AppsModel::setFlat(bool flat) { if (m_flat != flat) { m_flat = flat; refresh(); emit flatChanged(); } } +bool AppsModel::showSeparators() const +{ + return m_showSeparators; +} + +void AppsModel::setShowSeparators(bool showSeparators) { + if (m_showSeparators != showSeparators) { + m_showSeparators = showSeparators; + + refresh(); + + emit showSeparatorsChanged(); + } +} + int AppsModel::appNameFormat() const { return m_appNameFormat; } void AppsModel::setAppNameFormat(int format) { if (m_appNameFormat != (AppEntry::NameFormat)format) { m_appNameFormat = (AppEntry::NameFormat)format; refresh(); emit appNameFormatChanged(); } } QStringList AppsModel::hiddenEntries() const { return m_hiddenEntries; } void AppsModel::refresh() { + if (m_staticEntryList) { + return; + } + beginResetModel(); - emit refreshing(); + refreshInternal(); + + endResetModel(); + + emit countChanged(); + emit separatorCountChanged(); +} + +void AppsModel::refreshInternal() +{ + if (m_staticEntryList) { + return; + } + + if (m_entryList.count()) { + qDeleteAll(m_entryList); + m_entryList.clear(); + emit cleared(); + } - qDeleteAll(m_entryList); - m_entryList.clear(); m_hiddenEntries.clear(); m_separatorCount = 0; m_sortNeeded = false; if (m_entryPath.isEmpty()) { KServiceGroup::Ptr group = KServiceGroup::root(); bool sortByGenericName = (appNameFormat() == AppEntry::GenericNameOnly || appNameFormat() == AppEntry::GenericNameAndName); KServiceGroup::List list = group->entries(true /* sorted */, true /* excludeNoDisplay */, true /* allowSeparators */, sortByGenericName /* sortByGenericName */); for (KServiceGroup::List::ConstIterator it = list.constBegin(); it != list.constEnd(); it++) { const KSycocaEntry::Ptr p = (*it); if (p->isType(KST_KServiceGroup)) { KServiceGroup::Ptr subGroup(static_cast(p.data())); if (!subGroup->noDisplay() && subGroup->childCount() > 0) { - AppGroupEntry *groupEntry = new AppGroupEntry(this, subGroup, m_flat, m_appNameFormat); + AppGroupEntry *groupEntry = new AppGroupEntry(this, subGroup, m_flat, m_showSeparators, m_appNameFormat); m_entryList << groupEntry; } } } m_changeTimer = new QTimer(this); m_changeTimer->setSingleShot(true); m_changeTimer->setInterval(100); connect(m_changeTimer, SIGNAL(timeout()), this, SLOT(refresh())); connect(KSycoca::self(), SIGNAL(databaseChanged(QStringList)), SLOT(checkSycocaChanges(QStringList))); } else { KServiceGroup::Ptr group = KServiceGroup::group(m_entryPath); processServiceGroup(group); if (m_entryList.count()) { while (m_entryList.last()->type() == AbstractEntry::SeparatorType) { m_entryList.removeLast(); --m_separatorCount; } } if (m_sortNeeded) { - QCollator c; - - std::sort(m_entryList.begin(), m_entryList.end(), - [&c](AbstractEntry* a, AbstractEntry* b) { - if (a->type() != b->type()) { - return a->type() > b->type(); - } else { - return c.compare(a->name(), b->name()) < 0; - } - }); + sortEntries(); } } - - endResetModel(); - - emit countChanged(); - emit separatorCountChanged(); } void AppsModel::processServiceGroup(KServiceGroup::Ptr group) { if (!group || !group->isValid()) { return; } bool hasSubGroups = false; foreach(KServiceGroup::Ptr subGroup, group->groupEntries(KServiceGroup::ExcludeNoDisplay)) { if (subGroup->childCount() > 0) { hasSubGroups = true; break; } } bool sortByGenericName = (appNameFormat() == AppEntry::GenericNameOnly || appNameFormat() == AppEntry::GenericNameAndName); KServiceGroup::List list = group->entries(true /* sorted */, true /* excludeNoDisplay */, (!m_flat || (m_flat && !hasSubGroups)) /* allowSeparators */, sortByGenericName /* sortByGenericName */); QStringList hiddenApps; QObject *appletInterface = rootModel()->property("appletInterface").value(); QQmlPropertyMap *appletConfig = qobject_cast(appletInterface->property("configuration").value()); if (appletConfig && appletConfig->contains("hiddenApplications")) { hiddenApps = appletConfig->value("hiddenApplications").toStringList(); } for (KServiceGroup::List::ConstIterator it = list.constBegin(); it != list.constEnd(); it++) { const KSycocaEntry::Ptr p = (*it); if (p->isType(KST_KService)) { const KService::Ptr service(static_cast(p.data())); if (service->noDisplay()) { continue; } if (hiddenApps.contains(service->menuId())) { m_hiddenEntries << service->menuId(); continue; } bool found = false; foreach (const AbstractEntry *entry, m_entryList) { if (entry->type() == AbstractEntry::RunnableType && static_cast(entry)->service()->storageId() == service->storageId()) { found = true; } } if (!found) { m_entryList << new AppEntry(this, service, m_appNameFormat); } - } else if (p->isType(KST_KServiceSeparator)) { + } else if (p->isType(KST_KServiceSeparator) && m_showSeparators) { if (!m_entryList.count()) { continue; } if (m_entryList.last()->type() == AbstractEntry::SeparatorType) { continue; } m_entryList << new SeparatorEntry(this); ++m_separatorCount; } else if (p->isType(KST_KServiceGroup)) { const KServiceGroup::Ptr subGroup(static_cast(p.data())); if (subGroup->childCount() == 0) { continue; } if (m_flat) { m_sortNeeded = true; const KServiceGroup::Ptr serviceGroup(static_cast(p.data())); processServiceGroup(serviceGroup); } else { - AppGroupEntry *groupEntry = new AppGroupEntry(this, subGroup, m_flat, m_appNameFormat); + AppGroupEntry *groupEntry = new AppGroupEntry(this, subGroup, m_flat, m_showSeparators, m_appNameFormat); m_entryList << groupEntry; } } } } +void AppsModel::sortEntries() +{ + QCollator c; + + std::sort(m_entryList.begin(), m_entryList.end(), + [&c](AbstractEntry* a, AbstractEntry* b) { + if (a->type() != b->type()) { + return a->type() > b->type(); + } else { + return c.compare(a->name(), b->name()) < 0; + } + }); +} + void AppsModel::checkSycocaChanges(const QStringList &changes) { if (changes.contains("services") || changes.contains("apps") || changes.contains("xdgdata-apps")) { m_changeTimer->start(); } } void AppsModel::entryChanged(AbstractEntry *entry) { int i = m_entryList.indexOf(entry); if (i != -1) { QModelIndex idx = index(i, 0); emit dataChanged(idx, idx); } } diff --git a/appsmodel.h b/appsmodel.h index 230fb4fde..3cd05772f 100644 --- a/appsmodel.h +++ b/appsmodel.h @@ -1,92 +1,110 @@ /*************************************************************************** * Copyright (C) 2012 Aurélien Gâteau * * Copyright (C) 2013-2015 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 . * ***************************************************************************/ #ifndef APPSMODEL_H #define APPSMODEL_H #include "abstractmodel.h" #include "appentry.h" #include class AppGroupEntry; class QTimer; class AppsModel : public AbstractModel { Q_OBJECT Q_PROPERTY(bool flat READ flat WRITE setFlat NOTIFY flatChanged) + Q_PROPERTY(bool showSeparators READ showSeparators WRITE setShowSeparators NOTIFY showSeparatorsChanged) Q_PROPERTY(int appNameFormat READ appNameFormat WRITE setAppNameFormat NOTIFY appNameFormatChanged) public: - explicit AppsModel(const QString &entryPath = QString(), bool flat = false, QObject *parent = 0); + explicit AppsModel(const QString &entryPath = QString(), bool flat = false, bool separators = true, QObject *parent = 0); + explicit AppsModel(const QList entryList, bool deleteEntriesOnDestruction, QObject *parent = 0); ~AppsModel(); + QString description() const; + void setDescription(const QString &text); + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; Q_INVOKABLE virtual bool trigger(int row, const QString &actionId, const QVariant &argument); Q_INVOKABLE AbstractModel *modelForRow(int row); int separatorCount() const; bool flat() const; void setFlat(bool flat); + bool showSeparators() const; + void setShowSeparators(bool showSeparators); + int appNameFormat() const; void setAppNameFormat(int format); QStringList hiddenEntries() const; void entryChanged(AbstractEntry *entry); Q_SIGNALS: - void refreshing() const; + void cleared() const; void flatChanged() const; + void showSeparatorsChanged() const; void appNameFormatChanged() const; - void hiddenEntriesChanged(); + void hiddenEntriesChanged() const; protected Q_SLOTS: virtual void refresh(); protected: + void refreshInternal(); + QList m_entryList; + bool m_deleteEntriesOnDestruction; int m_separatorCount; + bool m_showSeparators; private Q_SLOTS: void checkSycocaChanges(const QStringList &changes); private: void processServiceGroup(KServiceGroup::Ptr group); + void sortEntries(); + QString m_description; QString m_entryPath; + bool m_staticEntryList; QTimer *m_changeTimer; bool m_flat; bool m_sortNeeded; AppEntry::NameFormat m_appNameFormat; QStringList m_hiddenEntries; static MenuEntryEditor *m_menuEntryEditor; }; #endif diff --git a/favoritesmodel.cpp b/favoritesmodel.cpp index 97fab39d8..054641431 100644 --- a/favoritesmodel.cpp +++ b/favoritesmodel.cpp @@ -1,215 +1,227 @@ /*************************************************************************** * Copyright (C) 2014-2015 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 "favoritesmodel.h" #include "appentry.h" #include "contactentry.h" #include "fileentry.h" #include "systementry.h" #include "actionlist.h" +#include + FavoritesModel::FavoritesModel(QObject *parent) : AbstractModel(parent) { } FavoritesModel::~FavoritesModel() { qDeleteAll(m_entryList); } +QString FavoritesModel::description() const +{ + return i18n("Favorites"); +} + QVariant FavoritesModel::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.row() >= m_entryList.count()) { return QVariant(); } const AbstractEntry *entry = m_entryList.at(index.row()); if (role == Qt::DisplayRole) { return entry->name(); } else if (role == Qt::DecorationRole) { return entry->icon(); } else if (role == Kicker::FavoriteIdRole) { return entry->id(); } else if (role == Kicker::HasActionListRole) { return entry->hasActions(); } else if (role == Kicker::ActionListRole) { return entry->actions(); } return QVariant(); } int FavoritesModel::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_entryList.count(); } bool FavoritesModel::trigger(int row, const QString &actionId, const QVariant &argument) { if (row < 0 || row >= m_entryList.count()) { return false; } return m_entryList.at(row)->run(actionId, argument); } QStringList FavoritesModel::favorites() const { return m_favorites; } void FavoritesModel::setFavorites(const QStringList& favorites) { QStringList _favorites(favorites); _favorites.removeDuplicates(); if (_favorites != m_favorites) { m_favorites = _favorites; refresh(); } } bool FavoritesModel::isFavorite(const QString &id) const { return m_favorites.contains(id); } void FavoritesModel::addFavorite(const QString &id) { if (id.isEmpty()) { return; } AbstractEntry *entry = favoriteFromId(id); if (!entry || !entry->isValid()) { delete entry; return; } beginInsertRows(QModelIndex(), m_entryList.count(), m_entryList.count()); m_entryList << entry; m_favorites << entry->id(); endInsertRows(); emit countChanged(); emit favoritesChanged(); } void FavoritesModel::removeFavorite(const QString &id) { int index = m_favorites.indexOf(id); if (index != -1) { beginRemoveRows(QModelIndex(), index, index); delete m_entryList[index]; m_entryList.removeAt(index); m_favorites.removeAt(index); endRemoveRows(); emit countChanged(); emit favoritesChanged(); } } void FavoritesModel::moveRow(int from, int to) { if (from >= m_favorites.count() || to >= m_favorites.count()) { return; } if (from == to) { return; } int modelTo = to + (to > from ? 1 : 0); bool ok = beginMoveRows(QModelIndex(), from, from, QModelIndex(), modelTo); if (ok) { m_entryList.move(from, to); m_favorites.move(from, to); endMoveRows(); emit favoritesChanged(); } } +AbstractModel *FavoritesModel::favoritesModel() +{ + return this; +} + void FavoritesModel::refresh() { beginResetModel(); int oldCount = m_entryList.count(); qDeleteAll(m_entryList); m_entryList.clear(); QStringList newFavorites; foreach(const QString &id, m_favorites) { AbstractEntry *entry = favoriteFromId(id); if (entry && entry->isValid()) { m_entryList << entry; newFavorites << entry->id(); } else if (entry) { delete entry; } } m_favorites = newFavorites; endResetModel(); if (oldCount != m_entryList.count()) { emit countChanged(); } emit favoritesChanged(); } AbstractEntry *FavoritesModel::favoriteFromId(const QString &id) { const QUrl url(id); const QString &s = url.scheme(); if ((s.isEmpty() && id.contains(QStringLiteral(".desktop"))) || s == QStringLiteral("preferred")) { return new AppEntry(this, id); } else if (s == QStringLiteral("ktp")) { return new ContactEntry(this, id); } else { AbstractEntry *entry = new SystemEntry(this, id); if (!entry->isValid()) { delete entry; return new FileEntry(this, url); } return entry; } return nullptr; } diff --git a/favoritesmodel.h b/favoritesmodel.h index 1b9fe9301..7db252a8c 100644 --- a/favoritesmodel.h +++ b/favoritesmodel.h @@ -1,67 +1,71 @@ /*************************************************************************** * Copyright (C) 2014-2015 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 . * ***************************************************************************/ #ifndef FAVORITESMODEL_H #define FAVORITESMODEL_H #include "abstractmodel.h" #include #include class FavoritesModel : public AbstractModel { Q_OBJECT Q_PROPERTY(QStringList favorites READ favorites WRITE setFavorites NOTIFY favoritesChanged) public: explicit FavoritesModel(QObject *parent = 0); ~FavoritesModel(); + QString description() const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument); QStringList favorites() const; void setFavorites(const QStringList &favorites); Q_INVOKABLE bool isFavorite(const QString &id) const; Q_INVOKABLE void addFavorite(const QString &id); Q_INVOKABLE void removeFavorite(const QString &id); Q_INVOKABLE void moveRow(int from, int to); + AbstractModel* favoritesModel(); + public Q_SLOTS: virtual void refresh(); Q_SIGNALS: void favoritesChanged() const; private: AbstractEntry *favoriteFromId(const QString &id); QList m_entryList; QStringList m_favorites; }; #endif diff --git a/forwardingmodel.cpp b/forwardingmodel.cpp index 2c33aa9d5..91099e6e6 100644 --- a/forwardingmodel.cpp +++ b/forwardingmodel.cpp @@ -1,233 +1,264 @@ /*************************************************************************** * Copyright (C) 2015 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 "forwardingmodel.h" ForwardingModel::ForwardingModel(QObject *parent) : AbstractModel(parent) { } ForwardingModel::~ForwardingModel() { } +QString ForwardingModel::description() const +{ + if (!m_sourceModel) { + return QString(); + } + + AbstractModel *abstractModel = qobject_cast(m_sourceModel); + + if (!abstractModel) { + return QString(); + } + + return abstractModel->description(); +} + QAbstractItemModel *ForwardingModel::sourceModel() const { return m_sourceModel; } void ForwardingModel::setSourceModel(QAbstractItemModel *sourceModel) { disconnectSignals(); beginResetModel(); m_sourceModel = sourceModel; connectSignals(); endResetModel(); emit countChanged(); - emit sourceModelChanged(); + emit descriptionChanged(); } bool ForwardingModel::canFetchMore(const QModelIndex &parent) const { if (!m_sourceModel) { return false; } return m_sourceModel->canFetchMore(indexToSourceIndex(parent)); } void ForwardingModel::fetchMore(const QModelIndex &parent) { if (m_sourceModel) { m_sourceModel->fetchMore(indexToSourceIndex(parent)); } } QModelIndex ForwardingModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent) if (!m_sourceModel) { return QModelIndex(); } return createIndex(row, column); } QModelIndex ForwardingModel::parent(const QModelIndex &index) const { Q_UNUSED(index) return QModelIndex(); } QVariant ForwardingModel::data(const QModelIndex &index, int role) const { if (!m_sourceModel) { return QVariant(); } return m_sourceModel->data(indexToSourceIndex(index), role); } int ForwardingModel::rowCount(const QModelIndex &parent) const { if (!m_sourceModel) { return 0; } return m_sourceModel->rowCount(indexToSourceIndex(parent)); } QModelIndex ForwardingModel::indexToSourceIndex(const QModelIndex& index) const { if (!m_sourceModel || !index.isValid()) { return QModelIndex(); } return m_sourceModel->index(index.row(), index.column(), index.parent().isValid() ? indexToSourceIndex(index.parent()) : QModelIndex()); } bool ForwardingModel::trigger(int row, const QString &actionId, const QVariant &argument) { if (!m_sourceModel) { return false; } AbstractModel *abstractModel = qobject_cast(m_sourceModel); if (!abstractModel) { return false; } return abstractModel->trigger(row, actionId, argument); } +QString ForwardingModel::labelForRow(int row) +{ + if (!m_sourceModel) { + return QString(); + } + + AbstractModel *abstractModel = qobject_cast(m_sourceModel); + + if (!abstractModel) { + return QString(); + } + + return abstractModel->labelForRow(row); +} + AbstractModel* ForwardingModel::modelForRow(int row) { if (!m_sourceModel) { return 0; } AbstractModel *abstractModel = qobject_cast(m_sourceModel); if (!abstractModel) { return 0; } return abstractModel->modelForRow(row); } AbstractModel* ForwardingModel::favoritesModel() { AbstractModel *sourceModel = qobject_cast(m_sourceModel); if (sourceModel) { return sourceModel->favoritesModel(); } return AbstractModel::favoritesModel(); } int ForwardingModel::separatorCount() const { if (!m_sourceModel) { return 0; } AbstractModel *abstractModel = qobject_cast(m_sourceModel); if (!abstractModel) { return 0; } return abstractModel->separatorCount(); } void ForwardingModel::reset() { emit beginResetModel(); emit endResetModel(); emit countChanged(); + emit separatorCountChanged(); } void ForwardingModel::connectSignals() { if (!m_sourceModel) { return; } connect(m_sourceModel, SIGNAL(destroyed()), this, SLOT(reset())); connect(m_sourceModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), this, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), Qt::UniqueConnection); connect(m_sourceModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), Qt::UniqueConnection); connect(m_sourceModel, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), this, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), Qt::UniqueConnection); connect(m_sourceModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), Qt::UniqueConnection); connect(m_sourceModel, SIGNAL(layoutAboutToBeChanged(QList,QAbstractItemModel::LayoutChangeHint)), this, SIGNAL(layoutAboutToBeChanged(QList,QAbstractItemModel::LayoutChangeHint)), Qt::UniqueConnection); connect(m_sourceModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(rowsInserted(QModelIndex,int,int)), Qt::UniqueConnection); connect(m_sourceModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged()), Qt::UniqueConnection); connect(m_sourceModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), Qt::UniqueConnection); connect(m_sourceModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(rowsRemoved(QModelIndex,int,int)), Qt::UniqueConnection); connect(m_sourceModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged()), Qt::UniqueConnection); connect(m_sourceModel, SIGNAL(modelAboutToBeReset()), this, SIGNAL(modelAboutToBeReset()), Qt::UniqueConnection); connect(m_sourceModel, SIGNAL(modelReset()), this, SIGNAL(modelReset()), Qt::UniqueConnection); connect(m_sourceModel, SIGNAL(modelReset()), this, SIGNAL(countChanged()), Qt::UniqueConnection); connect(m_sourceModel, SIGNAL(layoutChanged(QList,QAbstractItemModel::LayoutChangeHint)), this, SIGNAL(layoutChanged(QList,QAbstractItemModel::LayoutChangeHint)), Qt::UniqueConnection); } void ForwardingModel::disconnectSignals() { if (!m_sourceModel) { return; } disconnect(m_sourceModel, 0, this, 0); } diff --git a/forwardingmodel.h b/forwardingmodel.h index f8dd7b8e1..3d4edf643 100644 --- a/forwardingmodel.h +++ b/forwardingmodel.h @@ -1,73 +1,77 @@ /*************************************************************************** * Copyright (C) 2015 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 . * ***************************************************************************/ #ifndef FORWARDINGMODEL_H #define FORWARDINGMODEL_H #include "abstractmodel.h" #include class ForwardingModel : public AbstractModel { Q_OBJECT Q_PROPERTY(QAbstractItemModel* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged); public: explicit ForwardingModel(QObject *parent = 0); ~ForwardingModel(); + QString description() const; + QAbstractItemModel *sourceModel() const; virtual void setSourceModel(QAbstractItemModel *sourceModel); bool canFetchMore(const QModelIndex &parent) const; void fetchMore(const QModelIndex &parent); QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; QModelIndex parent(const QModelIndex &index) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument); + Q_INVOKABLE QString labelForRow(int row); + Q_INVOKABLE AbstractModel *modelForRow(int row); AbstractModel* favoritesModel(); int separatorCount() const; public Q_SLOTS: void reset(); Q_SIGNALS: void sourceModelChanged() const; protected: QModelIndex indexToSourceIndex(const QModelIndex &index) const; void connectSignals(); void disconnectSignals(); QPointer m_sourceModel; }; #endif diff --git a/fullscreenwindow.cpp b/fullscreenwindow.cpp new file mode 100644 index 000000000..3f1964ff4 --- /dev/null +++ b/fullscreenwindow.cpp @@ -0,0 +1,121 @@ +/*************************************************************************** + * Copyright (C) 2015 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 "fullscreenwindow.h" + +#include +#include + +#include +#include + +FullScreenWindow::FullScreenWindow(QQuickItem *parent) : QQuickWindow(parent ? parent->window() : 0) +, m_mainItem(0) +{ + setClearBeforeRendering(true); + setColor(QColor(0, 0, 0, 188)); + setFlags(Qt::FramelessWindowHint); + + setIcon(QIcon::fromTheme("plasma")); + + connect(&m_theme, &Plasma::Theme::themeChanged, this, &FullScreenWindow::updateTheme); + + if (parent && parent->window()) { + connect(parent->window(), &QWindow::screenChanged, this, &FullScreenWindow::parentScreenChanged); + } +} + +FullScreenWindow::~FullScreenWindow() +{ +} + +QQuickItem *FullScreenWindow::mainItem() const +{ + return m_mainItem; +} + +void FullScreenWindow::setMainItem(QQuickItem *mainItem) +{ + if (m_mainItem != mainItem) { + if (m_mainItem) { + m_mainItem->setVisible(false); + } + + m_mainItem = mainItem; + + if (mainItem) { + m_mainItem->setVisible(isVisible()); + m_mainItem->setParentItem(contentItem()); + } + + emit mainItemChanged(); + } +} + +void FullScreenWindow::toggle() { + if (isVisible()) { + close(); + } else { + resize(screen()->size()); + showFullScreen(); + } +} + +void FullScreenWindow::showEvent(QShowEvent *event) +{ + updateTheme(); + + if (m_mainItem) { + m_mainItem->setVisible(true); + } + + QQuickWindow::showEvent(event); + + KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); +} + +void FullScreenWindow::hideEvent(QHideEvent *event) +{ + if (m_mainItem) { + m_mainItem->setVisible(false); + } + + QQuickWindow::hideEvent(event); +} + +void FullScreenWindow::updateTheme() +{ + KWindowEffects::enableBlurBehind(winId(), true); + + return; + + KWindowEffects::enableBackgroundContrast(winId(), + m_theme.backgroundContrastEnabled(), + m_theme.backgroundContrast(), + m_theme.backgroundIntensity(), + m_theme.backgroundSaturation()); +} + +void FullScreenWindow::parentScreenChanged(const QScreen *screen) +{ + if (screen) { + setScreen(parent()->screen()); + resize(screen->size()); + } +} diff --git a/recentcontactsmodel.h b/fullscreenwindow.h similarity index 60% copy from recentcontactsmodel.h copy to fullscreenwindow.h index 7e0a5c3b5..01ea92f49 100644 --- a/recentcontactsmodel.h +++ b/fullscreenwindow.h @@ -1,57 +1,61 @@ /*************************************************************************** - * Copyright (C) 2012 by Aurélien Gâteau * - * Copyright (C) 2014-2015 by Eike Hein * + * Copyright (C) 2015 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 . * ***************************************************************************/ -#ifndef RECENTCONTACTSMODEL_H -#define RECENTCONTACTSMODEL_H +#ifndef FULLSCREENWINDOW_H +#define FULLSCREENWINDOW_H -#include "forwardingmodel.h" +#include -namespace KPeople { - class PersonData; -} +#include +#include -class RecentContactsModel : public ForwardingModel +class FullScreenWindow : public QQuickWindow { Q_OBJECT + Q_PROPERTY(QQuickItem* mainItem READ mainItem WRITE setMainItem NOTIFY mainItemChanged) + + Q_CLASSINFO("DefaultProperty", "mainItem") + public: - explicit RecentContactsModel(QObject *parent = 0); - ~RecentContactsModel(); + FullScreenWindow(QQuickItem *parent = 0); + ~FullScreenWindow(); - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QQuickItem *mainItem() const; + void setMainItem(QQuickItem *mainItem); - Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument); + Q_INVOKABLE void toggle(); - bool hasActions() const; - QVariantList actions() const; + Q_SIGNALS: + void mainItemChanged() const; private Q_SLOTS: - void refresh(); - void buildCache(); - void personDataChanged(); + void updateTheme(); + void parentScreenChanged(const QScreen *screen); - private: - void insertPersonData(const QString &id, int row); + protected: + void showEvent(QShowEvent *event) Q_DECL_OVERRIDE; + void hideEvent(QHideEvent *event) Q_DECL_OVERRIDE; - QHash m_idToData; - QHash m_dataToRow; + private: + QQuickItem *m_mainItem; + Plasma::Theme m_theme; }; #endif diff --git a/funnelmodel.cpp b/funnelmodel.cpp index aaa436e55..467d36dda 100644 --- a/funnelmodel.cpp +++ b/funnelmodel.cpp @@ -1,94 +1,96 @@ /*************************************************************************** * Copyright (C) 2014 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 "funnelmodel.h" FunnelModel::FunnelModel(QObject *parent) : ForwardingModel(parent) { } FunnelModel::~FunnelModel() { } void FunnelModel::setSourceModel(QAbstractItemModel *model) { if (model && m_sourceModel == model) { return; } if (!model) { reset(); return; } connect(model, SIGNAL(destroyed(QObject*)), this, SLOT(reset())); if (!m_sourceModel) { emit beginResetModel(); m_sourceModel = model; connectSignals(); emit endResetModel(); emit countChanged(); emit sourceModelChanged(); + emit descriptionChanged(); return; } int oldCount = m_sourceModel->rowCount(); int newCount = model->rowCount(); disconnectSignals(); m_sourceModel = model; connectSignals(); if (newCount > oldCount) { beginInsertRows(QModelIndex(), oldCount, newCount - 1); endInsertRows(); } else if (newCount < oldCount) { if (newCount == 0) { beginResetModel(); endResetModel(); } else { beginRemoveRows(QModelIndex(), newCount, oldCount - 1); endRemoveRows(); } } if (newCount > 0) { emit dataChanged(index(0, 0), index(qMin(oldCount, newCount) - 1, 0)); } if (oldCount != newCount) { emit countChanged(); } emit sourceModelChanged(); + emit descriptionChanged(); } diff --git a/kickerplugin.cpp b/kickerplugin.cpp index 9504fddb1..1ad9e352d 100644 --- a/kickerplugin.cpp +++ b/kickerplugin.cpp @@ -1,47 +1,49 @@ /*************************************************************************** * Copyright (C) 2014 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 "kickerplugin.h" #include "abstractmodel.h" #include "draghelper.h" +#include "fullscreenwindow.h" #include "funnelmodel.h" #include "processrunner.h" #include "rootmodel.h" #include "runnermodel.h" #include "submenu.h" #include "systemsettings.h" #include "windowsystem.h" #include void KickerPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("org.kde.plasma.private.kicker")); qmlRegisterType(); qmlRegisterType(uri, 0, 1, "DragHelper"); + qmlRegisterType(uri, 0, 1, "FullScreenWindow"); qmlRegisterType(uri, 0, 1, "FunnelModel"); qmlRegisterType(uri, 0, 1, "ProcessRunner"); qmlRegisterType(uri, 0, 1, "RootModel"); qmlRegisterType(uri, 0, 1, "RunnerModel"); qmlRegisterType(uri, 0, 1, "SubMenu"); qmlRegisterType(uri, 0, 1, "SystemSettings"); qmlRegisterType(uri, 0, 1, "WindowSystem"); } diff --git a/recentappsmodel.cpp b/recentappsmodel.cpp index fd6a20eac..3d167c567 100644 --- a/recentappsmodel.cpp +++ b/recentappsmodel.cpp @@ -1,195 +1,200 @@ /*************************************************************************** * Copyright (C) 2014-2015 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 "recentappsmodel.h" #include "actionlist.h" #include "appsmodel.h" #include "appentry.h" #include #include #if HAVE_X11 #include #endif #include #include #include #include #include #include #include #include namespace KAStats = KActivities::Experimental::Stats; using namespace KAStats; using namespace KAStats::Terms; RecentAppsModel::RecentAppsModel(QObject *parent) : ForwardingModel(parent) { refresh(); } RecentAppsModel::~RecentAppsModel() { } +QString RecentAppsModel::description() const +{ + return i18n("Applications"); +} + QVariant RecentAppsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } const QString storageId = sourceModel()->data(index, ResultModel::ResourceRole).toString().section(':', 1); KService::Ptr service = KService::serviceByStorageId(storageId); if (!service || !service->isApplication()) { return QVariant(); } if (role == Qt::DisplayRole) { return AppEntry::nameFromService(service, (AppEntry::NameFormat)qobject_cast(QObject::parent())->appNameFormat()); } else if (role == Qt::DecorationRole) { return QIcon::fromTheme(service->icon(), QIcon::fromTheme("unknown")); } else if (role == Kicker::FavoriteIdRole) { return service->storageId(); } else if (role == Kicker::HasActionListRole) { return true; } else if (role == Kicker::ActionListRole) { QVariantList actionList; actionList << Kicker::recentDocumentActions(service); if (actionList.count()) { actionList << Kicker::createSeparatorActionItem(); } const QVariantMap &forgetAction = Kicker::createActionItem(i18n("Forget Application"), "forget"); actionList << forgetAction; const QVariantMap &forgetAllAction = Kicker::createActionItem(i18n("Forget All Applications"), "forgetAll"); actionList << forgetAllAction; return actionList; } return QVariant(); } bool RecentAppsModel::trigger(int row, const QString &actionId, const QVariant &argument) { Q_UNUSED(argument) bool withinBounds = row >= 0 && row < rowCount(); if (actionId.isEmpty() && withinBounds) { const QString storageId = sourceModel()->data(sourceModel()->index(row, 0), ResultModel::ResourceRole).toString().section(':', 1); KService::Ptr service = KService::serviceByStorageId(storageId); if (!service) { return false; } quint32 timeStamp = 0; #if HAVE_X11 if (QX11Info::isPlatformX11()) { timeStamp = QX11Info::appUserTime(); } #endif new KRun(QUrl::fromLocalFile(service->entryPath()), 0, true, KStartupInfo::createNewStartupIdForTimestamp(timeStamp)); KActivities::ResourceInstance::notifyAccessed(QUrl("applications:" + storageId), "org.kde.plasma.kicker"); return true; } else if (actionId == "forget" && withinBounds) { if (sourceModel()) { ResultModel *resultModel = static_cast(sourceModel()); resultModel->forgetResource(row); } return false; } else if (actionId == "forgetAll") { if (sourceModel()) { ResultModel *resultModel = static_cast(sourceModel()); resultModel->forgetAllResources(); } return false; } else if (withinBounds) { const QString storageId = sourceModel()->data(sourceModel()->index(row, 0), ResultModel::ResourceRole).toString().section(':', 1); KService::Ptr service = KService::serviceByStorageId(storageId); if (service) { return Kicker::handleRecentDocumentAction(service, actionId, argument); } } return false; } bool RecentAppsModel::hasActions() const { return rowCount(); } QVariantList RecentAppsModel::actions() const { QVariantList actionList; if (rowCount()) { actionList << Kicker::createActionItem(i18n("Forget All Applications"), "forgetAll"); } return actionList; } void RecentAppsModel::refresh() { QObject *oldModel = sourceModel(); auto query = UsedResources | RecentlyUsedFirst | Agent::any() | Type::any() | Activity::current() | Url::startsWith("applications:") | Limit(15); ResultModel *model = new ResultModel(query); QModelIndex index; if (model->canFetchMore(index)) { model->fetchMore(index); } setSourceModel(model); delete oldModel; } diff --git a/recentappsmodel.h b/recentappsmodel.h index 1e4105c98..6ee55c11c 100644 --- a/recentappsmodel.h +++ b/recentappsmodel.h @@ -1,44 +1,46 @@ /*************************************************************************** * Copyright (C) 2014-2015 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 . * ***************************************************************************/ #ifndef RECENTAPPSMODEL_H #define RECENTAPPSMODEL_H #include "forwardingmodel.h" class RecentAppsModel : public ForwardingModel { Q_OBJECT public: explicit RecentAppsModel(QObject *parent = 0); ~RecentAppsModel(); + QString description() const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument); bool hasActions() const; QVariantList actions() const; private Q_SLOTS: void refresh(); }; #endif diff --git a/recentcontactsmodel.cpp b/recentcontactsmodel.cpp index 9a5a53363..182b7d059 100644 --- a/recentcontactsmodel.cpp +++ b/recentcontactsmodel.cpp @@ -1,239 +1,244 @@ /*************************************************************************** * Copyright (C) 2012 by Aurélien Gâteau * * Copyright (C) 2014-2015 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 "recentcontactsmodel.h" #include "actionlist.h" #include "contactentry.h" #include #include #include #include #include //FIXME TODO: Pretty include in KPeople broken. #include #include namespace KAStats = KActivities::Experimental::Stats; using namespace KAStats; using namespace KAStats::Terms; RecentContactsModel::RecentContactsModel(QObject *parent) : ForwardingModel(parent) { refresh(); } RecentContactsModel::~RecentContactsModel() { } +QString RecentContactsModel::description() const +{ + return i18n("Contacts"); +} + QVariant RecentContactsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } QString id = sourceModel()->data(index, ResultModel::ResourceRole).toString(); KPeople::PersonData *data = 0; if (m_idToData.contains(id)) { data = m_idToData[id]; } if (!data) { const_cast(this)->insertPersonData(id, index.row()); return QVariant(); } if (role == Qt::DisplayRole) { return data->name(); } else if (role == Qt::DecorationRole) { return data->presenceIconName(); } else if (role == Kicker::FavoriteIdRole) { return id; } else if (role == Kicker::HasActionListRole) { return true; } else if (role == Kicker::ActionListRole) { QVariantList actionList ; const QVariantMap &forgetAction = Kicker::createActionItem(i18n("Forget Contact"), "forget"); actionList << forgetAction; const QVariantMap &forgetAllAction = Kicker::createActionItem(i18n("Forget All Contacts"), "forgetAll"); actionList << forgetAllAction; actionList << Kicker::createSeparatorActionItem(); actionList << Kicker::createActionItem(i18n("Show Contact Information..."), "showContactInfo"); return actionList; } return QVariant(); } bool RecentContactsModel::trigger(int row, const QString &actionId, const QVariant &argument) { Q_UNUSED(argument) bool withinBounds = row >= 0 && row < rowCount(); if (actionId.isEmpty() && withinBounds) { QString id = sourceModel()->data(sourceModel()->index(row, 0), ResultModel::ResourceRole).toString(); const QList actionList = KPeople::actionsForPerson(id, this); if (!actionList.isEmpty()) { QAction *chat = 0; foreach (QAction *action, actionList) { const QVariant &actionType = action->property("actionType"); if (!actionType.isNull() && actionType.toInt() == KPeople::ActionType::TextChatAction) { chat = action; } } if (chat) { chat->trigger(); return true; } } return false; } else if (actionId == "showContactInfo" && withinBounds) { ContactEntry::showPersonDetailsDialog(sourceModel()->data(sourceModel()->index(row, 0), ResultModel::ResourceRole).toString()); } else if (actionId == "forget" && withinBounds) { if (sourceModel()) { ResultModel *resultModel = static_cast(sourceModel()); resultModel->forgetResource(row); } return false; } else if (actionId == "forgetAll") { if (sourceModel()) { ResultModel *resultModel = static_cast(sourceModel()); resultModel->forgetAllResources(); } return false; } return false; } bool RecentContactsModel::hasActions() const { return rowCount(); } QVariantList RecentContactsModel::actions() const { QVariantList actionList; if (rowCount()) { actionList << Kicker::createActionItem(i18n("Forget All Contacts"), "forgetAll"); } return actionList; } void RecentContactsModel::refresh() { QObject *oldModel = sourceModel(); auto query = UsedResources | RecentlyUsedFirst | Agent("KTp") | Type::any() | Activity::current() | Url::startsWith("ktp") | Limit(15); ResultModel *model = new ResultModel(query); QModelIndex index; if (model->canFetchMore(index)) { model->fetchMore(index); } // FIXME TODO: Don't wipe entire cache on transactions. connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(buildCache()), Qt::UniqueConnection); connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(buildCache()), Qt::UniqueConnection); connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(buildCache()), Qt::UniqueConnection); connect(model, SIGNAL(modelReset()), this, SLOT(buildCache()), Qt::UniqueConnection); setSourceModel(model); buildCache(); delete oldModel; } void RecentContactsModel::buildCache() { qDeleteAll(m_idToData.values()); m_idToData.clear(); m_dataToRow.clear(); QString id; for(int i = 0; i < sourceModel()->rowCount(); ++i) { id = sourceModel()->data(sourceModel()->index(i, 0), ResultModel::ResourceRole).toString(); if (!m_idToData.contains(id)) { insertPersonData(id, i); } } } void RecentContactsModel::insertPersonData(const QString& id, int row) { KPeople::PersonData *data = new KPeople::PersonData(id); m_idToData[id] = data; m_dataToRow[data] = row; connect(data, SIGNAL(dataChanged()), this, SLOT(personDataChanged())); } void RecentContactsModel::personDataChanged() { KPeople::PersonData *data = static_cast(sender()); if (m_dataToRow.contains(data)) { int row = m_dataToRow[data]; QModelIndex idx = sourceModel()->index(row, 0); emit dataChanged(idx, idx); } } diff --git a/recentcontactsmodel.h b/recentcontactsmodel.h index 7e0a5c3b5..ef659fc65 100644 --- a/recentcontactsmodel.h +++ b/recentcontactsmodel.h @@ -1,57 +1,59 @@ /*************************************************************************** * Copyright (C) 2012 by Aurélien Gâteau * * Copyright (C) 2014-2015 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 . * ***************************************************************************/ #ifndef RECENTCONTACTSMODEL_H #define RECENTCONTACTSMODEL_H #include "forwardingmodel.h" namespace KPeople { class PersonData; } class RecentContactsModel : public ForwardingModel { Q_OBJECT public: explicit RecentContactsModel(QObject *parent = 0); ~RecentContactsModel(); + QString description() const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument); bool hasActions() const; QVariantList actions() const; private Q_SLOTS: void refresh(); void buildCache(); void personDataChanged(); private: void insertPersonData(const QString &id, int row); QHash m_idToData; QHash m_dataToRow; }; #endif diff --git a/recentdocsmodel.cpp b/recentdocsmodel.cpp index 7de536570..ae7306fb0 100644 --- a/recentdocsmodel.cpp +++ b/recentdocsmodel.cpp @@ -1,164 +1,169 @@ /*************************************************************************** * Copyright (C) 2012 by Aurélien Gâteau * * Copyright (C) 2014-2015 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 "recentdocsmodel.h" #include "actionlist.h" #include #include #include #include #include #include #include namespace KAStats = KActivities::Experimental::Stats; using namespace KAStats; using namespace KAStats::Terms; RecentDocsModel::RecentDocsModel(QObject *parent) : ForwardingModel(parent) { refresh(); } RecentDocsModel::~RecentDocsModel() { } +QString RecentDocsModel::description() const +{ + return i18n("Documents"); +} + QVariant RecentDocsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } const QUrl url(sourceModel()->data(index, ResultModel::ResourceRole).toString()); const KFileItem fileItem(url); if (!url.isValid() || !fileItem.isFile()) { return QVariant(); } if (role == Qt::DisplayRole) { return url.fileName(); } else if (role == Qt::DecorationRole) { return QIcon::fromTheme(fileItem.iconName(), QIcon::fromTheme("unknown")); } else if (role == Kicker::FavoriteIdRole) { return url.toString(); } else if (role == Kicker::HasActionListRole) { return true; } else if (role == Kicker::ActionListRole) { QVariantList actionList = Kicker::createActionListForFileItem(fileItem); actionList << Kicker::createSeparatorActionItem(); const QVariantMap &forgetAction = Kicker::createActionItem(i18n("Forget Document"), "forget"); actionList << forgetAction; const QVariantMap &forgetAllAction = Kicker::createActionItem(i18n("Forget All Documents"), "forgetAll"); actionList << forgetAllAction; return actionList; } return QVariant(); } bool RecentDocsModel::trigger(int row, const QString &actionId, const QVariant &argument) { bool withinBounds = row >= 0 && row < rowCount(); if (actionId.isEmpty() && withinBounds) { QUrl url(sourceModel()->data(sourceModel()->index(row, 0), ResultModel::ResourceRole).toString()); new KRun(url, 0); return true; } else if (actionId == "forget" && withinBounds) { if (sourceModel()) { ResultModel *resultModel = static_cast(sourceModel()); resultModel->forgetResource(row); } return false; } else if (actionId == "forgetAll") { if (sourceModel()) { ResultModel *resultModel = static_cast(sourceModel()); resultModel->forgetAllResources(); } return false; } else if (withinBounds) { bool close = false; QUrl url(sourceModel()->data(sourceModel()->index(row, 0), ResultModel::ResourceRole).toString()); KFileItem item(url); if (Kicker::handleFileItemAction(item, actionId, argument, &close)) { return close; } } return false; } bool RecentDocsModel::hasActions() const { return rowCount(); } QVariantList RecentDocsModel::actions() const { QVariantList actionList; if (rowCount()) { actionList << Kicker::createActionItem(i18n("Forget All Documents"), "forgetAll"); } return actionList; } void RecentDocsModel::refresh() { QObject *oldModel = sourceModel(); auto query = UsedResources | RecentlyUsedFirst | Agent::any() | Type::any() | Activity::current() | Url::file() | Limit(15); ResultModel *model = new ResultModel(query); QModelIndex index; if (model->canFetchMore(index)) { model->fetchMore(index); } setSourceModel(model); delete oldModel; } diff --git a/recentdocsmodel.h b/recentdocsmodel.h index 216c0db87..df12d1b2b 100644 --- a/recentdocsmodel.h +++ b/recentdocsmodel.h @@ -1,45 +1,47 @@ /*************************************************************************** * Copyright (C) 2012 by Aurélien Gâteau * * Copyright (C) 2014-2015 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 . * ***************************************************************************/ #ifndef RECENTDOCSMODEL_H #define RECENTDOCSMODEL_H #include "forwardingmodel.h" class RecentDocsModel : public ForwardingModel { Q_OBJECT public: explicit RecentDocsModel(QObject *parent = 0); ~RecentDocsModel(); + QString description() const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument); bool hasActions() const; QVariantList actions() const; private Q_SLOTS: void refresh(); }; #endif diff --git a/rootmodel.cpp b/rootmodel.cpp index 5619f1795..aadccc28b 100644 --- a/rootmodel.cpp +++ b/rootmodel.cpp @@ -1,291 +1,321 @@ /*************************************************************************** * Copyright (C) 2014-2015 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 "rootmodel.h" #include "actionlist.h" #include "favoritesmodel.h" #include "recentappsmodel.h" #include "recentdocsmodel.h" #include "recentcontactsmodel.h" #include "systemmodel.h" #include GroupEntry::GroupEntry(RootModel *parentModel, const QString &name, const QString &iconName, AbstractModel *childModel) : AbstractGroupEntry(parentModel) , m_name(name) , m_iconName(iconName) , m_childModel(childModel) { - QObject::connect(parentModel, &RootModel::refreshing, childModel, &AbstractModel::deleteLater); + QObject::connect(parentModel, &RootModel::cleared, childModel, &AbstractModel::deleteLater); QObject::connect(childModel, &AbstractModel::countChanged, [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } } ); } QString GroupEntry::name() const { return m_name; } QIcon GroupEntry::icon() const { return QIcon::fromTheme(m_iconName, QIcon::fromTheme("unknown")); } bool GroupEntry::hasChildren() const { return m_childModel && m_childModel->count() > 0; } AbstractModel *GroupEntry::childModel() const { return m_childModel; } RootModel::RootModel(QObject *parent) : AppsModel(QString(), parent) , m_favorites(new FavoritesModel(this)) , m_systemModel(nullptr) +, m_showAllSubtree(false) , m_showRecentApps(true) , m_showRecentDocs(true) , m_showRecentContacts(false) , m_recentAppsModel(0) , m_recentDocsModel(0) , m_recentContactsModel(0) , m_appletInterface(0) { } RootModel::~RootModel() { } QVariant RootModel::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.row() >= m_entryList.count()) { return QVariant(); } if (role == Kicker::HasActionListRole || role == Kicker::ActionListRole) { const AbstractEntry *entry = m_entryList.at(index.row()); if (entry->type() == AbstractEntry::GroupType) { const GroupEntry *group = static_cast(entry); AbstractModel *model = group->childModel(); if (model == m_recentAppsModel || model == m_recentDocsModel || model == m_recentContactsModel) { if (role == Kicker::HasActionListRole) { return true; } else if (role == Kicker::ActionListRole) { QVariantList actionList; actionList << model->actions(); actionList << Kicker::createSeparatorActionItem(); actionList << Kicker::createActionItem(i18n("Hide %1", group->name()), "hideCategory"); return actionList; } } } } return AppsModel::data(index, role); } bool RootModel::trigger(int row, const QString& actionId, const QVariant& argument) { const AbstractEntry *entry = m_entryList.at(row); if (entry->type() == AbstractEntry::GroupType) { if (actionId == "hideCategory") { AbstractModel *model = entry->childModel(); if (model == m_recentAppsModel) { setShowRecentApps(false); return true; } else if (model == m_recentDocsModel) { setShowRecentDocs(false); return true; } else if (model == m_recentContactsModel) { setShowRecentContacts(false); return true; } } else if (entry->childModel()->hasActions()) { return entry->childModel()->trigger(-1, actionId, QVariant()); } } return AppsModel::trigger(row, actionId, argument); } bool RootModel::showRecentApps() const { return m_showRecentApps; } void RootModel::setShowRecentApps(bool show) { if (show != m_showRecentApps) { m_showRecentApps = show; refresh(); emit showRecentAppsChanged(); } } +bool RootModel::showAllSubtree() const +{ + return m_showAllSubtree; +} + +void RootModel::setShowAllSubtree(bool show) +{ + if (m_showAllSubtree != show) { + m_showAllSubtree = show; + + refresh(); + + emit showAllSubtreeChanged(); + } +} + bool RootModel::showRecentDocs() const { return m_showRecentDocs; } void RootModel::setShowRecentDocs(bool show) { if (show != m_showRecentDocs) { m_showRecentDocs = show; refresh(); emit showRecentDocsChanged(); } } bool RootModel::showRecentContacts() const { return m_showRecentContacts; } void RootModel::setShowRecentContacts(bool show) { if (show != m_showRecentContacts) { m_showRecentContacts = show; refresh(); emit showRecentContactsChanged(); } } QObject* RootModel::appletInterface() const { return m_appletInterface; } void RootModel::setAppletInterface(QObject* appletInterface) { if (m_appletInterface != appletInterface) { m_appletInterface = appletInterface; refresh(); emit appletInterfaceChanged(); } } AbstractModel* RootModel::favoritesModel() { return m_favorites; } AbstractModel* RootModel::systemFavoritesModel() { if (m_systemModel) { return m_systemModel->favoritesModel(); } return nullptr; } void RootModel::refresh() { if (!m_appletInterface) { return; } - AppsModel::refresh(); - extendEntryList(); - m_favorites->refresh(); -} + beginResetModel(); -void RootModel::extendEntryList() -{ - m_recentAppsModel = 0; - - if (m_showRecentApps) { - m_recentAppsModel = new RecentAppsModel(this); - } + AppsModel::refreshInternal(); - m_recentDocsModel = 0; + AppsModel *allModel = nullptr; + m_recentAppsModel = nullptr; + m_recentDocsModel = nullptr; + m_recentContactsModel = nullptr; - if (m_showRecentDocs) { - m_recentDocsModel = new RecentDocsModel(this); - } + if (m_showAllSubtree) { + QHash> m_categoryHash; - m_recentContactsModel = 0; + foreach (const AbstractEntry *groupEntry, m_entryList) { + AbstractModel *model = groupEntry->childModel(); - if (m_showRecentContacts) { - m_recentContactsModel = new RecentContactsModel(this); - } + for (int i = 0; i < model->count(); ++i) { + AbstractEntry *appEntry = static_cast(model->index(i, 0).internalPointer()); + const QChar &first = appEntry->name().at(0).toUpper(); + m_categoryHash[first.isDigit() ? QStringLiteral("0-9") : first].append(appEntry); + } + } - int insertCount = 0; - if (m_recentAppsModel) ++insertCount; - if (m_recentDocsModel) ++insertCount; - if (m_recentContactsModel) ++insertCount; + QList groups; + QHashIterator> i(m_categoryHash); - if (insertCount) { - beginInsertRows(QModelIndex(), 0, insertCount); + while (i.hasNext()) { + i.next(); + AppsModel *model = new AppsModel(i.value(), false, this); + model->setDescription(i.key()); + groups.append(new GroupEntry(this, i.key(), QString(), model)); + } - GroupEntry *entry = 0; + allModel = new AppsModel(groups, true, this); + allModel->setDescription(QStringLiteral("KICKER_ALL_MODEL")); // Intentionally no i18n. + } + if (m_showSeparators) { m_entryList.prepend(new SeparatorEntry(this)); ++m_separatorCount; + } - if (m_recentContactsModel) { - entry = new GroupEntry(this, i18n("Recent Contacts"), QString(), m_recentContactsModel); - m_entryList.prepend(entry); - } + if (allModel) { + m_entryList.prepend(new GroupEntry(this, i18n("All Applications"), QString(), allModel)); + } - if (m_recentDocsModel) { - entry = new GroupEntry(this, i18n("Recent Documents"), QString(), m_recentDocsModel); - m_entryList.prepend(entry); - } + if (m_showRecentContacts) { + m_recentContactsModel = new RecentContactsModel(this); + m_entryList.prepend(new GroupEntry(this, i18n("Recent Contacts"), QString(), m_recentContactsModel)); + } - if (m_recentAppsModel) { - entry = new GroupEntry(this, i18n("Recent Applications"), QString(), m_recentAppsModel); - m_entryList.prepend(entry); - } + if (m_showRecentDocs) { + m_recentDocsModel = new RecentDocsModel(this); + m_entryList.prepend(new GroupEntry(this, i18n("Recent Documents"), QString(), m_recentDocsModel)); + } - endInsertRows(); + if (m_showRecentApps) { + m_recentAppsModel = new RecentAppsModel(this); + m_entryList.prepend(new GroupEntry(this, i18n("Recent Applications"), QString(), m_recentAppsModel)); } - beginInsertRows(QModelIndex(), m_entryList.size(), m_entryList.size()); m_systemModel = new SystemModel(this); m_entryList << new GroupEntry(this, i18n("Power / Session"), QString(), m_systemModel); - endInsertRows(); + + endResetModel(); + + m_favorites->refresh(); emit systemFavoritesModelChanged(); emit countChanged(); emit separatorCountChanged(); + + emit refreshed(); +} + +void RootModel::extendEntryList() +{ + } diff --git a/rootmodel.h b/rootmodel.h index 44b6154db..5acdb8ed2 100644 --- a/rootmodel.h +++ b/rootmodel.h @@ -1,114 +1,121 @@ /*************************************************************************** * Copyright (C) 2014-2015 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 . * ***************************************************************************/ #ifndef ROOTMODEL_H #define ROOTMODEL_H #include "appsmodel.h" class FavoritesModel; class RecentAppsModel; class RecentDocsModel; class RecentContactsModel; class SystemModel; class RootModel; class GroupEntry : public AbstractGroupEntry { public: GroupEntry(RootModel *parentModel, const QString &name, const QString &iconName, AbstractModel *childModel); QIcon icon() const; QString name() const; bool hasChildren() const; AbstractModel *childModel() const; private: QString m_name; QString m_iconName; QPointer m_childModel; }; class RootModel : public AppsModel { Q_OBJECT Q_PROPERTY(QObject* systemFavoritesModel READ systemFavoritesModel NOTIFY systemFavoritesModelChanged) + Q_PROPERTY(bool showAllSubtree READ showAllSubtree WRITE setShowAllSubtree NOTIFY showAllSubtreeChanged) Q_PROPERTY(bool showRecentApps READ showRecentApps WRITE setShowRecentApps NOTIFY showRecentAppsChanged) Q_PROPERTY(bool showRecentDocs READ showRecentDocs WRITE setShowRecentDocs NOTIFY showRecentDocsChanged) Q_PROPERTY(bool showRecentContacts READ showRecentContacts WRITE setShowRecentContacts NOTIFY showRecentContactsChanged) Q_PROPERTY(QObject* appletInterface READ appletInterface WRITE setAppletInterface NOTIFY appletInterfaceChanged); public: explicit RootModel(QObject *parent = 0); ~RootModel(); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; Q_INVOKABLE virtual bool trigger(int row, const QString &actionId, const QVariant &argument); + bool showAllSubtree() const; + void setShowAllSubtree(bool show); + bool showRecentApps() const; void setShowRecentApps(bool show); bool showRecentDocs() const; void setShowRecentDocs(bool show); bool showRecentContacts() const; void setShowRecentContacts(bool show); QObject *appletInterface() const; void setAppletInterface(QObject *appletInterface); AbstractModel* favoritesModel(); AbstractModel* systemFavoritesModel(); Q_SIGNALS: + void refreshed() const; void systemFavoritesModelChanged() const; + void showAllSubtreeChanged() const; void showRecentAppsChanged() const; void showRecentDocsChanged() const; void showRecentContactsChanged() const; void recentAppsModelChanged() const; void appletInterfaceChanged() const; protected Q_SLOTS: void refresh(); private: void extendEntryList(); FavoritesModel *m_favorites; SystemModel *m_systemModel; + bool m_showAllSubtree; bool m_showRecentApps; bool m_showRecentDocs; bool m_showRecentContacts; RecentAppsModel *m_recentAppsModel; RecentDocsModel *m_recentDocsModel; RecentContactsModel *m_recentContactsModel; QObject *m_appletInterface; }; #endif diff --git a/runnermatchesmodel.cpp b/runnermatchesmodel.cpp index f2e73870b..d375fd728 100644 --- a/runnermatchesmodel.cpp +++ b/runnermatchesmodel.cpp @@ -1,169 +1,175 @@ /*************************************************************************** * Copyright (C) 2012 by Aurélien Gâteau * * Copyright (C) 2014-2015 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 "runnermatchesmodel.h" #include "runnermodel.h" #include "actionlist.h" #include #include +#include #include RunnerMatchesModel::RunnerMatchesModel(const QString &runnerId, const QString &name, Plasma::RunnerManager *manager, QObject *parent) : AbstractModel(parent) , m_runnerId(runnerId) , m_name(name) , m_runnerManager(manager) { } +QString RunnerMatchesModel::description() const +{ + return m_name; +} + QVariant RunnerMatchesModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= m_matches.count()) { return QVariant(); } Plasma::QueryMatch match = m_matches.at(index.row()); if (role == Qt::DisplayRole) { return match.text(); } else if (role == Qt::DecorationRole) { return match.icon(); } else if (role == Kicker::FavoriteIdRole) { if (m_runnerId == "services") { return match.data().toString(); } } else if (role == Kicker::HasActionListRole) { // Hack to expose the protected Plasma::AbstractRunner::actions() method. class MyRunner : public Plasma::AbstractRunner { public: using Plasma::AbstractRunner::actions;; }; MyRunner *runner = static_cast(match.runner()); Q_ASSERT(runner); return !runner->actions().isEmpty(); } else if (role == Kicker::ActionListRole) { QVariantList actionList; foreach (QAction *action, m_runnerManager->actionsForMatch(match)) { QVariantMap item = Kicker::createActionItem(action->text(), "runnerAction", QVariant::fromValue(action)); item["icon"] = action->icon(); actionList << item; } return actionList; } return QVariant(); } int RunnerMatchesModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : m_matches.count(); } bool RunnerMatchesModel::trigger(int row, const QString &actionId, const QVariant &argument) { if (row < 0 || row >= m_matches.count()) { return false; } Plasma::QueryMatch match = m_matches.at(row); if (!match.isEnabled()) { return false; } if (!actionId.isEmpty()) { QObject *obj = argument.value(); if (!obj) { return false; } QAction *action = qobject_cast(obj); if (!action) { return false; } match.setSelectedAction(action); } m_runnerManager->run(match); return true; } void RunnerMatchesModel::setMatches(const QList< Plasma::QueryMatch > &matches) { int oldCount = m_matches.count(); int newCount = matches.count(); bool emitCountChange = (oldCount != newCount); int ceiling = qMin(oldCount, newCount); bool emitDataChange = false; for (int row = 0; row < ceiling; ++row) { if (!(m_matches.at(row) == matches.at(row))) { emitDataChange = true; break; } } if (newCount > oldCount) { beginInsertRows(QModelIndex(), oldCount, newCount - 1); m_matches = matches; endInsertRows(); } else if (newCount < oldCount) { beginRemoveRows(QModelIndex(), newCount, oldCount - 1); m_matches = matches; endRemoveRows(); } if (emitDataChange) { m_matches = matches; emit dataChanged(index(0, 0), index(ceiling - 1, 0)); } if (emitCountChange) { emit countChanged(); } } AbstractModel *RunnerMatchesModel::favoritesModel() { return static_cast(parent())->favoritesModel(); } diff --git a/runnermatchesmodel.h b/runnermatchesmodel.h index ef03452e2..246c2a8ae 100644 --- a/runnermatchesmodel.h +++ b/runnermatchesmodel.h @@ -1,62 +1,64 @@ /*************************************************************************** * Copyright (C) 2012 by Aurélien Gâteau * * Copyright (C) 2014 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 . * ***************************************************************************/ #ifndef RUNNERMATCHESMODEL_H #define RUNNERMATCHESMODEL_H #include "abstractmodel.h" #include namespace Plasma { class RunnerManager; } class RunnerMatchesModel : public AbstractModel { Q_OBJECT Q_PROPERTY(QString name READ name CONSTANT) public: explicit RunnerMatchesModel(const QString &runnerId, const QString &name, Plasma::RunnerManager *manager, QObject *parent = 0); + QString description() const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument); QString runnerId() const { return m_runnerId; } QString name() const { return m_name; } void setMatches(const QList &matches); AbstractModel* favoritesModel(); private: QString m_runnerId; QString m_name; Plasma::RunnerManager *m_runnerManager; QList m_matches; }; #endif diff --git a/runnermodel.cpp b/runnermodel.cpp index c3d1fb39e..4eb6ccc9f 100644 --- a/runnermodel.cpp +++ b/runnermodel.cpp @@ -1,234 +1,256 @@ /*************************************************************************** * Copyright (C) 2012 by Aurélien Gâteau * * Copyright (C) 2014 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 "runnermodel.h" #include "runnermatchesmodel.h" #include #include #include RunnerModel::RunnerModel(QObject *parent) : QAbstractListModel(parent) , m_favoritesModel(0) , m_runnerManager(0) +, m_deleteWhenEmpty(false) { QHash roles; roles.insert(Qt::DisplayRole, "display"); setRoleNames(roles); m_queryTimer.setSingleShot(true); m_queryTimer.setInterval(10); connect(&m_queryTimer, SIGNAL(timeout()), this, SLOT(startQuery())); } RunnerModel::~RunnerModel() { } AbstractModel *RunnerModel::favoritesModel() const { return m_favoritesModel; } void RunnerModel::setFavoritesModel(AbstractModel *model) { if (m_favoritesModel != model) { m_favoritesModel = model; clear(); if (!m_query.isEmpty()) { m_queryTimer.start(); } emit favoritesModelChanged(); } } +bool RunnerModel::deleteWhenEmpty() const +{ + return m_deleteWhenEmpty; +} + +void RunnerModel::setDeleteWhenEmpty(bool deleteWhenEmpty) +{ + if (m_deleteWhenEmpty != deleteWhenEmpty) { + m_deleteWhenEmpty = deleteWhenEmpty; + + emit deleteWhenEmptyChanged(); + } +} + QVariant RunnerModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= m_models.count()) { return QVariant(); } if (role == Qt::DisplayRole) { return m_models.at(index.row())->name(); } return QVariant(); } int RunnerModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : m_models.count(); } int RunnerModel::count() const { return rowCount(); } QObject *RunnerModel::modelForRow(int row) { if (row < 0 || row >= m_models.count()) { return 0; } return m_models.at(row); } QStringList RunnerModel::runners() const { return m_runners; } void RunnerModel::setRunners(const QStringList &runners) { if (m_runners.toSet() != runners.toSet()) { m_runners = runners; if (m_runnerManager) { m_runnerManager->setAllowedRunners(runners); } emit runnersChanged(); } } QString RunnerModel::query() const { return m_query; } void RunnerModel::setQuery(const QString &query) { if (m_query != query) { m_query = query; m_queryTimer.start(); emit queryChanged(); } } void RunnerModel::startQuery() { if (m_query.isEmpty()) { clear(); } if (m_query.isEmpty() && m_runnerManager) { return; } createManager(); m_runnerManager->launchQuery(m_query); } void RunnerModel::matchesChanged(const QList &matches) { // Group matches by runner. // We do not use a QMultiHash here because it keeps values in LIFO order, while we want FIFO. QHash > matchesForRunner; foreach (const Plasma::QueryMatch &match, matches) { QString runnerId = match.runner()->id(); auto it = matchesForRunner.find(runnerId); if (it == matchesForRunner.end()) { it = matchesForRunner.insert(runnerId, QList()); } it.value().append(match); } // Assign matches to existing models. If there is no match for a model, delete it. for (int row = m_models.count() - 1; row >= 0; --row) { RunnerMatchesModel *matchesModel = m_models.at(row); QList matches = matchesForRunner.take(matchesModel->runnerId()); - matchesModel->setMatches(matches); + if (m_deleteWhenEmpty && matches.isEmpty()) { + beginRemoveRows(QModelIndex(), row, row); + m_models.removeAt(row); + delete matchesModel; + endRemoveRows(); + } else { + matchesModel->setMatches(matches); + } } // At this point, matchesForRunner contains only matches for runners which // do not have a model yet. Create new models for them. if (!matchesForRunner.isEmpty()) { auto it = matchesForRunner.constBegin(); auto end = matchesForRunner.constEnd(); int appendCount = 0; for (; it != end; ++it) { QList matches = it.value(); Q_ASSERT(!matches.isEmpty()); QString name = matches.first().runner()->name(); RunnerMatchesModel *matchesModel = new RunnerMatchesModel(it.key(), name, m_runnerManager, this); matchesModel->setMatches(matches); if (it.key() == "services") { beginInsertRows(QModelIndex(), 0, 0); m_models.prepend(matchesModel); endInsertRows(); emit countChanged(); } else { m_models.append(matchesModel); ++appendCount; } } if (appendCount > 0) { beginInsertRows(QModelIndex(), rowCount() - appendCount, rowCount() - 1); endInsertRows(); emit countChanged(); } } } void RunnerModel::createManager() { if (!m_runnerManager) { m_runnerManager = new Plasma::RunnerManager(this); // FIXME: Which KConfigGroup is this using now? m_runnerManager->setAllowedRunners(m_runners); connect(m_runnerManager, SIGNAL(matchesChanged(QList)), this, SLOT(matchesChanged(QList))); } } void RunnerModel::clear() { if (m_runnerManager) { m_runnerManager->reset(); } if (m_models.isEmpty()) { return; } beginResetModel(); qDeleteAll(m_models); m_models.clear(); endResetModel(); emit countChanged(); } diff --git a/runnermodel.h b/runnermodel.h index 254cf67ab..d827e6d8a 100644 --- a/runnermodel.h +++ b/runnermodel.h @@ -1,89 +1,95 @@ /*************************************************************************** * Copyright (C) 2012 by Aurélien Gâteau * * Copyright (C) 2014 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 . * ***************************************************************************/ #ifndef RUNNERMODEL_H #define RUNNERMODEL_H #include "abstractmodel.h" #include #include #include namespace Plasma { class RunnerManager; } class AbstractModel; class RunnerMatchesModel; class RunnerModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(int count READ count NOTIFY countChanged) Q_PROPERTY(AbstractModel* favoritesModel READ favoritesModel WRITE setFavoritesModel NOTIFY favoritesModelChanged) Q_PROPERTY(QStringList runners READ runners WRITE setRunners NOTIFY runnersChanged); Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged); + Q_PROPERTY(bool deleteWhenEmpty READ deleteWhenEmpty WRITE setDeleteWhenEmpty NOTIFY deleteWhenEmptyChanged) public: explicit RunnerModel(QObject *parent = 0); ~RunnerModel(); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; int count() const; Q_INVOKABLE QObject *modelForRow(int row); QStringList runners() const; void setRunners(const QStringList &runners); QString query() const; void setQuery(const QString &query); AbstractModel *favoritesModel() const; void setFavoritesModel(AbstractModel *model); + bool deleteWhenEmpty() const; + void setDeleteWhenEmpty(bool deleteWhenEmpty); + Q_SIGNALS: void countChanged() const; void favoritesModelChanged() const; void runnersChanged() const; void queryChanged() const; + void deleteWhenEmptyChanged(); private Q_SLOTS: void startQuery(); void matchesChanged(const QList &matches); private: void createManager(); void clear(); AbstractModel *m_favoritesModel; Plasma::RunnerManager *m_runnerManager; QStringList m_runners; QList m_models; QString m_query; QTimer m_queryTimer; + bool m_deleteWhenEmpty; }; #endif diff --git a/systemmodel.cpp b/systemmodel.cpp index 5527dc66f..593919d12 100644 --- a/systemmodel.cpp +++ b/systemmodel.cpp @@ -1,94 +1,101 @@ /*************************************************************************** * Copyright (C) 2014-2015 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 "systemmodel.h" #include "actionlist.h" #include "favoritesmodel.h" #include "systementry.h" +#include + SystemModel::SystemModel(QObject *parent) : AbstractModel(parent) , m_favoritesModel(new FavoritesModel(this)) { QList actions; actions << new SystemEntry(this, SystemEntry::LockSession); actions << new SystemEntry(this, SystemEntry::LogoutSession); actions << new SystemEntry(this, SystemEntry::SaveSession); actions << new SystemEntry(this, SystemEntry::NewSession); actions << new SystemEntry(this, SystemEntry::SuspendToRam); actions << new SystemEntry(this, SystemEntry::SuspendToDisk); actions << new SystemEntry(this, SystemEntry::Reboot); actions << new SystemEntry(this, SystemEntry::Shutdown); foreach(SystemEntry *entry, actions) { if (entry->isValid()) { m_entryList << entry; } else { delete entry; } } } SystemModel::~SystemModel() { qDeleteAll(m_entryList); } +QString SystemModel::description() const +{ + return i18n("System actions"); +} + QVariant SystemModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= m_entryList.count()) { return QVariant(); } const SystemEntry *entry = m_entryList.at(index.row()); if (role == Qt::DisplayRole) { return entry->name(); } else if (role == Qt::DecorationRole) { return entry->iconName(); } else if (role == Kicker::FavoriteIdRole) { return entry->id(); } return QVariant(); } int SystemModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : m_entryList.count(); } bool SystemModel::trigger(int row, const QString &actionId, const QVariant &argument) { Q_UNUSED(actionId) Q_UNUSED(argument) if (row >= 0 && row < m_entryList.count()) { m_entryList.at(row)->run(); return true; } return false; } AbstractModel* SystemModel::favoritesModel() { return m_favoritesModel; } diff --git a/systemmodel.h b/systemmodel.h index b368d46d2..995ff2a9a 100644 --- a/systemmodel.h +++ b/systemmodel.h @@ -1,48 +1,50 @@ /*************************************************************************** * Copyright (C) 2014-2015 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 . * ***************************************************************************/ #ifndef SYSTEMMODEL_H #define SYSTEMMODEL_H #include "abstractmodel.h" class SystemEntry; class SystemModel : public AbstractModel { Q_OBJECT public: explicit SystemModel(QObject *parent = 0); ~SystemModel(); + QString description() const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; Q_INVOKABLE bool trigger(int row, const QString &actionId, const QVariant &argument); AbstractModel* favoritesModel(); private: QList m_entryList; AbstractModel *m_favoritesModel; }; #endif