diff --git a/applets/kicker/package/contents/ui/ConfigGeneral.qml b/applets/kicker/package/contents/ui/ConfigGeneral.qml index 514e5a6dc..d461a1e46 100644 --- a/applets/kicker/package/contents/ui/ConfigGeneral.qml +++ b/applets/kicker/package/contents/ui/ConfigGeneral.qml @@ -1,243 +1,251 @@ /*************************************************************************** * 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 . * ***************************************************************************/ import QtQuick 2.5 import QtQuick.Controls 2.5 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.kquickcontrolsaddons 2.0 as KQuickAddons import org.kde.draganddrop 2.0 as DragDrop import org.kde.kirigami 2.5 as Kirigami import org.kde.plasma.private.kicker 0.1 as Kicker Kirigami.FormLayout { id: configGeneral anchors.left: parent.left anchors.right: parent.right property bool isDash: (plasmoid.pluginName === "org.kde.plasma.kickerdash") property string cfg_icon: plasmoid.configuration.icon property bool cfg_useCustomButtonImage: plasmoid.configuration.useCustomButtonImage property string cfg_customButtonImage: plasmoid.configuration.customButtonImage property alias cfg_appNameFormat: appNameFormat.currentIndex property alias cfg_limitDepth: limitDepth.checked property alias cfg_alphaSort: alphaSort.checked + property alias cfg_showIconsRootLevel: showIconsRootLevel.checked property alias cfg_recentOrdering: recentOrdering.currentIndex property alias cfg_showRecentApps: showRecentApps.checked property alias cfg_showRecentDocs: showRecentDocs.checked property alias cfg_showRecentContacts: showRecentContacts.checked property alias cfg_useExtraRunners: useExtraRunners.checked property alias cfg_alignResultsToBottom: alignResultsToBottom.checked Button { id: iconButton Kirigami.FormData.label: i18n("Icon:") implicitWidth: previewFrame.width + units.smallSpacing * 2 implicitHeight: previewFrame.height + units.smallSpacing * 2 // Just to provide some visual feedback when dragging; // cannot have checked without checkable enabled checkable: true checked: dropArea.containsAcceptableDrag onPressed: iconMenu.opened ? iconMenu.close() : iconMenu.open() DragDrop.DropArea { id: dropArea property bool containsAcceptableDrag: false anchors.fill: parent onDragEnter: { // Cannot use string operations (e.g. indexOf()) on "url" basic type. var urlString = event.mimeData.url.toString(); // This list is also hardcoded in KIconDialog. var extensions = [".png", ".xpm", ".svg", ".svgz"]; containsAcceptableDrag = urlString.indexOf("file:///") === 0 && extensions.some(function (extension) { return urlString.indexOf(extension) === urlString.length - extension.length; // "endsWith" }); if (!containsAcceptableDrag) { event.ignore(); } } onDragLeave: containsAcceptableDrag = false onDrop: { if (containsAcceptableDrag) { // Strip file:// prefix, we already verified in onDragEnter that we have only local URLs. iconDialog.setCustomButtonImage(event.mimeData.url.toString().substr("file://".length)); } containsAcceptableDrag = false; } } KQuickAddons.IconDialog { id: iconDialog function setCustomButtonImage(image) { cfg_customButtonImage = image || cfg_icon || "start-here-kde" cfg_useCustomButtonImage = true; } onIconNameChanged: setCustomButtonImage(iconName); } PlasmaCore.FrameSvgItem { id: previewFrame anchors.centerIn: parent imagePath: plasmoid.location === PlasmaCore.Types.Vertical || plasmoid.location === PlasmaCore.Types.Horizontal ? "widgets/panel-background" : "widgets/background" width: units.iconSizes.large + fixedMargins.left + fixedMargins.right height: units.iconSizes.large + fixedMargins.top + fixedMargins.bottom PlasmaCore.IconItem { anchors.centerIn: parent width: units.iconSizes.large height: width source: cfg_useCustomButtonImage ? cfg_customButtonImage : cfg_icon } } Menu { id: iconMenu // Appear below the button y: +parent.height onClosed: iconButton.checked = false; MenuItem { text: i18nc("@item:inmenu Open icon chooser dialog", "Choose...") icon.name: "document-open-folder" onClicked: iconDialog.open() } MenuItem { text: i18nc("@item:inmenu Reset icon to default", "Clear Icon") icon.name: "edit-clear" onClicked: { cfg_icon = "start-here-kde" cfg_useCustomButtonImage = false } } } } Item { Kirigami.FormData.isSection: true } ComboBox { id: appNameFormat Kirigami.FormData.label: i18n("Show applications as:") model: [i18n("Name only"), i18n("Description only"), i18n("Name (Description)"), i18n("Description (Name)")] } Item { Kirigami.FormData.isSection: true } CheckBox { id: alphaSort Kirigami.FormData.label: i18n("Behavior:") text: i18n("Sort applications alphabetically") } CheckBox { id: limitDepth visible: !isDash text: i18n("Flatten sub-menus to a single level") } + CheckBox { + id: showIconsRootLevel + + visible: !isDash + + text: i18n("Show icons on the root level of the menu") + } Item { Kirigami.FormData.isSection: true } CheckBox { id: showRecentApps Kirigami.FormData.label: i18n("Show categories:") text: recentOrdering.currentIndex == 0 ? i18n("Recent applications") : i18n("Often used applications") } CheckBox { id: showRecentDocs text: recentOrdering.currentIndex == 0 ? i18n("Recent documents") : i18n("Often used documents") } CheckBox { id: showRecentContacts text: recentOrdering.currentIndex == 0 ? i18n("Recent contacts") : i18n("Often used contacts") } ComboBox { id: recentOrdering Kirigami.FormData.label: i18n("Sort items in categories by:") model: [i18nc("@item:inlistbox Sort items in categories by [Recently used | Often used]", "Recently used"), i18nc("@item:inlistbox Sort items in categories by [Recently used | Ofetn used]", "Often used")] } Item { Kirigami.FormData.isSection: true } CheckBox { id: useExtraRunners Kirigami.FormData.label: i18n("Search:") text: i18n("Expand search to bookmarks, files and emails") } CheckBox { id: alignResultsToBottom visible: !isDash text: i18n("Align search results to bottom") } } diff --git a/applets/kicker/plugin/rootmodel.cpp b/applets/kicker/plugin/rootmodel.cpp index 3cf5676d8..f7a605ad3 100644 --- a/applets/kicker/plugin/rootmodel.cpp +++ b/applets/kicker/plugin/rootmodel.cpp @@ -1,428 +1,432 @@ /*************************************************************************** * 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 "kastatsfavoritesmodel.h" #include "recentcontactsmodel.h" #include "recentusagemodel.h" #include "systemmodel.h" #include #include GroupEntry::GroupEntry(AppsModel *parentModel, const QString &name, const QString &iconName, AbstractModel *childModel) : AbstractGroupEntry(parentModel) , m_name(name) , m_iconName(iconName) , m_childModel(childModel) { 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(QStringLiteral("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 KAStatsFavoritesModel(this)) , m_systemModel(nullptr) , m_showAllApps(false) , m_showRecentApps(true) , m_showRecentDocs(true) , m_showRecentContacts(false) , m_recentOrdering(RecentUsageModel::Recent) , m_showPowerSession(true) , m_recentAppsModel(nullptr) , m_recentDocsModel(nullptr) , m_recentContactsModel(nullptr) { } 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()), QStringLiteral("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 == QLatin1String("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::showAllApps() const { return m_showAllApps; } void RootModel::setShowAllApps(bool show) { if (m_showAllApps != show) { m_showAllApps = show; refresh(); emit showAllAppsChanged(); } } bool RootModel::showRecentApps() const { return m_showRecentApps; } void RootModel::setShowRecentApps(bool show) { if (show != m_showRecentApps) { m_showRecentApps = show; refresh(); emit showRecentAppsChanged(); } } 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(); } } int RootModel::recentOrdering() const { return m_recentOrdering; } void RootModel::setRecentOrdering(int ordering) { if (ordering != m_recentOrdering) { m_recentOrdering = ordering; refresh(); emit recentOrderingChanged(); } } bool RootModel::showPowerSession() const { return m_showPowerSession; } void RootModel::setShowPowerSession(bool show) { if (show != m_showPowerSession) { m_showPowerSession = show; refresh(); emit showPowerSessionChanged(); } } AbstractModel* RootModel::favoritesModel() { return m_favorites; } AbstractModel* RootModel::systemFavoritesModel() { if (m_systemModel) { return m_systemModel->favoritesModel(); } return nullptr; } void RootModel::refresh() { if (!m_complete) { return; } beginResetModel(); AppsModel::refreshInternal(); AppsModel *allModel = nullptr; m_recentAppsModel = nullptr; m_recentDocsModel = nullptr; m_recentContactsModel = nullptr; if (m_showAllApps) { QList groups; if (m_paginate) { m_favorites = new KAStatsFavoritesModel(this); emit favoritesModelChanged(); QHash appsHash; QList apps; foreach (const AbstractEntry *groupEntry, m_entryList) { AbstractModel *model = groupEntry->childModel(); if (!model) continue; for (int i = 0; i < model->count(); ++i) { GroupEntry *subGroupEntry = static_cast(model->index(i, 0).internalPointer()); AbstractModel *subModel = subGroupEntry->childModel(); for (int j = 0; j < subModel->count(); ++j) { AppEntry *appEntry = static_cast(subModel->index(j, 0).internalPointer()); if (appEntry->name().isEmpty()) { continue; } appsHash.insert(appEntry->service()->menuId(), appEntry); } } } apps = appsHash.values(); QCollator c; std::sort(apps.begin(), apps.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; } }); int at = 0; QList page; page.reserve(m_pageSize); foreach(AppEntry *app, apps) { page.append(app); if (at == (m_pageSize - 1)) { at = 0; AppsModel *model = new AppsModel(page, false, this); groups.append(new GroupEntry(this, QString(), QString(), model)); page.clear(); } else { ++at; } } if (!page.isEmpty()) { AppsModel *model = new AppsModel(page, false, this); groups.append(new GroupEntry(this, QString(), QString(), model)); } groups.prepend(new GroupEntry(this, QString(), QString(), m_favorites)); } else { QHash> m_categoryHash; foreach (const AbstractEntry *groupEntry, m_entryList) { AbstractModel *model = groupEntry->childModel(); if (!model) continue; for (int i = 0; i < model->count(); ++i) { AbstractEntry *appEntry = static_cast(model->index(i, 0).internalPointer()); if (appEntry->name().isEmpty()) { continue; } const QChar &first = appEntry->name().at(0).toUpper(); m_categoryHash[first.isDigit() ? QStringLiteral("0-9") : first].append(appEntry); } } QHashIterator> i(m_categoryHash); 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)); } } allModel = new AppsModel(groups, true, this); allModel->setDescription(QStringLiteral("KICKER_ALL_MODEL")); // Intentionally no i18n. } int separatorPosition = 0; if (allModel) { m_entryList.prepend(new GroupEntry(this, i18n("All Applications"), QString(), allModel)); ++separatorPosition; } if (m_showRecentContacts) { m_recentContactsModel = new RecentContactsModel(this); - m_entryList.prepend(new GroupEntry(this, i18n("Recent Contacts"), QString(), m_recentContactsModel)); + m_entryList.prepend(new GroupEntry(this, i18n("Recent Contacts"), QString("view-history"), m_recentContactsModel)); ++separatorPosition; } if (m_showRecentDocs) { m_recentDocsModel = new RecentUsageModel(this, RecentUsageModel::OnlyDocs, m_recentOrdering); m_entryList.prepend(new GroupEntry(this, m_recentOrdering == RecentUsageModel::Recent ? i18n("Recent Documents") : i18n("Often Used Documents"), - QString(), + m_recentOrdering == RecentUsageModel::Recent + ? QString("view-history") + : QString("office-chart-pie"), m_recentDocsModel)); ++separatorPosition; } if (m_showRecentApps) { m_recentAppsModel = new RecentUsageModel(this, RecentUsageModel::OnlyApps, m_recentOrdering); m_entryList.prepend(new GroupEntry(this, m_recentOrdering == RecentUsageModel::Recent ? i18n("Recent Applications") : i18n("Often Used Applications"), - QString(), + m_recentOrdering == RecentUsageModel::Recent + ? QString("view-history") + : QString("office-chart-pie"), m_recentAppsModel)); ++separatorPosition; } if (m_showSeparators && separatorPosition > 0) { m_entryList.insert(separatorPosition, new SeparatorEntry(this)); ++m_separatorCount; } m_systemModel = new SystemModel(this); if (m_showPowerSession) { - m_entryList << new GroupEntry(this, i18n("Power / Session"), QString(), m_systemModel); + m_entryList << new GroupEntry(this, i18n("Power / Session"), QString("system-log-out"), m_systemModel); } endResetModel(); m_favorites->refresh(); emit systemFavoritesModelChanged(); emit countChanged(); emit separatorCountChanged(); emit refreshed(); }