1
0
mirror of https://github.com/KDE/latte-dock.git synced 2025-02-07 13:57:40 +03:00
latte-dock/app/layoutmanager.cpp
Michail Vourlakos eefbc5e9ec fix #636,new approach for Layout,Global launchers
--changed the design totally and use only signals
between plasmoid in order to update their models. This
way the launcherList from their tasksmodel should be
updated only on the initialization phase of the Latte
plasmoid. Afterwards signaling between them takes
place, in order to inform each other for adding,
removing launchers, moving them and assigning them
to activities
2017-07-31 03:53:39 +03:00

441 lines
12 KiB
C++

/*
* Copyright 2017 Smith AR <audoban@openmailbox.org>
* Michail Vourlakos <mvourlakos@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "layoutmanager.h"
#include "infoview.h"
#include <QDir>
#include <QFile>
#include <QQmlProperty>
#include <QtDBus/QtDBus>
#include <KActivities/Consumer>
#include <KLocalizedString>
#include <KNotification>
namespace Latte {
LayoutManager::LayoutManager(QObject *parent)
: QObject(parent),
m_importer(new Importer(this)),
m_launchersSignals(new LaunchersSignals(this))
{
m_corona = qobject_cast<DockCorona *>(parent);
if (m_corona) {
//! create the add widgets action
const QIcon addWidIcon = QIcon::fromTheme("add");
m_addWidgetsAction = new QAction(addWidIcon, i18n("Add Widgets..."), this);
m_addWidgetsAction->setStatusTip(i18n("Show Plasma Widget Explorer"));
connect(m_addWidgetsAction, &QAction::triggered, this, &LayoutManager::showWidgetsExplorer);
connect(m_corona->universalSettings(), &UniversalSettings::currentLayoutNameChanged, this, &LayoutManager::currentLayoutNameChanged);
connect(m_corona->universalSettings(), &UniversalSettings::showInfoWindowChanged, this, &LayoutManager::showInfoWindowChanged);
m_dynamicSwitchTimer.setSingleShot(true);
showInfoWindowChanged();
connect(&m_dynamicSwitchTimer, &QTimer::timeout, this, &LayoutManager::confirmDynamicSwitch);
}
}
LayoutManager::~LayoutManager()
{
m_importer->deleteLater();
m_launchersSignals->deleteLater();
if (m_currentLayout) {
m_currentLayout->deleteLater();
}
}
void LayoutManager::load()
{
int configVer = m_corona->universalSettings()->version();
qDebug() << "Universal Settings version : " << configVer;
if (configVer < 2 && QFile(QDir::homePath() + "/.config/lattedockrc").exists()) {
qDebug() << "Latte must update its configuration...";
m_importer->updateOldConfiguration();
importPresets(false);
} else if (!QFile(QDir::homePath() + "/.config/lattedockrc").exists()) {
//startup create what is necessary....
QDir layoutDir(QDir::homePath() + "/.config/latte");
if (!layoutDir.exists()) {
QDir(QDir::homePath() + "/.config").mkdir("latte");
}
newLayout(i18n("My Layout"));
importPresets(false);
m_corona->universalSettings()->setCurrentLayoutName(i18n("My Layout"));
m_corona->universalSettings()->setVersion(2);
}
qDebug() << "Latte is loading its layouts...";
connect(m_corona->m_activityConsumer, &KActivities::Consumer::currentActivityChanged,
this, &LayoutManager::currentActivityChanged);
loadLayouts();
}
DockCorona *LayoutManager::corona()
{
return m_corona;
}
Importer *LayoutManager::importer()
{
return m_importer;
}
QAction *LayoutManager::addWidgetsAction()
{
return m_addWidgetsAction;
}
LayoutSettings *LayoutManager::currentLayout()
{
return m_currentLayout;
}
LaunchersSignals *LayoutManager::launchersSignals()
{
return m_launchersSignals;
}
QString LayoutManager::currentLayoutName() const
{
if (m_corona && m_corona->universalSettings()) {
return m_corona->universalSettings()->currentLayoutName();
}
return QString();
}
QStringList LayoutManager::layouts() const
{
return m_layouts;
}
QStringList LayoutManager::menuLayouts() const
{
QStringList fixedMenuLayouts = m_menuLayouts;
//! in case the current layout isnt checked to be shown in the menus
//! we must add it on top
if (!fixedMenuLayouts.contains(currentLayoutName())) {
fixedMenuLayouts.prepend(currentLayoutName());
}
return fixedMenuLayouts;
}
void LayoutManager::setMenuLayouts(QStringList layouts)
{
if (m_menuLayouts == layouts) {
return;
}
m_menuLayouts = layouts;
emit menuLayoutsChanged();
}
QStringList LayoutManager::activities()
{
return m_corona->m_activityConsumer->activities();
}
QStringList LayoutManager::presetsPaths() const
{
return m_presetsPaths;
}
QString LayoutManager::layoutPath(QString layoutName)
{
QString path = QDir::homePath() + "/.config/latte/" + layoutName + ".layout.latte";
if (!QFile(path).exists()) {
path = "";
}
return path;
}
void LayoutManager::currentActivityChanged(const QString &id)
{
qDebug() << "activity changed :: " << id;
m_shouldSwitchToLayout = shouldSwitchToLayout(id);
m_dynamicSwitchTimer.start();
}
void LayoutManager::showInfoWindowChanged()
{
if (m_corona->universalSettings()->showInfoWindow()) {
m_dynamicSwitchTimer.setInterval(1800);
} else {
m_dynamicSwitchTimer.setInterval(2300);
}
}
QString LayoutManager::shouldSwitchToLayout(QString activityId)
{
if (m_assignedLayouts.contains(activityId) && m_assignedLayouts[activityId] != currentLayoutName()) {
return m_assignedLayouts[activityId];
} else if (!m_assignedLayouts.contains(activityId) && !m_corona->universalSettings()->lastNonAssignedLayoutName().isEmpty()
&& m_corona->universalSettings()->lastNonAssignedLayoutName() != currentLayoutName()) {
return m_corona->universalSettings()->lastNonAssignedLayoutName();
}
return QString();
}
void LayoutManager::confirmDynamicSwitch()
{
QString tempShouldSwitch = shouldSwitchToLayout(m_corona->m_activityConsumer->currentActivity());
if (tempShouldSwitch.isEmpty()) {
return;
}
if (m_shouldSwitchToLayout == tempShouldSwitch && m_shouldSwitchToLayout != currentLayoutName()) {
qDebug() << "dynamic switch to layout :: " << m_shouldSwitchToLayout;
if (m_corona->universalSettings()->showInfoWindow()) {
showInfoWindow(i18n("Switching to layout <b>%0</b> ...").arg(m_shouldSwitchToLayout), 4000);
QTimer::singleShot(500, [this, tempShouldSwitch]() {
switchToLayout(tempShouldSwitch);
});
} else {
switchToLayout(m_shouldSwitchToLayout);
}
} else {
m_shouldSwitchToLayout = tempShouldSwitch;
m_dynamicSwitchTimer.start();
}
}
void LayoutManager::loadLayouts()
{
m_layouts.clear();
m_menuLayouts.clear();
m_presetsPaths.clear();
m_assignedLayouts.clear();
QDir layoutDir(QDir::homePath() + "/.config/latte");
QStringList filter;
filter.append(QString("*.layout.latte"));
QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks);
foreach (auto layout, files) {
LayoutSettings layoutSets(this, layoutDir.absolutePath() + "/" + layout);
QStringList validActivityIds = validActivities(layoutSets.activities());
layoutSets.setActivities(validActivityIds);
foreach (auto activity, validActivityIds) {
m_assignedLayouts[activity] = layoutSets.name();
}
m_layouts.append(layoutSets.name());
if (layoutSets.showInMenu()) {
m_menuLayouts.append(layoutSets.name());
}
}
m_presetsPaths.append(m_corona->kPackage().filePath("preset1"));
m_presetsPaths.append(m_corona->kPackage().filePath("preset2"));
m_presetsPaths.append(m_corona->kPackage().filePath("preset3"));
m_presetsPaths.append(m_corona->kPackage().filePath("preset4"));
emit layoutsChanged();
emit menuLayoutsChanged();
}
bool LayoutManager::switchToLayout(QString layoutName)
{
if (m_currentLayout && m_currentLayout->name() == layoutName) {
return false;
}
QString lPath = layoutPath(layoutName);
if (lPath.isEmpty() && layoutName == i18n("Alternative")) {
lPath = newLayout(i18n("Alternative"), i18n("Default"));
}
if (!lPath.isEmpty()) {
//! this code must be called asynchronously because it is called
//! also from qml (Tasks plasmoid). This change fixes a very important
//! crash when switching sessions through the Tasks plasmoid Context menu
//! Latte was unstable and was crashing very often during changing
//! sessions.
QTimer::singleShot(0, [this, layoutName, lPath]() {
qDebug() << layoutName << " - " << lPath;
m_corona->loadLatteLayout(lPath);
m_corona->universalSettings()->setCurrentLayoutName(layoutName);
if (!layoutIsAssigned(layoutName)) {
m_corona->universalSettings()->setLastNonAssignedLayoutName(layoutName);
}
if (m_currentLayout) {
m_currentLayout->deleteLater();
}
m_currentLayout = new LayoutSettings(this, lPath, layoutName);
emit currentLayoutChanged();
});
}
return true;
}
QString LayoutManager::newLayout(QString layoutName, QString preset)
{
QDir layoutDir(QDir::homePath() + "/.config/latte");
QStringList filter;
filter.append(QString(layoutName + "*.layout.latte"));
QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks);
//! if the newLayout already exists provide a newName that doesnt
if (files.count() >= 1) {
int newCounter = files.count() + 1;
layoutName = layoutName + "-" + QString::number(newCounter);
}
QString newLayoutPath = layoutDir.absolutePath() + "/" + layoutName + ".layout.latte";
qDebug() << "adding layout : " << layoutName << " based on preset:" << preset;
if (preset == i18n("Default") && !QFile(newLayoutPath).exists()) {
qDebug() << "adding layout : succeed";
QFile(m_corona->kPackage().filePath("preset1")).copy(newLayoutPath);
}
return newLayoutPath;
}
void LayoutManager::importPresets(bool includeDefault)
{
int start = 1;
if (!includeDefault) {
start = 2;
}
for (int i = start; i <= 4; ++i) {
QByteArray presetNameOrig = QString("preset" + QString::number(i)).toUtf8();
QString presetPath = m_corona->kPackage().filePath(presetNameOrig);
QString presetName = LayoutSettings::layoutName(presetPath);
QByteArray presetNameChars = presetName.toUtf8();
presetName = i18n(presetNameChars);
QString newLayoutFile = QDir::homePath() + "/.config/latte/" + presetName + ".layout.latte";
if (!QFile(newLayoutFile).exists()) {
QFile(presetPath).copy(newLayoutFile);
}
}
}
QStringList LayoutManager::validActivities(QStringList currentList)
{
QStringList validIds;
foreach (auto activity, currentList) {
if (activities().contains(activity)) {
validIds.append(activity);
}
}
return validIds;
}
bool LayoutManager::layoutIsAssigned(QString layoutName)
{
QHashIterator<const QString, QString> i(m_assignedLayouts);
while (i.hasNext()) {
i.next();
if (i.value() == layoutName) {
return true;
}
}
return false;
}
void LayoutManager::showLayoutConfigDialog()
{
if (!m_layoutConfigDialog)
m_layoutConfigDialog = new LayoutConfigDialog(nullptr, this);
m_layoutConfigDialog->show();
if (m_layoutConfigDialog->isMinimized()) {
m_layoutConfigDialog->showNormal();
}
m_layoutConfigDialog->activateWindow();
}
void LayoutManager::showInfoWindow(QString info, int duration)
{
foreach (auto screen, qGuiApp->screens()) {
InfoView *infoView = new InfoView(m_corona, info, screen);
infoView->show();
QTimer::singleShot(duration, [this, infoView]() {
infoView->deleteLater();
});
}
}
void LayoutManager::showWidgetsExplorer()
{
QDBusInterface iface("org.kde.plasmashell", "/PlasmaShell", "", QDBusConnection::sessionBus());
if (iface.isValid()) {
iface.call("toggleWidgetExplorer");
}
}
//! it is used just in order to provide translations for the presets
void LayoutManager::ghostForTranslatedPresets()
{
QString preset1 = i18n("Default");
QString preset2 = i18n("Plasma");
QString preset3 = i18n("Unity");
QString preset4 = i18n("Extended");
}
}