You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
latte-dock/app/layouts/manager.cpp

446 lines
13 KiB
C++

/*
SPDX-FileCopyrightText: 2017 Smith AR <audoban@openmailbox.org>
SPDX-FileCopyrightText: 2019 Michail Vourlakos <mvourlakos@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "manager.h"
// local
#include "importer.h"
#include "syncedlaunchers.h"
#include "../infoview.h"
#include "../screenpool.h"
#include "../data/layoutdata.h"
#include "../data/generictable.h"
#include "../layout/abstractlayout.h"
#include "../layout/centrallayout.h"
fix #96,FEATURE:AllScreens and AllSecondaryScreens --This is a HUGE FEATURE and so important for multi-screens users. It is introduced as one single commit because it reimplements plenty of infrastructure changes and it will be easier to identify newly introduced bugs. --Users can now choose for their docks and panels to belong at various screen groups. The first two screen groups introduced are AllScreens and AllSecondayScreens. In the future it might be possible to provide CustomScreensGroup that the user will be able to define specific screens in which a dock or panel should be always present. --Current solution specifies an Original dock or panel and clones/copies itself automatically to other screens. So docks and panels in other screens are just real docks and panels that reference themselves to original docks and panels. --Clones are destroyed during layout startup and are automaticaly recreated. It is suggested to export your layouts through the official Layouts Editor in order to share them because in that case clones are not included in the new generated layout file. If in any case you do not this and you share your layout with any previous versions then your clones will just appear as separate docks and panels that belong to specific screens. --Automatic syncing was introduced in order to keep up-to-date the configuration of Original docks and panels with their referenced Clones. --Automatic syncing currently works for all docks and panels settings, for all normal applets configurations and for all subcontaiments configuration such as systrays. --Automatic syncing does not work for applets inside subcontainments such as Group Plasmoid. In such case it is suggested to configure your applets inside your Group Plasmoid in the original dock or panel and afterwards to trigger a recreation for the relevant clones --Manual recreation of clones is easily possible by just choosing the dock or panel to be OnPrimary or OnSpecificScreen and rechoosing afterwards the AllScreensGroup or AllSecondaryScreensGroup
3 years ago
#include "../layouts/storage.h"
#include "../settings/universalsettings.h"
#include "../templates/templatesmanager.h"
#include "../tools/commontools.h"
// Qt
#include <QDir>
#include <QFile>
#include <QMessageBox>
#include <QLatin1String>
// KDE
#include <KMessageBox>
#include <KLocalizedString>
#include <KNotification>
namespace Latte {
namespace Layouts {
Manager::Manager(QObject *parent)
: QObject(parent),
m_importer(new Importer(this)),
m_syncedLaunchers(new SyncedLaunchers(this))
{
m_corona = qobject_cast<Latte::Corona *>(parent);
//! needs to be created AFTER corona assignment
m_synchronizer = new Synchronizer(this);
if (m_corona) {
connect(m_synchronizer, &Synchronizer::centralLayoutsChanged, this, &Manager::centralLayoutsChanged);
connect(m_synchronizer, &Synchronizer::currentLayoutIsSwitching, this, &Manager::currentLayoutIsSwitching);
}
}
Manager::~Manager()
{
if (memoryUsage() == Latte::MemoryUsage::MultipleLayouts) {
m_importer->setMultipleLayoutsStatus(Latte::MultipleLayouts::Paused);
}
m_importer->deleteLater();
m_syncedLaunchers->deleteLater();
//! no needed because Latte:Corona is calling it at better place
// unload();
m_synchronizer->deleteLater();
}
void Manager::init()
{
QDir layoutsDir(Layouts::Importer::layoutUserDir());
bool firstRun = !layoutsDir.exists();
int configVer = m_corona->universalSettings()->version();
qDebug() << "Universal Settings version : " << configVer;
if (firstRun) {
m_corona->universalSettings()->setVersion(2);
m_corona->universalSettings()->setSingleModeLayoutName(i18n("My Layout"));
//startup create what is necessary....
if (!layoutsDir.exists()) {
QDir(Latte::configPath()).mkdir("latte");
}
QString defpath = m_corona->templatesManager()->newLayout(i18n("My Layout"), i18n(Templates::DEFAULTLAYOUTTEMPLATENAME));
setOnAllActivities(Layout::AbstractLayout::layoutName(defpath));
m_corona->templatesManager()->importSystemLayouts();
} else if (configVer < 2 && !firstRun) {
m_corona->universalSettings()->setVersion(2);
bool isOlderVersion = m_importer->updateOldConfiguration();
if (isOlderVersion) {
qDebug() << "Latte is updating its older configuration...";
m_corona->templatesManager()->importSystemLayouts();
} else {
m_corona->universalSettings()->setSingleModeLayoutName(i18n("My Layout"));
}
}
//! Custom Templates path creation
QDir localTemplatesDir(Latte::configPath() + "/latte/templates");
if (!localTemplatesDir.exists()) {
QDir(Latte::configPath() + "/latte").mkdir("templates");
}
//! Check if the multiple-layouts hidden file is present, add it if it isnt
if (!QFile(Layouts::Importer::layoutUserFilePath(Layout::MULTIPLELAYOUTSHIDDENNAME)).exists()) {
m_corona->templatesManager()->newLayout("", Layout::MULTIPLELAYOUTSHIDDENNAME);
}
qDebug() << "Latte is loading its layouts...";
m_synchronizer->initLayouts();
}
void Manager::unload()
{
m_synchronizer->unloadLayouts();
}
Latte::Corona *Manager::corona()
{
return m_corona;
}
Importer *Manager::importer()
{
return m_importer;
}
SyncedLaunchers *Manager::syncedLaunchers() const
{
return m_syncedLaunchers;
}
Synchronizer *Manager::synchronizer() const
{
return m_synchronizer;
}
MemoryUsage::LayoutsMemory Manager::memoryUsage() const
{
return m_corona->universalSettings()->layoutsMemoryUsage();
}
void Manager::setMemoryUsage(MemoryUsage::LayoutsMemory memoryUsage)
{
m_corona->universalSettings()->setLayoutsMemoryUsage(memoryUsage);
}
QStringList Manager::centralLayoutsNames()
{
return m_synchronizer->centralLayoutsNames();
}
QStringList Manager::currentLayoutsNames() const
{
return m_synchronizer->currentLayoutsNames();
}
QStringList Manager::viewTemplateNames() const
{
Latte::Data::GenericTable<Data::Generic> viewtemplates = m_corona->templatesManager()->viewTemplates();
QStringList names;
for(int i=0; i<viewtemplates.rowCount(); ++i) {
names << viewtemplates[i].name;
}
return names;
}
QStringList Manager::viewTemplateIds() const
{
Latte::Data::GenericTable<Data::Generic> viewtemplates = m_corona->templatesManager()->viewTemplates();
QStringList ids;
for(int i=0; i<viewtemplates.rowCount(); ++i) {
ids << viewtemplates[i].id;
}
return ids;
}
Latte::Data::LayoutIcon Manager::iconForLayout(const QString &storedLayoutName) const
{
Data::Layout l = m_synchronizer->data(storedLayoutName);
return iconForLayout(l);
}
Latte::Data::LayoutIcon Manager::iconForLayout(const Data::Layout &layout) const
{
Latte::Data::LayoutIcon _icon;
if (!layout.icon.isEmpty()) {
//! if there is specific icon set from the user for this layout we draw only that icon
_icon.name = layout.icon;
_icon.isBackgroundFile = false;
return _icon;
}
//! fallback icon: background image
if (_icon.isEmpty()) {
QString colorPath = m_corona->kPackage().path() + "../../shells/org.kde.latte.shell/contents/images/canvas/";
if (layout.backgroundStyle == Layout::PatternBackgroundStyle && layout.background.isEmpty()) {
colorPath += "defaultcustomprint.jpg";
} else {
colorPath = layout.background.startsWith("/") ? layout.background : colorPath + layout.color + "print.jpg";
}
if (QFileInfo(colorPath).exists()) {
_icon.isBackgroundFile = true;
_icon.name = colorPath;
return _icon;
}
}
return Latte::Data::LayoutIcon();
}
QList<CentralLayout *> Manager::currentLayouts() const
{
return m_synchronizer->currentLayouts();
}
bool Manager::switchToLayout(QString layoutName, MemoryUsage::LayoutsMemory newMemoryUsage)
{
return m_synchronizer->switchToLayout(layoutName, newMemoryUsage);
}
void Manager::loadLayoutOnStartup(QString layoutName)
{
/* QStringList layouts = m_importer->checkRepairMultipleLayoutsLinkedFile();
//! Latte didn't close correctly, maybe a crash
if (layouts.size() > 0) {
QDialog* dialog = new QDialog(nullptr);
dialog->setWindowTitle(i18n("Multiple Layouts Startup Warning"));
dialog->setObjectName("sorry");
dialog->setAttribute(Qt::WA_DeleteOnClose);
auto buttonbox = new QDialogButtonBox(QDialogButtonBox::Ok);
KMessageBox::createKMessageBox(dialog,
buttonbox,
QMessageBox::Warning,
i18np("<b>Multiple Layouts based on Activities</b> mode did not close properly during the last session.<br/><br/>The following layout <b>[ %2 ]</b> had to be updated for consistency!",
"<b>Multiple Layouts based on Activities</b> mode did not close properly during the last session.<br/><br/>The following layouts <b>[ %2 ]</b> had to be updated for consistency!",
layouts.count(),
layouts.join(", ")),
QStringList(),
QString(),
0,
KMessageBox::NoExec,
QString());
dialog->show();
}*/
m_synchronizer->switchToLayout(layoutName);
}
void Manager::moveView(QString originLayoutName, uint originViewId, QString destinationLayoutName)
{
if (memoryUsage() != Latte::MemoryUsage::MultipleLayouts
|| originLayoutName.isEmpty()
|| destinationLayoutName.isEmpty()
|| originViewId <= 0
|| originLayoutName == destinationLayoutName) {
return;
}
auto originlayout = m_synchronizer->layout(originLayoutName);
auto destinationlayout = m_synchronizer->layout(destinationLayoutName);
if (!originlayout || !destinationlayout || originlayout == destinationlayout) {
return;
}
Plasma::Containment *originviewcontainment = originlayout->containmentForId(originViewId);
Latte::View *originview = originlayout->viewForContainment(originViewId);
if (!originviewcontainment) {
return;
}
QList<Plasma::Containment *> origincontainments = originlayout->unassignFromLayout(originviewcontainment);
if (origincontainments.size() > 0) {
destinationlayout->assignToLayout(originview, origincontainments);
}
}
void Manager::loadLatteLayout(QString layoutPath)
{
qDebug() << " -------------------------------------------------------------------- ";
qDebug() << " -------------------------------------------------------------------- ";
if (m_corona->containments().size() > 0) {
qDebug() << "LOAD LATTE LAYOUT ::: There are still containments present !!!! :: " << m_corona->containments().size();
}
if (!layoutPath.isEmpty() && m_corona->containments().size() == 0) {
cleanupOnStartup(layoutPath);
qDebug() << "LOADING CORONA LAYOUT:" << layoutPath;
m_corona->loadLayout(layoutPath);
}
}
void Manager::setOnAllActivities(QString layoutName)
{
CentralLayout *central = m_synchronizer->centralLayout(layoutName);
if (central) {
central->setActivities(QStringList(Data::Layout::ALLACTIVITIESID));
} else if (m_importer->layoutExists(layoutName)) {
CentralLayout storage(this, m_importer->layoutUserFilePath(layoutName));
storage.setActivities(QStringList(Data::Layout::ALLACTIVITIESID));
}
}
void Manager::setOnActivities(QString layoutName, QStringList activities)
{
CentralLayout *central = m_synchronizer->centralLayout(layoutName);
if (central) {
central->setActivities(activities);
} else if (m_importer->layoutExists(layoutName)) {
CentralLayout storage(this, m_importer->layoutUserFilePath(layoutName));
storage.setActivities(activities);
}
}
void Manager::cleanupOnStartup(QString path)
{
fix #96,FEATURE:AllScreens and AllSecondaryScreens --This is a HUGE FEATURE and so important for multi-screens users. It is introduced as one single commit because it reimplements plenty of infrastructure changes and it will be easier to identify newly introduced bugs. --Users can now choose for their docks and panels to belong at various screen groups. The first two screen groups introduced are AllScreens and AllSecondayScreens. In the future it might be possible to provide CustomScreensGroup that the user will be able to define specific screens in which a dock or panel should be always present. --Current solution specifies an Original dock or panel and clones/copies itself automatically to other screens. So docks and panels in other screens are just real docks and panels that reference themselves to original docks and panels. --Clones are destroyed during layout startup and are automaticaly recreated. It is suggested to export your layouts through the official Layouts Editor in order to share them because in that case clones are not included in the new generated layout file. If in any case you do not this and you share your layout with any previous versions then your clones will just appear as separate docks and panels that belong to specific screens. --Automatic syncing was introduced in order to keep up-to-date the configuration of Original docks and panels with their referenced Clones. --Automatic syncing currently works for all docks and panels settings, for all normal applets configurations and for all subcontaiments configuration such as systrays. --Automatic syncing does not work for applets inside subcontainments such as Group Plasmoid. In such case it is suggested to configure your applets inside your Group Plasmoid in the original dock or panel and afterwards to trigger a recreation for the relevant clones --Manual recreation of clones is easily possible by just choosing the dock or panel to be OnPrimary or OnSpecificScreen and rechoosing afterwards the AllScreensGroup or AllSecondaryScreensGroup
3 years ago
Layouts::Storage::self()->removeAllClonedViews(path);
KSharedConfigPtr filePtr = KSharedConfig::openConfig(path);
KConfigGroup actionGroups = KConfigGroup(filePtr, "ActionPlugins");
QStringList deprecatedActionGroup;
for (const auto &actId : actionGroups.groupList()) {
QString pluginId = actionGroups.group(actId).readEntry("RightButton;NoModifier", "");
if (pluginId == QStringLiteral("org.kde.contextmenu")) {
deprecatedActionGroup << actId;
}
}
for (const auto &pId : deprecatedActionGroup) {
qDebug() << "!!!!!!!!!!!!!!!! !!!!!!!!!!!! !!!!!!! REMOVING :::: " << pId;
actionGroups.group(pId).deleteGroup();
}
KConfigGroup containmentGroups = KConfigGroup(filePtr, "Containments");
QStringList removeContaimentsList;
for (const auto &cId : containmentGroups.groupList()) {
QString pluginId = containmentGroups.group(cId).readEntry("plugin", "");
if (pluginId == QStringLiteral("org.kde.desktopcontainment")) { //!must remove ghost containments first
removeContaimentsList << cId;
}
}
for (const auto &cId : removeContaimentsList) {
containmentGroups.group(cId).deleteGroup();
}
}
void Manager::showAboutDialog()
{
m_corona->aboutApplication();
}
void Manager::clearUnloadedContainmentsFromLinkedFile(QStringList containmentsIds, bool bypassChecks)
{
if (!m_corona || (memoryUsage() == MemoryUsage::SingleLayout && !bypassChecks)) {
return;
}
auto containments = m_corona->config()->group("Containments");
for (const auto &conId : containmentsIds) {
qDebug() << "unloads ::: " << conId;
KConfigGroup containment = containments.group(conId);
containment.deleteGroup();
containment.sync();
}
containments.sync();
}
void Manager::showLatteSettingsDialog(int firstPage, bool toggleCurrentPage)
{
if (!m_latteSettingsDialog) {
m_latteSettingsDialog = new Latte::Settings::Dialog::SettingsDialog(nullptr, m_corona);
}
m_latteSettingsDialog->show();
if (m_latteSettingsDialog->isMinimized()) {
m_latteSettingsDialog->showNormal();
}
if (toggleCurrentPage) {
m_latteSettingsDialog->toggleCurrentPage();
} else {
m_latteSettingsDialog->setCurrentPage(firstPage);
}
m_latteSettingsDialog->activateWindow();
}
void Manager::hideLatteSettingsDialog()
{
if (m_latteSettingsDialog) {
m_latteSettingsDialog->deleteLater();
m_latteSettingsDialog = nullptr;
}
}
void Manager::showInfoWindow(QString info, int duration, QStringList activities)
{
for (const auto screen : qGuiApp->screens()) {
InfoView *infoView = new InfoView(m_corona, info, screen);
infoView->show();
infoView->setOnActivities(activities);
QTimer::singleShot(duration, [this, infoView]() {
infoView->deleteLater();
});
}
}
}
}