File indexing completed on 2025-01-12 08:02:34
0001 /* 0002 SPDX-FileCopyrightText: 2020 Michail Vourlakos <mvourlakos@gmail.com> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "layoutscontroller.h" 0007 0008 // local 0009 #include "ui_settingsdialog.h" 0010 #include "settingsdialog.h" 0011 #include "tablayoutshandler.h" 0012 #include "templateskeeper.h" 0013 #include "delegates/activitiesdelegate.h" 0014 #include "delegates/backgrounddelegate.h" 0015 #include "delegates/checkboxdelegate.h" 0016 #include "delegates/layoutnamedelegate.h" 0017 #include "../universalsettings.h" 0018 #include "../generic/generictools.h" 0019 #include "../../screenpool.h" 0020 #include "../../data/uniqueidinfo.h" 0021 #include "../../layout/centrallayout.h" 0022 #include "../../layouts/importer.h" 0023 #include "../../layouts/manager.h" 0024 #include "../../layouts/synchronizer.h" 0025 #include "../../templates/templatesmanager.h" 0026 0027 // Qt 0028 #include <QDir> 0029 #include <QFile> 0030 #include <QHeaderView> 0031 #include <QItemSelection> 0032 #include <QStringList> 0033 #include <QTemporaryDir> 0034 #include <QTemporaryFile> 0035 0036 // KDE 0037 #include <KArchive/KTar> 0038 #include <KArchive/KArchiveEntry> 0039 #include <KArchive/KArchiveDirectory> 0040 #include <KMessageWidget> 0041 0042 namespace Latte { 0043 namespace Settings { 0044 namespace Controller { 0045 0046 Layouts::Layouts(Settings::Handler::TabLayouts *parent) 0047 : QObject(parent), 0048 m_handler(parent), 0049 m_model(new Model::Layouts(this, m_handler->corona())), 0050 m_proxyModel(new QSortFilterProxyModel(this)), 0051 m_view(m_handler->ui()->layoutsView), 0052 m_headerView(new Settings::Layouts::HeaderView(Qt::Horizontal, m_handler->dialog())), 0053 m_storage(KConfigGroup(KSharedConfig::openConfig(),"LatteSettingsDialog").group("TabLayouts")) 0054 { 0055 m_templatesKeeper = new Settings::Part::TemplatesKeeper(this, m_handler->corona()); 0056 0057 loadConfig(); 0058 m_proxyModel->setSourceModel(m_model); 0059 0060 connect(m_handler->corona()->layoutsManager()->synchronizer(), &Latte::Layouts::Synchronizer::newLayoutAdded, this, &Layouts::onLayoutAddedExternally); 0061 connect(m_handler->corona()->layoutsManager()->synchronizer(), &Latte::Layouts::Synchronizer::layoutActivitiesChanged, this, &Layouts::onLayoutActivitiesChangedExternally); 0062 0063 connect(m_model, &QAbstractItemModel::dataChanged, this, &Layouts::dataChanged); 0064 connect(m_model, &Model::Layouts::rowsInserted, this, &Layouts::dataChanged); 0065 connect(m_model, &Model::Layouts::rowsRemoved, this, &Layouts::dataChanged); 0066 0067 connect(m_model, &Model::Layouts::nameDuplicated, this, &Layouts::onNameDuplicatedFrom); 0068 0069 connect(m_headerView, &QObject::destroyed, this, [&]() { 0070 m_viewSortColumn = m_headerView->sortIndicatorSection(); 0071 m_viewSortOrder = m_headerView->sortIndicatorOrder(); 0072 }); 0073 0074 initView(); 0075 initLayouts(); 0076 0077 //! connect them after initialization of the view 0078 connect(m_model, &Model::Layouts::inMultipleModeChanged, this, [&]() { 0079 applyColumnWidths(true); 0080 }); 0081 0082 connect(m_handler->corona()->universalSettings(), &UniversalSettings::canDisableBordersChanged, this, [&]() { 0083 applyColumnWidths(false); 0084 }); 0085 } 0086 0087 Layouts::~Layouts() 0088 { 0089 saveConfig(); 0090 0091 for (const auto &tempDir : m_tempDirectories) { 0092 QDir tDir(tempDir); 0093 0094 if (tDir.exists() && tempDir.startsWith("/tmp/")) { 0095 tDir.removeRecursively(); 0096 } 0097 } 0098 } 0099 0100 QAbstractItemModel *Layouts::proxyModel() const 0101 { 0102 return m_proxyModel; 0103 } 0104 0105 QAbstractItemModel *Layouts::baseModel() const 0106 { 0107 return m_model; 0108 } 0109 0110 QTableView *Layouts::view() const 0111 { 0112 return m_view; 0113 } 0114 0115 Settings::Part::TemplatesKeeper *Layouts::templatesKeeper() const 0116 { 0117 return m_templatesKeeper; 0118 } 0119 0120 void Layouts::initView() 0121 { 0122 m_view->setModel(m_proxyModel); 0123 m_view->setHorizontalHeader(m_headerView); 0124 m_view->verticalHeader()->setVisible(false); 0125 m_view->setSortingEnabled(true); 0126 0127 m_proxyModel->setSortRole(Model::Layouts::SORTINGROLE); 0128 m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); 0129 0130 m_view->sortByColumn(m_viewSortColumn, m_viewSortOrder); 0131 0132 //!find the available colors 0133 m_iconsPath = m_handler->corona()->kPackage().path() + "../../shells/org.kde.latte.shell/contents/images/canvas/"; 0134 0135 QDir layoutDir(m_iconsPath); 0136 QStringList filter; 0137 filter.append(QString("*print.jpg")); 0138 QStringList files = layoutDir.entryList(filter, QDir::Files | QDir::NoSymLinks); 0139 QStringList colors; 0140 0141 for (auto &file : files) { 0142 int colorEnd = file.lastIndexOf("print.jpg"); 0143 QString color = file.remove(colorEnd, 9); 0144 colors.append(color); 0145 } 0146 0147 m_view->setItemDelegateForColumn(Model::Layouts::NAMECOLUMN, new Settings::Layout::Delegate::LayoutName(this)); 0148 m_view->setItemDelegateForColumn(Model::Layouts::BACKGROUNDCOLUMN, new Settings::Layout::Delegate::BackgroundDelegate(this)); 0149 m_view->setItemDelegateForColumn(Model::Layouts::MENUCOLUMN, new Settings::Layout::Delegate::CheckBox(this)); 0150 m_view->setItemDelegateForColumn(Model::Layouts::BORDERSCOLUMN, new Settings::Layout::Delegate::CheckBox(this)); 0151 m_view->setItemDelegateForColumn(Model::Layouts::ACTIVITYCOLUMN, new Settings::Layout::Delegate::Activities(this)); 0152 0153 connect(m_view->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Layouts::onCurrentRowChanged); 0154 0155 connect(m_view, &QObject::destroyed, this, [&]() { 0156 storeColumnWidths(m_model->inMultipleMode()); 0157 }); 0158 } 0159 0160 bool Layouts::hasChangedData() const 0161 { 0162 return m_model->hasChangedData(); 0163 } 0164 0165 bool Layouts::layoutsAreChanged() const 0166 { 0167 return m_model->layoutsAreChanged(); 0168 } 0169 0170 bool Layouts::modeIsChanged() const 0171 { 0172 return m_model-modeIsChanged(); 0173 } 0174 0175 void Layouts::setOriginalInMultipleMode(const bool &inmultiple) 0176 { 0177 m_model->setOriginalInMultipleMode(inmultiple); 0178 } 0179 0180 bool Layouts::hasSelectedLayout() const 0181 { 0182 return m_view->selectionModel()->hasSelection(); 0183 } 0184 0185 bool Layouts::isLayoutOriginal(const QString ¤tLayoutId) const 0186 { 0187 return m_model->originalLayoutsData().containsId(currentLayoutId); 0188 } 0189 0190 bool Layouts::isSelectedLayoutOriginal() const 0191 { 0192 if (!hasSelectedLayout()) { 0193 return false; 0194 } 0195 0196 Data::Layout currentData = selectedLayoutCurrentData(); 0197 0198 return m_model->originalLayoutsData().containsId(currentData.id); 0199 } 0200 0201 QString Layouts::colorPath(const QString color) const 0202 { 0203 QString path = m_iconsPath + color + "print.jpg"; 0204 0205 if (!QFileInfo(path).exists()) { 0206 return m_iconsPath + "blueprint.jpg"; 0207 } 0208 0209 return path; 0210 } 0211 0212 QString Layouts::iconsPath() const 0213 { 0214 return m_iconsPath; 0215 } 0216 0217 const Latte::Data::ViewsTable Layouts::selectedLayoutViews() 0218 { 0219 Latte::Data::ViewsTable views; 0220 int selectedRow = m_view->currentIndex().row(); 0221 0222 if (selectedRow < 0) { 0223 return views; 0224 } 0225 0226 QString selectedId = m_proxyModel->data(m_proxyModel->index(selectedRow, Model::Layouts::IDCOLUMN), Qt::UserRole).toString(); 0227 Data::Layout selectedCurrentData = m_model->currentData(selectedId); 0228 0229 Data::Layout originalSelectedData = selectedLayoutOriginalData(); 0230 CentralLayout *central = m_handler->corona()->layoutsManager()->synchronizer()->centralLayout(originalSelectedData.name); 0231 0232 bool islayoutactive{true}; 0233 0234 if (!central) { 0235 islayoutactive = false; 0236 central = new CentralLayout(this, selectedCurrentData.id); 0237 } 0238 0239 selectedCurrentData.views = central->viewsTable().onlyOriginals(); 0240 selectedCurrentData.views.isInitialized = true; 0241 0242 if (!islayoutactive) { 0243 central->deleteLater(); 0244 } 0245 0246 return selectedCurrentData.views; 0247 } 0248 0249 const Latte::Data::LayoutIcon Layouts::selectedLayoutIcon() const 0250 { 0251 int selectedRow = m_view->currentIndex().row(); 0252 0253 if (selectedRow >= 0) { 0254 QString selectedId = m_proxyModel->data(m_proxyModel->index(selectedRow, Model::Layouts::IDCOLUMN), Qt::UserRole).toString(); 0255 return m_model->currentLayoutIcon(selectedId); 0256 } 0257 0258 return Latte::Data::LayoutIcon(); 0259 0260 } 0261 0262 const Latte::Data::Layout Layouts::selectedLayoutCurrentData() const 0263 { 0264 int selectedRow = m_view->currentIndex().row(); 0265 if (selectedRow >= 0) { 0266 QString selectedId = m_proxyModel->data(m_proxyModel->index(selectedRow, Model::Layouts::IDCOLUMN), Qt::UserRole).toString(); 0267 return m_model->currentData(selectedId); 0268 } else { 0269 return Latte::Data::Layout(); 0270 } 0271 } 0272 0273 const Latte::Data::Layout Layouts::selectedLayoutOriginalData() const 0274 { 0275 int selectedRow = m_view->currentIndex().row(); 0276 QString selectedId = m_proxyModel->data(m_proxyModel->index(selectedRow, Model::Layouts::IDCOLUMN), Qt::UserRole).toString(); 0277 0278 return m_model->originalData(selectedId); 0279 } 0280 0281 const Latte::Data::Layout Layouts::currentData(const QString ¤tLayoutId) const 0282 { 0283 return m_model->currentData(currentLayoutId); 0284 } 0285 0286 const Latte::Data::Layout Layouts::originalData(const QString ¤tLayoutId) const 0287 { 0288 return m_model->originalData(currentLayoutId); 0289 } 0290 0291 const Latte::Data::ScreensTable Layouts::screensData() 0292 { 0293 Latte::Data::ScreensTable scrtable = m_handler->corona()->screenPool()->screensTable(); 0294 0295 QList<int> expscreens; 0296 0297 //! update activeness 0298 for (int i=1; i<scrtable.rowCount(); ++i) { 0299 scrtable[i].isActive = m_handler->corona()->screenPool()->isScreenActive(scrtable[i].id.toInt()); 0300 } 0301 0302 //! update removability based on activeness 0303 for (int i=0; i<scrtable.rowCount(); ++i) { 0304 scrtable[i].isRemovable = !scrtable[i].isActive; 0305 } 0306 0307 //! retrieve all layouts data 0308 Latte::Data::LayoutsTable originalLayouts = m_model->originalLayoutsData(); 0309 Latte::Data::LayoutsTable currentLayouts = m_model->currentLayoutsData(); 0310 Latte::Data::LayoutsTable removedLayouts = originalLayouts.subtracted(currentLayouts); 0311 0312 //! temp removed layouts should be considered because they may not be deleted in the end 0313 for (int i=0; i<removedLayouts.rowCount(); ++i) { 0314 CentralLayout *central = centralLayout(removedLayouts[i].id); 0315 0316 if (!central) { 0317 continue; 0318 } 0319 0320 QList<int> newexps = central->viewsExplicitScreens(); 0321 0322 expscreens = join(expscreens, newexps); 0323 } 0324 0325 //! current layouts should be considered 0326 for (int i=0; i<currentLayouts.rowCount(); ++i) { 0327 CentralLayout *central = centralLayout(currentLayouts[i].id); 0328 0329 if (!central) { 0330 continue; 0331 } 0332 0333 QList<int> newexps = central->viewsExplicitScreens(); 0334 0335 expscreens = join(expscreens, newexps); 0336 } 0337 0338 //! discovered explicit screens should be flagged as NOREMOVABLE 0339 for (int i=0; i<expscreens.count(); ++i) { 0340 QString expscridstr = QString::number(expscreens[i]); 0341 0342 if (scrtable.containsId(expscridstr)) { 0343 scrtable[expscridstr].isRemovable = false; 0344 } 0345 } 0346 0347 //! Print no-removable screens 0348 /*for (int i=0; i<scrtable.rowCount(); ++i) { 0349 if (!scrtable[i].isRemovable) { 0350 qDebug() <<"org.kde.latte NO REMOVABLE EXP SCREEN ::: " << scrtable[i].id; 0351 } 0352 }*/ 0353 0354 return scrtable; 0355 } 0356 0357 bool Layouts::inMultipleMode() const 0358 { 0359 return m_model->inMultipleMode(); 0360 } 0361 0362 void Layouts::setInMultipleMode(bool inMultiple) 0363 { 0364 m_model->setInMultipleMode(inMultiple); 0365 } 0366 0367 CentralLayout *Layouts::centralLayout(const QString ¤tLayoutId) 0368 { 0369 Data::Layout originlayoutdata = originalData(currentLayoutId); 0370 auto activelayout = isLayoutOriginal(currentLayoutId) ? 0371 m_handler->corona()->layoutsManager()->synchronizer()->centralLayout(originlayoutdata.name) : nullptr; 0372 0373 Latte::CentralLayout *centrallayout = activelayout ? activelayout : new Latte::CentralLayout(this, currentLayoutId); 0374 0375 return centrallayout; 0376 } 0377 0378 void Layouts::applyColumnWidths(bool storeValues) 0379 { 0380 bool isLastModeMultiple = !m_model->inMultipleMode(); 0381 0382 //! save previous values 0383 if (storeValues) { 0384 storeColumnWidths(isLastModeMultiple); 0385 } 0386 0387 if (m_model->inMultipleMode()) { 0388 m_view->horizontalHeader()->setSectionResizeMode(Model::Layouts::ACTIVITYCOLUMN, QHeaderView::Stretch); 0389 m_view->horizontalHeader()->setSectionResizeMode(Model::Layouts::NAMECOLUMN, QHeaderView::Interactive); 0390 } else { 0391 m_view->horizontalHeader()->setSectionResizeMode(Model::Layouts::NAMECOLUMN, QHeaderView::Stretch); 0392 m_view->horizontalHeader()->setSectionResizeMode(Model::Layouts::ACTIVITYCOLUMN, QHeaderView::Interactive); 0393 } 0394 0395 //! this line should be commented for debugging layouts window functionality 0396 m_view->setColumnHidden(Model::Layouts::IDCOLUMN, true); 0397 m_view->setColumnHidden(Model::Layouts::HIDDENTEXTCOLUMN, true); 0398 0399 int maxColumns = Model::Layouts::ACTIVITYCOLUMN - Model::Layouts::BACKGROUNDCOLUMN; //4 - multiple 0400 0401 if (m_handler->corona()->universalSettings()->canDisableBorders()) { 0402 m_view->setColumnHidden(Model::Layouts::BORDERSCOLUMN, false); 0403 } else { 0404 m_view->setColumnHidden(Model::Layouts::BORDERSCOLUMN, true); 0405 } 0406 0407 if (m_model->inMultipleMode()) { 0408 m_view->setColumnHidden(Model::Layouts::MENUCOLUMN, true); 0409 m_view->setColumnHidden(Model::Layouts::ACTIVITYCOLUMN, false); 0410 } else { 0411 m_view->setColumnHidden(Model::Layouts::MENUCOLUMN, false); 0412 m_view->setColumnHidden(Model::Layouts::ACTIVITYCOLUMN, true); 0413 } 0414 0415 if (!m_viewColumnWidths.isEmpty()) { 0416 for (int i=0; i<qMin(m_viewColumnWidths.count(), maxColumns); ++i) { 0417 int currentColumn = Model::Layouts::BACKGROUNDCOLUMN+i; 0418 0419 if ((currentColumn == Model::Layouts::BORDERSCOLUMN && !m_handler->corona()->universalSettings()->canDisableBorders()) 0420 || (currentColumn == Model::Layouts::NAMECOLUMN && isLastModeMultiple) 0421 || (currentColumn == Model::Layouts::MENUCOLUMN && !isLastModeMultiple) 0422 || (currentColumn == Model::Layouts::ACTIVITYCOLUMN)) { 0423 continue; 0424 } 0425 0426 m_view->setColumnWidth(currentColumn, m_viewColumnWidths[i].toInt()); 0427 } 0428 } else { 0429 m_view->resizeColumnsToContents(); 0430 } 0431 } 0432 0433 int Layouts::rowForId(QString id) const 0434 { 0435 for (int i = 0; i < m_proxyModel->rowCount(); ++i) { 0436 QString rowId = m_proxyModel->data(m_proxyModel->index(i, Model::Layouts::IDCOLUMN), Qt::UserRole).toString(); 0437 0438 if (rowId == id) { 0439 return i; 0440 } 0441 } 0442 0443 return -1; 0444 } 0445 0446 int Layouts::rowForName(QString layoutName) const 0447 { 0448 for (int i = 0; i < m_proxyModel->rowCount(); ++i) { 0449 QString rowName = m_proxyModel->data(m_proxyModel->index(i, Model::Layouts::NAMECOLUMN), Qt::UserRole).toString(); 0450 0451 if (rowName == layoutName) { 0452 return i; 0453 } 0454 } 0455 0456 return -1; 0457 } 0458 0459 QString Layouts::uniqueTempDirectory() 0460 { 0461 QTemporaryDir tempDir; 0462 tempDir.setAutoRemove(false); 0463 m_tempDirectories.append(tempDir.path()); 0464 0465 return tempDir.path(); 0466 } 0467 0468 QString Layouts::uniqueLayoutName(QString name) 0469 { 0470 int pos_ = name.lastIndexOf(QRegExp(QString(" - [0-9]+"))); 0471 0472 if (m_model->containsCurrentName(name) && pos_ > 0) { 0473 name = name.left(pos_); 0474 } 0475 0476 int i = 2; 0477 0478 QString namePart = name; 0479 0480 while (m_model->containsCurrentName(name)) { 0481 name = namePart + " - " + QString::number(i); 0482 i++; 0483 } 0484 0485 return name; 0486 } 0487 0488 void Layouts::removeSelected() 0489 { 0490 if (m_view->currentIndex().row() < 0) { 0491 return; 0492 } 0493 0494 Latte::Data::Layout selectedOriginal = selectedLayoutOriginalData(); 0495 0496 if (m_handler->corona()->layoutsManager()->synchronizer()->layout(selectedOriginal.name)) { 0497 return; 0498 } 0499 0500 int row = m_view->currentIndex().row(); 0501 row = qMin(row, m_proxyModel->rowCount() - 1); 0502 m_view->selectRow(row); 0503 0504 Latte::Data::Layout selected = selectedLayoutCurrentData(); 0505 m_model->removeLayout(selected.id); 0506 } 0507 0508 void Layouts::toggleEnabledForSelected() 0509 { 0510 if (!hasSelectedLayout()) { 0511 return; 0512 } 0513 0514 Latte::Data::Layout selected = selectedLayoutCurrentData(); 0515 0516 if (!selected.activities.isEmpty()) { 0517 m_proxyModel->setData(m_proxyModel->index(m_view->currentIndex().row(), Model::Layouts::ACTIVITYCOLUMN), QStringList(), Qt::UserRole); 0518 } else { 0519 QStringList activities; 0520 0521 bool layoutsenabledonlyinspecificactivities = m_model->hasEnabledLayout() 0522 && !m_model->hasEnabledLayoutInAllActitivities() 0523 && !m_model->hasEnabledLayoutInFreeActivities(); 0524 0525 if (m_model->hasEnabledLayoutInCurrentActivity() || layoutsenabledonlyinspecificactivities) { 0526 activities << m_model->currentActivityId(); 0527 } else if (m_model->hasEnabledLayoutInFreeActivities()) { 0528 activities << Data::Layout::FREEACTIVITIESID; 0529 } else { 0530 activities << Data::Layout::ALLACTIVITIESID; 0531 } 0532 0533 m_proxyModel->setData(m_proxyModel->index(m_view->currentIndex().row(), Model::Layouts::ACTIVITYCOLUMN), activities, Qt::UserRole); 0534 } 0535 } 0536 0537 void Layouts::toggleLockedForSelected() 0538 { 0539 if (!hasSelectedLayout()) { 0540 return; 0541 } 0542 0543 Latte::Data::Layout selected = selectedLayoutCurrentData(); 0544 0545 m_proxyModel->setData(m_proxyModel->index(m_view->currentIndex().row(), Model::Layouts::NAMECOLUMN), !selected.isLocked, Settings::Model::Layouts::ISLOCKEDROLE); 0546 } 0547 0548 void Layouts::selectRow(const QString &id) 0549 { 0550 m_view->selectRow(rowForId(id)); 0551 } 0552 0553 void Layouts::setLayoutProperties(const Latte::Data::Layout &layout) 0554 { 0555 m_model->setLayoutProperties(layout); 0556 } 0557 0558 QString Layouts::layoutNameForFreeActivities() const 0559 { 0560 return m_model->layoutNameForFreeActivities(); 0561 } 0562 0563 void Layouts::setOriginalLayoutForFreeActivities(const QString &id) 0564 { 0565 m_model->setOriginalLayoutForFreeActivities(id); 0566 emit dataChanged(); 0567 } 0568 0569 void Layouts::initLayouts() 0570 { 0571 m_model->clear(); 0572 bool inMultiple{m_handler->corona()->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts}; 0573 setInMultipleMode(inMultiple); 0574 0575 m_handler->corona()->layoutsManager()->synchronizer()->updateLayoutsTable(); 0576 Latte::Data::LayoutsTable layouts = m_handler->corona()->layoutsManager()->synchronizer()->layoutsTable(); 0577 0578 //! Send original loaded data to model 0579 m_model->setOriginalInMultipleMode(inMultiple); 0580 m_model->setOriginalData(layouts); 0581 0582 QStringList currentLayoutNames = m_handler->corona()->layoutsManager()->currentLayoutsNames(); 0583 if (currentLayoutNames.count() > 0) { 0584 m_view->selectRow(rowForName(currentLayoutNames[0])); 0585 } 0586 0587 applyColumnWidths(); 0588 0589 showInitialErrorWarningMessages(); 0590 } 0591 0592 void Layouts::initialMessageForErroredLayouts(const int &count) 0593 { 0594 if (count <= 0) { 0595 return; 0596 } 0597 0598 m_handler->showInlineMessage(i18ncp("settings:counted layout with errors", 0599 "<b>Error:</b> There is <b>1 layout</b> that has reported errors.", 0600 "<b>Error:</b> There are <b>%1 layouts</b> that have reported errors.", 0601 count), 0602 KMessageWidget::Error); 0603 } 0604 0605 void Layouts::initialMessageForWarningLayouts(const int &count) 0606 { 0607 if (count <= 0) { 0608 return; 0609 } 0610 0611 m_handler->showInlineMessage(i18ncp("settings:counted layout with warnings", 0612 "<b>Warning:</b> There is <b>1 layout</b> that has reported warnings.", 0613 "<b>Warning:</b> There are <b>%1 layouts</b> that have reported warnings.", 0614 count), 0615 KMessageWidget::Warning); 0616 } 0617 0618 0619 void Layouts::messageForErroredLayout(const Data::Layout &layout) 0620 { 0621 //! add actions 0622 QAction *examineaction = new QAction(i18n("Examine..."), this); 0623 examineaction->setIcon(QIcon::fromTheme("document-preview")); 0624 examineaction->setData(layout.id); 0625 QList<QAction *> actions; 0626 actions << examineaction; 0627 0628 connect(examineaction, &QAction::triggered, this, [&, examineaction]() { 0629 QString currentid = examineaction->data().toString(); 0630 0631 if (!currentid.isEmpty()) { 0632 selectRow(currentid); 0633 m_handler->showViewsDialog(); 0634 } 0635 }); 0636 0637 if (!layout.hasErrors() && layout.hasWarnings()) { 0638 //! add only warnings first 0639 m_handler->showInlineMessage(i18ncp("settings:named layout with warnings", 0640 "<b>Warning: %2</b> layout has reported <b>1 warning</b> that need your attention.", 0641 "<b>Warning: %2</b> layout has reported <b>%1 warnings</b> that need your attention.", 0642 layout.warnings, 0643 layout.name), 0644 KMessageWidget::Warning, 0645 false, 0646 actions); 0647 } else if (layout.hasErrors() && !layout.hasWarnings()) { 0648 //! add errors in the end in order to be read by the user 0649 m_handler->showInlineMessage(i18nc("settings:named layout with errors", 0650 "<b>Error: %2</b> layout has reported <b>1 error</b> that you need to repair.", 0651 "<b>Error: %2</b> layout has reported <b>%1 errors</b> that you need to repair.", 0652 layout.errors, 0653 layout.name), 0654 KMessageWidget::Error, 0655 true, 0656 actions); 0657 } else if (layout.hasErrors() && layout.hasWarnings()) { 0658 //! add most important errors in the end in order to be read by the user 0659 QString errorstr = i18ncp("errors count", 0660 "1 error", 0661 "%1 errors", 0662 layout.errors); 0663 QString warningstr = i18ncp("warnings count", 0664 "1 warning", 0665 "%1 warnings", 0666 layout.warnings); 0667 0668 m_handler->showInlineMessage(i18nc("settings: named layout with %2 errors and %3 warnings", 0669 "<b>Error: %1</b> layout has reported <b>%2</b> and <b>%3</b> that you need to repair.", 0670 layout.name, 0671 errorstr, 0672 warningstr), 0673 KMessageWidget::Error, 0674 true, 0675 actions); 0676 } 0677 } 0678 0679 void Layouts::showInitialErrorWarningMessages() 0680 { 0681 if (!m_hasShownInitialErrorWarningMessages) { 0682 m_hasShownInitialErrorWarningMessages = true; 0683 0684 Latte::Data::LayoutsTable layouts = m_handler->corona()->layoutsManager()->synchronizer()->layoutsTable(); 0685 0686 int erroredlayouts{0}; 0687 int warninglayouts{0}; 0688 0689 for (int i=0; i<layouts.rowCount(); ++i) { 0690 if (layouts[i].hasErrors()) { 0691 erroredlayouts++; 0692 } else if (layouts[i].hasWarnings()) { 0693 warninglayouts++; 0694 } 0695 } 0696 0697 onCurrentRowChanged(); 0698 initialMessageForWarningLayouts(warninglayouts); 0699 initialMessageForErroredLayouts(erroredlayouts); 0700 } 0701 } 0702 0703 void Layouts::onCurrentRowChanged() 0704 { 0705 if (!hasSelectedLayout()) { 0706 return; 0707 } 0708 0709 if (!m_handler->isViewsDialogVisible()) { 0710 Latte::Data::Layout selectedlayout = selectedLayoutCurrentData(); 0711 0712 if (selectedlayout.hasErrors() || selectedlayout.hasWarnings()) { 0713 messageForErroredLayout(selectedlayout); 0714 } 0715 } 0716 } 0717 0718 void Layouts::onLayoutActivitiesChangedExternally(const Data::Layout &layout) 0719 { 0720 m_model->setOriginalActivitiesForLayout(layout); 0721 } 0722 0723 void Layouts::onLayoutAddedExternally(const Data::Layout &layout) 0724 { 0725 m_model->appendOriginalLayout(layout); 0726 } 0727 0728 void Layouts::setLayoutCurrentErrorsWarnings(const QString &layoutCurrentId, const int &errors, const int &warnings) 0729 { 0730 Latte::Data::Layout layout = m_model->currentData(layoutCurrentId); 0731 0732 if (!layout.isNull()) { 0733 layout.errors = errors; 0734 layout.warnings = warnings; 0735 setLayoutProperties(layout); 0736 } 0737 0738 } 0739 0740 void Layouts::sortByColumn(int column, Qt::SortOrder order) 0741 { 0742 m_view->sortByColumn(column, order); 0743 } 0744 0745 const Latte::Data::Layout Layouts::addLayoutForFile(QString file, QString layoutName, bool newTempDirectory) 0746 { 0747 if (layoutName.isEmpty()) { 0748 layoutName = CentralLayout::layoutName(file); 0749 } 0750 0751 layoutName = uniqueLayoutName(layoutName); 0752 0753 Latte::Data::Layout copied; 0754 0755 if (newTempDirectory) { 0756 copied.id = uniqueTempDirectory() + "/" + layoutName + ".layout.latte"; 0757 QFile(file).copy(copied.id); 0758 } else { 0759 copied.id = file; 0760 } 0761 0762 QFileInfo newFileInfo(copied.id); 0763 0764 if (newFileInfo.exists() && !newFileInfo.isWritable()) { 0765 QFile(copied.id).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); 0766 } 0767 0768 CentralLayout *settings = new CentralLayout(this, copied.id); 0769 0770 copied = settings->data(); 0771 copied.name = uniqueLayoutName(layoutName); 0772 0773 m_model->appendLayout(copied); 0774 m_view->selectRow(rowForId(copied.id)); 0775 0776 return copied; 0777 } 0778 0779 const Latte::Data::Layout Layouts::addLayoutByText(QString rawLayoutText) 0780 { 0781 QTemporaryFile tempFile; 0782 tempFile.open(); 0783 QTextStream stream(&tempFile); 0784 stream << rawLayoutText; 0785 stream.flush(); 0786 tempFile.close(); 0787 0788 Latte::Data::Layout newLayout = addLayoutForFile(tempFile.fileName(),i18n("Dropped Raw Layout")); 0789 0790 int selectedRow = m_view->currentIndex().row(); 0791 QModelIndex tIndex = m_proxyModel->index(selectedRow, Model::Layouts::NAMECOLUMN); 0792 m_view->edit(tIndex); 0793 0794 /**Window has to be activated explicitely since the window where the drag 0795 * started would otherwise be the active window. By activating the window 0796 the user can immediately change the name by simply typing.*/ 0797 m_handler->dialog()->activateWindow(); 0798 0799 return newLayout; 0800 } 0801 0802 void Layouts::duplicateSelectedLayout() 0803 { 0804 int row = m_view->currentIndex().row(); 0805 0806 if (row < 0) { 0807 return; 0808 } 0809 0810 Latte::Data::Layout selectedLayoutCurrent = selectedLayoutCurrentData(); 0811 Latte::Data::Layout selectedLayoutOriginal = selectedLayoutOriginalData(); 0812 selectedLayoutOriginal = selectedLayoutOriginal.isEmpty() ? selectedLayoutCurrent : selectedLayoutOriginal; 0813 0814 0815 //! Update original layout before duplicating if this layout is active 0816 if (m_handler->corona()->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) { 0817 Latte::CentralLayout *central = m_handler->corona()->layoutsManager()->synchronizer()->centralLayout(selectedLayoutOriginal.name); 0818 if (central) { 0819 central->syncToLayoutFile(); 0820 } 0821 } 0822 0823 Latte::Data::Layout copied = selectedLayoutCurrent; 0824 0825 copied.name = uniqueLayoutName(selectedLayoutCurrent.name); 0826 copied.id = uniqueTempDirectory() + "/" + copied.name + ".layout.latte";; 0827 copied.isActive = false; 0828 copied.isConsideredActive = false; 0829 copied.isLocked = false; 0830 copied.activities = QStringList(); 0831 0832 QFile(selectedLayoutCurrent.id).copy(copied.id); 0833 QFileInfo newFileInfo(copied.id); 0834 0835 if (newFileInfo.exists() && !newFileInfo.isWritable()) { 0836 QFile(copied.id).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther); 0837 } 0838 0839 CentralLayout *settings = new CentralLayout(this, copied.id); 0840 settings->clearLastUsedActivity(); 0841 settings->setActivities(QStringList()); 0842 0843 m_model->appendLayout(copied); 0844 0845 m_view->selectRow(rowForId(copied.id)); 0846 } 0847 0848 bool Layouts::importLayoutsFromV1ConfigFile(QString file) 0849 { 0850 KTar archive(file, QStringLiteral("application/x-tar")); 0851 archive.open(QIODevice::ReadOnly); 0852 0853 //! if the file isnt a tar archive 0854 if (archive.isOpen()) { 0855 QDir tempDir{uniqueTempDirectory()}; 0856 0857 const auto archiveRootDir = archive.directory(); 0858 0859 for (const auto &name : archiveRootDir->entries()) { 0860 auto fileEntry = archiveRootDir->file(name); 0861 fileEntry->copyTo(tempDir.absolutePath()); 0862 } 0863 0864 QString name = Latte::Layouts::Importer::nameOfConfigFile(file); 0865 0866 QString applets(tempDir.absolutePath() + "/" + "lattedock-appletsrc"); 0867 0868 if (QFile(applets).exists()) { 0869 QStringList importedlayouts; 0870 0871 if (m_handler->corona()->layoutsManager()->importer()->importOldLayout(applets, name, false, tempDir.absolutePath())) { 0872 Latte::Data::Layout imported = addLayoutForFile(tempDir.absolutePath() + "/" + name + ".layout.latte", name); 0873 importedlayouts << imported.name; 0874 } 0875 0876 QString alternativeName = name + "-" + i18nc("layout", "Alternative"); 0877 0878 if (m_handler->corona()->layoutsManager()->importer()->importOldLayout(applets, alternativeName, false, tempDir.absolutePath())) { 0879 Latte::Data::Layout imported = addLayoutForFile(tempDir.absolutePath() + "/" + alternativeName + ".layout.latte", alternativeName, false); 0880 importedlayouts << imported.name; 0881 } 0882 0883 if (importedlayouts.count() > 0) { 0884 m_handler->showInlineMessage(i18np("Layout <b>%2</b> imported successfully...", 0885 "Layouts <b>%2</b> imported successfully...", 0886 importedlayouts.count(), 0887 importedlayouts.join(",")), 0888 KMessageWidget::Positive); 0889 0890 return true; 0891 } 0892 } 0893 } 0894 0895 return false; 0896 } 0897 0898 void Layouts::reset() 0899 { 0900 m_model->resetData(); 0901 QStringList currentLayoutNames = m_handler->corona()->layoutsManager()->currentLayoutsNames(); 0902 if (currentLayoutNames.count() > 0) { 0903 m_view->selectRow(rowForName(currentLayoutNames[0])); 0904 } 0905 0906 //! Clear any templates keeper data 0907 m_templatesKeeper->clear(); 0908 } 0909 0910 void Layouts::save() 0911 { 0912 //! Update Layouts 0913 QStringList knownActivities = m_handler->corona()->layoutsManager()->synchronizer()->activities(); 0914 0915 QTemporaryDir layoutTempDir; 0916 0917 qDebug() << "Temporary Directory ::: " << layoutTempDir.path(); 0918 0919 QString switchToLayout; 0920 0921 QHash<QString, Latte::CentralLayout *> activeLayoutsToRename; 0922 0923 Latte::Data::LayoutsTable originalLayouts = m_model->originalLayoutsData(); 0924 Latte::Data::LayoutsTable currentLayouts = m_model->currentLayoutsData(); 0925 Latte::Data::LayoutsTable removedLayouts = originalLayouts.subtracted(currentLayouts); 0926 0927 //! remove layouts that have been removed from the user 0928 for (int i=0; i<removedLayouts.rowCount(); ++i) { 0929 QFile(removedLayouts[i].id).remove(); 0930 } 0931 0932 QList<Data::UniqueIdInfo> alteredIdsInfo; 0933 0934 QList<Latte::Data::Layout> alteredLayouts = m_model->alteredLayouts(); 0935 0936 for (int i = 0; i < alteredLayouts.count(); ++i) { 0937 Latte::Data::Layout iLayoutCurrentData = alteredLayouts[i]; 0938 Latte::Data::Layout iLayoutOriginalData = m_model->originalData(iLayoutCurrentData.id); 0939 iLayoutOriginalData = iLayoutOriginalData.isEmpty() ? iLayoutCurrentData : iLayoutOriginalData; 0940 0941 //qDebug() << i << ". " << id << " - " << color << " - " << name << " - " << menu << " - " << lActivities; 0942 //! update the generic parts of the layouts 0943 bool isOriginalLayout = m_model->originalLayoutsData().containsId(iLayoutCurrentData.id); 0944 Latte::CentralLayout *centralActive= isOriginalLayout ? m_handler->corona()->layoutsManager()->synchronizer()->centralLayout(iLayoutOriginalData.name) : nullptr; 0945 Latte::CentralLayout *central = centralActive ? centralActive : new Latte::CentralLayout(this, iLayoutCurrentData.id); 0946 0947 //! unlock read-only layout 0948 if (!central->isWritable()) { 0949 central->unlock(); 0950 } 0951 0952 //! Icon 0953 central->setIcon(iLayoutCurrentData.icon); 0954 0955 //! Custom Scheme 0956 central->setSchemeFile(iLayoutCurrentData.schemeFile); 0957 0958 //! Backgrounds 0959 central->setBackgroundStyle(iLayoutCurrentData.backgroundStyle); 0960 central->setColor(iLayoutCurrentData.color); 0961 central->setCustomBackground(iLayoutCurrentData.background); 0962 central->setCustomTextColor(iLayoutCurrentData.textColor); 0963 0964 //! Extra Properties 0965 central->setShowInMenu(iLayoutCurrentData.isShownInMenu); 0966 central->setDisableBordersForMaximizedWindows(iLayoutCurrentData.hasDisabledBorders); 0967 central->setPopUpMargin(iLayoutCurrentData.popUpMargin); 0968 central->setActivities(iLayoutCurrentData.activities); 0969 0970 //! If the layout name changed OR the layout path is a temporary one 0971 if ((iLayoutCurrentData.name != iLayoutOriginalData.name) || iLayoutCurrentData.isTemporary()) { 0972 //! If the layout is Active in MultipleLayouts 0973 if (central->isActive()) { 0974 qDebug() << " Active Layout Should Be Renamed From : " << central->name() << " TO :: " << iLayoutCurrentData.name; 0975 activeLayoutsToRename[iLayoutCurrentData.name] = central; 0976 } 0977 0978 QString tempFile = layoutTempDir.path() + "/" + QString(central->name() + ".layout.latte"); 0979 qDebug() << "new temp file ::: " << tempFile; 0980 0981 QFile(iLayoutCurrentData.id).rename(tempFile); 0982 0983 Data::UniqueIdInfo idInfo; 0984 0985 idInfo.oldId = iLayoutCurrentData.id; 0986 idInfo.newId = tempFile; 0987 idInfo.newName = iLayoutCurrentData.name; 0988 0989 alteredIdsInfo << idInfo; 0990 } 0991 } 0992 0993 //! this is necessary in case two layouts have to swap names 0994 //! so we copy first the layouts in a temp directory and afterwards all 0995 //! together we move them in the official layout directory 0996 for (int i = 0; i < alteredIdsInfo.count(); ++i) { 0997 Data::UniqueIdInfo idInfo = alteredIdsInfo[i]; 0998 0999 QString newFile = Latte::Layouts::Importer::layoutUserFilePath(idInfo.newName); 1000 QFile(idInfo.newId).rename(newFile); 1001 1002 1003 //! updating the #SETTINGSID in the model for the layout that was renamed 1004 for (int j = 0; j < m_model->rowCount(); ++j) { 1005 Latte::Data::Layout jLayout = m_model->at(j); 1006 1007 if (jLayout.id == idInfo.oldId) { 1008 m_model->setData(m_model->index(j, Model::Layouts::IDCOLUMN), newFile, Qt::UserRole); 1009 } 1010 } 1011 } 1012 1013 if (m_handler->corona()->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) { 1014 for (const auto &newLayoutName : activeLayoutsToRename.keys()) { 1015 Latte::CentralLayout *layoutPtr = activeLayoutsToRename[newLayoutName]; 1016 qDebug() << " Active Layout of Type: " << layoutPtr->type() << " Is Renamed From : " << activeLayoutsToRename[newLayoutName]->name() << " TO :: " << newLayoutName; 1017 layoutPtr->renameLayout(newLayoutName); 1018 } 1019 } 1020 1021 //! lock layouts in the end when the user has chosen it 1022 for (int i = 0; i < alteredLayouts.count(); ++i) { 1023 Latte::Data::Layout layoutCurrentData = alteredLayouts[i]; 1024 Latte::Data::Layout layoutOriginalData = m_model->originalData(layoutCurrentData.id); 1025 layoutOriginalData = layoutOriginalData.isEmpty() ? layoutCurrentData : layoutOriginalData; 1026 1027 Latte::CentralLayout *layoutPtr = m_handler->corona()->layoutsManager()->synchronizer()->centralLayout(layoutOriginalData.name); 1028 1029 if (!layoutPtr) { 1030 layoutPtr = new Latte::CentralLayout(this, layoutCurrentData.id); 1031 } 1032 1033 if (layoutCurrentData.isLocked && layoutPtr && layoutPtr->isWritable()) { 1034 layoutPtr->lock(); 1035 } 1036 } 1037 1038 //! send new layouts data in layoutsmanager 1039 m_handler->corona()->layoutsManager()->synchronizer()->setLayoutsTable(currentLayouts); 1040 1041 //! make sure that there is a layout for free activities 1042 //! send to layout manager in which layout to switch 1043 MemoryUsage::LayoutsMemory inMemoryOption = inMultipleMode() ? Latte::MemoryUsage::MultipleLayouts : Latte::MemoryUsage::SingleLayout; 1044 1045 if (inMemoryOption == MemoryUsage::SingleLayout) { 1046 bool inrenamingsingleactivelayout = (activeLayoutsToRename.count() > 0); 1047 QString currentSingleLayoutName = m_handler->corona()->universalSettings()->singleModeLayoutName(); 1048 QString nextSingleLayoutName = inrenamingsingleactivelayout ? activeLayoutsToRename.keys()[0] : currentSingleLayoutName; 1049 1050 if (inrenamingsingleactivelayout && !currentLayouts.containsName(currentSingleLayoutName)) { 1051 m_handler->corona()->layoutsManager()->synchronizer()->setIsSingleLayoutInDeprecatedRenaming(true); 1052 } 1053 1054 m_handler->corona()->layoutsManager()->switchToLayout(nextSingleLayoutName, MemoryUsage::SingleLayout); 1055 } else { 1056 m_handler->corona()->layoutsManager()->switchToLayout("", MemoryUsage::MultipleLayouts); 1057 } 1058 1059 m_model->applyData(); 1060 1061 //! Clear any templates keeper data 1062 m_templatesKeeper->clear(); 1063 1064 emit dataChanged(); 1065 } 1066 1067 void Layouts::storeColumnWidths(bool inMultipleMode) 1068 { 1069 if (m_viewColumnWidths.isEmpty()) { 1070 m_viewColumnWidths << "" << "" << "" << ""; 1071 } 1072 1073 m_viewColumnWidths[0] = QString::number(m_view->columnWidth(Model::Layouts::BACKGROUNDCOLUMN)); 1074 1075 if (inMultipleMode) { 1076 m_viewColumnWidths[1] = QString::number(m_view->columnWidth(Model::Layouts::NAMECOLUMN)); 1077 } 1078 1079 if (!inMultipleMode) { 1080 m_viewColumnWidths[2] = QString::number(m_view->columnWidth(Model::Layouts::MENUCOLUMN)); 1081 } 1082 1083 if (m_handler->corona()->universalSettings()->canDisableBorders()) { 1084 m_viewColumnWidths[3] = QString::number(m_view->columnWidth(Model::Layouts::BORDERSCOLUMN)); 1085 } 1086 } 1087 1088 void Layouts::onNameDuplicatedFrom(const QString &provenId, const QString &trialId) 1089 { 1090 //! duplicated layout name 1091 int pRow = rowForId(provenId); 1092 int tRow = rowForId(trialId); 1093 1094 int originalRow = m_model->rowForId(provenId); 1095 Latte::Data::Layout provenLayout = m_model->at(originalRow); 1096 1097 m_handler->showInlineMessage(i18nc("settings: layout name used","Layout <b>%1</b> is already used, please provide a different name...", provenLayout.name), 1098 KMessageWidget::Error); 1099 1100 QModelIndex tIndex = m_proxyModel->index(tRow, Model::Layouts::NAMECOLUMN); 1101 1102 //! avoid losing focuse 1103 QTimer::singleShot(0, [this, tIndex]() { 1104 m_view->edit(tIndex); 1105 }); 1106 } 1107 1108 QList<int> Layouts::join(const QList<int> ¤tRecords, const QList<int> &newRecords) 1109 { 1110 QList<int> result = currentRecords; 1111 1112 for(int i=0; i<newRecords.count(); ++i) { 1113 if (!result.contains(newRecords[i])) { 1114 result << newRecords[i]; 1115 } 1116 } 1117 1118 return result; 1119 } 1120 1121 void Layouts::loadConfig() 1122 { 1123 QStringList defaultcolumnwidths; 1124 defaultcolumnwidths << QString::number(48) << QString::number(508) << QString::number(129) << QString::number(104); 1125 1126 //! new storage 1127 m_viewColumnWidths = m_storage.readEntry("columnWidths", defaultcolumnwidths); 1128 m_viewSortColumn = m_storage.readEntry("sortColumn", (int)Model::Layouts::NAMECOLUMN); 1129 m_viewSortOrder = static_cast<Qt::SortOrder>(m_storage.readEntry("sortOrder", (int)Qt::AscendingOrder)); 1130 } 1131 1132 void Layouts::saveConfig() 1133 { 1134 m_storage.writeEntry("columnWidths", m_viewColumnWidths); 1135 m_storage.writeEntry("sortColumn", m_viewSortColumn); 1136 m_storage.writeEntry("sortOrder", (int)m_viewSortOrder); 1137 } 1138 1139 } 1140 } 1141 }