Warning, file /plasma/latte-dock/app/settings/settingsdialog.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  * Copyright 2017  Smith AR <audoban@openmailbox.org>
0003  *                 Michail Vourlakos <mvourlakos@gmail.com>
0004  *
0005  * This file is part of Latte-Dock
0006  *
0007  * Latte-Dock is free software; you can redistribute it and/or
0008  * modify it under the terms of the GNU General Public License as
0009  * published by the Free Software Foundation; either version 2 of
0010  * the License, or (at your option) any later version.
0011  *
0012  * Latte-Dock is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015  * GNU General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU General Public License
0018  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0019  *
0020  */
0021 
0022 #include "settingsdialog.h"
0023 
0024 // local
0025 #include "universalsettings.h"
0026 #include "ui_settingsdialog.h"
0027 #include "../lattecorona.h"
0028 #include "../screenpool.h"
0029 #include "../layout/genericlayout.h"
0030 #include "../layout/centrallayout.h"
0031 #include "../layout/sharedlayout.h"
0032 #include "../layouts/importer.h"
0033 #include "../layouts/manager.h"
0034 #include "../layouts/synchronizer.h"
0035 #include "../liblatte2/types.h"
0036 #include "../plasma/extended/theme.h"
0037 #include "delegates/activitiesdelegate.h"
0038 #include "delegates/checkboxdelegate.h"
0039 #include "delegates/colorcmbboxdelegate.h"
0040 #include "delegates/layoutnamedelegate.h"
0041 #include "delegates/shareddelegate.h"
0042 
0043 // Qt
0044 #include <QButtonGroup>
0045 #include <QColorDialog>
0046 #include <QDesktopServices>
0047 #include <QDir>
0048 #include <QFileDialog>
0049 #include <QHeaderView>
0050 #include <QMenuBar>
0051 #include <QMessageBox>
0052 #include <QProcess>
0053 #include <QStandardItem>
0054 #include <QStandardItemModel>
0055 #include <QTemporaryDir>
0056 
0057 // KDE
0058 #include <KActivities/Controller>
0059 #include <KArchive/KTar>
0060 #include <KArchive/KArchiveEntry>
0061 #include <KArchive/KArchiveDirectory>
0062 #include <KLocalizedString>
0063 #include <KNotification>
0064 #include <KWindowSystem>
0065 #include <KNewStuff3/KNS3/DownloadDialog>
0066 
0067 namespace Latte {
0068 
0069 const int IDCOLUMN = 0;
0070 const int HIDDENTEXTCOLUMN = 1;
0071 const int COLORCOLUMN = 2;
0072 const int NAMECOLUMN = 3;
0073 const int MENUCOLUMN = 4;
0074 const int BORDERSCOLUMN = 5;
0075 const int ACTIVITYCOLUMN = 6;
0076 const int SHAREDCOLUMN = 7;
0077 
0078 const int SCREENTRACKERDEFAULTVALUE = 2500;
0079 const int OUTLINEDEFAULTWIDTH = 1;
0080 
0081 const QChar CheckMark{0x2714};
0082 
0083 SettingsDialog::SettingsDialog(QWidget *parent, Latte::Corona *corona)
0084     : QDialog(parent),
0085       ui(new Ui::SettingsDialog),
0086       m_corona(corona)
0087 {
0088     ui->setupUi(this);
0089 
0090     setAttribute(Qt::WA_DeleteOnClose, true);
0091     setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
0092     resize(m_corona->universalSettings()->layoutsWindowSize());
0093 
0094     connect(ui->buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked
0095             , this, &SettingsDialog::apply);
0096     connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked
0097             , this, &SettingsDialog::restoreDefaults);
0098 
0099     m_model = new QStandardItemModel(m_corona->layoutsManager()->layouts().count(), 6, this);
0100 
0101     ui->layoutsView->setModel(m_model);
0102     ui->layoutsView->horizontalHeader()->setStretchLastSection(true);
0103     ui->layoutsView->verticalHeader()->setVisible(false);
0104 
0105     connect(m_corona->layoutsManager(), &Layouts::Manager::currentLayoutNameChanged, this, &SettingsDialog::layoutsChanged);
0106     connect(m_corona->layoutsManager(), &Layouts::Manager::centralLayoutsChanged, this, &SettingsDialog::layoutsChanged);
0107 
0108     QString iconsPath(m_corona->kPackage().path() + "../../plasmoids/org.kde.latte.containment/contents/icons/");
0109 
0110     //!find the available colors
0111     QDir layoutDir(iconsPath);
0112     QStringList filter;
0113     filter.append(QString("*print.jpg"));
0114     QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks);
0115     QStringList colors;
0116 
0117     for (auto &file : files) {
0118         int colorEnd = file.lastIndexOf("print.jpg");
0119         QString color = file.remove(colorEnd, 9);
0120         colors.append(color);
0121     }
0122 
0123     ui->layoutsView->setItemDelegateForColumn(NAMECOLUMN, new LayoutNameDelegate(this));
0124     ui->layoutsView->setItemDelegateForColumn(COLORCOLUMN, new ColorCmbBoxDelegate(this, iconsPath, colors));
0125     ui->layoutsView->setItemDelegateForColumn(MENUCOLUMN, new CheckBoxDelegate(this));
0126     ui->layoutsView->setItemDelegateForColumn(BORDERSCOLUMN, new CheckBoxDelegate(this));
0127     ui->layoutsView->setItemDelegateForColumn(ACTIVITYCOLUMN, new ActivitiesDelegate(this));
0128     ui->layoutsView->setItemDelegateForColumn(SHAREDCOLUMN, new SharedDelegate(this));
0129 
0130     m_inMemoryButtons = new QButtonGroup(this);
0131     m_inMemoryButtons->addButton(ui->singleToolBtn, Latte::Types::SingleLayout);
0132     m_inMemoryButtons->addButton(ui->multipleToolBtn, Latte::Types::MultipleLayouts);
0133     m_inMemoryButtons->setExclusive(true);
0134 
0135     if (KWindowSystem::isPlatformWayland()) {
0136         m_inMemoryButtons->button(Latte::Types::MultipleLayouts)->setEnabled(false);
0137     }
0138 
0139     m_mouseSensitivityButtons = new QButtonGroup(this);
0140     m_mouseSensitivityButtons->addButton(ui->lowSensitivityBtn, Latte::Types::LowSensitivity);
0141     m_mouseSensitivityButtons->addButton(ui->mediumSensitivityBtn, Latte::Types::MediumSensitivity);
0142     m_mouseSensitivityButtons->addButton(ui->highSensitivityBtn, Latte::Types::HighSensitivity);
0143     m_mouseSensitivityButtons->setExclusive(true);
0144 
0145     ui->screenTrackerSpinBox->setValue(m_corona->universalSettings()->screenTrackerInterval());
0146     ui->outlineSpinBox->setValue(m_corona->themeExtended()->outlineWidth());
0147 
0148     //! About Menu
0149     QMenuBar *menuBar = new QMenuBar(this);
0150     // QMenuBar *rightAlignedMenuBar = new QMenuBar(menuBar);
0151 
0152     layout()->setMenuBar(menuBar);
0153     //menuBar->setCornerWidget(rightAlignedMenuBar);
0154 
0155     QMenu *fileMenu = new QMenu(i18n("File"), menuBar);
0156     menuBar->addMenu(fileMenu);
0157 
0158     QMenu *layoutMenu = new QMenu(i18n("Layout"), menuBar);
0159     menuBar->addMenu(layoutMenu);
0160 
0161     //! Help menu
0162     m_helpMenu = new KHelpMenu(menuBar);
0163     menuBar->addMenu(m_helpMenu->menu());
0164     //rightAlignedMenuBar->addMenu(helpMenu);
0165 
0166     //! hide help menu actions that are not used
0167     m_helpMenu->action(KHelpMenu::menuHelpContents)->setVisible(false);
0168     m_helpMenu->action(KHelpMenu::menuWhatsThis)->setVisible(false);
0169 
0170 
0171     QAction *screensAction = fileMenu->addAction(i18n("Sc&reens..."));
0172     screensAction->setIcon(QIcon::fromTheme("document-properties"));
0173     screensAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
0174 
0175     QAction *quitAction = fileMenu->addAction(i18n("&Quit Latte"));
0176     quitAction->setIcon(QIcon::fromTheme("application-exit"));
0177     quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
0178 
0179     m_editLayoutAction = layoutMenu->addAction(i18nc("edit layout","&Edit..."));
0180     m_editLayoutAction->setIcon(QIcon::fromTheme("document-edit"));
0181     m_editLayoutAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E));
0182     m_editLayoutAction->setToolTip("You can edit layout file when layout is not active or locked");
0183 
0184     QAction *infoLayoutAction = layoutMenu->addAction(i18nc("layout information","&Information..."));
0185     infoLayoutAction->setIcon(QIcon::fromTheme("document-properties"));
0186     infoLayoutAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_I));
0187 
0188     //! RTL support for labels in preferences
0189     if (qApp->layoutDirection() == Qt::RightToLeft) {
0190         ui->behaviorLbl->setAlignment(Qt::AlignRight | Qt::AlignTop);
0191         ui->mouseSensetivityLbl->setAlignment(Qt::AlignRight | Qt::AlignTop);
0192         ui->delayLbl->setAlignment(Qt::AlignRight | Qt::AlignTop);
0193     }
0194 
0195     loadSettings();
0196 
0197     //! SIGNALS
0198 
0199     connect(m_model, &QStandardItemModel::itemChanged, this, &SettingsDialog::itemChanged);
0200     connect(ui->layoutsView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, [&]() {
0201         updatePerLayoutButtonsState();
0202         updateApplyButtonsState();
0203     });
0204 
0205     connect(m_inMemoryButtons, static_cast<void(QButtonGroup::*)(int, bool)>(&QButtonGroup::buttonToggled),
0206             [ = ](int id, bool checked) {
0207         updateApplyButtonsState();
0208         updateSharedLayoutsStates();
0209         updateSharedLayoutsUiElements();
0210     });
0211 
0212     connect(m_mouseSensitivityButtons, static_cast<void(QButtonGroup::*)(int, bool)>(&QButtonGroup::buttonToggled),
0213             [ = ](int id, bool checked) {
0214         updateApplyButtonsState();
0215     });
0216 
0217     connect(ui->screenTrackerSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), [ = ](int i) {
0218         updateApplyButtonsState();
0219     });
0220 
0221     connect(ui->outlineSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), [ = ](int i) {
0222         updateApplyButtonsState();
0223     });
0224 
0225     connect(ui->autostartChkBox, &QCheckBox::stateChanged, this, &SettingsDialog::updateApplyButtonsState);
0226     connect(ui->badges3DStyleChkBox, &QCheckBox::stateChanged, this, &SettingsDialog::updateApplyButtonsState);
0227     connect(ui->metaPressChkBox, &QCheckBox::stateChanged, this, &SettingsDialog::updateApplyButtonsState);
0228     connect(ui->metaPressHoldChkBox, &QCheckBox::stateChanged, this, &SettingsDialog::updateApplyButtonsState);
0229     connect(ui->infoWindowChkBox, &QCheckBox::stateChanged, this, &SettingsDialog::updateApplyButtonsState);
0230     connect(ui->tabWidget, &QTabWidget::currentChanged, this, &SettingsDialog::updateApplyButtonsState);
0231 
0232     connect(ui->noBordersForMaximizedChkBox, &QCheckBox::stateChanged, this, [&]() {
0233         bool noBordersForMaximized = ui->noBordersForMaximizedChkBox->isChecked();
0234 
0235         if (noBordersForMaximized) {
0236             ui->layoutsView->setColumnHidden(BORDERSCOLUMN, false);
0237         } else {
0238             ui->layoutsView->setColumnHidden(BORDERSCOLUMN, true);
0239         }
0240 
0241         updateApplyButtonsState();
0242     });
0243 
0244     connect(quitAction, &QAction::triggered, this, [&]() {
0245         close();
0246         m_corona->closeApplication();
0247     });
0248 
0249     connect(m_editLayoutAction, &QAction::triggered, this, [&]() {
0250         QString file = idForRow(ui->layoutsView->currentIndex().row());
0251 
0252         if (!file.isEmpty()) {
0253             QProcess::startDetached("kwrite \"" + file + "\"");
0254         }
0255     });
0256 
0257     connect(infoLayoutAction, &QAction::triggered, this, &SettingsDialog::showLayoutInformation);
0258     connect(screensAction, &QAction::triggered, this, &SettingsDialog::showScreensInformation);
0259 
0260     //! update all layouts view when runningActivities changed. This way we update immediately
0261     //! the running Activities in Activities checkboxes which are shown as bold
0262     connect(m_corona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged,
0263             this, [&]() {
0264         ui->layoutsView->update();
0265     });
0266 
0267     blockDeleteOnActivityStopped();
0268 }
0269 
0270 SettingsDialog::~SettingsDialog()
0271 {
0272     qDebug() << Q_FUNC_INFO;
0273 
0274     qDeleteAll(m_layouts);
0275 
0276     if (m_model) {
0277         delete m_model;
0278     }
0279 
0280     if (m_corona && m_corona->universalSettings()) {
0281         m_corona->universalSettings()->setLayoutsWindowSize(size());
0282 
0283         QStringList columnWidths;       
0284         columnWidths << QString::number(ui->layoutsView->columnWidth(COLORCOLUMN));
0285         columnWidths << QString::number(ui->layoutsView->columnWidth(NAMECOLUMN));
0286         columnWidths << QString::number(ui->layoutsView->columnWidth(MENUCOLUMN));
0287         columnWidths << QString::number(ui->layoutsView->columnWidth(BORDERSCOLUMN));
0288 
0289         Latte::Types::LayoutsMemoryUsage inMemoryOption = static_cast<Latte::Types::LayoutsMemoryUsage>(m_inMemoryButtons->checkedId());
0290 
0291         if (inMemoryOption == Latte::Types::MultipleLayouts) {
0292             columnWidths << QString::number(ui->layoutsView->columnWidth(ACTIVITYCOLUMN));
0293         } else {
0294             //! In Single Mode, keed recorded value for ACTIVITYCOLUMN
0295             QStringList currentWidths = m_corona->universalSettings()->layoutsColumnWidths();
0296             if (currentWidths.count()>=5) {
0297                 columnWidths << currentWidths[4];
0298             }
0299         }
0300 
0301         m_corona->universalSettings()->setLayoutsColumnWidths(columnWidths);
0302     }
0303 
0304     m_inMemoryButtons->deleteLater();
0305     m_mouseSensitivityButtons->deleteLater();
0306 
0307     for (const auto &tempDir : m_tempDirectories) {
0308         QDir tDir(tempDir);
0309 
0310         if (tDir.exists() && tempDir.startsWith("/tmp/")) {
0311             tDir.removeRecursively();
0312         }
0313     }
0314 }
0315 
0316 void SettingsDialog::blockDeleteOnActivityStopped()
0317 {
0318     connect(m_corona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged,
0319             this, [&]() {
0320         m_blockDeleteOnReject = true;
0321         m_activityClosedTimer.start();
0322     });
0323 
0324     m_activityClosedTimer.setSingleShot(true);
0325     m_activityClosedTimer.setInterval(500);
0326     connect(&m_activityClosedTimer, &QTimer::timeout, this, [&]() {
0327         m_blockDeleteOnReject = false;
0328     });
0329 }
0330 
0331 QStringList SettingsDialog::activities()
0332 {
0333     return m_corona->layoutsManager()->synchronizer()->activities();
0334 }
0335 
0336 QStringList SettingsDialog::availableActivities()
0337 {
0338     return m_availableActivities;
0339 }
0340 
0341 QStringList SettingsDialog::availableSharesFor(int row)
0342 {
0343     QStringList availables;
0344     QStringList regs;
0345 
0346     for (int i = 0; i < m_model->rowCount(); ++i) {
0347         QString id = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString();
0348         QStringList shares = m_model->data(m_model->index(i, SHAREDCOLUMN), Qt::UserRole).toStringList();
0349 
0350         if (i != row) {
0351             if (shares.isEmpty()) {
0352                 availables << id;
0353             } else {
0354                 regs << shares;
0355             }
0356         }
0357     }
0358 
0359     for (const auto r : regs) {
0360         availables.removeAll(r);
0361     }
0362 
0363     return availables;
0364 }
0365 
0366 void SettingsDialog::toggleCurrentPage()
0367 {
0368     if (ui->tabWidget->currentIndex() == 0) {
0369         ui->tabWidget->setCurrentIndex(1);
0370     } else {
0371         ui->tabWidget->setCurrentIndex(0);
0372     }
0373 }
0374 
0375 void SettingsDialog::on_newButton_clicked()
0376 {
0377     qDebug() << Q_FUNC_INFO;
0378 
0379     //! find Default preset path
0380     for (const auto &preset : m_corona->layoutsManager()->presetsPaths()) {
0381         QString presetName = CentralLayout::layoutName(preset);
0382 
0383         if (presetName == "Default") {
0384             QByteArray presetNameChars = presetName.toUtf8();
0385             const char *prset_str = presetNameChars.data();
0386             presetName = uniqueLayoutName(i18n(prset_str));
0387 
0388             addLayoutForFile(preset, presetName, true, false);
0389             break;
0390         }
0391     }
0392 }
0393 
0394 void SettingsDialog::on_copyButton_clicked()
0395 {
0396     qDebug() << Q_FUNC_INFO;
0397 
0398     int row = ui->layoutsView->currentIndex().row();
0399 
0400     if (row < 0) {
0401         return;
0402     }
0403 
0404     //! Update original layout before copying if this layout is active
0405     if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
0406         QString lName = (m_model->data(m_model->index(row, NAMECOLUMN), Qt::DisplayRole)).toString();
0407 
0408         Layout::GenericLayout *generic = m_corona->layoutsManager()->synchronizer()->layout(lName);
0409         if (generic) {
0410             generic->syncToLayoutFile();
0411         }
0412     }
0413 
0414     QString tempDir = uniqueTempDirectory();
0415 
0416     QString id = m_model->data(m_model->index(row, IDCOLUMN), Qt::DisplayRole).toString();
0417     QString color = m_model->data(m_model->index(row, COLORCOLUMN), Qt::BackgroundRole).toString();
0418     QString textColor = m_model->data(m_model->index(row, COLORCOLUMN), Qt::UserRole).toString();
0419     QString layoutName = uniqueLayoutName(m_model->data(m_model->index(row, NAMECOLUMN), Qt::DisplayRole).toString());
0420     bool menu = m_model->data(m_model->index(row, MENUCOLUMN), Qt::DisplayRole).toString() == CheckMark;
0421     bool disabledBorders = m_model->data(m_model->index(row, BORDERSCOLUMN), Qt::DisplayRole).toString() == CheckMark;
0422 
0423     QString copiedId = tempDir + "/" + layoutName + ".layout.latte";
0424     QFile(id).copy(copiedId);
0425 
0426     QFileInfo newFileInfo(copiedId);
0427 
0428     if (newFileInfo.exists() && !newFileInfo.isWritable()) {
0429         QFile(copiedId).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
0430     }
0431 
0432     CentralLayout *settings = new CentralLayout(this, copiedId);
0433     m_layouts[copiedId] = settings;
0434 
0435     insertLayoutInfoAtRow(row + 1, copiedId, color, textColor, layoutName, menu, disabledBorders, QStringList(), false);
0436 
0437     ui->layoutsView->selectRow(row + 1);
0438 }
0439 
0440 void SettingsDialog::on_downloadButton_clicked()
0441 {
0442     qDebug() << Q_FUNC_INFO;
0443 
0444     KNS3::DownloadDialog dialog(QStringLiteral("latte-layouts.knsrc"), this);
0445     dialog.resize(m_corona->universalSettings()->downloadWindowSize());
0446     dialog.exec();
0447 
0448     bool layoutAdded{false};
0449 
0450     if (!dialog.changedEntries().isEmpty() || !dialog.installedEntries().isEmpty()) {
0451         for (const auto &entry : dialog.installedEntries()) {
0452             for (const auto &entryFile : entry.installedFiles()) {
0453                 Layouts::Importer::LatteFileVersion version = Layouts::Importer::fileVersion(entryFile);
0454 
0455                 if (version == Layouts::Importer::LayoutVersion2) {
0456                     layoutAdded = true;
0457                     addLayoutForFile(entryFile);
0458                     break;
0459                 }
0460             }
0461         }
0462     }
0463 
0464     m_corona->universalSettings()->setDownloadWindowSize(dialog.size());
0465 
0466     if (layoutAdded) {
0467         apply();
0468     }
0469 }
0470 
0471 void SettingsDialog::on_removeButton_clicked()
0472 {
0473     qDebug() << Q_FUNC_INFO;
0474 
0475     int row = ui->layoutsView->currentIndex().row();
0476 
0477     if (row < 0) {
0478         return;
0479     }
0480 
0481     QString layoutName = m_model->data(m_model->index(row, NAMECOLUMN), Qt::DisplayRole).toString();
0482 
0483     if (m_corona->layoutsManager()->synchronizer()->centralLayout(layoutName)) {
0484         return;
0485     }
0486 
0487     m_model->removeRow(row);
0488 
0489     updateApplyButtonsState();
0490 
0491     row = qMax(row - 1, 0);
0492 
0493     ui->layoutsView->selectRow(row);
0494 }
0495 
0496 void SettingsDialog::on_lockedButton_clicked()
0497 {
0498     qDebug() << Q_FUNC_INFO;
0499 
0500     int row = ui->layoutsView->currentIndex().row();
0501 
0502     if (row < 0) {
0503         return;
0504     }
0505 
0506     bool lockedModel = m_model->data(m_model->index(row, NAMECOLUMN), Qt::UserRole).toBool();
0507 
0508     m_model->setData(m_model->index(row, NAMECOLUMN), QVariant(!lockedModel), Qt::UserRole);
0509 
0510     updatePerLayoutButtonsState();
0511     updateApplyButtonsState();
0512 }
0513 
0514 void SettingsDialog::on_sharedButton_clicked()
0515 {
0516     qDebug() << Q_FUNC_INFO;
0517 
0518     int row = ui->layoutsView->currentIndex().row();
0519 
0520     if (row < 0) {
0521         return;
0522     }
0523 
0524     if (isShared(row)) {
0525         m_model->setData(m_model->index(row, SHAREDCOLUMN), QStringList(), Qt::UserRole);
0526     } else {
0527         bool assigned{false};
0528         QStringList assignedList;
0529 
0530         QStringList availableShares = availableSharesFor(row);
0531 
0532         for (const auto &id : availableShares) {
0533             QString name = nameForId(id);
0534             if (m_corona->layoutsManager()->synchronizer()->layout(name)) {
0535                 assignedList << id;
0536                 m_model->setData(m_model->index(row, SHAREDCOLUMN), assignedList, Qt::UserRole);
0537                 assigned = true;
0538                 break;
0539             }
0540         }
0541 
0542         if (!assigned && availableShares.count()>0) {
0543             assignedList << availableShares[0];
0544             m_model->setData(m_model->index(row, SHAREDCOLUMN), assignedList, Qt::UserRole);
0545             assigned = true;
0546         }
0547     }
0548 
0549     updatePerLayoutButtonsState();
0550     updateApplyButtonsState();
0551 }
0552 
0553 void SettingsDialog::on_importButton_clicked()
0554 {
0555     qDebug() << Q_FUNC_INFO;
0556 
0557 
0558     QFileDialog *fileDialog = new QFileDialog(this, i18nc("import layout/configuration", "Import Layout/Configuration")
0559                                               , QDir::homePath()
0560                                               , QStringLiteral("layout.latte"));
0561 
0562     fileDialog->setFileMode(QFileDialog::AnyFile);
0563     fileDialog->setAcceptMode(QFileDialog::AcceptOpen);
0564     fileDialog->setDefaultSuffix("layout.latte");
0565 
0566     QStringList filters;
0567     filters << QString(i18nc("import latte layout", "Latte Dock Layout file v0.2") + "(*.layout.latte)")
0568             << QString(i18nc("import latte layouts/configuration", "Latte Dock Full Configuration file (v0.1, v0.2)") + "(*.latterc)");
0569     fileDialog->setNameFilters(filters);
0570 
0571     connect(fileDialog, &QFileDialog::finished
0572             , fileDialog, &QFileDialog::deleteLater);
0573 
0574     connect(fileDialog, &QFileDialog::fileSelected
0575             , this, [&](const QString & file) {
0576         Layouts::Importer::LatteFileVersion version = Layouts::Importer::fileVersion(file);
0577         qDebug() << "VERSION :::: " << version;
0578 
0579         if (version == Layouts::Importer::LayoutVersion2) {
0580             addLayoutForFile(file);
0581         } else if (version == Layouts::Importer::ConfigVersion1) {
0582             auto msg = new QMessageBox(this);
0583             msg->setIcon(QMessageBox::Warning);
0584             msg->setWindowTitle(i18n("Import: Configuration file version v0.1"));
0585             msg->setText(
0586                         i18n("You are going to import an old version <b>v0.1</b> configuration file.<br><b>Be careful</b>, importing the entire configuration <b>will erase all</b> your current configuration!!!<br><br> <i>Alternative, you can <b>import safely</b> from this file<br><b>only the contained layouts...</b></i>"));
0587             msg->setStandardButtons(QMessageBox::Cancel);
0588 
0589             QPushButton *fullBtn = new QPushButton(msg);
0590             QPushButton *layoutsBtn = new QPushButton(msg);
0591             fullBtn->setText(i18nc("import full configuration", "Full Configuration"));
0592             fullBtn->setIcon(QIcon::fromTheme("settings"));
0593             layoutsBtn->setText(i18nc("import only the layouts", "Only Layouts"));
0594             layoutsBtn->setIcon(QIcon::fromTheme("user-identity"));
0595 
0596             msg->addButton(fullBtn, QMessageBox::AcceptRole);
0597             msg->addButton(layoutsBtn, QMessageBox::AcceptRole);
0598 
0599             msg->setDefaultButton(layoutsBtn);
0600 
0601             connect(msg, &QMessageBox::finished, msg, &QMessageBox::deleteLater);
0602 
0603             msg->open();
0604 
0605             connect(layoutsBtn, &QPushButton::clicked
0606                     , this, [ &, file](bool check) {
0607                 importLayoutsFromV1ConfigFile(file);
0608             });
0609 
0610             connect(fullBtn, &QPushButton::clicked
0611                     , this, [ &, file](bool check) {
0612                 //!NOTE: Restart latte for import the new configuration
0613                 QProcess::startDetached(qGuiApp->applicationFilePath() + " --import-full \"" + file + "\"");
0614                 qGuiApp->exit();
0615             });
0616         } else if (version == Layouts::Importer::ConfigVersion2) {
0617             auto msg = new QMessageBox(this);
0618             msg->setIcon(QMessageBox::Warning);
0619             msg->setWindowTitle(i18n("Import: Configuration file version v0.2"));
0620             msg->setText(
0621                         i18n("You are going to import a <b>v0.2</b> configuration file.<br><b>Be careful</b>, importing <b>will erase all</b> your current configuration!!!<br><br><i>Would you like to proceed?</i>"));
0622             msg->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
0623             msg->setDefaultButton(QMessageBox::No);
0624 
0625             connect(msg, &QMessageBox::finished, this, [ &, msg, file](int result) {
0626                 if (result == QMessageBox::Yes) {
0627                     //!NOTE: Restart latte for import the new configuration
0628                     msg->deleteLater();
0629                     QProcess::startDetached(qGuiApp->applicationFilePath() + " --import-full \"" + file + "\"");
0630                     qGuiApp->exit();
0631                 }
0632             });
0633 
0634             msg->open();
0635         }
0636     });
0637 
0638     fileDialog->open();
0639 }
0640 
0641 bool SettingsDialog::importLayoutsFromV1ConfigFile(QString file)
0642 {
0643     KTar archive(file, QStringLiteral("application/x-tar"));
0644     archive.open(QIODevice::ReadOnly);
0645 
0646     //! if the file isnt a tar archive
0647     if (archive.isOpen()) {
0648         QDir tempDir{uniqueTempDirectory()};
0649 
0650         const auto archiveRootDir = archive.directory();
0651 
0652         for (const auto &name : archiveRootDir->entries()) {
0653             auto fileEntry = archiveRootDir->file(name);
0654             fileEntry->copyTo(tempDir.absolutePath());
0655         }
0656 
0657         QString name = Layouts::Importer::nameOfConfigFile(file);
0658 
0659         QString applets(tempDir.absolutePath() + "/" + "lattedock-appletsrc");
0660 
0661         if (QFile(applets).exists()) {
0662             if (m_corona->layoutsManager()->importer()->importOldLayout(applets, name, false, tempDir.absolutePath())) {
0663                 addLayoutForFile(tempDir.absolutePath() + "/" + name + ".layout.latte", name, false);
0664             }
0665 
0666             QString alternativeName = name + "-" + i18nc("layout", "Alternative");
0667 
0668             if (m_corona->layoutsManager()->importer()->importOldLayout(applets, alternativeName, false, tempDir.absolutePath())) {
0669                 addLayoutForFile(tempDir.absolutePath() + "/" + alternativeName + ".layout.latte", alternativeName, false);
0670             }
0671         }
0672 
0673         return true;
0674     }
0675 
0676     return false;
0677 }
0678 
0679 
0680 void SettingsDialog::on_exportButton_clicked()
0681 {
0682     int row = ui->layoutsView->currentIndex().row();
0683 
0684     if (row < 0) {
0685         return;
0686     }
0687 
0688     QString layoutExported = m_model->data(m_model->index(row, IDCOLUMN), Qt::DisplayRole).toString();
0689 
0690     //! Update ALL active original layouts before exporting,
0691     //! this is needed because the export method can export also the full configuration
0692     qDebug() << Q_FUNC_INFO;
0693 
0694     m_corona->layoutsManager()->synchronizer()->syncActiveLayoutsToOriginalFiles();
0695 
0696     QFileDialog *fileDialog = new QFileDialog(this, i18nc("export layout/configuration", "Export Layout/Configuration")
0697                                               , QDir::homePath(), QStringLiteral("layout.latte"));
0698 
0699     fileDialog->setFileMode(QFileDialog::AnyFile);
0700     fileDialog->setAcceptMode(QFileDialog::AcceptSave);
0701     fileDialog->setDefaultSuffix("layout.latte");
0702 
0703     QStringList filters;
0704     QString filter1(i18nc("export layout", "Latte Dock Layout file v0.2") + "(*.layout.latte)");
0705     QString filter2(i18nc("export full configuration", "Latte Dock Full Configuration file v0.2") + "(*.latterc)");
0706 
0707     filters << filter1
0708             << filter2;
0709 
0710     fileDialog->setNameFilters(filters);
0711 
0712     connect(fileDialog, &QFileDialog::finished
0713             , fileDialog, &QFileDialog::deleteLater);
0714 
0715     connect(fileDialog, &QFileDialog::fileSelected
0716             , this, [ &, layoutExported](const QString & file) {
0717         auto showNotificationError = []() {
0718             auto notification = new KNotification("export-fail", KNotification::CloseOnTimeout);
0719             notification->setText(i18nc("export layout", "Failed to export layout"));
0720             notification->sendEvent();
0721         };
0722 
0723         if (QFile::exists(file) && !QFile::remove(file)) {
0724             showNotificationError();
0725             return;
0726         }
0727 
0728         if (file.endsWith(".layout.latte")) {
0729             if (!QFile(layoutExported).copy(file)) {
0730                 showNotificationError();
0731                 return;
0732             }
0733 
0734             QFileInfo newFileInfo(file);
0735 
0736             if (newFileInfo.exists() && !newFileInfo.isWritable()) {
0737                 QFile(file).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
0738             }
0739 
0740             CentralLayout layoutS(this, file);
0741             layoutS.setActivities(QStringList());
0742             layoutS.clearLastUsedActivity();
0743 
0744             //NOTE: The pointer is automatically deleted when the event is closed
0745             auto notification = new KNotification("export-done", KNotification::CloseOnTimeout);
0746             notification->setActions({i18nc("export layout", "Open location")});
0747             notification->setText(i18nc("export layout", "Layout exported successfully"));
0748 
0749             connect(notification, &KNotification::action1Activated
0750                     , this, [file]() {
0751                 QDesktopServices::openUrl({QFileInfo(file).canonicalPath()});
0752             });
0753 
0754             notification->sendEvent();
0755         } else if (file.endsWith(".latterc")) {
0756             auto showNotificationError = []() {
0757                 auto notification = new KNotification("export-fail", KNotification::CloseOnTimeout);
0758                 notification->setText(i18nc("import/export config", "Failed to export configuration"));
0759                 notification->sendEvent();
0760             };
0761 
0762             if (m_corona->layoutsManager()->importer()->exportFullConfiguration(file)) {
0763 
0764                 auto notification = new KNotification("export-done", KNotification::CloseOnTimeout);
0765                 notification->setActions({i18nc("import/export config", "Open location")});
0766                 notification->setText(i18nc("import/export config", "Full Configuration exported successfully"));
0767 
0768                 connect(notification, &KNotification::action1Activated
0769                         , this, [file]() {
0770                     QDesktopServices::openUrl({QFileInfo(file).canonicalPath()});
0771                 });
0772 
0773                 notification->sendEvent();
0774             } else {
0775                 showNotificationError();
0776             }
0777         }
0778     });
0779 
0780 
0781     fileDialog->open();
0782 }
0783 
0784 void SettingsDialog::requestImagesDialog(int row)
0785 {
0786     QStringList mimeTypeFilters;
0787     mimeTypeFilters << "image/jpeg" // will show "JPEG image (*.jpeg *.jpg)
0788                     << "image/png";  // will show "PNG image (*.png)"
0789 
0790     QFileDialog dialog(this);
0791     dialog.setMimeTypeFilters(mimeTypeFilters);
0792 
0793     QString background = m_model->data(m_model->index(row, COLORCOLUMN), Qt::BackgroundRole).toString();
0794 
0795     if (background.startsWith("/") && QFileInfo(background).exists()) {
0796         dialog.setDirectory(QFileInfo(background).absolutePath());
0797         dialog.selectFile(background);
0798     }
0799 
0800     if (dialog.exec()) {
0801         QStringList files = dialog.selectedFiles();
0802 
0803         if (files.count() > 0) {
0804             m_model->setData(m_model->index(row, COLORCOLUMN), files[0], Qt::BackgroundRole);
0805         }
0806     }
0807 }
0808 
0809 void SettingsDialog::requestColorsDialog(int row)
0810 {
0811     QColorDialog dialog(this);
0812     QString textColor = m_model->data(m_model->index(row, COLORCOLUMN), Qt::UserRole).toString();
0813     dialog.setCurrentColor(QColor(textColor));
0814 
0815     if (dialog.exec()) {
0816         qDebug() << dialog.selectedColor().name();
0817         m_model->setData(m_model->index(row, COLORCOLUMN), dialog.selectedColor().name(), Qt::UserRole);
0818     }
0819 }
0820 
0821 
0822 void SettingsDialog::accept()
0823 {
0824     qDebug() << Q_FUNC_INFO;
0825 
0826     if (saveAllChanges()) {
0827         deleteLater();
0828     }
0829 }
0830 
0831 void SettingsDialog::reject()
0832 {
0833     qDebug() << Q_FUNC_INFO;
0834 
0835     if (!m_blockDeleteOnReject) {
0836         deleteLater();
0837     }
0838 }
0839 
0840 void SettingsDialog::apply()
0841 {
0842     qDebug() << Q_FUNC_INFO;
0843     saveAllChanges();
0844 
0845     o_settings = currentSettings();
0846     o_settingsLayouts = currentLayoutsSettings();
0847 
0848     updateApplyButtonsState();
0849     updatePerLayoutButtonsState();
0850 }
0851 
0852 void SettingsDialog::restoreDefaults()
0853 {
0854     qDebug() << Q_FUNC_INFO;
0855 
0856     if (ui->tabWidget->currentIndex() == 0) {
0857         //! Default layouts missing from layouts list
0858         for (const auto &preset : m_corona->layoutsManager()->presetsPaths()) {
0859             QString presetName = CentralLayout::layoutName(preset);
0860             QByteArray presetNameChars = presetName.toUtf8();
0861             const char *prset_str = presetNameChars.data();
0862             presetName = i18n(prset_str);
0863 
0864             if (!nameExistsInModel(presetName)) {
0865                 addLayoutForFile(preset, presetName);
0866             }
0867         }
0868     } else if (ui->tabWidget->currentIndex() == 1) {
0869         //! Defaults for general Latte settings
0870         ui->autostartChkBox->setChecked(true);
0871         ui->badges3DStyleChkBox->setChecked(true);
0872         ui->infoWindowChkBox->setChecked(true);
0873         ui->metaPressChkBox->setChecked(false);
0874         ui->metaPressHoldChkBox->setChecked(true);
0875         ui->noBordersForMaximizedChkBox->setChecked(false);
0876         ui->highSensitivityBtn->setChecked(true);
0877         ui->screenTrackerSpinBox->setValue(SCREENTRACKERDEFAULTVALUE);
0878         ui->outlineSpinBox->setValue(OUTLINEDEFAULTWIDTH);
0879     }
0880 }
0881 
0882 void SettingsDialog::addLayoutForFile(QString file, QString layoutName, bool newTempDirectory, bool showNotification)
0883 {
0884     if (layoutName.isEmpty()) {
0885         layoutName = CentralLayout::layoutName(file);
0886     }
0887 
0888     QString copiedId;
0889 
0890     if (newTempDirectory) {
0891         QString tempDir = uniqueTempDirectory();
0892         copiedId = tempDir + "/" + layoutName + ".layout.latte";
0893         QFile(file).copy(copiedId);
0894     } else {
0895         copiedId = file;
0896     }
0897 
0898     QFileInfo newFileInfo(copiedId);
0899 
0900     if (newFileInfo.exists() && !newFileInfo.isWritable()) {
0901         QFile(copiedId).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
0902     }
0903 
0904     if (m_layouts.contains(copiedId)) {
0905         CentralLayout *oldSettings = m_layouts.take(copiedId);
0906         delete oldSettings;
0907     }
0908 
0909     CentralLayout *settings = new CentralLayout(this, copiedId);
0910     m_layouts[copiedId] = settings;
0911 
0912     QString id = copiedId;
0913     QString color = settings->color();
0914     QString textColor = settings->textColor();
0915     QString background = settings->background();
0916     bool menu = settings->showInMenu();
0917     bool disabledBorders = settings->disableBordersForMaximizedWindows();
0918     bool locked = !settings->isWritable();
0919 
0920     layoutName = uniqueLayoutName(layoutName);
0921 
0922     int row = ascendingRowFor(layoutName);
0923 
0924     if (background.isEmpty()) {
0925         insertLayoutInfoAtRow(row, copiedId, color, QString(), layoutName, menu, disabledBorders, QStringList(), locked);
0926     } else {
0927         insertLayoutInfoAtRow(row, copiedId, background, textColor, layoutName, menu, disabledBorders, QStringList(), locked);
0928     }
0929 
0930     ui->layoutsView->selectRow(row);
0931 
0932     if (showNotification) {
0933         //NOTE: The pointer is automatically deleted when the event is closed
0934         auto notification = new KNotification("import-done", KNotification::CloseOnTimeout);
0935         notification->setText(i18nc("import-done", "Layout: <b>%0</b> imported successfully<br>").arg(layoutName));
0936         notification->sendEvent();
0937     }
0938 }
0939 
0940 void SettingsDialog::loadSettings()
0941 {
0942     m_initLayoutPaths.clear();
0943     m_model->clear();
0944     m_sharesMap.clear();
0945 
0946     int i = 0;
0947     QStringList brokenLayouts;
0948 
0949     if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
0950         m_corona->layoutsManager()->synchronizer()->syncActiveLayoutsToOriginalFiles();
0951     }
0952 
0953     for (const auto layout : m_corona->layoutsManager()->layouts()) {
0954         QString layoutPath = QDir::homePath() + "/.config/latte/" + layout + ".layout.latte";
0955         m_initLayoutPaths.append(layoutPath);
0956 
0957         CentralLayout *central = new CentralLayout(this, layoutPath);
0958         m_layouts[layoutPath] = central;
0959 
0960         QString background = central->background();
0961 
0962         //! add central layout properties
0963         if (background.isEmpty()) {
0964             insertLayoutInfoAtRow(i, layoutPath, central->color(), QString(), central->name(),
0965                                   central->showInMenu(), central->disableBordersForMaximizedWindows(),
0966                                   central->activities(), !central->isWritable());
0967         } else {
0968             insertLayoutInfoAtRow(i, layoutPath, background, central->textColor(), central->name(),
0969                                   central->showInMenu(), central->disableBordersForMaximizedWindows(),
0970                                   central->activities(), !central->isWritable());
0971         }
0972 
0973         //! create initial SHARES maps
0974         QString shared = central->sharedLayoutName();
0975         if (!shared.isEmpty()) {
0976             m_sharesMap[shared].append(layoutPath);
0977         }
0978 
0979         qDebug() << "counter:" << i << " total:" << m_model->rowCount();
0980 
0981         i++;
0982 
0983         if (central->name() == m_corona->layoutsManager()->currentLayoutName()) {
0984             ui->layoutsView->selectRow(i - 1);
0985         }
0986 
0987         Layout::GenericLayout *generic = m_corona->layoutsManager()->synchronizer()->layout(central->name());
0988 
0989         if ((generic && generic->layoutIsBroken()) || (!generic && central->layoutIsBroken())) {
0990             brokenLayouts.append(central->name());
0991         }
0992     }
0993 
0994     //! update SHARES map keys in order to use the #settingsid(s)
0995     QStringList forremoval;
0996 
0997     //! remove these records after updating
0998     for (QHash<const QString, QStringList>::iterator i=m_sharesMap.begin(); i!=m_sharesMap.end(); ++i) {
0999         forremoval << i.key();
1000     }
1001 
1002     //! update keys
1003     for (QHash<const QString, QStringList>::iterator i=m_sharesMap.begin(); i!=m_sharesMap.end(); ++i) {
1004         QString shareid = idForRow(rowForName(i.key()));
1005         m_sharesMap[shareid] = i.value();
1006     }
1007 
1008     //! remove deprecated keys
1009     for (const auto &key : forremoval) {
1010         m_sharesMap.remove(key);
1011     }
1012 
1013     qDebug() << "SHARES MAP ::: " << m_sharesMap;
1014 
1015     for (QHash<const QString, QStringList>::iterator i=m_sharesMap.begin(); i!=m_sharesMap.end(); ++i) {
1016         int sharedPos = rowForId(i.key());
1017 
1018         if (sharedPos >= 0) {
1019             m_model->setData(m_model->index(sharedPos, SHAREDCOLUMN), i.value(), Qt::UserRole);
1020         }
1021     }
1022 
1023     recalculateAvailableActivities();
1024 
1025     m_model->setHorizontalHeaderItem(IDCOLUMN, new QStandardItem(QString("#path")));
1026     m_model->setHorizontalHeaderItem(COLORCOLUMN, new QStandardItem(QIcon::fromTheme("games-config-background"),
1027                                                                     QString(i18nc("column for layout background", "Background"))));
1028     m_model->setHorizontalHeaderItem(NAMECOLUMN, new QStandardItem(QString(i18nc("column for layout name", "Name"))));
1029     m_model->setHorizontalHeaderItem(MENUCOLUMN, new QStandardItem(QString(i18nc("column for layout to show in menu", "In Menu"))));
1030     m_model->setHorizontalHeaderItem(BORDERSCOLUMN, new QStandardItem(QString(i18nc("column for layout to hide borders for maximized windows", "Borderless"))));
1031     m_model->setHorizontalHeaderItem(ACTIVITYCOLUMN, new QStandardItem(QIcon::fromTheme("preferences-activities"),
1032                                                                        QString(i18nc("column for layout to show which activities is assigned to", "Activities"))));
1033     m_model->setHorizontalHeaderItem(SHAREDCOLUMN, new QStandardItem(QIcon::fromTheme("document-share"),
1034                                                                      QString(i18nc("column for shared layout to show which layouts is assigned to", "Shared To"))));
1035 
1036     //! this line should be commented for debugging layouts window functionality
1037     ui->layoutsView->setColumnHidden(IDCOLUMN, true);
1038     ui->layoutsView->setColumnHidden(HIDDENTEXTCOLUMN, true);
1039 
1040     if (m_corona->universalSettings()->canDisableBorders()) {
1041         ui->layoutsView->setColumnHidden(BORDERSCOLUMN, false);
1042     } else {
1043         ui->layoutsView->setColumnHidden(BORDERSCOLUMN, true);
1044     }
1045 
1046     ui->layoutsView->resizeColumnsToContents();
1047 
1048     QStringList columnWidths = m_corona->universalSettings()->layoutsColumnWidths();
1049 
1050     if (!columnWidths.isEmpty()) {
1051         for (int i=0; i<qMin(columnWidths.count(),4); ++i) {
1052             ui->layoutsView->setColumnWidth(COLORCOLUMN+i, columnWidths[i].toInt());
1053         }
1054     }
1055 
1056     if (m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout) {
1057         ui->singleToolBtn->setChecked(true);
1058     } else if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
1059         ui->multipleToolBtn->setChecked(true);
1060     }
1061 
1062     updatePerLayoutButtonsState();
1063     updateSharedLayoutsStates();
1064 
1065     ui->autostartChkBox->setChecked(m_corona->universalSettings()->autostart());
1066     ui->badges3DStyleChkBox->setChecked(m_corona->universalSettings()->badges3DStyle());
1067     ui->infoWindowChkBox->setChecked(m_corona->universalSettings()->showInfoWindow());
1068     ui->metaPressChkBox->setChecked(m_corona->universalSettings()->kwin_metaForwardedToLatte());
1069     ui->metaPressHoldChkBox->setChecked(m_corona->universalSettings()->metaPressAndHoldEnabled());
1070     ui->noBordersForMaximizedChkBox->setChecked(m_corona->universalSettings()->canDisableBorders());
1071 
1072     if (m_corona->universalSettings()->mouseSensitivity() == Types::LowSensitivity) {
1073         ui->lowSensitivityBtn->setChecked(true);
1074     } else if (m_corona->universalSettings()->mouseSensitivity() == Types::MediumSensitivity) {
1075         ui->mediumSensitivityBtn->setChecked(true);
1076     } else if (m_corona->universalSettings()->mouseSensitivity() == Types::HighSensitivity) {
1077         ui->highSensitivityBtn->setChecked(true);
1078     }
1079 
1080     o_settings = currentSettings();
1081     o_settingsLayouts = currentLayoutsSettings();
1082     updateApplyButtonsState();
1083     updateSharedLayoutsUiElements();
1084 
1085     //! there are broken layouts and the user must be informed!
1086     if (brokenLayouts.count() > 0) {
1087         auto msg = new QMessageBox(this);
1088         msg->setIcon(QMessageBox::Warning);
1089         msg->setWindowTitle(i18n("Layout Warning"));
1090         msg->setText(i18n("The layout(s) <b>%0</b> have <i>broken configuration</i>!!! Please <b>remove them</b> to improve the system stability...").arg(brokenLayouts.join(",")));
1091         msg->setStandardButtons(QMessageBox::Ok);
1092 
1093         msg->open();
1094     }
1095 }
1096 
1097 QList<int> SettingsDialog::currentSettings()
1098 {
1099     QList<int> settings;
1100     settings << m_inMemoryButtons->checkedId();
1101     settings << (int)ui->autostartChkBox->isChecked();
1102     settings << (int)ui->badges3DStyleChkBox->isChecked();
1103     settings << (int)ui->infoWindowChkBox->isChecked();
1104     settings << (int)ui->metaPressChkBox->isChecked();
1105     settings << (int)ui->metaPressHoldChkBox->isChecked();
1106     settings << (int)ui->noBordersForMaximizedChkBox->isChecked();
1107     settings << m_mouseSensitivityButtons->checkedId();
1108     settings << ui->screenTrackerSpinBox->value();
1109     settings << ui->outlineSpinBox->value();
1110     settings << m_model->rowCount();
1111 
1112     return settings;
1113 }
1114 
1115 QStringList SettingsDialog::currentLayoutsSettings()
1116 {
1117     QStringList layoutSettings;
1118 
1119     for (int i = 0; i < m_model->rowCount(); ++i) {
1120         QString id = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString();
1121         QString color = m_model->data(m_model->index(i, COLORCOLUMN), Qt::BackgroundRole).toString();
1122         QString textColor = m_model->data(m_model->index(i, COLORCOLUMN), Qt::UserRole).toString();
1123         QString name = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString();
1124         bool locked = m_model->data(m_model->index(i, NAMECOLUMN), Qt::UserRole).toBool();
1125         bool menu = m_model->data(m_model->index(i, MENUCOLUMN), Qt::DisplayRole).toString() == CheckMark;
1126         bool borders = m_model->data(m_model->index(i, BORDERSCOLUMN), Qt::DisplayRole).toString() == CheckMark;
1127         QStringList lActivities = m_model->data(m_model->index(i, ACTIVITYCOLUMN), Qt::UserRole).toStringList();
1128         QStringList shares = m_model->data(m_model->index(i, SHAREDCOLUMN), Qt::UserRole).toStringList();
1129 
1130         layoutSettings << id;
1131         layoutSettings << color;
1132         layoutSettings << textColor;
1133         layoutSettings << name;
1134         layoutSettings << QString::number((int)locked);
1135         layoutSettings << QString::number((int)menu);
1136         layoutSettings << QString::number((int)borders);
1137         layoutSettings << lActivities;
1138         layoutSettings << shares;
1139     }
1140 
1141     return layoutSettings;
1142 }
1143 
1144 
1145 void SettingsDialog::insertLayoutInfoAtRow(int row, QString path, QString color, QString textColor, QString name, bool menu,
1146                                            bool disabledBorders, QStringList activities, bool locked)
1147 {
1148     QStandardItem *pathItem = new QStandardItem(path);
1149 
1150     QStandardItem *hiddenTextItem = new QStandardItem();
1151 
1152     QStandardItem *colorItem = new QStandardItem();
1153     colorItem->setSelectable(false);
1154 
1155     QStandardItem *nameItem = new QStandardItem(name);
1156     nameItem->setTextAlignment(Qt::AlignCenter);
1157 
1158     QStandardItem *menuItem = new QStandardItem();
1159     menuItem->setEditable(false);
1160     menuItem->setSelectable(true);
1161     menuItem->setText(menu ? CheckMark : QString());
1162     menuItem->setTextAlignment(Qt::AlignCenter);
1163 
1164     QStandardItem *bordersItem = new QStandardItem();
1165     bordersItem->setEditable(false);
1166     bordersItem->setSelectable(true);
1167     bordersItem->setText(disabledBorders ? CheckMark : QString());
1168     bordersItem->setTextAlignment(Qt::AlignCenter);
1169 
1170     QStandardItem *activitiesItem = new QStandardItem(activities.join(","));
1171 
1172     QStandardItem *sharesItem = new QStandardItem();
1173 
1174     QList<QStandardItem *> items;
1175 
1176     items.append(pathItem);
1177     items.append(hiddenTextItem);
1178     items.append(colorItem);
1179     items.append(nameItem);
1180     items.append(menuItem);
1181     items.append(bordersItem);
1182     items.append(activitiesItem);
1183     items.append(sharesItem);
1184 
1185     if (row > m_model->rowCount() - 1) {
1186         m_model->appendRow(items);
1187         row = m_model->rowCount() - 1;
1188 
1189         qDebug() << "append row at:" << row << " rows:" << m_model->rowCount();
1190     } else {
1191         m_model->insertRow(row, items);
1192         qDebug() << "insert row at:" << row << " rows:" << m_model->rowCount();
1193     }
1194 
1195     m_model->setData(m_model->index(row, IDCOLUMN), path, Qt::DisplayRole);
1196     m_model->setData(m_model->index(row, COLORCOLUMN), color, Qt::BackgroundRole);
1197     m_model->setData(m_model->index(row, COLORCOLUMN), textColor, Qt::UserRole);
1198 
1199     QFont font;
1200 
1201     if (m_corona->layoutsManager()->synchronizer()->layout(name)) {
1202         font.setBold(true);
1203     } else {
1204         font.setBold(false);
1205     }
1206 
1207     if (path.startsWith("/tmp/")) {
1208         font.setItalic(true);
1209     } else {
1210         font.setItalic(false);
1211     }
1212 
1213     m_model->setData(m_model->index(row, NAMECOLUMN), QVariant(name), Qt::DisplayRole);
1214     m_model->setData(m_model->index(row, NAMECOLUMN), font, Qt::FontRole);
1215     m_model->setData(m_model->index(row, NAMECOLUMN), QVariant(locked), Qt::UserRole);
1216 
1217     m_model->setData(m_model->index(row, ACTIVITYCOLUMN), activities, Qt::UserRole);
1218 }
1219 
1220 
1221 void SettingsDialog::on_switchButton_clicked()
1222 {
1223     if (ui->buttonBox->button(QDialogButtonBox::Apply)->isEnabled()) {
1224         //! thus there are changes in the settings
1225 
1226         QString lName;
1227         QStringList lActivities;
1228 
1229         if (m_inMemoryButtons->checkedId() == Latte::Types::MultipleLayouts) {
1230             lName = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), NAMECOLUMN), Qt::DisplayRole).toString();
1231             lActivities = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), ACTIVITYCOLUMN), Qt::UserRole).toStringList();
1232         }
1233 
1234         apply();
1235 
1236         if (!lName.isEmpty() && !lActivities.isEmpty()) {
1237             //! an activities-assigned layout is chosen and at the same time we are moving
1238             //! to multiple layouts state
1239             m_corona->layoutsManager()->switchToLayout(lName);
1240         }
1241     } else {
1242         QVariant value = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), NAMECOLUMN), Qt::DisplayRole);
1243 
1244         if (value.isValid()) {
1245             m_corona->layoutsManager()->switchToLayout(value.toString());
1246         } else {
1247             qDebug() << "not valid layout";
1248         }
1249     }
1250 
1251     updatePerLayoutButtonsState();
1252 }
1253 
1254 void SettingsDialog::on_pauseButton_clicked()
1255 {
1256     ui->pauseButton->setEnabled(false);
1257 
1258     QString id = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), IDCOLUMN), Qt::DisplayRole).toString();
1259     CentralLayout *layout = m_layouts[id];
1260 
1261     if (layout) {
1262         m_corona->layoutsManager()->synchronizer()->pauseLayout(layout->name());
1263     }
1264 }
1265 
1266 void SettingsDialog::layoutsChanged()
1267 {
1268     for (int i = 0; i < m_model->rowCount(); ++i) {
1269         QModelIndex nameIndex = m_model->index(i, NAMECOLUMN);
1270         QVariant value = m_model->data(nameIndex);
1271 
1272         if (value.isValid()) {
1273             QString name = value.toString();
1274             QFont font;
1275 
1276             if (m_corona->layoutsManager()->currentLayoutName() == name) {
1277                 font.setBold(true);
1278                 // ui->layoutsView->selectRow(i);
1279             } else {
1280                 Layout::GenericLayout *layout = m_corona->layoutsManager()->synchronizer()->layout(name);
1281 
1282                 if (layout && (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts)) {
1283                     font.setBold(true);
1284                 } else {
1285                     font.setBold(false);
1286                 }
1287             }
1288 
1289             m_model->setData(nameIndex, font, Qt::FontRole);
1290         }
1291     }
1292 }
1293 
1294 void SettingsDialog::itemChanged(QStandardItem *item)
1295 {
1296     updatePerLayoutButtonsState();
1297 
1298     if (item->column() == ACTIVITYCOLUMN) {
1299         //! recalculate the available activities
1300         recalculateAvailableActivities();
1301     } else if (item->column() == NAMECOLUMN) {
1302         int currentRow = ui->layoutsView->currentIndex().row();
1303 
1304         QString id = m_model->data(m_model->index(currentRow, IDCOLUMN), Qt::DisplayRole).toString();
1305         QString name = m_model->data(m_model->index(currentRow, NAMECOLUMN), Qt::DisplayRole).toString();
1306         QFont font = qvariant_cast<QFont>(m_model->data(m_model->index(currentRow, NAMECOLUMN), Qt::FontRole));
1307 
1308         if (m_corona->layoutsManager()->synchronizer()->layout(m_layouts[id]->name())) {
1309             font.setBold(true);
1310         } else {
1311             font.setBold(false);
1312         }
1313 
1314         if (m_layouts[id]->name() != name) {
1315             font.setItalic(true);
1316         } else {
1317             font.setItalic(false);
1318         }
1319 
1320         m_model->setData(m_model->index(currentRow, NAMECOLUMN), font, Qt::FontRole);
1321 
1322     } else if (item->column() == SHAREDCOLUMN) {
1323         updateSharedLayoutsStates();
1324     }
1325 
1326     updateApplyButtonsState();
1327 }
1328 
1329 void SettingsDialog::updateApplyButtonsState()
1330 {
1331     bool changed{false};
1332 
1333     //! Ok, Apply Buttons
1334     if ((o_settings != currentSettings())
1335             || (o_settingsLayouts != currentLayoutsSettings())) {
1336         changed = true;
1337     }
1338 
1339     if (changed) {
1340         ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
1341         ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
1342     } else {
1343         //ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
1344         ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
1345     }
1346 
1347     //! RestoreDefaults Button
1348     if (ui->tabWidget->currentIndex() == 0) {
1349         //! Check Default layouts missing from layouts list
1350 
1351         bool layoutMissing{false};
1352 
1353         for (const auto &preset : m_corona->layoutsManager()->presetsPaths()) {
1354             QString presetName = CentralLayout::layoutName(preset);
1355             QByteArray presetNameChars = presetName.toUtf8();
1356             const char *prset_str = presetNameChars.data();
1357             presetName = i18n(prset_str);
1358 
1359             if (!nameExistsInModel(presetName)) {
1360                 layoutMissing = true;
1361                 break;
1362             }
1363         }
1364 
1365         if (layoutMissing) {
1366             ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(true);
1367         } else {
1368             ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(false);
1369         }
1370     } else if (ui->tabWidget->currentIndex() == 1) {
1371         //! Defaults for general Latte settings
1372 
1373         if (!ui->autostartChkBox->isChecked()
1374                 || ui->badges3DStyleChkBox->isChecked()
1375                 || ui->metaPressChkBox->isChecked()
1376                 || !ui->metaPressHoldChkBox->isChecked()
1377                 || !ui->infoWindowChkBox->isChecked()
1378                 || ui->noBordersForMaximizedChkBox->isChecked()
1379                 || !ui->highSensitivityBtn->isChecked()
1380                 || ui->screenTrackerSpinBox->value() != SCREENTRACKERDEFAULTVALUE
1381                 || ui->outlineSpinBox->value() != OUTLINEDEFAULTWIDTH ) {
1382             ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(true);
1383         } else {
1384             ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(false);
1385         }
1386     }
1387 }
1388 
1389 void SettingsDialog::updatePerLayoutButtonsState()
1390 {
1391     int currentRow = ui->layoutsView->currentIndex().row();
1392 
1393     QString id = m_model->data(m_model->index(currentRow, IDCOLUMN), Qt::DisplayRole).toString();
1394     QString nameInModel = m_model->data(m_model->index(currentRow, NAMECOLUMN), Qt::DisplayRole).toString();
1395     QString originalName = m_layouts.contains(id) ? m_layouts[id]->name() : "";
1396     bool lockedInModel = m_model->data(m_model->index(currentRow, NAMECOLUMN), Qt::UserRole).toBool();
1397     bool sharedInModel = !m_model->data(m_model->index(currentRow, SHAREDCOLUMN), Qt::UserRole).toStringList().isEmpty();
1398     bool editable = !isActive(originalName) && !lockedInModel;
1399 
1400     //! Switch Button
1401     if (id.startsWith("/tmp/") || originalName != nameInModel) {
1402         ui->switchButton->setEnabled(false);
1403     } else {
1404         ui->switchButton->setEnabled(true);
1405     }
1406 
1407     //! Pause Button
1408     if (m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout) {
1409         ui->pauseButton->setVisible(false);
1410     } else if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
1411         ui->pauseButton->setVisible(true);
1412 
1413         QStringList lActivities = m_model->data(m_model->index(currentRow, ACTIVITYCOLUMN), Qt::UserRole).toStringList();
1414 
1415         Latte::CentralLayout *layout = m_layouts[id];
1416 
1417         if (!lActivities.isEmpty() && layout && m_corona->layoutsManager()->synchronizer()->centralLayout(layout->name())) {
1418             ui->pauseButton->setEnabled(true);
1419         } else {
1420             ui->pauseButton->setEnabled(false);
1421         }
1422     }
1423 
1424     //! Remove Layout Button
1425     if (originalName != nameInModel
1426             || (originalName == m_corona->layoutsManager()->currentLayoutName())
1427             || (m_corona->layoutsManager()->synchronizer()->centralLayout(originalName))
1428             || lockedInModel) {
1429         ui->removeButton->setEnabled(false);
1430     } else {
1431         ui->removeButton->setEnabled(true);
1432     }
1433 
1434     //! Layout Locked Button
1435     if (lockedInModel) {
1436         ui->lockedButton->setChecked(true);
1437     } else {
1438         ui->lockedButton->setChecked(false);
1439     }
1440 
1441     //! Layout Shared Button
1442     if (sharedInModel) {
1443         ui->sharedButton->setChecked(true);
1444     } else {
1445         ui->sharedButton->setChecked(false);
1446     }
1447 
1448     if (editable) {
1449         m_editLayoutAction->setEnabled(true);
1450     } else {
1451         m_editLayoutAction->setEnabled(false);
1452     }
1453 }
1454 
1455 void SettingsDialog::updateSharedLayoutsStates()
1456 {
1457     bool inMultiple{inMultipleLayoutsLook()};
1458 
1459     for (int i = 0; i < m_model->rowCount(); ++i) {
1460         QStringList shares = m_model->data(m_model->index(i, SHAREDCOLUMN), Qt::UserRole).toStringList();
1461 
1462         if (shares.isEmpty() || !inMultiple) {
1463             QStandardItem *item = m_model->item(i, MENUCOLUMN);
1464             item->setEnabled(true);
1465             item = m_model->item(i, BORDERSCOLUMN);
1466             item->setEnabled(true);
1467             item = m_model->item(i, ACTIVITYCOLUMN);
1468             item->setEnabled(true);
1469         } else {
1470             QStandardItem *item = m_model->item(i, MENUCOLUMN);
1471             item->setEnabled(false);
1472             item = m_model->item(i, BORDERSCOLUMN);
1473             item->setEnabled(false);
1474             item = m_model->item(i, ACTIVITYCOLUMN);
1475             item->setEnabled(false);
1476         }
1477 
1478         //! refresh LayoutName
1479         QStandardItem *nameItem = m_model->item(i, NAMECOLUMN);
1480         nameItem->setEnabled(false);
1481         nameItem->setEnabled(true);
1482     }  
1483 }
1484 
1485 void SettingsDialog::updateSharedLayoutsUiElements()
1486 {
1487     //! UI Elements that need to be enabled/disabled
1488 
1489     Latte::Types::LayoutsMemoryUsage inMemoryOption = static_cast<Latte::Types::LayoutsMemoryUsage>(m_inMemoryButtons->checkedId());
1490     if (inMemoryOption == Latte::Types::MultipleLayouts) {
1491         ui->layoutsView->setColumnHidden(SHAREDCOLUMN, false);
1492         ui->sharedButton->setVisible(true);
1493 
1494         //! column widths
1495         QStringList cWidths = m_corona->universalSettings()->layoutsColumnWidths();
1496 
1497         if (cWidths.count()>=5) {
1498             ui->layoutsView->setColumnWidth(ACTIVITYCOLUMN, cWidths[4].toInt());
1499         }
1500     } else {
1501         ui->layoutsView->setColumnHidden(SHAREDCOLUMN, true);
1502         ui->sharedButton->setVisible(false);
1503     }
1504 }
1505 
1506 void SettingsDialog::recalculateAvailableActivities()
1507 {
1508     QStringList tempActivities = m_corona->layoutsManager()->synchronizer()->activities();
1509 
1510     for (int i = 0; i < m_model->rowCount(); ++i) {
1511         QStringList assigned = m_model->data(m_model->index(i, ACTIVITYCOLUMN), Qt::UserRole).toStringList();
1512 
1513         for (const auto &activity : assigned) {
1514             if (tempActivities.contains(activity)) {
1515                 tempActivities.removeAll(activity);
1516             }
1517         }
1518     }
1519 
1520     m_availableActivities = tempActivities;
1521 }
1522 
1523 bool SettingsDialog::dataAreAccepted()
1524 {
1525     for (int i = 0; i < m_model->rowCount(); ++i) {
1526         QString layout1 = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString();
1527 
1528         for (int j = i + 1; j < m_model->rowCount(); ++j) {
1529             QString temp = m_model->data(m_model->index(j, NAMECOLUMN), Qt::DisplayRole).toString();
1530 
1531             //!same layout name exists again
1532             if (layout1 == temp) {
1533                 auto msg = new QMessageBox(this);
1534                 msg->setIcon(QMessageBox::Warning);
1535                 msg->setWindowTitle(i18n("Layout Warning"));
1536                 msg->setText(i18n("There are layouts with the same name, that is not permitted!!! Please update these names to re-apply the changes..."));
1537                 msg->setStandardButtons(QMessageBox::Ok);
1538 
1539                 connect(msg, &QMessageBox::finished, this, [ &, i, j](int result) {
1540                     QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::ClearAndSelect;
1541                     QModelIndex indexBase = m_model->index(i, NAMECOLUMN);
1542                     ui->layoutsView->selectionModel()->select(indexBase, flags);
1543 
1544                     QModelIndex indexOccurence = m_model->index(j, NAMECOLUMN);
1545                     ui->layoutsView->edit(indexOccurence);
1546                 });
1547 
1548 
1549                 msg->open();
1550 
1551                 return false;
1552             }
1553         }
1554     }
1555 
1556     return true;
1557 }
1558 
1559 void SettingsDialog::showLayoutInformation()
1560 {
1561     int currentRow = ui->layoutsView->currentIndex().row();
1562 
1563     QString id = m_model->data(m_model->index(currentRow, IDCOLUMN), Qt::DisplayRole).toString();
1564     QString name = m_model->data(m_model->index(currentRow, NAMECOLUMN), Qt::DisplayRole).toString();
1565 
1566     Layout::GenericLayout *genericActive= m_corona->layoutsManager()->synchronizer()->layout(m_layouts[id]->name());
1567     Layout::GenericLayout *generic = genericActive ? genericActive : m_layouts[id];
1568 
1569     auto msg = new QMessageBox(this);
1570     msg->setWindowTitle(name);
1571     msg->setText(generic->reportHtml(m_corona->screenPool()));
1572 
1573     msg->open();
1574 }
1575 
1576 void SettingsDialog::showScreensInformation()
1577 {
1578     QList<int> assignedScreens;
1579 
1580     for (int i = 0; i < m_model->rowCount(); ++i) {
1581         QString id = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString();
1582         QString name = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString();
1583 
1584         Layout::GenericLayout *genericActive= m_corona->layoutsManager()->synchronizer()->layout(m_layouts[id]->name());
1585         Layout::GenericLayout *generic = genericActive ? genericActive : m_layouts[id];
1586 
1587         QList<int> vScreens = generic->viewsScreens();
1588 
1589         for (const int scrId : vScreens) {
1590             if (!assignedScreens.contains(scrId)) {
1591                 assignedScreens << scrId;
1592             }
1593         }
1594     }
1595 
1596     auto msg = new QMessageBox(this);
1597     msg->setWindowTitle(i18n("Screens Information"));
1598     msg->setText(m_corona->screenPool()->reportHtml(assignedScreens));
1599 
1600     msg->open();
1601 }
1602 
1603 bool SettingsDialog::saveAllChanges()
1604 {
1605     if (!dataAreAccepted()) {
1606         return false;
1607     }
1608 
1609     //! Update universal settings
1610     Latte::Types::MouseSensitivity sensitivity = static_cast<Latte::Types::MouseSensitivity>(m_mouseSensitivityButtons->checkedId());
1611     bool autostart = ui->autostartChkBox->isChecked();
1612     bool badges3DStyle = ui->badges3DStyleChkBox->isChecked();
1613     bool forwardMetaPress = ui->metaPressChkBox->isChecked();
1614     bool metaPressAndHold = ui->metaPressHoldChkBox->isChecked();
1615     bool showInfoWindow = ui->infoWindowChkBox->isChecked();
1616     bool noBordersForMaximized = ui->noBordersForMaximizedChkBox->isChecked();
1617 
1618     m_corona->universalSettings()->setMouseSensitivity(sensitivity);
1619     m_corona->universalSettings()->setAutostart(autostart);
1620     m_corona->universalSettings()->setBadges3DStyle(badges3DStyle);
1621     m_corona->universalSettings()->kwin_forwardMetaToLatte(forwardMetaPress);
1622     m_corona->universalSettings()->setMetaPressAndHoldEnabled(metaPressAndHold);
1623     m_corona->universalSettings()->setShowInfoWindow(showInfoWindow);
1624     m_corona->universalSettings()->setCanDisableBorders(noBordersForMaximized);
1625     m_corona->universalSettings()->setScreenTrackerInterval(ui->screenTrackerSpinBox->value());
1626 
1627     m_corona->themeExtended()->setOutlineWidth(ui->outlineSpinBox->value());
1628 
1629     //! Update Layouts
1630     QStringList knownActivities = activities();
1631 
1632     QTemporaryDir layoutTempDir;
1633 
1634     qDebug() << "Temporary Directory ::: " << layoutTempDir.path();
1635 
1636     QStringList fromRenamePaths;
1637     QStringList toRenamePaths;
1638     QStringList toRenameNames;
1639 
1640     QString switchToLayout;
1641 
1642     QHash<QString, Layout::GenericLayout *> activeLayoutsToRename;
1643 
1644     //! remove layouts that have been removed from the user
1645     for (const auto &initLayout : m_initLayoutPaths) {
1646         if (!idExistsInModel(initLayout)) {
1647             QFile(initLayout).remove();
1648 
1649             if (m_layouts.contains(initLayout)) {
1650                 CentralLayout *removedLayout = m_layouts.take(initLayout);
1651                 delete removedLayout;
1652             }
1653         }
1654     }
1655 
1656     for (int i = 0; i < m_model->rowCount(); ++i) {
1657         QString id = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString();
1658         QString color = m_model->data(m_model->index(i, COLORCOLUMN), Qt::BackgroundRole).toString();
1659         QString textColor = m_model->data(m_model->index(i, COLORCOLUMN), Qt::UserRole).toString();
1660         QString name = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString();
1661         bool locked = m_model->data(m_model->index(i, NAMECOLUMN), Qt::UserRole).toBool();
1662         bool menu = m_model->data(m_model->index(i, MENUCOLUMN), Qt::DisplayRole).toString() == CheckMark;
1663         bool disabledBorders = m_model->data(m_model->index(i, BORDERSCOLUMN), Qt::DisplayRole).toString() == CheckMark;
1664         QStringList lActivities = m_model->data(m_model->index(i, ACTIVITYCOLUMN), Qt::UserRole).toStringList();
1665 
1666         QStringList cleanedActivities;
1667 
1668         //!update only activities that are valid
1669         for (const auto &activity : lActivities) {
1670             if (knownActivities.contains(activity)) {
1671                 cleanedActivities.append(activity);
1672             }
1673         }
1674 
1675         //qDebug() << i << ". " << id << " - " << color << " - " << name << " - " << menu << " - " << lActivities;
1676         //! update the generic parts of the layouts
1677         Layout::GenericLayout *genericActive= m_corona->layoutsManager()->synchronizer()->layout(m_layouts[id]->name());
1678         Layout::GenericLayout *generic = genericActive ? genericActive : m_layouts[id];
1679 
1680         //! unlock read-only layout
1681         if (!generic->isWritable()) {
1682             generic->unlock();
1683         }
1684 
1685         if (color.startsWith("/")) {
1686             //it is image file in such case
1687             if (color != generic->background()) {
1688                 generic->setBackground(color);
1689             }
1690 
1691             if (generic->textColor() != textColor) {
1692                 generic->setTextColor(textColor);
1693             }
1694         } else {
1695             if (color != generic->color()) {
1696                 generic->setColor(color);
1697                 generic->setBackground(QString());
1698                 generic->setTextColor(QString());
1699             }
1700         }
1701 
1702         //! update only the Central-specific layout parts
1703         CentralLayout *centralActive= m_corona->layoutsManager()->synchronizer()->centralLayout(m_layouts[id]->name());
1704         CentralLayout *central = centralActive ? centralActive : m_layouts[id];
1705 
1706         if (central->showInMenu() != menu) {
1707             central->setShowInMenu(menu);
1708         }
1709 
1710         if (central->disableBordersForMaximizedWindows() != disabledBorders) {
1711             central->setDisableBordersForMaximizedWindows(disabledBorders);
1712         }
1713 
1714         if (central->activities() != cleanedActivities) {
1715             central->setActivities(cleanedActivities);
1716         }
1717 
1718         //! If the layout name changed OR the layout path is a temporary one
1719         if (generic->name() != name || (id.startsWith("/tmp/"))) {
1720             //! If the layout is Active in MultipleLayouts
1721             if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts && generic->isActive()) {
1722                 qDebug() << " Active Layout Should Be Renamed From : " << generic->name() << " TO :: " << name;
1723                 activeLayoutsToRename[name] = generic;
1724             }
1725 
1726             QString tempFile = layoutTempDir.path() + "/" + QString(generic->name() + ".layout.latte");
1727             qDebug() << "new temp file ::: " << tempFile;
1728 
1729             if ((m_corona->layoutsManager()->memoryUsage() == Types::SingleLayout) && (generic->name() == m_corona->layoutsManager()->currentLayoutName())) {
1730                 switchToLayout = name;
1731             }
1732 
1733             generic = m_layouts.take(id);
1734             delete generic;
1735 
1736             QFile(id).rename(tempFile);
1737 
1738             fromRenamePaths.append(id);
1739             toRenamePaths.append(tempFile);
1740             toRenameNames.append(name);
1741         }
1742     }
1743 
1744     //! this is necessary in case two layouts have to swap names
1745     //! so we copy first the layouts in a temp directory and afterwards all
1746     //! together we move them in the official layout directory
1747     for (int i = 0; i < toRenamePaths.count(); ++i) {
1748         QString newFile = QDir::homePath() + "/.config/latte/" + toRenameNames[i] + ".layout.latte";
1749         QFile(toRenamePaths[i]).rename(newFile);
1750 
1751         CentralLayout *nLayout = new CentralLayout(this, newFile);
1752         m_layouts[newFile] = nLayout;
1753 
1754         //! updating the #SETTINGSID in the model for the layout that was renamed
1755         for (int j = 0; j < m_model->rowCount(); ++j) {
1756             QString tId = m_model->data(m_model->index(j, IDCOLUMN), Qt::DisplayRole).toString();
1757 
1758             if (tId == fromRenamePaths[i]) {
1759                 m_model->setData(m_model->index(j, IDCOLUMN), newFile, Qt::DisplayRole);
1760                 m_initLayoutPaths.append(newFile);
1761 
1762                 QFont font = qvariant_cast<QFont>(m_model->data(m_model->index(j, NAMECOLUMN), Qt::FontRole));
1763 
1764                 font.setItalic(false);
1765                 m_model->setData(m_model->index(j, NAMECOLUMN), font, Qt::FontRole);
1766             }
1767         }
1768     }
1769 
1770     QString orphanedLayout;
1771 
1772     if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
1773         for (const auto &newLayoutName : activeLayoutsToRename.keys()) {
1774             Layout::GenericLayout *layout = activeLayoutsToRename[newLayoutName];
1775             qDebug() << " Active Layout of Type: " << layout->type() << " Is Renamed From : " << activeLayoutsToRename[newLayoutName]->name() << " TO :: " << newLayoutName;
1776             layout->renameLayout(newLayoutName);
1777 
1778             if (layout->type() == Layout::Type::Central) {
1779                 CentralLayout *central = qobject_cast<CentralLayout *>(layout);
1780 
1781                 if (central->activities().isEmpty()) {
1782                     //! that means it is an active layout for orphaned Activities
1783                     orphanedLayout = newLayoutName;
1784                 }
1785             }
1786 
1787             //! broadcast the name change
1788             int row = rowForName(newLayoutName);
1789             QStandardItem *item = m_model->item(row, NAMECOLUMN);
1790             if (item) {
1791                 emit itemChanged(item);
1792             }
1793         }
1794     }
1795 
1796     //! lock layouts in the end when the user has chosen it
1797     for (int i = 0; i < m_model->rowCount(); ++i) {
1798         QString id = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString();
1799         QString name = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString();
1800         bool locked = m_model->data(m_model->index(i, NAMECOLUMN), Qt::UserRole).toBool();
1801 
1802         Layout::GenericLayout *generic = m_corona->layoutsManager()->synchronizer()->layout(m_layouts[id]->name());
1803         Layout::GenericLayout *layout = generic ? generic : m_layouts[id];
1804 
1805         if (layout && locked && layout->isWritable()) {
1806             layout->lock();
1807         }
1808     }
1809 
1810     //! update SharedLayouts that are Active
1811     syncActiveShares();
1812 
1813     //! reload layouts in layoutsmanager
1814     m_corona->layoutsManager()->synchronizer()->loadLayouts();
1815 
1816     //! send to layout manager in which layout to switch
1817     Latte::Types::LayoutsMemoryUsage inMemoryOption = static_cast<Latte::Types::LayoutsMemoryUsage>(m_inMemoryButtons->checkedId());
1818 
1819     if (m_corona->layoutsManager()->memoryUsage() != inMemoryOption) {
1820         Types::LayoutsMemoryUsage previousMemoryUsage = m_corona->layoutsManager()->memoryUsage();
1821         m_corona->layoutsManager()->setMemoryUsage(inMemoryOption);
1822 
1823         QVariant value = m_model->data(m_model->index(ui->layoutsView->currentIndex().row(), NAMECOLUMN), Qt::DisplayRole);
1824         QString layoutName = value.toString();
1825 
1826         m_corona->layoutsManager()->switchToLayout(layoutName, previousMemoryUsage);
1827     } else {
1828         if (!switchToLayout.isEmpty()) {
1829             m_corona->layoutsManager()->switchToLayout(switchToLayout);
1830         } else if (m_corona->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
1831             m_corona->layoutsManager()->synchronizer()->syncMultipleLayoutsToActivities(orphanedLayout);
1832         }
1833     }
1834 
1835     return true;
1836 }
1837 
1838 void SettingsDialog::syncActiveShares()
1839 {
1840     if (m_corona->layoutsManager()->memoryUsage() != Types::MultipleLayouts) {
1841         return;
1842     }
1843 
1844     QHash<const QString, QStringList> currentSharesIdMap;
1845     Layouts::SharesMap  currentSharesNamesMap;
1846 
1847     for (int i = 0; i < m_model->rowCount(); ++i) {
1848         if (isShared(i)) {
1849             QString id = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString();
1850             QString name = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString();
1851             QStringList shareIds = m_model->data(m_model->index(i, SHAREDCOLUMN), Qt::UserRole).toStringList();
1852             QStringList shareNames;
1853 
1854             for (const auto &shareid : shareIds) {
1855                 QString shareName = nameForId(shareid);
1856                 shareNames << shareName;
1857             }
1858 
1859             currentSharesIdMap[id] = shareIds;
1860             currentSharesNamesMap[name] = shareNames;
1861         }
1862     }
1863 
1864     QStringList deprecatedShares;
1865 
1866     for (const auto &oldSharesIds : m_sharesMap) {
1867         for(const auto &oldId : oldSharesIds) {
1868             QString oldShareName = nameForId(oldId);
1869             if (!m_corona->layoutsManager()->synchronizer()->mapHasRecord(oldShareName, currentSharesNamesMap)) {
1870                 deprecatedShares << oldShareName;
1871             }
1872         }
1873     }
1874 
1875     qDebug() << " CURRENT SHARES ID MAP  :: " << currentSharesIdMap;
1876     m_corona->layoutsManager()->synchronizer()->syncActiveShares(currentSharesNamesMap, deprecatedShares);
1877 
1878     m_sharesMap.clear();
1879     m_sharesMap = currentSharesIdMap;
1880 }
1881 
1882 void SettingsDialog::addActivityInCurrent(const QString &activityId)
1883 {
1884     int currentRow = ui->layoutsView->currentIndex().row();
1885     QStringList activities = m_model->data(m_model->index(currentRow, ACTIVITYCOLUMN), Qt::UserRole).toStringList();
1886     if (!activities.contains(activityId)) {
1887         activities << activityId;
1888         m_model->setData(m_model->index(currentRow, ACTIVITYCOLUMN), activities, Qt::UserRole);
1889     }
1890 }
1891 
1892 void SettingsDialog::removeActivityFromCurrent(const QString &activityId)
1893 {
1894     int currentRow = ui->layoutsView->currentIndex().row();
1895     QStringList activities = m_model->data(m_model->index(currentRow, ACTIVITYCOLUMN), Qt::UserRole).toStringList();
1896     if (activities.contains(activityId)) {
1897         activities.removeAll(activityId);
1898         m_model->setData(m_model->index(currentRow, ACTIVITYCOLUMN), activities, Qt::UserRole);
1899     }
1900 }
1901 
1902 void SettingsDialog::addShareInCurrent(const QString &layoutId)
1903 {
1904     int currentRow = ui->layoutsView->currentIndex().row();
1905     QStringList shares = m_model->data(m_model->index(currentRow, SHAREDCOLUMN), Qt::UserRole).toStringList();
1906     if (!shares.contains(layoutId)) {
1907         shares << layoutId;
1908         m_model->setData(m_model->index(currentRow, SHAREDCOLUMN), shares, Qt::UserRole);
1909     }
1910 }
1911 
1912 void SettingsDialog::removeShareFromCurrent(const QString &layoutId)
1913 {
1914     int currentRow = ui->layoutsView->currentIndex().row();
1915     QStringList shares = m_model->data(m_model->index(currentRow, SHAREDCOLUMN), Qt::UserRole).toStringList();
1916     if (shares.contains(layoutId)) {
1917         shares.removeAll(layoutId);
1918         m_model->setData(m_model->index(currentRow, SHAREDCOLUMN), shares, Qt::UserRole);
1919     }
1920 }
1921 
1922 bool SettingsDialog::idExistsInModel(QString id)
1923 {
1924     for (int i = 0; i < m_model->rowCount(); ++i) {
1925         QString rowId = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString();
1926 
1927         if (rowId == id) {
1928             return true;
1929         }
1930     }
1931 
1932     return false;
1933 }
1934 
1935 bool SettingsDialog::nameExistsInModel(QString name)
1936 {
1937     for (int i = 0; i < m_model->rowCount(); ++i) {
1938         QString rowName = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString();
1939 
1940         if (rowName == name) {
1941             return true;
1942         }
1943     }
1944 
1945     return false;
1946 }
1947 
1948 bool SettingsDialog::inMultipleLayoutsLook() const
1949 {
1950     Latte::Types::LayoutsMemoryUsage inMemoryOption = static_cast<Latte::Types::LayoutsMemoryUsage>(m_inMemoryButtons->checkedId());
1951     return inMemoryOption == Latte::Types::MultipleLayouts;
1952 }
1953 
1954 bool SettingsDialog::isActive(QString layoutName) const
1955 {
1956     return (m_corona->layoutsManager()->synchronizer()->layout(layoutName) != nullptr);
1957 }
1958 
1959 bool SettingsDialog::isMenuCell(int column) const
1960 {
1961     return column == MENUCOLUMN;
1962 }
1963 
1964 bool SettingsDialog::isShared(int row) const
1965 {
1966     if (row >=0 ) {
1967         QStringList shares = m_model->data(m_model->index(row, SHAREDCOLUMN), Qt::UserRole).toStringList();
1968         if (!shares.isEmpty()) {
1969             return true;
1970         }
1971     }
1972 
1973     return false;
1974 }
1975 
1976 int SettingsDialog::ascendingRowFor(QString name)
1977 {
1978     for (int i = 0; i < m_model->rowCount(); ++i) {
1979         QString rowName = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString();
1980 
1981         if (rowName.toUpper() > name.toUpper()) {
1982             return i;
1983         }
1984     }
1985 
1986     return m_model->rowCount();
1987 }
1988 
1989 int SettingsDialog::rowForId(QString id) const
1990 {
1991     for (int i = 0; i < m_model->rowCount(); ++i) {
1992         QString rowId = m_model->data(m_model->index(i, IDCOLUMN), Qt::DisplayRole).toString();
1993 
1994         if (rowId == id) {
1995             return i;
1996         }
1997     }
1998 
1999     return -1;
2000 }
2001 
2002 int SettingsDialog::rowForName(QString layoutName) const
2003 {
2004     for (int i = 0; i < m_model->rowCount(); ++i) {
2005         QString rowName = m_model->data(m_model->index(i, NAMECOLUMN), Qt::DisplayRole).toString();
2006 
2007         if (rowName == layoutName) {
2008             return i;
2009         }
2010     }
2011 
2012     return -1;
2013 }
2014 
2015 QString SettingsDialog::idForRow(int row) const
2016 {
2017     return m_model->data(m_model->index(row, IDCOLUMN), Qt::DisplayRole).toString();
2018 }
2019 
2020 QString SettingsDialog::nameForId(QString id) const
2021 {
2022     int row = rowForId(id);
2023     return m_model->data(m_model->index(row, NAMECOLUMN), Qt::DisplayRole).toString();
2024 }
2025 
2026 QString SettingsDialog::uniqueTempDirectory()
2027 {
2028     QTemporaryDir tempDir;
2029     tempDir.setAutoRemove(false);
2030     m_tempDirectories.append(tempDir.path());
2031 
2032     return tempDir.path();
2033 }
2034 
2035 QString SettingsDialog::uniqueLayoutName(QString name)
2036 {
2037     int pos_ = name.lastIndexOf(QRegExp(QString("[-][0-9]+")));
2038 
2039     if (nameExistsInModel(name) && pos_ > 0) {
2040         name = name.left(pos_);
2041     }
2042 
2043     int i = 2;
2044 
2045     QString namePart = name;
2046 
2047     while (nameExistsInModel(name)) {
2048         name = namePart + "-" + QString::number(i);
2049         i++;
2050     }
2051 
2052     return name;
2053 }
2054 
2055 }//end of namespace
2056