diff --git a/abstractmodel.cpp b/abstractmodel.cpp index c05daa0f3..8bfdc097c 100644 --- a/abstractmodel.cpp +++ b/abstractmodel.cpp @@ -1,137 +1,144 @@ /*************************************************************************** * 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::GroupRole, "group"); roles.insert(Kicker::DescriptionRole, "description"); 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; } +int AbstractModel::rowForModel(AbstractModel *model) +{ + Q_UNUSED(model) + + return -1; +} + 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 142de27db..029e8fc2e 100644 --- a/abstractmodel.h +++ b/abstractmodel.h @@ -1,80 +1,81 @@ /*************************************************************************** * 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); + Q_INVOKABLE virtual int rowForModel(AbstractModel *model); 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/appsmodel.cpp b/appsmodel.cpp index b2c246518..7669a6feb 100644 --- a/appsmodel.cpp +++ b/appsmodel.cpp @@ -1,514 +1,525 @@ /*************************************************************************** * 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, 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() { 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::DescriptionRole) { return entry->description(); } else if (role == Kicker::FavoriteIdRole && entry->type() == AbstractEntry::RunnableType) { return entry->id(); } else if (role == Kicker::UrlRole && entry->type() == AbstractEntry::RunnableType) { return entry->url(); } 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::rowForModel(AbstractModel *model) +{ + for (int i = 0; i < m_entryList.count(); ++i) { + if (m_entryList.at(i)->childModel() == model) { + return i; + } + } + + return -1; +} + 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(); 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(); } 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_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) { sortEntries(); } } } 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) && 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_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 3cd05772f..f907d7054 100644 --- a/appsmodel.h +++ b/appsmodel.h @@ -1,110 +1,111 @@ /*************************************************************************** * 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, 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); + Q_INVOKABLE int rowForModel(AbstractModel *model); 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 cleared() const; void flatChanged() const; void showSeparatorsChanged() const; void appNameFormatChanged() const; 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