1 /*
2     SPDX-FileCopyrightText: 2020 Michail Vourlakos <mvourlakos@gmail.com>
3     SPDX-License-Identifier: GPL-2.0-or-later
4 */
5 
6 #include "tablayoutshandler.h"
7 
8 //! local
9 #include <coretypes.h>
10 #include "ui_settingsdialog.h"
11 #include "settingsdialog.h"
12 #include "layoutscontroller.h"
13 #include "layoutsmodel.h"
14 #include "layoutstableview.h"
15 #include "../universalsettings.h"
16 #include "../detailsdialog/detailsdialog.h"
17 #include "../exporttemplatedialog/exporttemplatedialog.h"
18 #include "../viewsdialog/viewsdialog.h"
19 #include "../../apptypes.h"
20 #include "../../lattecorona.h"
21 #include "../../layout/centrallayout.h"
22 #include "../../layouts/importer.h"
23 #include "../../layouts/manager.h"
24 #include "../../templates/templatesmanager.h"
25 #include "../../tools/commontools.h"
26 
27 //! Qt
28 #include <QDBusInterface>
29 #include <QFileDialog>
30 #include <QFileInfo>
31 #include <QMimeData>
32 
33 //! KDE
34 #include <KWindowSystem>
35 #include <KLocalizedString>
36 #include <KActivities/Controller>
37 #include <KIO/OpenFileManagerWindowJob>
38 #include <KNewStuff3/KNS3/DownloadDialog>
39 
40 
41 namespace Latte {
42 namespace Settings {
43 namespace Handler {
44 
TabLayouts(Settings::Dialog::SettingsDialog * parent)45 TabLayouts::TabLayouts(Settings::Dialog::SettingsDialog *parent)
46     : Generic(parent),
47       m_parentDialog(parent),
48       m_corona(m_parentDialog->corona()),
49       m_ui(m_parentDialog->ui()),
50       m_storage(KConfigGroup(KSharedConfig::openConfig(),"LatteSettingsDialog").group("TabLayouts"))
51 {
52     //! load first the layouts view column widths
53     loadConfig();
54     m_layoutsController = new Settings::Controller::Layouts(this);
55 
56     //! create menu and assign actions before initializing the user interface
57     initLayoutMenu();
58     initUi();
59 }
60 
~TabLayouts()61 TabLayouts::~TabLayouts()
62 {
63     saveConfig();
64 }
65 
initUi()66 void TabLayouts::initUi()
67 {
68     m_inMemoryButtons = new QButtonGroup(this);
69     m_inMemoryButtons->addButton(m_ui->singleToolBtn, MemoryUsage::SingleLayout);
70     m_inMemoryButtons->addButton(m_ui->multipleToolBtn, MemoryUsage::MultipleLayouts);
71     m_inMemoryButtons->setExclusive(true);
72 
73     bool inMultiple{m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts};
74 
75     if (inMultiple) {
76         m_ui->multipleToolBtn->setChecked(true);
77     } else {
78         m_ui->singleToolBtn->setChecked(true);
79     }
80 
81     connect(m_ui->layoutsView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Generic::dataChanged);
82 
83     connect(m_layoutsController, &Settings::Controller::Layouts::dataChanged, this, &Generic::dataChanged);
84 
85     connect(this, &Settings::Handler::TabLayouts::dataChanged, this, &TabLayouts::updatePerLayoutButtonsState);
86     connect(m_corona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, &TabLayouts::updatePerLayoutButtonsState);
87 
88     connect(m_inMemoryButtons, static_cast<void(QButtonGroup::*)(int, bool)>(&QButtonGroup::buttonToggled),
89             [ = ](int id, bool checked) {
90 
91         if (checked) {
92             m_layoutsController->setInMultipleMode(id == MemoryUsage::MultipleLayouts);
93 
94             if (id == MemoryUsage::MultipleLayouts) {
95                 m_layoutsController->sortByColumn(Model::Layouts::ACTIVITYCOLUMN, Qt::AscendingOrder);
96             } else {
97                 m_layoutsController->sortByColumn(Model::Layouts::NAMECOLUMN, Qt::AscendingOrder);
98             }
99         }
100     });
101 
102     connect(m_ui->tabWidget, &QTabWidget::currentChanged, this, &TabLayouts::currentPageChanged);
103     connect(this, &TabLayouts::currentPageChanged, this, &TabLayouts::onCurrentPageChanged);
104 
105     updatePerLayoutButtonsState();
106 }
107 
initLayoutMenu()108 void TabLayouts::initLayoutMenu()
109 {
110     if (!m_layoutMenu) {
111         m_layoutMenu = new QMenu(i18n("Layout"), m_parentDialog->appMenuBar());
112         m_parentDialog->appMenuBar()->insertMenu(m_parentDialog->helpMenu()->menuAction(), m_layoutMenu);
113     }
114 
115     m_switchLayoutAction = m_layoutMenu->addAction(i18nc("switch layout","Switch"));
116     m_switchLayoutAction->setToolTip(i18n("Switch to selected layout"));
117     m_switchLayoutAction->setIcon(QIcon::fromTheme("user-identity"));
118     m_switchLayoutAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Tab));
119     connectActionWithButton(m_ui->switchButton, m_switchLayoutAction);
120     connect(m_switchLayoutAction, &QAction::triggered, this, &TabLayouts::switchLayout);
121 
122     m_activitiesManagerAction = m_layoutMenu->addAction(i18n("&Activities"));
123     m_activitiesManagerAction->setToolTip(i18n("Show Plasma Activities manager"));
124     m_activitiesManagerAction->setIcon(QIcon::fromTheme("activities"));
125     m_activitiesManagerAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_A));
126     connectActionWithButton(m_ui->activitiesButton, m_activitiesManagerAction);
127     connect(m_activitiesManagerAction, &QAction::triggered, this, &TabLayouts::toggleActivitiesManager);
128 
129     m_layoutMenu->addSeparator();
130 
131     m_newLayoutAction = m_layoutMenu->addAction(i18nc("new layout", "&New"));
132     m_newLayoutAction->setToolTip(i18n("New layout"));
133     m_newLayoutAction->setIcon(QIcon::fromTheme("add"));
134     m_newLayoutAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_N));
135     connectActionWithButton(m_ui->newButton, m_newLayoutAction);
136     connect(m_newLayoutAction, &QAction::triggered, m_ui->newButton, &QPushButton::showMenu);
137 
138     initLayoutTemplatesSubMenu();
139     m_newLayoutAction->setMenu(m_layoutTemplatesSubMenu);
140     m_ui->newButton->setMenu(m_layoutTemplatesSubMenu);
141 
142     connect(m_corona->templatesManager(), &Latte::Templates::Manager::layoutTemplatesChanged, this, &TabLayouts::initLayoutTemplatesSubMenu);
143 
144     m_duplicateLayoutAction = m_layoutMenu->addAction(i18nc("duplicate layout", "&Duplicate"));
145     m_duplicateLayoutAction->setToolTip(i18n("Duplicate selected layout"));
146     m_duplicateLayoutAction->setIcon(QIcon::fromTheme("edit-copy"));
147     m_duplicateLayoutAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_D));
148     connectActionWithButton(m_ui->duplicateButton, m_duplicateLayoutAction);
149     connect(m_duplicateLayoutAction, &QAction::triggered, this, &TabLayouts::duplicateLayout);
150 
151     m_removeLayoutAction = m_layoutMenu->addAction(i18nc("remove layout", "Remove"));
152     m_removeLayoutAction->setToolTip(i18n("Remove selected layout"));
153     m_removeLayoutAction->setIcon(QIcon::fromTheme("delete"));
154     m_removeLayoutAction->setShortcut(QKeySequence(Qt::Key_Delete));
155     connectActionWithButton(m_ui->removeButton, m_removeLayoutAction);
156     connect(m_removeLayoutAction, &QAction::triggered, this, &TabLayouts::removeLayout);
157     m_ui->removeButton->addAction(m_removeLayoutAction); //this is needed in order to be triggered properly
158 
159     m_layoutMenu->addSeparator();
160 
161     m_enabledLayoutAction = m_layoutMenu->addAction(i18n("Ena&bled"));
162     m_enabledLayoutAction->setToolTip(i18n("Assign in activities in order to be activated through Plasma Activities"));
163     m_enabledLayoutAction->setIcon(QIcon::fromTheme("edit-link"));
164     m_enabledLayoutAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_B));
165     m_enabledLayoutAction->setCheckable(true);
166     connectActionWithButton(m_ui->enabledButton, m_enabledLayoutAction);
167     connect(m_enabledLayoutAction, &QAction::triggered, this, &TabLayouts::toggleEnabledLayout);
168 
169     m_readOnlyLayoutAction = m_layoutMenu->addAction(i18nc("read only layout", "&Read Only"));
170     m_readOnlyLayoutAction->setToolTip(i18n("Make selected layout read-only"));
171     m_readOnlyLayoutAction->setIcon(QIcon::fromTheme("object-locked"));
172     m_readOnlyLayoutAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
173     m_readOnlyLayoutAction->setCheckable(true);
174     connectActionWithButton(m_ui->readOnlyButton, m_readOnlyLayoutAction);
175     connect(m_readOnlyLayoutAction, &QAction::triggered, this, &TabLayouts::lockLayout);
176 
177     m_viewsAction = m_layoutMenu->addAction(i18nc("layout docks / panels", "Docks, &Panels..."));
178     m_viewsAction->setToolTip(i18n("Show selected layouts docks and panels"));
179     m_viewsAction->setIcon(QIcon::fromTheme("window"));
180     m_viewsAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_P));
181     connectActionWithButton(m_ui->viewsBtn, m_viewsAction);
182     connect(m_viewsAction, &QAction::triggered, this, &TabLayouts::showViewsDialog);
183 
184     m_detailsAction = m_layoutMenu->addAction(i18nc("layout details", "De&tails..."));
185     m_detailsAction->setToolTip(i18n("Show selected layout details"));
186     m_detailsAction->setIcon(QIcon::fromTheme("view-list-details"));
187     m_detailsAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_T));
188     connectActionWithButton(m_ui->detailsButton, m_detailsAction);
189     connect(m_detailsAction, &QAction::triggered, this, &TabLayouts::showDetailsDialog);
190 
191     m_layoutMenu->addSeparator();
192 
193 
194     //! Import
195     m_importLayoutAction = m_layoutMenu->addAction(i18nc("import layout", "&Import"));
196     m_importLayoutAction->setToolTip(i18n("Import layout from various resources"));
197     m_importLayoutAction->setIcon(QIcon::fromTheme("document-import"));
198     m_importLayoutAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_I));
199     connectActionWithButton(m_ui->importButton, m_importLayoutAction);
200     connect(m_importLayoutAction, &QAction::triggered, m_ui->importButton, &QPushButton::showMenu);
201 
202     initImportLayoutSubMenu();
203     m_importLayoutAction->setMenu(m_layoutImportSubMenu);
204     m_ui->importButton->setMenu(m_layoutImportSubMenu);
205 
206     //! Export
207     m_exportLayoutAction = m_layoutMenu->addAction(i18nc("export layout", "&Export"));
208     m_exportLayoutAction->setToolTip(i18n("Export selected layout at your system"));
209     m_exportLayoutAction->setIcon(QIcon::fromTheme("document-export"));
210     m_exportLayoutAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E));
211     connectActionWithButton(m_ui->exportButton, m_exportLayoutAction);
212     connect(m_exportLayoutAction, &QAction::triggered, m_ui->exportButton, &QPushButton::showMenu);
213 
214     initExportLayoutSubMenu();
215     m_exportLayoutAction->setMenu(m_layoutExportSubMenu);
216     m_ui->exportButton->setMenu(m_layoutExportSubMenu);
217 }
218 
initImportLayoutSubMenu()219 void TabLayouts::initImportLayoutSubMenu()
220 {
221     if (!m_layoutImportSubMenu) {
222         m_layoutImportSubMenu = new QMenu(m_layoutMenu);
223         m_layoutImportSubMenu->setMinimumWidth(m_ui->importButton->width() * 2);
224     } else {
225         m_layoutImportSubMenu->clear();
226     }
227 
228     QAction *importLayoutAction = m_layoutImportSubMenu->addAction(i18nc("import layout", "&Import From Local File..."));
229     importLayoutAction->setIcon(QIcon::fromTheme("document-import"));
230     importLayoutAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I));
231     connect(importLayoutAction, &QAction::triggered, this, &TabLayouts::importLayout);
232 
233     QAction *downloadLayoutAction = m_layoutImportSubMenu->addAction(i18nc("download layout", "Import From K&DE Online Store..."));
234     downloadLayoutAction->setIcon(QIcon::fromTheme("get-hot-new-stuff"));
235     downloadLayoutAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_D));
236     connect(downloadLayoutAction, &QAction::triggered, this, &TabLayouts::downloadLayout);
237 }
238 
initExportLayoutSubMenu()239 void TabLayouts::initExportLayoutSubMenu()
240 {
241     if (!m_layoutExportSubMenu) {
242         m_layoutExportSubMenu = new QMenu(m_layoutMenu);
243         m_layoutExportSubMenu->setMinimumWidth(m_ui->exportButton->width() * 2);
244     } else {
245         m_layoutExportSubMenu->clear();
246     }
247 
248     QAction *exportForBackup = m_layoutExportSubMenu->addAction(i18nc("export for backup","&Export For Backup..."));
249     exportForBackup->setIcon(QIcon::fromTheme("document-export"));
250     exportForBackup->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT  + Qt::Key_E));
251     connect(exportForBackup, &QAction::triggered, this, &TabLayouts::exportLayoutForBackup);
252 
253     QAction *exportAsTemplate = m_layoutExportSubMenu->addAction(i18nc("export as template","Export As &Template..."));
254     exportAsTemplate->setIcon(QIcon::fromTheme("document-export"));
255     exportAsTemplate->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT  + Qt::Key_T));
256     connect(exportAsTemplate, &QAction::triggered, this, &TabLayouts::exportLayoutAsTemplate);
257 }
258 
initLayoutTemplatesSubMenu()259 void TabLayouts::initLayoutTemplatesSubMenu()
260 {
261     if (!m_layoutTemplatesSubMenu) {
262         m_layoutTemplatesSubMenu = new QMenu(m_layoutMenu);
263         m_layoutTemplatesSubMenu->setMinimumWidth(m_ui->newButton->width() * 2);
264     } else {
265         m_layoutTemplatesSubMenu->clear();
266     }
267 
268     /*Add Layout Templates for New Action*/
269     Data::LayoutsTable templates = m_corona->templatesManager()->layoutTemplates();
270 
271     bool customtemplateseparatoradded{false};
272 
273     for (int i=0; i<templates.rowCount(); ++i) {
274         if (!customtemplateseparatoradded && !templates[i].isSystemTemplate()) {
275             m_layoutTemplatesSubMenu->addSeparator();
276             customtemplateseparatoradded = true;
277         }
278 
279         QAction *newlayout = m_layoutTemplatesSubMenu->addAction(templates[i].name);
280         newlayout->setIcon(QIcon::fromTheme("document-new"));
281         QString templatename = templates[i].name;
282 
283         connect(newlayout, &QAction::triggered, this, [&, templatename]() {
284             newLayout(templatename);
285         });
286     }
287 
288     if (templates.rowCount() > 0) {
289         QAction *openTemplatesDirectory = m_layoutTemplatesSubMenu->addAction(i18n("Templates..."));
290         openTemplatesDirectory->setToolTip(i18n("Open templates directory"));
291         openTemplatesDirectory->setIcon(QIcon::fromTheme("edit"));
292 
293         connect(openTemplatesDirectory, &QAction::triggered, this, [&]() {
294             KIO::highlightInFileManager({QString(Latte::configPath() + "/latte/templates/Dock.layout.latte")});
295         });
296     }
297 }
298 
corona() const299 Latte::Corona *TabLayouts::corona() const
300 {
301     return m_corona;
302 }
303 
dialog() const304 Settings::Dialog::SettingsDialog *TabLayouts::dialog() const
305 {
306     return m_parentDialog;
307 }
308 
ui() const309 Ui::SettingsDialog *TabLayouts::ui() const
310 {
311     return m_ui;
312 }
313 
layoutsController() const314 Controller::Layouts *TabLayouts::layoutsController() const
315 {
316     return m_layoutsController;
317 }
318 
hasChangedData() const319 bool TabLayouts::hasChangedData() const
320 {
321     return m_layoutsController->hasChangedData();
322 }
323 
inDefaultValues() const324 bool TabLayouts::inDefaultValues() const
325 {
326     return true;
327 }
328 
isViewsDialogVisible() const329 bool TabLayouts::isViewsDialogVisible() const
330 {
331     return m_isViewsDialogVisible;
332 }
333 
reset()334 void TabLayouts::reset()
335 {
336     m_layoutsController->reset();
337 
338     if (m_layoutsController->inMultipleMode()) {
339         m_ui->multipleToolBtn->setChecked(true);
340     } else {
341         m_ui->singleToolBtn->setChecked(true);
342     }
343 }
344 
resetDefaults()345 void TabLayouts::resetDefaults()
346 {
347     //do nothing because there are no defaults
348 }
349 
save()350 void TabLayouts::save()
351 {
352     m_layoutsController->save();
353 }
354 
switchLayout()355 void TabLayouts::switchLayout()
356 {
357     if (!isCurrentTab() || !m_switchLayoutAction->isEnabled()) {
358         return;
359     }
360 
361     Latte::Data::Layout selectedLayoutCurrent = m_layoutsController->selectedLayoutCurrentData();
362     Latte::Data::Layout selectedLayoutOriginal = m_layoutsController->selectedLayoutOriginalData();
363     selectedLayoutOriginal = selectedLayoutOriginal.isEmpty() ? selectedLayoutCurrent : selectedLayoutOriginal;
364 
365     if (m_layoutsController->layoutsAreChanged()) {
366         showInlineMessage(i18nc("settings:not permitted switching layout","You need to <b>apply</b> your changes first to switch layout afterwards..."),
367                           KMessageWidget::Warning);
368         return;
369     }
370 
371     m_layoutsController->setOriginalInMultipleMode(false);
372     m_corona->layoutsManager()->switchToLayout(selectedLayoutOriginal.name, MemoryUsage::SingleLayout);
373 
374     updatePerLayoutButtonsState();
375 }
376 
toggleActivitiesManager()377 void TabLayouts::toggleActivitiesManager()
378 {
379     QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.plasmashell"),
380                                                           QStringLiteral("/PlasmaShell"),
381                                                           QStringLiteral("org.kde.PlasmaShell"),
382                                                           QStringLiteral("toggleActivityManager"));
383 
384     QDBusConnection::sessionBus().call(message, QDBus::NoBlock);
385 }
386 
toggleEnabledLayout()387 void TabLayouts::toggleEnabledLayout()
388 {
389     qDebug() << Q_FUNC_INFO;
390 
391     if (!isCurrentTab() || !m_layoutsController->inMultipleMode()) {
392         return;
393     }
394 
395     m_layoutsController->toggleEnabledForSelected();
396 
397     updatePerLayoutButtonsState();
398 }
399 
updatePerLayoutButtonsState()400 void TabLayouts::updatePerLayoutButtonsState()
401 {
402     //! UI Elements that need to be shown/hidden
403     setTwinProperty(m_switchLayoutAction, TWINVISIBLE, !m_layoutsController->inMultipleMode());
404     setTwinProperty(m_activitiesManagerAction, TWINVISIBLE, m_layoutsController->inMultipleMode());
405     setTwinProperty(m_enabledLayoutAction, TWINVISIBLE, m_layoutsController->inMultipleMode());
406 
407     if (!m_layoutsController->hasSelectedLayout()) {
408         setTwinProperty(m_enabledLayoutAction, TWINENABLED, false);
409         return;
410     }
411 
412     Latte::Data::Layout selectedLayout = m_layoutsController->selectedLayoutCurrentData();
413 
414     //! Switch Button
415     setTwinProperty(m_switchLayoutAction, TWINENABLED, true);
416 
417     //! Enabled Button
418     setTwinProperty(m_enabledLayoutAction, TWINENABLED, true);
419     setTwinProperty(m_enabledLayoutAction, TWINCHECKED, !selectedLayout.activities.isEmpty());
420 
421     //! Layout Read-Only Button
422     setTwinProperty(m_readOnlyLayoutAction, TWINCHECKED, selectedLayout.isLocked);
423 
424     //! Details Button
425     setTwinProperty(m_detailsAction, TWINENABLED, true);
426 }
427 
newLayout(const QString & templateName)428 void TabLayouts::newLayout(const QString &templateName)
429 {
430     qDebug() << Q_FUNC_INFO;
431 
432     if (!isCurrentTab() || !m_newLayoutAction->isEnabled()) {
433         return;
434     }
435 
436     //! retrieve Default layout template
437     Data::Layout tdata = m_corona->templatesManager()->layoutTemplateForName(templateName);
438 
439     if (!tdata.isNull()) {
440         Data::Layout newlayout = m_layoutsController->addLayoutForFile(tdata.id, tdata.name, true);
441 
442         if (newlayout.errors == 0 && newlayout.warnings == 0) {
443             showInlineMessage(i18nc("settings:layout added successfully","Layout <b>%1</b> added successfully...", newlayout.name),
444                               KMessageWidget::Positive);
445         }
446     }
447 }
448 
duplicateLayout()449 void TabLayouts::duplicateLayout()
450 {
451     qDebug() << Q_FUNC_INFO;
452 
453     if (!isCurrentTab() || !m_duplicateLayoutAction->isEnabled()) {
454         return;
455     }
456 
457     m_layoutsController->duplicateSelectedLayout();
458 }
459 
downloadLayout()460 void TabLayouts::downloadLayout()
461 {
462     qDebug() << Q_FUNC_INFO;
463 
464     if (!isCurrentTab()) {
465         return;
466     }
467 
468     KNS3::DownloadDialog dialog(QStringLiteral("latte-layouts.knsrc"), m_parentDialog);
469     dialog.resize(m_parentDialog->downloadWindowSize());
470     dialog.exec();
471 
472     if (!dialog.changedEntries().isEmpty() && !dialog.installedEntries().isEmpty()) {
473         for (const auto &entry : dialog.installedEntries()) {
474             for (const auto &entryFile : entry.installedFiles()) {
475                 Latte::Layouts::Importer::LatteFileVersion version = Latte::Layouts::Importer::fileVersion(entryFile);
476 
477                 if (version == Latte::Layouts::Importer::LayoutVersion2) {
478                     Latte::Data::Layout downloaded = m_layoutsController->addLayoutForFile(entryFile);
479                     showInlineMessage(i18nc("settings:layout downloaded successfully","Layout <b>%1</b> downloaded successfully...", downloaded.name),
480                                       KMessageWidget::Positive);
481                     break;
482                 }
483             }
484         }
485     }
486 
487     m_parentDialog->setDownloadWindowSize(dialog.size());
488 }
489 
removeLayout()490 void TabLayouts::removeLayout()
491 {
492     qDebug() << Q_FUNC_INFO;
493 
494     if (!isCurrentTab() || !m_removeLayoutAction->isEnabled()) {
495         return;
496     }
497 
498     if (!m_layoutsController->hasSelectedLayout()) {
499         return;
500     }
501 
502     Latte::Data::Layout selectedLayout = m_layoutsController->selectedLayoutCurrentData();
503 
504     if (selectedLayout.isActive) {
505         showInlineMessage(i18nc("settings: active layout remove","<b>Active</b> layouts can not be removed..."),
506                           KMessageWidget::Error);
507         return;
508     }
509 
510     if (selectedLayout.isLocked) {
511         showInlineMessage(i18nc("settings: locked layout remove","Locked layouts can not be removed..."),
512                           KMessageWidget::Error);
513         return;
514     }
515 
516     qDebug() << Q_FUNC_INFO;
517 
518     m_layoutsController->removeSelected();
519 }
520 
lockLayout()521 void TabLayouts::lockLayout()
522 {
523     qDebug() << Q_FUNC_INFO;
524 
525     if (!isCurrentTab() || !m_readOnlyLayoutAction->isEnabled()) {
526         return;
527     }
528 
529     m_layoutsController->toggleLockedForSelected();
530 
531     updatePerLayoutButtonsState();
532 }
533 
importLayout()534 void TabLayouts::importLayout()
535 {
536     qDebug() << Q_FUNC_INFO;
537 
538     if (!isCurrentTab() || !m_importLayoutAction->isEnabled()) {
539         return;
540     }
541 
542     QFileDialog *importFileDialog = new QFileDialog(m_parentDialog, i18nc("import layout", "Import Layout"), QDir::homePath(), QStringLiteral("layout.latte"));
543 
544     importFileDialog->setWindowIcon(QIcon::fromTheme("document-import"));
545     importFileDialog->setLabelText(QFileDialog::Accept, i18nc("import layout","Import"));
546     importFileDialog->setFileMode(QFileDialog::AnyFile);
547     importFileDialog->setAcceptMode(QFileDialog::AcceptOpen);
548     importFileDialog->setDefaultSuffix("layout.latte");
549 
550     QStringList filters;
551     filters << QString(i18nc("import latte layout", "Latte Dock Layout file v0.2") + "(*.layout.latte)")
552             << QString(i18nc("import older latte layout", "Latte Dock Layout file v0.1") + "(*.latterc)");
553     importFileDialog->setNameFilters(filters);
554 
555     connect(importFileDialog, &QFileDialog::finished, importFileDialog, &QFileDialog::deleteLater);
556 
557     connect(importFileDialog, &QFileDialog::fileSelected, this, [&](const QString & file) {
558         Latte::Layouts::Importer::LatteFileVersion version = Latte::Layouts::Importer::fileVersion(file);
559         qDebug() << "VERSION :::: " << version;
560 
561         if (version == Latte::Layouts::Importer::LayoutVersion2) {
562             Latte::Data::Layout importedlayout = m_layoutsController->addLayoutForFile(file);
563             showInlineMessage(i18nc("settings:layout imported successfully","Layout <b>%1</b> imported successfully...", importedlayout.name),
564                               KMessageWidget::Positive);
565         } else if (version == Latte::Layouts::Importer::ConfigVersion1) {
566             if (!m_layoutsController->importLayoutsFromV1ConfigFile(file)) {
567                 showInlineMessage(i18nc("settings:deprecated layouts import failed","Import layouts from deprecated version <b>failed</b>..."),
568                                   KMessageWidget::Error,
569                                   true);
570             }
571         }
572     });
573 
574     importFileDialog->open();
575 }
576 
exportLayoutAsTemplate()577 void TabLayouts::exportLayoutAsTemplate()
578 {
579     qDebug() << Q_FUNC_INFO;
580 
581     if (!isCurrentTab() || !m_exportLayoutAction->isEnabled()) {
582         return;
583     }
584 
585     if (!m_layoutsController->hasSelectedLayout()) {
586         return;
587     }
588 
589     Data::Layout o_layout = m_layoutsController->selectedLayoutOriginalData();
590     Data::Layout c_layout = m_layoutsController->selectedLayoutCurrentData();
591 
592     Data::Layout exp_layout = o_layout;
593     exp_layout.name = c_layout.name;
594 
595     CentralLayout *central =  m_layoutsController->centralLayout(c_layout.id);
596 
597     if (central->isActive()) {
598         central->syncToLayoutFile();
599     }
600 
601     Dialog::ExportTemplateDialog *exportDlg = new Dialog::ExportTemplateDialog(m_parentDialog, exp_layout);
602     exportDlg->exec();
603 }
604 
exportLayoutForBackup()605 void TabLayouts::exportLayoutForBackup()
606 {
607     qDebug() << Q_FUNC_INFO;
608 
609     if (!isCurrentTab() || !m_exportLayoutAction->isEnabled()) {
610         return;
611     }
612 
613     if (!m_layoutsController->hasSelectedLayout()) {
614         return;
615     }
616 
617     Latte::Data::Layout selectedLayout = m_layoutsController->selectedLayoutCurrentData();
618 
619     //! Update ALL active original layouts before exporting,
620     m_corona->layoutsManager()->synchronizer()->syncActiveLayoutsToOriginalFiles();
621     m_corona->universalSettings()->syncSettings();
622 
623     QFileDialog *exportFileDialog = new QFileDialog(m_parentDialog, i18n("Export Layout For Backup"), QDir::homePath(), QStringLiteral("layout.latte"));
624 
625     exportFileDialog->setLabelText(QFileDialog::Accept, i18nc("export layout","Export"));
626     exportFileDialog->setFileMode(QFileDialog::AnyFile);
627     exportFileDialog->setAcceptMode(QFileDialog::AcceptSave);
628     exportFileDialog->setDefaultSuffix("layout.latte");
629 
630     QStringList filters;
631     QString filter1(i18nc("export layout", "Latte Dock Layout file v0.2") + "(*.layout.latte)");
632 
633     filters << filter1;
634 
635     exportFileDialog->setNameFilters(filters);
636 
637     connect(exportFileDialog, &QFileDialog::finished, exportFileDialog, &QFileDialog::deleteLater);
638 
639     connect(exportFileDialog, &QFileDialog::fileSelected, this, [ &, selectedLayout](const QString & file) {
640         auto showExportLayoutError = [this](const Latte::Data::Layout &layout) {
641             showInlineMessage(i18nc("settings:layout export fail","Layout <b>%1</b> export <b>failed</b>...", layout.name),
642                               KMessageWidget::Error,
643                               true);
644         };
645 
646         if (QFile::exists(file) && !QFile::remove(file)) {
647             showExportLayoutError(selectedLayout);
648             return;
649         }
650 
651         if (file.endsWith(".layout.latte")) {
652             if (!QFile(selectedLayout.id).copy(file)) {
653                 showExportLayoutError(selectedLayout);
654                 return;
655             }
656 
657             QFileInfo newFileInfo(file);
658 
659             if (newFileInfo.exists() && !newFileInfo.isWritable()) {
660                 QFile(file).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
661             }
662 
663             CentralLayout layoutS(this, file);
664             layoutS.setActivities(QStringList());
665             layoutS.clearLastUsedActivity();
666 
667             QAction *openUrlAction = new QAction(i18n("Open Location..."), this);
668             openUrlAction->setData(file);
669             QList<QAction *> actions;
670             actions << openUrlAction;
671 
672             connect(openUrlAction, &QAction::triggered, this, [&, openUrlAction]() {
673                 QString file = openUrlAction->data().toString();
674 
675                 if (!file.isEmpty()) {
676                     KIO::highlightInFileManager({file});
677                 }
678             });
679 
680             showInlineMessage(i18nc("settings:layout export success","Layout <b>%1</b> export succeeded...", selectedLayout.name),
681                               KMessageWidget::Positive,
682                               false,
683                               actions);
684         } else if (file.endsWith(".latterc")) {
685             auto showExportConfigurationError = [this]() {
686                 showInlineMessage(i18n("Full configuration export <b>failed</b>..."),
687                                   KMessageWidget::Error,
688                                   true);
689             };
690 
691             if (m_corona->layoutsManager()->importer()->exportFullConfiguration(file)) {
692                 QAction *openUrlAction = new QAction(i18n("Open Location..."), this);
693                 openUrlAction->setIcon(QIcon::fromTheme("document-open"));
694                 openUrlAction->setData(file);
695                 QList<QAction *> actions;
696                 actions << openUrlAction;
697 
698                 connect(openUrlAction, &QAction::triggered, this, [&, openUrlAction]() {
699                     QString file = openUrlAction->data().toString();
700 
701                     if (!file.isEmpty()) {
702                         KIO::highlightInFileManager({file});
703                     }
704                 });
705 
706                 showInlineMessage(i18n("Full configuration export succeeded..."),
707                                   KMessageWidget::Positive,
708                                   false,
709                                   actions);
710             } else {
711                 showExportConfigurationError();
712             }
713         }
714     });
715 
716     exportFileDialog->open();
717     exportFileDialog->selectFile(selectedLayout.name);
718 }
719 
showDetailsDialog()720 void TabLayouts::showDetailsDialog()
721 {
722     qDebug() << Q_FUNC_INFO;
723 
724     if (!isCurrentTab() || !m_detailsAction->isEnabled()) {
725         return;
726     }
727 
728     if (!m_layoutsController->hasSelectedLayout()) {
729         return;
730     }
731 
732     Latte::Data::Layout selectedLayout = m_layoutsController->selectedLayoutCurrentData();
733     auto detailsDlg = new Settings::Dialog::DetailsDialog(m_parentDialog, m_layoutsController);
734 
735     detailsDlg->exec();
736 }
737 
showViewsDialog()738 void TabLayouts::showViewsDialog()
739 {
740     qDebug() << Q_FUNC_INFO;
741 
742     if (!isCurrentTab() || !m_viewsAction->isEnabled()) {
743         return;
744     }
745 
746     if (!m_layoutsController->hasSelectedLayout()) {
747         return;
748     }
749 
750     Latte::Data::Layout selectedLayout = m_layoutsController->selectedLayoutCurrentData();
751     auto viewsDlg = new Settings::Dialog::ViewsDialog(m_parentDialog, m_layoutsController);
752 
753     m_isViewsDialogVisible = true;
754     viewsDlg->exec();
755     m_isViewsDialogVisible = false;
756 }
757 
onLayoutFilesDropped(const QStringList & paths)758 void TabLayouts::onLayoutFilesDropped(const QStringList &paths)
759 {
760     QStringList layoutNames;
761 
762     for (int i=0; i<paths.count(); ++i) {
763         if (paths[i].endsWith(".layout.latte")) {
764             Latte::Data::Layout importedlayout = m_layoutsController->addLayoutForFile(paths[i]);
765             layoutNames << importedlayout.name;
766         }
767     }
768 
769     if(layoutNames.count() > 0) {
770         showInlineMessage(i18ncp("settings:layout imported successfully",
771                                  "Layout <b>%2</b> imported successfully...",
772                                  "Layouts <b>%2</b> imported successfully...",
773                                  layoutNames.count(),
774                                  layoutNames.join(", ")),
775                 KMessageWidget::Positive);
776     }
777 }
778 
onRawLayoutDropped(const QString & rawLayout)779 void TabLayouts::onRawLayoutDropped(const QString &rawLayout)
780 {
781     Latte::Data::Layout importedlayout = m_layoutsController->addLayoutByText(rawLayout);
782     showInlineMessage(i18nc("settings:layout imported successfully","Layout <b>%1</b> imported successfully...", importedlayout.name),
783                       KMessageWidget::Positive);
784 }
785 
isCurrentTab() const786 bool TabLayouts::isCurrentTab() const
787 {
788     return (m_parentDialog->currentPage() == Dialog::LayoutPage);
789 }
790 
isHoveringLayoutsTable(const QPoint & pos)791 bool TabLayouts::isHoveringLayoutsTable(const QPoint &pos)
792 {
793     QPoint topLeft = m_ui->layoutsView->mapTo(m_parentDialog, QPoint(0,0));
794     QRect geometry = m_ui->layoutsView->rect();
795     geometry.moveTopLeft(topLeft);
796 
797     return geometry.contains(pos);
798 }
799 
800 
onCurrentPageChanged()801 void TabLayouts::onCurrentPageChanged()
802 {
803     //int page = m_dialog->currentPage();
804     Dialog::ConfigurationPage cPage= m_parentDialog->currentPage();// static_cast<Dialog::ConfigurationPage>(page);
805 
806     if (cPage == Dialog::LayoutPage) {
807         m_layoutMenu->setEnabled(true);
808         m_layoutMenu->menuAction()->setVisible(true);
809 
810     } else {
811         m_layoutMenu->menuAction()->setVisible(false);
812         m_layoutMenu->setEnabled(false);
813     }
814 }
815 
onDragEnterEvent(QDragEnterEvent * event)816 void TabLayouts::onDragEnterEvent(QDragEnterEvent *event)
817 {
818     if (!isHoveringLayoutsTable(event->pos())) {
819         return;
820     }
821 
822     event->acceptProposedAction();
823     m_ui->layoutsView->dragEntered(event);
824 }
825 
onDragLeaveEvent(QDragLeaveEvent * event)826 void TabLayouts::onDragLeaveEvent(QDragLeaveEvent *event)
827 {
828     m_ui->layoutsView->dragLeft();
829 }
830 
onDragMoveEvent(QDragMoveEvent * event)831 void TabLayouts::onDragMoveEvent(QDragMoveEvent *event)
832 {
833     if (!isHoveringLayoutsTable(event->pos())) {
834         event->ignore();
835         m_ui->layoutsView->dragLeft();
836         return;
837     }
838 
839     event->acceptProposedAction();
840 }
841 
onDropEvent(QDropEvent * event)842 void TabLayouts::onDropEvent(QDropEvent *event)
843 {
844     if (!isHoveringLayoutsTable(event->pos())) {
845         event->ignore();
846         m_ui->layoutsView->dragLeft();
847         return;
848     }
849 
850     if (event->mimeData()->hasUrls()) {
851         QList<QUrl> urlList = event->mimeData()->urls();
852 
853         QStringList paths;
854 
855         for (int i = 0; i < qMin(urlList.size(), 20); ++i) {
856             QString layoutPath = urlList[i].path();
857 
858             if (layoutPath.endsWith(".layout.latte")) {
859                 paths << layoutPath;
860             }
861         }
862 
863         if (paths.count() > 0) {
864             onLayoutFilesDropped(paths);
865         }
866 
867         m_ui->layoutsView->dragLeft();
868     } else if (event->mimeData()->hasText()){
869         if(!event->mimeData()->text().isEmpty()){
870             onRawLayoutDropped(event->mimeData()->text());
871         } else if(!event->mimeData()->data("text/plain").isEmpty()) {
872             onRawLayoutDropped(event->mimeData()->data("text/plain"));
873         } else {
874             qDebug() << "Data from drag could not be retrieved!";
875         }
876         m_ui->layoutsView->dragLeft();
877     }
878 }
879 
880 
loadConfig()881 void TabLayouts::loadConfig()
882 {
883     //load settings
884 }
885 
saveConfig()886 void TabLayouts::saveConfig()
887 {
888     //save settings
889 }
890 
891 }
892 }
893 }
894 
895