diff --git a/app/indicator/factory.cpp b/app/indicator/factory.cpp index 1ede16c5..ba0f01a3 100644 --- a/app/indicator/factory.cpp +++ b/app/indicator/factory.cpp @@ -1,261 +1,308 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock 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. * * Latte-Dock 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, see . */ #include "factory.h" // local #include "../importer.h" // Qt #include #include #include +#include #include #include // KDE #include #include #include #include #include #include #include #include #include namespace Latte { namespace Indicator { Factory::Factory(QObject *parent) : QObject(parent) { m_parentWidget = new QWidget(); m_watchedPaths = Latte::Importer::standardPaths(); for(int i=0; iaddDir(dir); } connect(KDirWatch::self(), &KDirWatch::dirty, this, [ & ](const QString & path) { if (m_watchedPaths.contains(path)) { reload(); } }); qDebug() << m_plugins["org.kde.latte.default"].name(); } Factory::~Factory() { m_parentWidget->deleteLater(); } +bool Factory::pluginExists(QString id) const +{ + return m_plugins.contains(id); +} + int Factory::customPluginsCount() { return m_customPluginIds.count(); } QStringList Factory::customPluginIds() { return m_customPluginIds; } QStringList Factory::customPluginNames() { return m_customPluginNames; } +QStringList Factory::customLocalPluginIds() +{ + return m_customLocalPluginIds; +} KPluginMetaData Factory::metadata(QString pluginId) { if (m_plugins.contains(pluginId)) { return m_plugins[pluginId]; } return KPluginMetaData(); } void Factory::reload() { m_plugins.clear(); m_customPluginIds.clear(); m_customPluginNames.clear(); + m_customLocalPluginIds.clear(); for(const auto &path : m_watchedPaths) { QDir standard(path); if (standard.exists()) { QStringList pluginDirs = standard.entryList(QStringList(),QDir::AllDirs | QDir::NoSymLinks); foreach (auto pluginDir, pluginDirs) { if (pluginDir != "." && pluginDir != "..") { QString metadataFile = standard.absolutePath() + "/" + pluginDir + "/metadata.desktop"; KPluginMetaData metadata = KPluginMetaData::fromDesktopFile(metadataFile); if (metadataAreValid(metadata)) { QString uiFile = standard.absolutePath() + "/" + pluginDir + "/package/" + metadata.value("X-Latte-MainScript"); if (QFileInfo(uiFile).exists() && !m_plugins.contains(metadata.pluginId())) { m_plugins[metadata.pluginId()] = metadata; if ((metadata.pluginId() != "org.kde.latte.default") && (metadata.pluginId() != "org.kde.latte.plasma")) { m_customPluginIds << metadata.pluginId(); m_customPluginNames << metadata.name(); } + if (standard.absolutePath().startsWith(QDir::homePath())) { + m_customLocalPluginIds << metadata.pluginId(); + } + QString pluginPath = metadata.fileName().remove("metadata.desktop"); qDebug() << " Indicator Package Loaded ::: " << metadata.name() << " [" << metadata.pluginId() << "]" << " - [" <setText(i18n("Failed to import indicator")); notification->sendEvent(); }; auto showNotificationSucceed = [](QString name, bool updated) { auto notification = new KNotification("import-done", KNotification::CloseOnTimeout); notification->setText(updated ? i18nc("indicator_name, imported updated","%0 indicator updated successfully").arg(name) : i18nc("indicator_name, imported success","%0 indicator installed successfully").arg(name)); notification->sendEvent(); }; KArchive *archive; KZip *zipArchive = new KZip(compressedFile); zipArchive->open(QIODevice::ReadOnly); //! if the file isnt a zip archive if (!zipArchive->isOpen()) { delete zipArchive; KTar *tarArchive = new KTar(compressedFile, QStringLiteral("application/x-tar")); tarArchive->open(QIODevice::ReadOnly); if (!tarArchive->isOpen()) { delete tarArchive; showNotificationError(); return Latte::Types::Failed; } else { archive = tarArchive; } } else { archive = zipArchive; } QTemporaryDir archiveTempDir; archive->directory()->copyTo(archiveTempDir.path()); //metadata file QString packagePath = archiveTempDir.path(); QString metadataFile = archiveTempDir.path() + "/metadata.desktop"; if (!QFileInfo(metadataFile).exists()){ QDirIterator iter(archiveTempDir.path(), QDir::Dirs | QDir::NoDotAndDotDot); while(iter.hasNext() ) { QString currentPath = iter.next(); QString tempMetadata = currentPath + "/metadata.desktop"; if (QFileInfo(tempMetadata).exists()) { metadataFile = tempMetadata; packagePath = currentPath; } } } KPluginMetaData metadata = KPluginMetaData::fromDesktopFile(metadataFile); if (metadataAreValid(metadata)) { QStringList standardPaths = Latte::Importer::standardPaths(); QString installPath = standardPaths[0] + "/latte/indicators/" + metadata.pluginId(); bool updated{QDir(installPath).exists()}; if (QDir(installPath).exists()) { QDir(installPath).removeRecursively(); } - //! Identify Plasma Desktop version QProcess process; process.start(QString("mv " +packagePath + " " + installPath)); process.waitForFinished(); QString output(process.readAllStandardOutput()); showNotificationSucceed(metadata.name(), updated); return Latte::Types::Installed; } showNotificationError(); return Latte::Types::Failed; } +void Factory::removeIndicator(QString id) +{ + if (m_plugins.contains(id)) { + QString pluginName = m_plugins[id].name(); + + auto msg = new QMessageBox(m_parentWidget); + msg->setIcon(QMessageBox::Warning); + msg->setWindowTitle(i18n("Remove Indicator")); + msg->setText( + i18n("Do you want to remove %0 indicator from your system?").arg(pluginName)); + msg->setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msg->setDefaultButton(QMessageBox::No); + + connect(msg, &QMessageBox::finished, this, [ &, msg, id, pluginName](int result) { + auto showRemovedSucceed = [](QString name) { + auto notification = new KNotification("remove-done", KNotification::CloseOnTimeout); + notification->setText(i18nc("indicator_name, removed success","%0 indicator removed successfully").arg(name)); + notification->sendEvent(); + }; + + if (result == QMessageBox::Yes) { + qDebug() << "Trying to remove indicator :: " << id; + QProcess process; + process.start(QString("kpackagetool5 -r " +id + " -t Latte/Indicator")); + process.waitForFinished(); + showRemovedSucceed(pluginName); + } + }); + + msg->open(); + } +} + void Factory::downloadIndicator() { KNS3::DownloadDialog dialog(QStringLiteral("latte-indicators.knsrc"), m_parentWidget); dialog.exec(); } } } diff --git a/app/indicator/factory.h b/app/indicator/factory.h index c043b29c..fde2f86c 100644 --- a/app/indicator/factory.h +++ b/app/indicator/factory.h @@ -1,78 +1,84 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock 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. * * Latte-Dock 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, see . */ #ifndef INDICATORFACTORY_H #define INDICATORFACTORY_H // local #include "../../liblatte2/types.h" // Qt #include #include #include class KPluginMetaData; namespace Latte { namespace Indicator { class Factory : public QObject { Q_OBJECT public: Factory(QObject *parent); ~Factory() override; - void reload(); - int customPluginsCount(); QStringList customPluginIds(); QStringList customPluginNames(); + QStringList customLocalPluginIds(); KPluginMetaData metadata(QString pluginId); void downloadIndicator(); + void removeIndicator(QString id); + + bool pluginExists(QString id) const; //! metadata record static bool metadataAreValid(KPluginMetaData &metadata); //! metadata file static bool metadataAreValid(QString &file); //! imports an indicator compressed file static Latte::Types::ImportExportState importIndicatorFile(QString compressedFile); signals: void customPluginsChanged(); +private: + void reload(); + private: QHash m_plugins; QStringList m_customPluginIds; QStringList m_customPluginNames; + QStringList m_customLocalPluginIds; QStringList m_watchedPaths; QWidget *m_parentWidget; }; } } #endif diff --git a/app/lattedock.notifyrc b/app/lattedock.notifyrc index d4c8727e..b1d6e51d 100644 --- a/app/lattedock.notifyrc +++ b/app/lattedock.notifyrc @@ -1,127 +1,135 @@ [Global] Name=Latte Dock Name[ar]=رصيف لاتيه Name[ca]=Latte Dock Name[ca@valencia]=Latte Dock Name[cs]=Dok Latte Name[da]=Latte-dok Name[de]=Latte-Dock Name[el]=Πίνακας Latte Name[en_GB]=Latte Dock Name[es]=Latte Dock Name[eu]=Latte Dock Name[fi]=Latte-telakka Name[gl]=Doca Latte Name[it]=Latte Dock Name[nl]=Latte Dock Name[nn]=Latte-dokk Name[pl]=Dok Latte Name[pt]=Área Acoplável do Latte Name[pt_BR]=Latte Dock Name[ru]=Latte Dock Name[sv]=Latte dockningsfönster Name[uk]=Панель Латте Name[x-test]=xxLatte Dockxx Name[zh_CN]=Latte 停靠栏 Name[zh_TW]=Latte Dock Comment=Latte Dock Comment[ar]=رصيف لاتيه Comment[ca]=Acoblable del Latte Comment[ca@valencia]=Acoblable del Latte Comment[cs]=Dok Latte Comment[da]=Latte-dok Comment[de]=Latte-Dock Comment[el]=Πίνακας Latte Comment[en_GB]=Latte Dock Comment[es]=Latte Dock Comment[eu]=Latte Dock Comment[fi]=Latte-telakka Comment[gl]=Doca Latte. Comment[it]=Latte Dock Comment[nl]=Latte Dock Comment[nn]=Latte-dokk Comment[pl]=Dok Latte Comment[pt]=Área Acoplável do Latte Comment[pt_BR]=Latte Dock Comment[ru]=Latte Dock Comment[sv]=Latte dockningsfönster Comment[uk]=Панель Латте Comment[x-test]=xxLatte Dockxx Comment[zh_CN]=Latte 停靠栏 Comment[zh_TW]=Latte Dock IconName=latte-dock [Event/import-done] Name=Imported successfully Name[ca]=S'ha importat amb èxit Name[en_GB]=Imported successfully Name[gl]=Importouse correctamente Name[nl]=Met succes geïmporteerd Name[pt]=Importado com sucesso Name[sv]=Importerade med lyckat resultat Name[uk]=Успішно імпортовано Name[x-test]=xxImported successfullyxx Action=Popup [Event/import-fail] Name=Failed to import Name[ca]=Ha fallat en importar Name[en_GB]=Failed to import Name[gl]=Non se puido importar Name[nl]=Importeren is mislukt Name[pt]=Não foi possível importar Name[sv]=Misslyckades importera Name[uk]=Не вдалося імпортувати Name[x-test]=xxFailed to importxx Action=Popup [Event/export-done] Name=Exported successfully Name[ca]=S'ha exportat amb èxit Name[en_GB]=Exported successfully Name[gl]=Exportouse correctamente Name[nl]=Met succes geëxporteerd Name[pt]=Exportada com sucesso Name[sv]=Exporterade med lyckat resultat Name[uk]=Успішно експортовано Name[x-test]=xxExported successfullyxx Action=Popup [Event/export-fail] Name=Failed to export Name[ca]=Ha fallat en exportar Name[en_GB]=Failed to export Name[gl]=Non se puido exportar Name[nl]=Exporteren is mislukt Name[pt]=Não foi possível exportar Name[sv]=Misslyckades exportera Name[uk]=Не вдалося експортувати Name[x-test]=xxFailed to exportxx Action=Popup +[Event/remove-done] +Name=Removed successfully +Action=Popup + +[Event/remove-fail] +Name=Failed to remove +Action=Popup + [Event/switch-layout] Name=Switch to layout Name[ca]=Commuta a la disposició Name[ca@valencia]=Commuta a la disposició Name[cs]=Přepnout na rozložení Name[da]=Skift til layout Name[de]=Wechsel zu Profil Name[el]=Εναλλαγή στο προφίλ Name[en_GB]=Switch to layout Name[es]=Cambiar a distribución Name[eu]=Aldatu diseinura Name[fi]=Vaihda asetteluun Name[gl]=Cambiar á disposición Name[it]=Passa alla disposizione Name[nl]=Naar indeling omschakelen Name[nn]=Byt til utforming Name[pl]=Przełącz układ Name[pt]=Mudar para a disposição Name[pt_BR]=Mudar para o layout Name[ru]=Использовать макет Name[sv]=Byt till layout Name[uk]=Перемкнутися на компонування Name[x-test]=xxSwitch to layoutxx Name[zh_CN]=切换布局 Name[zh_TW]=切換至佈局 Action=Popup diff --git a/app/view/indicator.cpp b/app/view/indicator.cpp index fbaefe91..edc90e5d 100644 --- a/app/view/indicator.cpp +++ b/app/view/indicator.cpp @@ -1,426 +1,445 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock 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. * * Latte-Dock 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, see . */ #include "indicator.h" // local #include "view.h" #include "../lattecorona.h" #include "../indicator/factory.h" #include "../../liblatte2/types.h" // Qt #include // KDE #include #include #include #include namespace Latte { namespace ViewPart { Indicator::Indicator(Latte::View *parent) : QObject(parent), m_view(parent) { m_corona = qobject_cast(m_view->corona()); loadConfig(); connect(this, &Indicator::enabledChanged, this, &Indicator::saveConfig); connect(this, &Indicator::enabledForAppletsChanged, this, &Indicator::saveConfig); connect(this, &Indicator::paddingChanged, this, &Indicator::saveConfig); connect(this, &Indicator::reversedChanged, this, &Indicator::saveConfig); connect(this, &Indicator::pluginChanged, this, &Indicator::saveConfig); connect(m_view, &Latte::View::latteTasksArePresentChanged, this, &Indicator::latteTasksArePresentChanged); - connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::customPluginsChanged, this, &Indicator::customPluginsChanged); + connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::customPluginsChanged, [this]() { + if (!m_corona->indicatorFactory()->pluginExists(m_type)) { + setType("org.kde.latte.default"); + } + + emit customPluginsChanged(); + }); connect(this, &Indicator::pluginChanged, [this]() { if ((m_type != "org.kde.latte.default") && m_type != "org.kde.latte.plasma") { setCustomType(m_type); } }); load(m_type); loadPlasmaComponent(); } Indicator::~Indicator() { if (m_component) { m_component->deleteLater(); } if (m_configLoader) { m_configLoader->deleteLater(); } if (m_configuration) { m_configuration->deleteLater(); } } bool Indicator::enabled() const { return m_enabled; } void Indicator::setEnabled(bool enabled) { if (m_enabled == enabled) { return; } m_enabled = enabled; emit enabledChanged(); } bool Indicator::enabledForApplets() const { return m_enabledForApplets; } void Indicator::setEnabledForApplets(bool enabled) { if (m_enabledForApplets == enabled) { return; } m_enabledForApplets = enabled; emit enabledForAppletsChanged(); } bool Indicator::latteTasksArePresent() { return m_view->latteTasksArePresent(); } bool Indicator::providesConfigUi() const { return m_providesConfigUi; } void Indicator::setProvidesConfigUi(bool provides) { if (m_providesConfigUi == provides) { return; } m_providesConfigUi = provides; emit providesConfigUiChanged(); } bool Indicator::reversed() const { return m_reversed; } void Indicator::setReversed(bool reversed) { if (m_reversed == reversed) { return; } m_reversed = reversed; emit reversedChanged(); } float Indicator::padding() const { return m_padding; } void Indicator::setPadding(float padding) { if (m_padding == padding) { return; } m_padding = padding; emit paddingChanged(); } bool Indicator::pluginIsReady() { return m_pluginIsReady; } void Indicator::setPluginIsReady(bool ready) { if (m_pluginIsReady == ready) { return; } m_pluginIsReady = ready; emit pluginIsReadyChanged(); } QString Indicator::type() const { return m_type; } void Indicator::setType(QString type) { if (m_type == type) { return; } load(type); } QString Indicator::customType() const { return m_customType; } void Indicator::setCustomType(QString type) { if (m_customType == type) { return; } m_customType = type; emit customPluginChanged(); } int Indicator::customPluginsCount() const { return m_corona->indicatorFactory()->customPluginsCount(); } QStringList Indicator::customPluginIds() const { return m_corona->indicatorFactory()->customPluginIds(); } QStringList Indicator::customPluginNames() const { return m_corona->indicatorFactory()->customPluginNames(); } +QStringList Indicator::customLocalPluginIds() const +{ + return m_corona->indicatorFactory()->customLocalPluginIds(); +} + QQmlComponent *Indicator::component() const { return m_component; } QQmlComponent *Indicator::plasmaComponent() const { return m_plasmaComponent; } QObject *Indicator::configuration() const { return m_configuration; } void Indicator::load(QString type) { KPluginMetaData metadata = m_corona->indicatorFactory()->metadata(type); if (metadata.isValid()) { bool state{m_enabled}; //! remove all previous indicators setPluginIsReady(false); m_metadata = metadata; m_type = type; QString path = m_metadata.fileName(); m_pluginPath = path.remove("metadata.desktop"); updateScheme(); updateComponent(); emit pluginChanged(); //! create all indicators with the new type setPluginIsReady(true); } else if (type!="org.kde.latte.default") { qDebug() << " Indicator metadata are not valid : " << type; setType("org.kde.latte.default"); } } void Indicator::updateComponent() { auto prevComponent = m_component; QString uiPath = m_metadata.value("X-Latte-MainScript"); if (!uiPath.isEmpty()) { uiPath = m_pluginPath + "package/" + uiPath; m_component = new QQmlComponent(m_view->engine(), uiPath); } if (prevComponent) { prevComponent->deleteLater(); } } void Indicator::loadPlasmaComponent() { auto prevComponent = m_plasmaComponent; KPluginMetaData metadata = m_corona->indicatorFactory()->metadata("org.kde.latte.plasma"); QString uiPath = metadata.value("X-Latte-MainScript"); if (!uiPath.isEmpty()) { QString path = metadata.fileName(); path = path.remove("metadata.desktop"); uiPath = path + "package/" + uiPath; m_plasmaComponent = new QQmlComponent(m_view->engine(), uiPath); } if (prevComponent) { prevComponent->deleteLater(); } emit plasmaComponentChanged(); } void Indicator::configUiFor(QString type, QQuickItem *parent) { if (m_lastCreatedConfigUi) { delete m_lastCreatedConfigUi; m_lastCreatedConfigUi = nullptr; } auto prevConfigUi = m_lastCreatedConfigUi; KPluginMetaData metadata; if (m_metadata.pluginId() == type) { metadata = m_metadata; } else { metadata = m_corona->indicatorFactory()->metadata(type); } if (metadata.isValid()) { QString uiPath = metadata.value("X-Latte-ConfigUi"); if (!uiPath.isEmpty()) { m_lastCreatedConfigUi = new KDeclarative::QmlObjectSharedEngine(parent); m_lastCreatedConfigUi->setInitializationDelayed(true); uiPath = m_pluginPath + "package/" + uiPath; m_lastCreatedConfigUi->setSource(QUrl::fromLocalFile(uiPath)); m_lastCreatedConfigUi->rootContext()->setContextProperty(QStringLiteral("indicator"), this); m_lastCreatedConfigUi->completeInitialization(); m_lastCreatedConfigUi->setTranslationDomain(QLatin1String("latte_indicator_") + m_metadata.pluginId()); QQuickItem *qmlItem = qobject_cast(m_lastCreatedConfigUi->rootObject()); qmlItem->setParentItem(parent); setProvidesConfigUi(true); } else { setProvidesConfigUi(false); } } } void Indicator::updateScheme() { auto prevConfigLoader = m_configLoader; auto prevConfiguration = m_configuration; QString xmlPath = m_metadata.value("X-Latte-ConfigXml"); if (!xmlPath.isEmpty()) { QFile file(m_pluginPath + "package/" + xmlPath); m_configLoader = new KConfigLoader(m_view->containment()->config().group("Indicator").group(m_metadata.pluginId()), &file); m_configuration = new KDeclarative::ConfigPropertyMap(m_configLoader, this); } else { m_configLoader = nullptr; m_configuration = nullptr; } if (prevConfigLoader) { prevConfigLoader->deleteLater(); } if (prevConfiguration) { prevConfiguration->deleteLater(); } } void Indicator::addIndicator() { QFileDialog *fileDialog = new QFileDialog(nullptr , i18nc("add indicator", "Add Indicator") , QDir::homePath() , QStringLiteral("indicator.latte")); fileDialog->setFileMode(QFileDialog::AnyFile); fileDialog->setAcceptMode(QFileDialog::AcceptOpen); fileDialog->setDefaultSuffix("indicator.latte"); QStringList filters; filters << QString(i18nc("add indicator file", "Latte Indicator") + "(*.indicator.latte)"); fileDialog->setNameFilters(filters); connect(fileDialog, &QFileDialog::finished, fileDialog, &QFileDialog::deleteLater); connect(fileDialog, &QFileDialog::fileSelected, this, [&](const QString & file) { qDebug() << "Trying to import indicator file ::: " << file; m_corona->indicatorFactory()->importIndicatorFile(file); }); fileDialog->open(); } void Indicator::downloadIndicator() { //! call asynchronously in order to not crash when view settings window //! loses focus and it closes QTimer::singleShot(0, [this]() { m_corona->indicatorFactory()->downloadIndicator(); }); } +void Indicator::removeIndicator(QString pluginId) +{ //! call asynchronously in order to not crash when view settings window + //! loses focus and it closes + QTimer::singleShot(0, [this, pluginId]() { + m_corona->indicatorFactory()->removeIndicator(pluginId); + }); +} + void Indicator::loadConfig() { auto config = m_view->containment()->config().group("Indicator"); m_customType = config.readEntry("customType", QString()); m_enabled = config.readEntry("enabled", true); m_enabledForApplets = config.readEntry("enabledForApplets", true); m_padding = config.readEntry("padding", (float)0.08); m_reversed = config.readEntry("reversed", false); m_type = config.readEntry("type", "org.kde.latte.default"); } void Indicator::saveConfig() { auto config = m_view->containment()->config().group("Indicator"); config.writeEntry("customType", m_customType); config.writeEntry("enabled", m_enabled); config.writeEntry("enabledForApplets", m_enabledForApplets); config.writeEntry("padding", m_padding); config.writeEntry("reversed", m_reversed); config.writeEntry("type", m_type); config.sync(); } } } diff --git a/app/view/indicator.h b/app/view/indicator.h index 954a5ad8..3e9551dc 100644 --- a/app/view/indicator.h +++ b/app/view/indicator.h @@ -1,172 +1,175 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock 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. * * Latte-Dock 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, see . */ #ifndef VIEWINDICATOR_H #define VIEWINDICATOR_H // Qt #include #include #include #include #include // KDE #include #include namespace KDeclarative { class ConfigPropertyMap; class QmlObjectSharedEngine; } namespace Latte { class Corona; class View; } namespace Latte { namespace ViewPart { class Indicator: public QObject { Q_OBJECT Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(bool enabledForApplets READ enabledForApplets WRITE setEnabledForApplets NOTIFY enabledForAppletsChanged) Q_PROPERTY(bool latteTasksArePresent READ latteTasksArePresent NOTIFY latteTasksArePresentChanged) Q_PROPERTY(bool pluginIsReady READ pluginIsReady NOTIFY pluginIsReadyChanged) Q_PROPERTY(bool providesConfigUi READ providesConfigUi NOTIFY providesConfigUiChanged) Q_PROPERTY(bool reversed READ reversed WRITE setReversed NOTIFY reversedChanged) Q_PROPERTY(float padding READ padding WRITE setPadding NOTIFY paddingChanged) Q_PROPERTY(QString type READ type WRITE setType NOTIFY pluginChanged) Q_PROPERTY(QString customType READ customType NOTIFY customPluginChanged) /* Custom plugins */ Q_PROPERTY(int customPluginsCount READ customPluginsCount NOTIFY customPluginsChanged) Q_PROPERTY(QStringList customPluginIds READ customPluginIds NOTIFY customPluginsChanged) Q_PROPERTY(QStringList customPluginNames READ customPluginNames NOTIFY customPluginsChanged) + Q_PROPERTY(QStringList customLocalPluginIds READ customLocalPluginIds NOTIFY customPluginsChanged) /** * Configuration object: each config key will be a writable property of this object. property bindings work. */ Q_PROPERTY(QObject *configuration READ configuration NOTIFY pluginChanged) Q_PROPERTY(QQmlComponent *component READ component NOTIFY pluginChanged) Q_PROPERTY(QQmlComponent *plasmaComponent READ plasmaComponent NOTIFY plasmaComponentChanged) public: Indicator(Latte::View *parent); virtual ~Indicator(); bool enabled() const; void setEnabled(bool enabled); bool enabledForApplets() const; void setEnabledForApplets(bool enabled); bool latteTasksArePresent(); bool providesConfigUi() const; bool pluginIsReady(); bool reversed() const; void setReversed(bool reversed); float padding() const; void setPadding(float padding); QString type() const; void setType(QString type); QString customType() const; int customPluginsCount() const; QStringList customPluginIds() const; QStringList customPluginNames() const; + QStringList customLocalPluginIds() const; QObject *configuration() const; QQmlComponent *component() const; QQmlComponent *plasmaComponent() const; void load(QString type); public slots: Q_INVOKABLE void configUiFor(QString type, QQuickItem *parent); Q_INVOKABLE void addIndicator(); Q_INVOKABLE void downloadIndicator(); + Q_INVOKABLE void removeIndicator(QString pluginId); signals: void customPluginsChanged(); void enabledChanged(); void enabledForAppletsChanged(); void customPluginChanged(); void latteTasksArePresentChanged(); void paddingChanged(); void plasmaComponentChanged(); void pluginChanged(); void pluginIsReadyChanged(); void providesConfigUiChanged(); void reversedChanged(); private: void loadConfig(); void saveConfig(); void setPluginIsReady(bool ready); void setProvidesConfigUi(bool provides); void setCustomType(QString type); void loadPlasmaComponent(); void updateComponent(); void updateScheme(); private: bool m_enabled{true}; bool m_enabledForApplets{true}; bool m_pluginIsReady{false}; bool m_providesConfigUi{true}; bool m_reversed{false}; float m_padding{0.08}; QString m_pluginPath; QString m_type{"org.kde.latte.default"}; QString m_customType; QPointer m_component; QPointer m_plasmaComponent; QPointer m_configUi; QPointer m_configLoader; QPointer m_corona; QPointer m_view; KPluginMetaData m_metadata; QPointer m_configuration; QPointer m_lastCreatedConfigUi; }; } } #endif diff --git a/declarativeimports/components/ComboBox.qml b/declarativeimports/components/ComboBox.qml index ff8c7d31..afa917a2 100644 --- a/declarativeimports/components/ComboBox.qml +++ b/declarativeimports/components/ComboBox.qml @@ -1,337 +1,349 @@ /* * Copyright 2016 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details * * You should have received a copy of the GNU Library 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.7 import QtQuick.Window 2.2 import QtQuick.Templates 2.2 as T import QtQuick.Controls 2.2 as Controls import QtGraphicalEffects 1.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.kirigami 2.5 as Kirigami import "private" as Private T.ComboBox { id: control implicitWidth: Math.max(background ? background.implicitWidth : 0, contentItem.implicitWidth + leftPadding + rightPadding) + indicator.implicitWidth + rightPadding implicitHeight: units.gridUnit * 1.6 baselineOffset: contentItem.y + contentItem.baselineOffset hoverEnabled: true topPadding: surfaceNormal.margins.top leftPadding: surfaceNormal.margins.left rightPadding: surfaceNormal.margins.right + units.gridUnit * 2 bottomPadding: surfaceNormal.margins.bottom wheelEnabled: false property bool blankSpaceForEmptyIcons: false property bool forcePressed: false property bool popUpAlignRight: true property int minimumPopUpWidth: 150 property int popUpRelativeX: 0 + property string enabledRole property string iconRole + property string iconToolTipRole + property string iconOnlyWhenHoveredRole + + signal iconClicked(int index); delegate: ItemDelegate { width: control.popup.width enabled: control.enabledRole ? (Array.isArray(control.model) ? modelData[control.enabledRole] : model[control.enabledRole]) : true text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData icon: control.iconRole ? (Array.isArray(control.model) ? modelData[control.iconRole] : model[control.iconRole]) : '' + iconToolTip: control.iconToolTipRole ? (Array.isArray(control.model) ? modelData[control.iconToolTipRole] : model[control.iconToolTipRole]) : '' + iconOnlyWhenHovered: control.iconOnlyWhenHoveredRole ? (Array.isArray(control.model) ? modelData[control.iconOnlyWhenHoveredRole] : model[control.iconOnlyWhenHoveredRole]) : '' + highlighted: mouseArea.pressed ? listView.currentIndex == index : control.currentIndex == index blankSpaceForEmptyIcons: control.blankSpaceForEmptyIcons property bool separatorVisible: false } indicator: PlasmaCore.SvgItem { implicitWidth: units.iconSizes.small implicitHeight: implicitWidth anchors { right: parent.right rightMargin: surfaceNormal.margins.right verticalCenter: parent.verticalCenter } svg: PlasmaCore.Svg { imagePath: "widgets/arrows" colorGroup: PlasmaCore.Theme.ButtonColorGroup } elementId: "down-arrow" } // contentItem: Label { // text: control.displayText // font: control.font // color: theme.buttonTextColor // horizontalAlignment: Text.AlignLeft // verticalAlignment: Text.AlignVCenter // elide: Text.ElideRight // } contentItem: MouseArea { id: mouseArea anchors.fill: parent acceptedButtons: Qt.LeftButton preventStealing: true property int indexUnderMouse: -1 onWheel: { if (!control.wheelEnabled) { return; } if (wheel.pixelDelta.y < 0 || wheel.angleDelta.y < 0) { control.currentIndex = Math.min(control.currentIndex + 1, delegateModel.count -1); } else { control.currentIndex = Math.max(control.currentIndex - 1, 0); } control.activated(control.currentIndex); } onPressed: { indexUnderMouse = -1; listView.currentIndex = control.highlightedIndex control.down = true; control.pressed = true; control.popup.visible = !control.popup.visible; } onReleased: { if (!containsMouse) { control.down = false; control.pressed = false; control.popup.visible = false; } if (indexUnderMouse > -1) { control.currentIndex = indexUnderMouse; } } onCanceled: { control.down = false; control.pressed = false; } onPositionChanged: { var pos = listView.mapFromItem(this, mouse.x, mouse.y); indexUnderMouse = listView.indexAt(pos.x, pos.y); listView.currentIndex = indexUnderMouse; control.activated(indexUnderMouse); } Connections { target: popup onClosed: { control.down = false; control.pressed = false; } } T.TextField { id: textField padding: 0 anchors { fill:parent leftMargin: control.leftPadding rightMargin: control.rightPadding topMargin: control.topPadding bottomMargin: control.bottomPadding } text: control.editable ? control.editText : control.displayText enabled: control.editable autoScroll: control.editable readOnly: control.down || !control.hasOwnProperty("editable") || !control.editable inputMethodHints: control.inputMethodHints validator: control.validator // Work around Qt bug where NativeRendering breaks for non-integer scale factors // https://bugreports.qt.io/browse/QTBUG-67007 renderType: Screen.devicePixelRatio % 1 !== 0 ? Text.QtRendering : Text.NativeRendering color: theme.buttonTextColor //control.enabled ? theme.textColor : theme.disabledTextColor selectionColor: Kirigami.Theme.highlightColor selectedTextColor: Kirigami.Theme.highlightedTextColor selectByMouse: !Kirigami.Settings.tabletMode cursorDelegate: Kirigami.Settings.tabletMode ? mobileCursor : undefinedCursor font: control.font horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter opacity: control.enabled ? 1 : 0.6 onFocusChanged: { if (focus) { Private.MobileTextActionsToolBar.controlRoot = textField; } } onPressAndHold: { if (!Kirigami.Settings.tabletMode) { return; } forceActiveFocus(); cursorPosition = positionAt(event.x, event.y); selectWord(); } } } Component { id: mobileCursor Private.MobileCursor { target: textField } } Component { id: undefinedCursor Item{} } Private.MobileCursor { target: textField selectionStartHandle: true property var rect: textField.positionToRectangle(textField.selectionStart) //FIXME: this magic values seem to be always valid, for every font,every dpi, every scaling x: rect.x + 5 y: rect.y + 6 } background: PlasmaCore.FrameSvgItem { id: surfaceNormal //retrocompatibility with old controls implicitWidth: units.gridUnit * 6 anchors.fill: parent readonly property bool editable: control.hasOwnProperty("editable") && control.editable imagePath: editable ? "widgets/lineedit" : "widgets/button" prefix: editable ? "base" : (control.pressed || control.forcePressed ? "pressed" : "normal") Private.TextFieldFocus { visible: parent.editable z: -1 state: control.activeFocus ? "focus" : (control.hovered ? "hover" : "hidden") anchors.fill: parent } Private.ButtonShadow { z: -1 visible: !parent.editable anchors.fill: parent state: { if (control.pressed) { return "hidden" } else if (control.hovered) { return "hover" } else if (control.activeFocus) { return "focus" } else { return "shadow" } } } MouseArea { anchors { fill: parent leftMargin: control.leftPadding rightMargin: control.rightPadding } acceptedButtons: Qt.NoButton onWheel: { if (!control.wheelEnabled) { return; } if (wheel.pixelDelta.y < 0 || wheel.angleDelta.y < 0) { control.currentIndex = Math.min(control.currentIndex + 1, delegateModel.count -1); } else { control.currentIndex = Math.max(control.currentIndex - 1, 0); } control.activated(control.currentIndex); } } } popup: T.Popup { x: { if (!control.mirrored) { if (popUpRelativeX !== 0) { var adjustedX = exceedsContent && control.popUpAlignRight ? -(width - control.width) : popUpRelativeX; return adjustedX; } else { return 0; } } else { //! mirrored case if (exceedsContent && control.popUpAlignRight) { var adjustedX = width - control.width - popUpRelativeX; return -adjustedX; } else { return 0; } } } y: control.height width: Math.max(control.width, control.minimumPopUpWidth) implicitHeight: contentItem.implicitHeight topMargin: 6 bottomMargin: 6 readonly property bool exceedsContent: control.width < width /*onVisibleChanged: { if (visible) { console.log(" mirrored:" + control.mirrored); console.log(" exceeds: " + exceedsContent); console.log(" popupAR: " + control.popUpAlignRight); console.log(" popupRX: " + popUpRelativeX); } }*/ contentItem: ListView { id: listView clip: true implicitHeight: contentHeight model: control.popup.visible ? control.delegateModel : null currentIndex: control.highlightedIndex highlightRangeMode: ListView.ApplyRange highlightMoveDuration: 0 // HACK: When the ComboBox is not inside a top-level Window, it's Popup does not inherit // the LayoutMirroring options. This is a workaround to fix this by enforcing // the LayoutMirroring options properly. // QTBUG: https://bugreports.qt.io/browse/QTBUG-66446 LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true T.ScrollBar.vertical: Controls.ScrollBar { } + + signal iconClicked(int index); + + onIconClicked: control.iconClicked(index); } background: Rectangle { anchors { fill: parent margins: -1 } radius: 2 color: theme.viewBackgroundColor border.color: Qt.rgba(theme.textColor.r, theme.textColor.g, theme.textColor.b, 0.3) layer.enabled: true layer.effect: DropShadow { transparentBorder: true radius: 4 samples: 8 horizontalOffset: 2 verticalOffset: 2 color: Qt.rgba(0, 0, 0, 0.3) } } } } diff --git a/declarativeimports/components/ComboBoxButton.qml b/declarativeimports/components/ComboBoxButton.qml index acb3505c..5209f31d 100644 --- a/declarativeimports/components/ComboBoxButton.qml +++ b/declarativeimports/components/ComboBoxButton.qml @@ -1,123 +1,131 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock 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. * * Latte-Dock 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, see . */ import QtQuick 2.1 import QtQuick.Layouts 1.3 import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.components 3.0 as PlasmaComponents3 import org.kde.latte 0.2 as Latte import org.kde.latte.components 1.0 as LatteComponents Rectangle { id: root color: "transparent" implicitWidth: buttonMetrics.implicitWidth implicitHeight: buttonMetrics.implicitHeight readonly property Item comboBox: mainComboBox readonly property Item button: mainButton property bool buttonEnabled: true property string buttonText:"" property string buttonIconSource:"" property string buttonToolTip: "" property QtObject buttonExclusiveGroup: null property bool checked: false property bool checkable: false property bool comboBoxEnabled: true property bool comboBoxBlankSpaceForEmptyIcons: false property bool comboBoxForcePressed: false property bool comboBoxPopUpAlignRight: true property int comboBoxMinimumPopUpWidth: 150 property string comboBoxEnabledRole: "" property string comboBoxTextRole: "" property string comboBoxIconRole: "" + property string comboBoxIconToolTipRole: "" + property string comboBoxIconOnlyWhenHoveredRole: "" + + signal iconClicked(int index); PlasmaComponents.Button { id: mainButton anchors.left: Qt.application.layoutDirection === Qt.RightToLeft ? undefined : parent.left anchors.right: Qt.application.layoutDirection === Qt.RightToLeft ? parent.right : undefined LayoutMirroring.enabled: false enabled: buttonEnabled checked: root.checked checkable: root.checkable exclusiveGroup: buttonExclusiveGroup width: parent.width height: mainComboBox.height text: checkable ? " " : buttonText iconSource: buttonIconSource tooltip: buttonToolTip } //overlayed combobox LatteComponents.ComboBox { id: mainComboBox anchors.right: mainButton.right anchors.top: parent.top width: units.iconSizes.medium - units.smallSpacing height: parent.height enabled: comboBoxEnabled enabledRole: comboBoxEnabledRole iconRole: comboBoxIconRole textRole: comboBoxTextRole + iconToolTipRole: comboBoxIconToolTipRole + iconOnlyWhenHoveredRole: comboBoxIconOnlyWhenHoveredRole blankSpaceForEmptyIcons: comboBoxBlankSpaceForEmptyIcons forcePressed: comboBoxForcePressed popUpAlignRight: comboBoxPopUpAlignRight popUpRelativeX: Qt.application.layoutDirection === Qt.RightToLeft ? (popUpAlignRight ? root.width - width : 0) : (popUpAlignRight ? width : -(root.width - width)) minimumPopUpWidth: Math.max(comboBoxMinimumPopUpWidth, root.width) + + onIconClicked: root.iconClicked(index); } Label{ width: labelMetrics.exceeds ? parent.width-mainComboBox.width : parent.width height: parent.height text: buttonText font: mainButton.font color: theme.buttonTextColor visible: root.checkable elide: Text.ElideRight horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } Label{ id: labelMetrics text: root.buttonText opacity: 0 elide: Text.ElideNone horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter readonly property bool exceeds: width>(mainButton.width-mainComboBox.width) } } diff --git a/declarativeimports/components/ItemDelegate.qml b/declarativeimports/components/ItemDelegate.qml index 54e2a13c..8dfabdaf 100644 --- a/declarativeimports/components/ItemDelegate.qml +++ b/declarativeimports/components/ItemDelegate.qml @@ -1,99 +1,125 @@ /* * Copyright 2016 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details * * You should have received a copy of the GNU Library 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.Layouts 1.3 import QtQuick.Templates 2.2 as T import org.kde.plasma.core 2.0 as PlasmaCore + +import org.kde.latte.components 1.0 as LatteComponents + import "private" as Private T.CheckDelegate { id: control implicitWidth: contentItem.implicitWidth + leftPadding + rightPadding implicitHeight: Math.max(contentItem.implicitHeight, indicator ? indicator.implicitHeight : 0) + topPadding + bottomPadding hoverEnabled: true topPadding: margin bottomPadding: margin leftPadding: margin rightPadding: margin spacing: units.smallSpacing property bool blankSpaceForEmptyIcons: false property string icon + property string iconToolTip + property bool iconOnlyWhenHovered + readonly property bool isHovered: hovered || iconMouseArea.containsMouse readonly property int margin: 4 contentItem: RowLayout { Layout.leftMargin: control.mirrored ? (control.indicator ? control.indicator.width : 0) + control.spacing : 0 Layout.rightMargin: !control.mirrored ? (control.indicator ? control.indicator.width : 0) + control.spacing : 0 spacing: units.smallSpacing enabled: control.enabled - PlasmaCore.IconItem { + Rectangle { Layout.minimumWidth: parent.height Layout.maximumWidth: parent.height Layout.minimumHeight: parent.height Layout.maximumHeight: parent.height - //height: parent.height - 2*control.margin - //width: parent.height - 2*control.margin - colorGroup: PlasmaCore.Theme.ButtonColorGroup - source: control.icon - visible: icon + visible: icon && (!control.iconOnlyWhenHovered || (control.iconOnlyWhenHovered && control.isHovered)) + color: control.iconToolTip && iconMouseArea.containsMouse ? theme.highlightColor : "transparent" + + PlasmaCore.IconItem { + id: iconElement + anchors.fill: parent + colorGroup: PlasmaCore.Theme.ButtonColorGroup + source: control.icon + } + + LatteComponents.ToolTip{ + parent: iconElement + text: iconToolTip + visible: iconMouseArea.containsMouse + delay: 6 * units.longDuration + } + + MouseArea { + id: iconMouseArea + anchors.fill: parent + hoverEnabled: true + visible: control.iconToolTip + + onClicked: control.ListView.view.iconClicked(index); + } } Rectangle { //blank space when no icon is shown Layout.minimumHeight: parent.height Layout.minimumWidth: parent.height - visible: !icon && control.blankSpaceForEmptyIcons + visible: control.blankSpaceForEmptyIcons && (!icon || (control.iconOnlyWhenHovered && !control.isHovered) ) color: "transparent" } Label { Layout.fillWidth: true text: control.text font: control.font color: theme.viewTextColor elide: Text.ElideRight visible: control.text horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter } } //background: Private.DefaultListItemBackground {} background: Rectangle { visible: control.ListView.view ? control.ListView.view.highlight === null : true enabled: control.enabled opacity: { if (control.highlighted || control.pressed) { return 0.6; - } else if (control.hovered && !control.pressed) { + } else if (control.isHovered && !control.pressed) { return 0.3; } return 0; } color: theme.highlightColor } } diff --git a/shell/package/contents/controls/CustomIndicatorButton.qml b/shell/package/contents/controls/CustomIndicatorButton.qml index 4c38ec5d..0e4b0c95 100644 --- a/shell/package/contents/controls/CustomIndicatorButton.qml +++ b/shell/package/contents/controls/CustomIndicatorButton.qml @@ -1,152 +1,190 @@ /* * Copyright 2019 Michail Vourlakos * * This file is part of Latte-Dock * * Latte-Dock 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. * * Latte-Dock 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, see . */ import QtQuick 2.1 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.latte.components 1.0 as LatteComponents LatteComponents.ComboBoxButton{ id: custom checkable: true buttonToolTip: checked ? i18n("Use %0 style for your indicators").arg(buttonText) : i18n("Download indicator styles from the internet") comboBoxTextRole: "name" comboBoxIconRole: "icon" + comboBoxIconToolTipRole: "iconToolTip" + comboBoxIconOnlyWhenHoveredRole: "iconOnlyWhenHovered" comboBoxBlankSpaceForEmptyIcons: true comboBoxForcePressed: latteView.indicator.type === type comboBoxPopUpAlignRight: Qt.application.layoutDirection !== Qt.RightToLeft property string type: "" Component.onCompleted: { reloadModel(); updateButtonInformation(); } ListModel { id: actionsModel } Connections{ target: latteView.indicator onCustomPluginsCountChanged: { custom.reloadModel(); custom.updateButtonInformation() } } Connections{ target: custom.button onClicked: { if (custom.type === "download:") { latteView.indicator.downloadIndicator(); } else { latteView.indicator.type = custom.type; } } } Connections{ target: custom.comboBox onActivated: { if (index>=0) { var item = actionsModel.get(index); if (item.pluginId === "add:") { latteView.indicator.addIndicator(); } else if (item.pluginId === "download:") { latteView.indicator.downloadIndicator(); } else { latteView.indicator.type = item.pluginId; } } custom.updateButtonInformation(); } + + onIconClicked: { + if (index>=0) { + var item = actionsModel.get(index); + var pluginId = item.pluginId; + if (latteView.indicator.customLocalPluginIds.indexOf(pluginId)>=0) { + latteView.indicator.removeIndicator(pluginId); + custom.comboBox.popup.close(); + } + } + } } function updateButtonInformation() { if (latteView.indicator.customPluginsCount === 0) { custom.buttonText = i18n("Download"); custom.type = "download:"; custom.checkable = false; } else { custom.checkable = true; var curCustomIndex = latteView.indicator.customPluginIds.indexOf(latteView.indicator.customType); if (curCustomIndex>=0) { custom.buttonText = actionsModel.get(curCustomIndex).name; custom.type = actionsModel.get(curCustomIndex).pluginId; } else { custom.buttonText = actionsModel.get(0).name; custom.type = actionsModel.get(0).pluginId; } } } function reloadModel() { actionsModel.clear(); if (latteView.indicator.customPluginsCount > 0) { var pluginIds = latteView.indicator.customPluginIds; var pluginNames = latteView.indicator.customPluginNames; + var localPluginIds = latteView.indicator.customLocalPluginIds; for(var i=0; i=0; + var iconString = canBeRemoved ? 'remove' : ''; + var iconTip = canBeRemoved ? i18n('Remove indicator') : ''; + var iconOnlyForHovered = canBeRemoved ? true : false; + + var element = { + pluginId: pluginIds[i], + name: pluginNames[i], + icon: iconString, + iconToolTip: iconTip, + iconOnlyWhenHovered: iconOnlyForHovered + }; + actionsModel.append(element); } } appendDefaults(); comboBox.model = actionsModel; if (custom.type === latteView.indicator.type) { comboBox.currentIndex = 0; } else { comboBox.currentIndex = -1; } } function emptyModel() { actionsModel.clear(); appendDefaults(); comboBox.model = actionsModel; comboBox.currentIndex = -1; } function appendDefaults() { //! add - var addElement = {pluginId: 'add:', name: i18n('Add Indicator...'), icon: 'list-add'}; + var addElement = { + pluginId: 'add:', + name: i18n('Add Indicator...'), + icon: 'list-add', + iconToolTip: '', + iconOnlyWhenHovered: false + }; actionsModel.append(addElement); //! download - var downloadElement = {pluginId: 'download:', name: i18n('Get New Indicators...'), icon: 'favorites'}; + var downloadElement = { + pluginId: 'download:', + name: i18n('Get New Indicators...'), + icon: 'favorites', + iconToolTip: '', + iconOnlyWhenHovered: false + }; actionsModel.append(downloadElement); } }