File indexing completed on 2024-04-07 15:23:34

0001 /*
0002 *  Copyright 2017  Smith AR <audoban@openmailbox.org>
0003 *                  Michail Vourlakos <mvourlakos@gmail.com>
0004 *
0005 *            2019  Michail Vourlakos <mvourlakos@gmail.com>
0006 *
0007 *  This file is part of Latte-Dock
0008 *
0009 *  Latte-Dock is free software; you can redistribute it and/or
0010 *  modify it under the terms of the GNU General Public License as
0011 *  published by the Free Software Foundation; either version 2 of
0012 *  the License, or (at your option) any later version.
0013 *
0014 *  Latte-Dock is distributed in the hope that it will be useful,
0015 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0016 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0017 *  GNU General Public License for more details.
0018 *
0019 *  You should have received a copy of the GNU General Public License
0020 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0021 */
0022 
0023 #include "manager.h"
0024 
0025 // local
0026 #include "importer.h"
0027 #include "launcherssignals.h"
0028 #include "../infoview.h"
0029 #include "../screenpool.h"
0030 #include "../layout/abstractlayout.h"
0031 #include "../layout/centrallayout.h"
0032 #include "../settings/settingsdialog.h"
0033 #include "../settings/universalsettings.h"
0034 
0035 // Qt
0036 #include <QDir>
0037 #include <QFile>
0038 #include <QMessageBox>
0039 
0040 // KDE
0041 #include <KLocalizedString>
0042 #include <KNotification>
0043 
0044 namespace Latte {
0045 namespace Layouts {
0046 
0047 const int MultipleLayoutsPresetId = 10;
0048 
0049 Manager::Manager(QObject *parent)
0050     : QObject(parent),
0051       m_importer(new Importer(this)),
0052       m_launchersSignals(new LaunchersSignals(this))
0053 {
0054     m_corona = qobject_cast<Latte::Corona *>(parent);
0055     //! needs to be created AFTER corona assignment
0056     m_synchronizer = new Synchronizer(this);
0057 
0058     if (m_corona) {
0059         connect(m_corona->universalSettings(), &UniversalSettings::currentLayoutNameChanged, this, &Manager::currentLayoutNameChanged);
0060 
0061         connect(m_synchronizer, &Synchronizer::centralLayoutsChanged, this, &Manager::centralLayoutsChanged);
0062         connect(m_synchronizer, &Synchronizer::currentLayoutNameChanged, this, &Manager::currentLayoutNameChanged);
0063         connect(m_synchronizer, &Synchronizer::currentLayoutIsSwitching, this, &Manager::currentLayoutIsSwitching);
0064         connect(m_synchronizer, &Synchronizer::layoutsChanged, this, &Manager::layoutsChanged);
0065         connect(m_synchronizer, &Synchronizer::menuLayoutsChanged, this, &Manager::menuLayoutsChanged);
0066     }
0067 }
0068 
0069 Manager::~Manager()
0070 {
0071     m_importer->deleteLater();
0072     m_launchersSignals->deleteLater();
0073 
0074     //! no needed because Latte:Corona is calling it at better place
0075     // unload();
0076 
0077     m_synchronizer->deleteLater();
0078 }
0079 
0080 void Manager::load()
0081 {
0082     m_presetsPaths.clear();
0083 
0084     QDir layoutsDir(QDir::homePath() + "/.config/latte");
0085     bool firstRun = !layoutsDir.exists();
0086 
0087     int configVer = m_corona->universalSettings()->version();
0088     qDebug() << "Universal Settings version : " << configVer;
0089 
0090     if (firstRun) {
0091         m_corona->universalSettings()->setVersion(2);
0092         m_corona->universalSettings()->setCurrentLayoutName(i18n("My Layout"));
0093 
0094         //startup create what is necessary....
0095         if (!layoutsDir.exists()) {
0096             QDir(QDir::homePath() + "/.config").mkdir("latte");
0097         }
0098 
0099         newLayout(i18n("My Layout"));
0100         importPresets(false);
0101     } else if (configVer < 2 && !firstRun) {
0102         m_corona->universalSettings()->setVersion(2);
0103 
0104         bool isOlderVersion = m_importer->updateOldConfiguration();
0105         if (isOlderVersion) {
0106             qDebug() << "Latte is updating its older configuration...";
0107             importPresets(false);
0108         } else {
0109             m_corona->universalSettings()->setCurrentLayoutName(i18n("My Layout"));
0110         }
0111     }
0112 
0113     //! Check if the multiple-layouts hidden file is present, add it if it isnt
0114     if (!QFile(QDir::homePath() + "/.config/latte/" + Layout::AbstractLayout::MultipleLayoutsName + ".layout.latte").exists()) {
0115         importPreset(MultipleLayoutsPresetId, false);
0116     }
0117 
0118     qDebug() << "Latte is loading  its layouts...";
0119 
0120     m_presetsPaths.append(m_corona->kPackage().filePath("preset1"));
0121     m_presetsPaths.append(m_corona->kPackage().filePath("preset2"));
0122     m_presetsPaths.append(m_corona->kPackage().filePath("preset3"));
0123     m_presetsPaths.append(m_corona->kPackage().filePath("preset4"));
0124 
0125     m_synchronizer->loadLayouts();
0126 }
0127 
0128 void Manager::unload()
0129 {
0130     m_synchronizer->unloadLayouts();
0131 
0132     //! Remove no-needed temp files
0133     QString temp1File = QDir::homePath() + "/.config/lattedock.copy1.bak";
0134     QString temp2File = QDir::homePath() + "/.config/lattedock.copy2.bak";
0135 
0136     QFile file1(temp1File);
0137     QFile file2(temp2File);
0138 
0139     if (file1.exists()) {
0140         file1.remove();
0141     }
0142 
0143     if (file2.exists()) {
0144         file2.remove();
0145     }
0146 }
0147 
0148 Latte::Corona *Manager::corona()
0149 {
0150     return m_corona;
0151 }
0152 
0153 Importer *Manager::importer()
0154 {
0155     return m_importer;
0156 }
0157 
0158 LaunchersSignals *Manager::launchersSignals() const
0159 {
0160     return m_launchersSignals;
0161 }
0162 
0163 Synchronizer *Manager::synchronizer() const
0164 {
0165     return m_synchronizer;
0166 }
0167 
0168 QString Manager::currentLayoutName() const
0169 {
0170     return m_synchronizer->currentLayoutName();
0171 }
0172 
0173 QString Manager::defaultLayoutName() const
0174 {
0175     QByteArray presetNameOrig = QString("preset" + QString::number(1)).toUtf8();
0176     QString presetPath = m_corona->kPackage().filePath(presetNameOrig);
0177     QString presetName = CentralLayout::layoutName(presetPath);
0178     QByteArray presetNameChars = presetName.toUtf8();
0179     presetName = i18n(presetNameChars);
0180 
0181     return presetName;
0182 }
0183 
0184 QStringList Manager::layouts() const
0185 {
0186     return m_synchronizer->layouts();
0187 }
0188 
0189 QStringList Manager::menuLayouts() const
0190 {
0191     return m_synchronizer->menuLayouts();
0192 }
0193 
0194 void Manager::setMenuLayouts(QStringList layouts)
0195 {
0196     m_synchronizer->setMenuLayouts(layouts);
0197 }
0198 
0199 QStringList Manager::presetsPaths() const
0200 {
0201     return m_presetsPaths;
0202 }
0203 
0204 Types::LayoutsMemoryUsage Manager::memoryUsage() const
0205 {
0206     return m_corona->universalSettings()->layoutsMemoryUsage();
0207 }
0208 
0209 int Manager::layoutsMemoryUsage()
0210 {
0211     return (int)m_corona->universalSettings()->layoutsMemoryUsage();
0212 }
0213 
0214 void Manager::setMemoryUsage(Types::LayoutsMemoryUsage memoryUsage)
0215 {
0216     m_corona->universalSettings()->setLayoutsMemoryUsage(memoryUsage);
0217 }
0218 
0219 QStringList Manager::centralLayoutsNames()
0220 {
0221     return m_synchronizer->centralLayoutsNames();
0222 }
0223 
0224 QStringList Manager::sharedLayoutsNames()
0225 {
0226     return m_synchronizer->sharedLayoutsNames();
0227 }
0228 
0229 QStringList Manager::storedSharedLayouts() const
0230 {
0231     return m_synchronizer->storedSharedLayouts();
0232 }
0233 
0234 CentralLayout *Manager::currentLayout() const
0235 {
0236     return m_synchronizer->currentLayout();
0237 }
0238 
0239 bool Manager::switchToLayout(QString layoutName, int previousMemoryUsage)
0240 {
0241     return m_synchronizer->switchToLayout(layoutName, previousMemoryUsage);
0242 }
0243 
0244 void Manager::loadLayoutOnStartup(QString layoutName)
0245 {
0246     QStringList layouts = m_importer->checkRepairMultipleLayoutsLinkedFile();
0247 
0248     //! Latte didn't close correctly, maybe a crash
0249     if (layouts.size() > 0) {
0250         QMessageBox *msg = new QMessageBox();
0251         msg->setAttribute(Qt::WA_DeleteOnClose);
0252         msg->setIcon(QMessageBox::Warning);
0253         msg->setWindowTitle(i18n("Multiple Layouts Warning"));
0254         msg->setText(i18n("Latte did not close properly in the previous session. The following layout(s) <b>[%0]</b> were updated for consistency!!!").arg(layouts.join(",")));
0255         msg->setStandardButtons(QMessageBox::Ok);
0256 
0257         msg->open();
0258     }
0259 
0260     m_synchronizer->switchToLayout(layoutName);
0261 }
0262 
0263 void Manager::loadLatteLayout(QString layoutPath)
0264 {
0265     qDebug() << " -------------------------------------------------------------------- ";
0266     qDebug() << " -------------------------------------------------------------------- ";
0267 
0268     if (m_corona->containments().size() > 0) {
0269         qDebug() << "LOAD LATTE LAYOUT ::: There are still containments present !!!! :: " << m_corona->containments().size();
0270     }
0271 
0272     if (!layoutPath.isEmpty() && m_corona->containments().size() == 0) {
0273         cleanupOnStartup(layoutPath);
0274         qDebug() << "LOADING CORONA LAYOUT:" << layoutPath;
0275         m_corona->loadLayout(layoutPath);
0276     }
0277 }
0278 
0279 void Manager::cleanupOnStartup(QString path)
0280 {
0281     KSharedConfigPtr filePtr = KSharedConfig::openConfig(path);
0282 
0283     KConfigGroup actionGroups = KConfigGroup(filePtr, "ActionPlugins");
0284 
0285     QStringList deprecatedActionGroup;
0286 
0287     for (const auto &actId : actionGroups.groupList()) {
0288         QString pluginId = actionGroups.group(actId).readEntry("RightButton;NoModifier", "");
0289 
0290         if (pluginId == "org.kde.contextmenu") {
0291             deprecatedActionGroup << actId;
0292         }
0293     }
0294 
0295     for (const auto &pId : deprecatedActionGroup) {
0296         qDebug() << "!!!!!!!!!!!!!!!!  !!!!!!!!!!!! !!!!!!! REMOVING :::: " << pId;
0297         actionGroups.group(pId).deleteGroup();
0298     }
0299 
0300     KConfigGroup containmentGroups = KConfigGroup(filePtr, "Containments");
0301 
0302     QStringList removeContaimentsList;
0303 
0304     for (const auto &cId : containmentGroups.groupList()) {
0305         QString pluginId = containmentGroups.group(cId).readEntry("plugin", "");
0306 
0307         if (pluginId == "org.kde.desktopcontainment") { //!must remove ghost containments first
0308             removeContaimentsList << cId;
0309         }
0310     }
0311 
0312     for (const auto &cId : removeContaimentsList) {
0313         containmentGroups.group(cId).deleteGroup();
0314     }
0315 
0316 
0317     actionGroups.sync();
0318     containmentGroups.sync();
0319 }
0320 
0321 
0322 void Manager::showAboutDialog()
0323 {
0324     m_corona->aboutApplication();
0325 }
0326 
0327 void Manager::clearUnloadedContainmentsFromLinkedFile(QStringList containmentsIds, bool bypassChecks)
0328 {
0329     if (!m_corona || (memoryUsage() == Types::SingleLayout && !bypassChecks)) {
0330         return;
0331     }
0332 
0333     auto containments = m_corona->config()->group("Containments");
0334 
0335     for (const auto &conId : containmentsIds) {
0336         qDebug() << "unloads ::: " << conId;
0337         KConfigGroup containment = containments.group(conId);
0338         containment.deleteGroup();
0339     }
0340 
0341     containments.sync();
0342 }
0343 
0344 QString Manager::newLayout(QString layoutName, QString preset)
0345 {
0346     QDir layoutDir(QDir::homePath() + "/.config/latte");
0347     QStringList filter;
0348     filter.append(QString(layoutName + "*.layout.latte"));
0349     QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks);
0350 
0351     //! if the newLayout already exists provide a newName that doesn't
0352     if (files.count() >= 1) {
0353         int newCounter = files.count() + 1;
0354 
0355         layoutName = layoutName + "-" + QString::number(newCounter);
0356     }
0357 
0358     QString newLayoutPath = layoutDir.absolutePath() + "/" + layoutName + ".layout.latte";
0359 
0360     qDebug() << "adding layout : " << layoutName << " based on preset:" << preset;
0361 
0362     if (preset == i18n("Default") && !QFile(newLayoutPath).exists()) {
0363         qDebug() << "adding layout : succeed";
0364         QFile(m_corona->kPackage().filePath("preset1")).copy(newLayoutPath);
0365     }
0366 
0367     return newLayoutPath;
0368 }
0369 
0370 void Manager::importDefaultLayout(bool newInstanceIfPresent)
0371 {
0372     importPreset(1, newInstanceIfPresent);
0373 
0374     if (newInstanceIfPresent) {
0375         m_synchronizer->loadLayouts();
0376     }
0377 }
0378 
0379 void Manager::importPresets(bool includeDefault)
0380 {
0381     int start = 1;
0382 
0383     if (!includeDefault) {
0384         start = 2;
0385     }
0386 
0387     for (int i = start; i <= 4; ++i) {
0388         importPreset(i, false);
0389     }
0390 }
0391 
0392 void Manager::importPreset(int presetNo, bool newInstanceIfPresent)
0393 {
0394     QDir configDir(QDir::homePath() + "/.config");
0395 
0396     if (!QDir(configDir.absolutePath() + "/latte").exists()) {
0397         configDir.mkdir("latte");
0398     }
0399 
0400     QByteArray presetNameOrig = QString("preset" + QString::number(presetNo)).toUtf8();
0401     QString presetPath = m_corona->kPackage().filePath(presetNameOrig);
0402     QString presetName = Layout::AbstractLayout::layoutName(presetPath);
0403     QByteArray presetNameChars = presetName.toUtf8();
0404     presetName = i18n(presetNameChars);
0405 
0406     //! hide the multiple layouts layout file from user
0407     presetName = (presetNo == MultipleLayoutsPresetId) ? "." + presetName : presetName;
0408 
0409     QString newLayoutFile = "";
0410 
0411     if (newInstanceIfPresent) {
0412         newLayoutFile = QDir::homePath() + "/.config/latte/" + m_importer->uniqueLayoutName(presetName) + ".layout.latte";
0413     } else {
0414         newLayoutFile = QDir::homePath() + "/.config/latte/" + presetName + ".layout.latte";
0415     }
0416 
0417     if (!QFile(newLayoutFile).exists()) {
0418         QFile(presetPath).copy(newLayoutFile);
0419         QFileInfo newFileInfo(newLayoutFile);
0420 
0421         if (newFileInfo.exists() && !newFileInfo.isWritable()) {
0422             QFile(newLayoutFile).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
0423         }
0424     }
0425 }
0426 
0427 void Manager::showLatteSettingsDialog(int page)
0428 {
0429     bool created{false};
0430 
0431     if (!m_latteSettingsDialog) {
0432         m_latteSettingsDialog = new SettingsDialog(nullptr, m_corona);
0433         created = true;
0434     }
0435     m_latteSettingsDialog->show();
0436 
0437     if (m_latteSettingsDialog->isMinimized()) {
0438         m_latteSettingsDialog->showNormal();
0439     }
0440 
0441     if (!created) {
0442         Types::LatteConfigPage configPage = static_cast<Types::LatteConfigPage>(page);
0443         m_latteSettingsDialog->toggleCurrentPage();
0444     }
0445 
0446     m_latteSettingsDialog->activateWindow();
0447 }
0448 
0449 void Manager::hideLatteSettingsDialog()
0450 {
0451     if (m_latteSettingsDialog) {
0452         m_latteSettingsDialog->deleteLater();
0453         m_latteSettingsDialog = nullptr;
0454     }
0455 }
0456 
0457 void Manager::showInfoWindow(QString info, int duration, QStringList activities)
0458 {
0459     for (const auto screen : qGuiApp->screens()) {
0460         InfoView *infoView = new InfoView(m_corona, info, screen);
0461 
0462         infoView->show();
0463         infoView->setOnActivities(activities);
0464 
0465         QTimer::singleShot(duration, [this, infoView]() {
0466             infoView->deleteLater();
0467         });
0468     }
0469 }
0470 
0471 //! it is used just in order to provide translations for the presets
0472 void Manager::ghostForTranslatedPresets()
0473 {
0474     QString preset1 = i18n("Default");
0475     QString preset2 = i18n("Plasma");
0476     QString preset3 = i18n("Unity");
0477     QString preset4 = i18n("Extended");
0478 }
0479 
0480 }
0481 }