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