You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
379 lines
13 KiB
379 lines
13 KiB
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
|
* (c)LGPL2+
|
|
*
|
|
* LXQt - a lightweight, Qt based, desktop toolset
|
|
* http://lxqt.org
|
|
*
|
|
* Copyright: 2015 LXQt team
|
|
*
|
|
* This program or library is free software; you can redistribute it
|
|
* and/or modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General
|
|
* Public License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA
|
|
*
|
|
* END_COMMON_COPYRIGHT_HEADER */
|
|
|
|
#include "panelpluginsmodel.h"
|
|
#include "plugin.h"
|
|
#include "ilxqtpanelplugin.h"
|
|
#include "lxqtpanel.h"
|
|
#include "lxqtpanelapplication.h"
|
|
#include <QPointer>
|
|
#include <XdgIcon>
|
|
#include <LXQt/Settings>
|
|
|
|
#include <QDebug>
|
|
|
|
PanelPluginsModel::PanelPluginsModel(LXQtPanel * panel,
|
|
QString const & namesKey,
|
|
QStringList const & desktopDirs,
|
|
QObject * parent/* = nullptr*/)
|
|
: QAbstractListModel{parent},
|
|
mNamesKey(namesKey),
|
|
mPanel(panel)
|
|
{
|
|
loadPlugins(desktopDirs);
|
|
}
|
|
|
|
PanelPluginsModel::~PanelPluginsModel()
|
|
{
|
|
qDeleteAll(plugins());
|
|
}
|
|
|
|
int PanelPluginsModel::rowCount(const QModelIndex & parent/* = QModelIndex()*/) const
|
|
{
|
|
return QModelIndex() == parent ? mPlugins.size() : 0;
|
|
}
|
|
|
|
|
|
QVariant PanelPluginsModel::data(const QModelIndex & index, int role/* = Qt::DisplayRole*/) const
|
|
{
|
|
Q_ASSERT(QModelIndex() == index.parent()
|
|
&& 0 == index.column()
|
|
&& mPlugins.size() > index.row()
|
|
);
|
|
|
|
pluginslist_t::const_reference plugin = mPlugins[index.row()];
|
|
QVariant ret;
|
|
switch (role)
|
|
{
|
|
case Qt::DisplayRole:
|
|
if (plugin.second.isNull())
|
|
ret = QString("<b>Unknown</b> (%1)").arg(plugin.first);
|
|
else
|
|
ret = QString("<b>%1</b> (%2)").arg(plugin.second->name(), plugin.first);
|
|
break;
|
|
case Qt::DecorationRole:
|
|
if (plugin.second.isNull())
|
|
ret = XdgIcon::fromTheme("preferences-plugin");
|
|
else
|
|
ret = plugin.second->desktopFile().icon(XdgIcon::fromTheme("preferences-plugin"));
|
|
break;
|
|
case Qt::UserRole:
|
|
ret = QVariant::fromValue(const_cast<Plugin const *>(plugin.second.data()));
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Qt::ItemFlags PanelPluginsModel::flags(const QModelIndex & index) const
|
|
{
|
|
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
|
|
}
|
|
|
|
QStringList PanelPluginsModel::pluginNames() const
|
|
{
|
|
QStringList names;
|
|
for (auto const & p : mPlugins)
|
|
names.append(p.first);
|
|
return names;
|
|
}
|
|
|
|
QList<Plugin *> PanelPluginsModel::plugins() const
|
|
{
|
|
QList<Plugin *> plugins;
|
|
for (auto const & p : mPlugins)
|
|
if (!p.second.isNull())
|
|
plugins.append(p.second.data());
|
|
return plugins;
|
|
}
|
|
|
|
Plugin* PanelPluginsModel::pluginByName(QString name) const
|
|
{
|
|
for (auto const & p : mPlugins)
|
|
if (p.first == name)
|
|
return p.second.data();
|
|
return nullptr;
|
|
}
|
|
|
|
Plugin const * PanelPluginsModel::pluginByID(QString id) const
|
|
{
|
|
for (auto const & p : mPlugins)
|
|
{
|
|
Plugin *plugin = p.second.data();
|
|
if (plugin && plugin->desktopFile().id() == id)
|
|
return plugin;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void PanelPluginsModel::addPlugin(const LXQt::PluginInfo &desktopFile)
|
|
{
|
|
if (dynamic_cast<LXQtPanelApplication const *>(qApp)->isPluginSingletonAndRunnig(desktopFile.id()))
|
|
return;
|
|
|
|
QString name = findNewPluginSettingsGroup(desktopFile.id());
|
|
|
|
QPointer<Plugin> plugin = loadPlugin(desktopFile, name);
|
|
if (plugin.isNull())
|
|
return;
|
|
|
|
beginInsertRows(QModelIndex(), mPlugins.size(), mPlugins.size());
|
|
mPlugins.append({name, plugin});
|
|
endInsertRows();
|
|
mPanel->settings()->setValue(mNamesKey, pluginNames());
|
|
emit pluginAdded(plugin.data());
|
|
}
|
|
|
|
void PanelPluginsModel::removePlugin(pluginslist_t::iterator plugin)
|
|
{
|
|
if (mPlugins.end() != plugin)
|
|
{
|
|
mPanel->settings()->remove(plugin->first);
|
|
Plugin * p = plugin->second.data();
|
|
const int row = plugin - mPlugins.begin();
|
|
beginRemoveRows(QModelIndex(), row, row);
|
|
mPlugins.erase(plugin);
|
|
endRemoveRows();
|
|
emit pluginRemoved(p); // p can be nullptr
|
|
mPanel->settings()->setValue(mNamesKey, pluginNames());
|
|
if (nullptr != p)
|
|
p->deleteLater();
|
|
}
|
|
}
|
|
|
|
void PanelPluginsModel::removePlugin()
|
|
{
|
|
Plugin * p = qobject_cast<Plugin*>(sender());
|
|
auto plugin = std::find_if(mPlugins.begin(), mPlugins.end(),
|
|
[p] (pluginslist_t::const_reference obj) { return p == obj.second; });
|
|
removePlugin(std::move(plugin));
|
|
}
|
|
|
|
void PanelPluginsModel::movePlugin(Plugin * plugin, QString const & nameAfter)
|
|
{
|
|
//merge list of plugins (try to preserve original position)
|
|
//subtract mPlugin.begin() from the found Plugins to get the model index
|
|
const int from =
|
|
std::find_if(mPlugins.begin(), mPlugins.end(), [plugin] (pluginslist_t::const_reference obj) { return plugin == obj.second.data(); })
|
|
- mPlugins.begin();
|
|
const int to =
|
|
std::find_if(mPlugins.begin(), mPlugins.end(), [nameAfter] (pluginslist_t::const_reference obj) { return nameAfter == obj.first; })
|
|
- mPlugins.begin();
|
|
/* 'from' is the current position of the Plugin to be moved ("moved Plugin"),
|
|
* 'to' is the position of the Plugin behind the one that is being moved
|
|
* ("behind Plugin"). There are several cases to distinguish:
|
|
* 1. from > to: The moved Plugin had been behind the behind Plugin before
|
|
* and is moved to the front of the behind Plugin. The moved Plugin will
|
|
* be inserted at position 'to', the behind Plugin and all the following
|
|
* Plugins (until the former position of the moved Plugin) will increment
|
|
* their indexes.
|
|
* 2. from < to: The moved Plugin had already been located before the
|
|
* behind Plugin. In this case, the move operation only reorders the
|
|
* Plugins before the behind Plugin. All the Plugins between the moved
|
|
* Plugin and the behind Plugin will decrement their index. Therefore, the
|
|
* movedPlugin will not be at position 'to' but rather on position 'to-1'.
|
|
* 3. from == to: This does not make sense, we catch this case to prevent
|
|
* errors.
|
|
* 4. from == to-1: The moved Plugin has not moved because it had already
|
|
* been located in front of the behind Plugin.
|
|
*/
|
|
const int to_plugins = from < to ? to - 1 : to;
|
|
|
|
if (from != to && from != to_plugins)
|
|
{
|
|
/* Although the new position of the moved Plugin will be 'to-1' if
|
|
* from < to, we insert 'to' here. This is exactly how it is done
|
|
* in the Qt documentation.
|
|
*/
|
|
beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
|
|
// For the QList::move method, use the right position
|
|
mPlugins.move(from, to_plugins);
|
|
endMoveRows();
|
|
emit pluginMoved(plugin);
|
|
mPanel->settings()->setValue(mNamesKey, pluginNames());
|
|
}
|
|
}
|
|
|
|
void PanelPluginsModel::loadPlugins(QStringList const & desktopDirs)
|
|
{
|
|
QStringList plugin_names = mPanel->settings()->value(mNamesKey).toStringList();
|
|
|
|
#ifdef DEBUG_PLUGIN_LOADTIME
|
|
QElapsedTimer timer;
|
|
timer.start();
|
|
qint64 lastTime = 0;
|
|
#endif
|
|
for (auto const & name : plugin_names)
|
|
{
|
|
pluginslist_t::iterator i = mPlugins.insert(mPlugins.end(), {name, nullptr});
|
|
QString type = mPanel->settings()->value(name + "/type").toString();
|
|
if (type.isEmpty())
|
|
{
|
|
qWarning() << QString("Section \"%1\" not found in %2.").arg(name, mPanel->settings()->fileName());
|
|
continue;
|
|
}
|
|
#ifdef WITH_SCREENSAVER_FALLBACK
|
|
if (QStringLiteral("screensaver") == type)
|
|
{
|
|
//plugin-screensaver was dropped
|
|
//convert settings to plugin-quicklaunch
|
|
const QString & lock_desktop = QStringLiteral(LXQT_LOCK_DESKTOP);
|
|
qWarning().noquote() << "Found deprecated plugin of type 'screensaver', migrating to 'quicklaunch' with '" << lock_desktop << '\'';
|
|
type = QStringLiteral("quicklaunch");
|
|
LXQt::Settings * settings = mPanel->settings();
|
|
settings->beginGroup(name);
|
|
settings->remove(QString{});//remove all existing keys
|
|
settings->setValue(QStringLiteral("type"), type);
|
|
settings->beginWriteArray(QStringLiteral("apps"), 1);
|
|
settings->setArrayIndex(0);
|
|
settings->setValue(QStringLiteral("desktop"), lock_desktop);
|
|
settings->endArray();
|
|
settings->endGroup();
|
|
}
|
|
#endif
|
|
|
|
LXQt::PluginInfoList list = LXQt::PluginInfo::search(desktopDirs, "LXQtPanel/Plugin", QString("%1.desktop").arg(type));
|
|
if( !list.count())
|
|
{
|
|
qWarning() << QString("Plugin \"%1\" not found.").arg(type);
|
|
continue;
|
|
}
|
|
|
|
i->second = loadPlugin(list.first(), name);
|
|
#ifdef DEBUG_PLUGIN_LOADTIME
|
|
qDebug() << "load plugin" << type << "takes" << (timer.elapsed() - lastTime) << "ms";
|
|
lastTime = timer.elapsed();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
QPointer<Plugin> PanelPluginsModel::loadPlugin(LXQt::PluginInfo const & desktopFile, QString const & settingsGroup)
|
|
{
|
|
std::unique_ptr<Plugin> plugin(new Plugin(desktopFile, mPanel->settings(), settingsGroup, mPanel));
|
|
if (plugin->isLoaded())
|
|
{
|
|
connect(mPanel, &LXQtPanel::realigned, plugin.get(), &Plugin::realign);
|
|
connect(plugin.get(), &Plugin::remove,
|
|
this, static_cast<void (PanelPluginsModel::*)()>(&PanelPluginsModel::removePlugin));
|
|
return plugin.release();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
QString PanelPluginsModel::findNewPluginSettingsGroup(const QString &pluginType) const
|
|
{
|
|
QStringList groups = mPanel->settings()->childGroups();
|
|
groups.sort();
|
|
|
|
// Generate new section name
|
|
QString pluginName = QString("%1").arg(pluginType);
|
|
|
|
if (!groups.contains(pluginName))
|
|
return pluginName;
|
|
else
|
|
{
|
|
for (int i = 2; true; ++i)
|
|
{
|
|
pluginName = QString("%1%2").arg(pluginType).arg(i);
|
|
if (!groups.contains(pluginName))
|
|
return pluginName;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool PanelPluginsModel::isIndexValid(QModelIndex const & index) const
|
|
{
|
|
return index.isValid() && QModelIndex() == index.parent()
|
|
&& 0 == index.column() && mPlugins.size() > index.row();
|
|
}
|
|
|
|
void PanelPluginsModel::onMovePluginUp(QModelIndex const & index)
|
|
{
|
|
if (!isIndexValid(index))
|
|
return;
|
|
|
|
const int row = index.row();
|
|
if (0 >= row)
|
|
return; //can't move up
|
|
|
|
beginMoveRows(QModelIndex(), row, row, QModelIndex(), row - 1);
|
|
mPlugins.swap(row - 1, row);
|
|
endMoveRows();
|
|
pluginslist_t::const_reference moved_plugin = mPlugins[row - 1];
|
|
pluginslist_t::const_reference prev_plugin = mPlugins[row];
|
|
|
|
emit pluginMoved(moved_plugin.second.data());
|
|
//emit signal for layout only in case both plugins are loaded/displayed
|
|
if (!moved_plugin.second.isNull() && !prev_plugin.second.isNull())
|
|
emit pluginMovedUp(moved_plugin.second.data());
|
|
|
|
mPanel->settings()->setValue(mNamesKey, pluginNames());
|
|
}
|
|
|
|
void PanelPluginsModel::onMovePluginDown(QModelIndex const & index)
|
|
{
|
|
if (!isIndexValid(index))
|
|
return;
|
|
|
|
const int row = index.row();
|
|
if (mPlugins.size() <= row + 1)
|
|
return; //can't move down
|
|
|
|
beginMoveRows(QModelIndex(), row, row, QModelIndex(), row + 2);
|
|
mPlugins.swap(row, row + 1);
|
|
endMoveRows();
|
|
pluginslist_t::const_reference moved_plugin = mPlugins[row + 1];
|
|
pluginslist_t::const_reference next_plugin = mPlugins[row];
|
|
|
|
emit pluginMoved(moved_plugin.second.data());
|
|
//emit signal for layout only in case both plugins are loaded/displayed
|
|
if (!moved_plugin.second.isNull() && !next_plugin.second.isNull())
|
|
emit pluginMovedUp(next_plugin.second.data());
|
|
|
|
mPanel->settings()->setValue(mNamesKey, pluginNames());
|
|
}
|
|
|
|
void PanelPluginsModel::onConfigurePlugin(QModelIndex const & index)
|
|
{
|
|
if (!isIndexValid(index))
|
|
return;
|
|
|
|
Plugin * const plugin = mPlugins[index.row()].second.data();
|
|
if (nullptr != plugin && (ILXQtPanelPlugin::HaveConfigDialog & plugin->iPlugin()->flags()))
|
|
plugin->showConfigureDialog();
|
|
}
|
|
|
|
void PanelPluginsModel::onRemovePlugin(QModelIndex const & index)
|
|
{
|
|
if (!isIndexValid(index))
|
|
return;
|
|
|
|
auto plugin = mPlugins.begin() + index.row();
|
|
if (plugin->second.isNull())
|
|
removePlugin(std::move(plugin));
|
|
else
|
|
plugin->second->requestRemove();
|
|
}
|