File indexing completed on 2024-12-08 08:00:07
0001 /* 0002 SPDX-FileCopyrightText: 2021 Michail Vourlakos <mvourlakos@gmail.com> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "viewshandler.h" 0007 0008 // local 0009 #include "ui_viewsdialog.h" 0010 #include "viewscontroller.h" 0011 #include "viewsdialog.h" 0012 #include "viewstableview.h" 0013 #include "../exporttemplatedialog/exporttemplatedialog.h" 0014 #include "../settingsdialog/layoutscontroller.h" 0015 #include "../settingsdialog/layoutsmodel.h" 0016 #include "../settingsdialog/delegates/layoutcmbitemdelegate.h" 0017 #include "../../data/layoutstable.h" 0018 #include "../../data/genericbasictable.h" 0019 #include "../../data/viewstable.h" 0020 #include "../../lattecorona.h" 0021 #include "../../layout/abstractlayout.h" 0022 #include "../../layout/centrallayout.h" 0023 #include "../../layouts/manager.h" 0024 #include "../../layouts/storage.h" 0025 #include "../../layouts/synchronizer.h" 0026 #include "../../templates/templatesmanager.h" 0027 #include "../../tools/commontools.h" 0028 0029 // Qt 0030 #include <QFileDialog> 0031 0032 // KDE 0033 #include <KLocalizedString> 0034 #include <KStandardGuiItem> 0035 #include <KIO/OpenFileManagerWindowJob> 0036 0037 namespace Latte { 0038 namespace Settings { 0039 namespace Handler { 0040 0041 ViewsHandler::ViewsHandler(Dialog::ViewsDialog *dialog) 0042 : Generic(dialog), 0043 m_dialog(dialog), 0044 m_ui(m_dialog->ui()) 0045 { 0046 m_viewsController = new Settings::Controller::Views(this); 0047 0048 init(); 0049 } 0050 0051 ViewsHandler::~ViewsHandler() 0052 { 0053 } 0054 0055 void ViewsHandler::init() 0056 { 0057 //! Layouts 0058 m_layoutsProxyModel = new QSortFilterProxyModel(this); 0059 m_layoutsProxyModel->setSourceModel(m_dialog->layoutsController()->baseModel()); 0060 m_layoutsProxyModel->setSortRole(Model::Layouts::SORTINGROLE); 0061 m_layoutsProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); 0062 m_layoutsProxyModel->sort(Model::Layouts::NAMECOLUMN, Qt::AscendingOrder); 0063 0064 m_ui->layoutsCmb->setModel(m_layoutsProxyModel); 0065 m_ui->layoutsCmb->setModelColumn(Model::Layouts::NAMECOLUMN); 0066 m_ui->layoutsCmb->setItemDelegate(new Settings::Layout::Delegate::LayoutCmbItemDelegate(this)); 0067 0068 //! New Button 0069 m_newViewAction = new QAction(i18nc("new view", "&New"), this); 0070 m_newViewAction->setToolTip(i18n("New dock or panel")); 0071 m_newViewAction->setIcon(QIcon::fromTheme("add")); 0072 m_newViewAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_N)); 0073 connectActionWithButton(m_ui->newBtn, m_newViewAction); 0074 connect(m_newViewAction, &QAction::triggered, m_ui->newBtn, &QPushButton::showMenu); 0075 0076 initViewTemplatesSubMenu(); 0077 m_newViewAction->setMenu(m_viewTemplatesSubMenu); 0078 m_ui->newBtn->setMenu(m_viewTemplatesSubMenu); 0079 0080 connect(corona()->templatesManager(), &Latte::Templates::Manager::viewTemplatesChanged, this, &ViewsHandler::initViewTemplatesSubMenu); 0081 0082 //! Duplicate Button 0083 m_duplicateViewAction = new QAction(i18nc("duplicate dock or panel", "&Duplicate"), this); 0084 m_duplicateViewAction->setToolTip(i18n("Duplicate selected dock or panel")); 0085 m_duplicateViewAction->setIcon(QIcon::fromTheme("edit-copy")); 0086 m_duplicateViewAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_D)); 0087 connectActionWithButton(m_ui->duplicateBtn, m_duplicateViewAction); 0088 connect(m_duplicateViewAction, &QAction::triggered, m_viewsController, &Controller::Views::duplicateSelectedViews); 0089 0090 //! Remove Button 0091 m_removeViewAction = new QAction(i18nc("remove layout", "Remove"), m_ui->removeBtn); 0092 m_removeViewAction->setToolTip(i18n("Remove selected view")); 0093 m_removeViewAction->setIcon(QIcon::fromTheme("delete")); 0094 m_removeViewAction->setShortcut(QKeySequence(Qt::Key_Delete)); 0095 connectActionWithButton(m_ui->removeBtn, m_removeViewAction); 0096 connect(m_removeViewAction, &QAction::triggered, this, &ViewsHandler::removeSelectedViews); 0097 m_ui->removeBtn->addAction(m_removeViewAction); //this is needed in order to be triggered properly 0098 0099 //! Import 0100 m_importViewAction =new QAction(i18nc("import dock/panel","&Import...")); 0101 m_duplicateViewAction->setToolTip(i18n("Import dock or panel from local file")); 0102 m_importViewAction->setIcon(QIcon::fromTheme("document-import")); 0103 m_importViewAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I)); 0104 connectActionWithButton(m_ui->importBtn, m_importViewAction); 0105 connect(m_importViewAction, &QAction::triggered, this, &ViewsHandler::importView); 0106 0107 //! Export 0108 m_exportViewAction = new QAction(i18nc("export layout", "&Export"), this); 0109 m_exportViewAction->setToolTip(i18n("Export selected dock or panel at your system")); 0110 m_exportViewAction->setIcon(QIcon::fromTheme("document-export")); 0111 m_exportViewAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E)); 0112 connectActionWithButton(m_ui->exportBtn, m_exportViewAction); 0113 connect(m_exportViewAction, &QAction::triggered, m_ui->exportBtn, &QPushButton::showMenu); 0114 0115 initViewExportSubMenu(); 0116 m_exportViewAction->setMenu(m_viewExportSubMenu); 0117 m_ui->exportBtn->setMenu(m_viewExportSubMenu); 0118 0119 //! signals 0120 connect(this, &ViewsHandler::currentLayoutChanged, this, &ViewsHandler::reload); 0121 0122 reload(); 0123 m_lastConfirmedLayoutIndex =m_ui->layoutsCmb->currentIndex(); 0124 0125 emit currentLayoutChanged(); 0126 0127 //! connect layout combobox after the selected layout has been loaded 0128 connect(m_ui->layoutsCmb, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ViewsHandler::onCurrentLayoutIndexChanged); 0129 0130 //! 0131 connect(m_viewsController, &Settings::Controller::Views::dataChanged, this, &ViewsHandler::dataChanged); 0132 0133 connect(m_ui->viewsTable, &View::ViewsTableView::selectionsChanged, this, &ViewsHandler::onSelectionChanged); 0134 0135 onSelectionChanged(); 0136 } 0137 0138 void ViewsHandler::initViewTemplatesSubMenu() 0139 { 0140 if (!m_viewTemplatesSubMenu) { 0141 m_viewTemplatesSubMenu = new QMenu(m_ui->newBtn); 0142 m_viewTemplatesSubMenu->setMinimumWidth(m_ui->newBtn->width() * 2); 0143 } else { 0144 m_viewTemplatesSubMenu->clear(); 0145 } 0146 0147 /*Add View Templates for New Action*/ 0148 Data::GenericBasicTable templates = corona()->templatesManager()->viewTemplates(); 0149 0150 bool customtemplateseparatoradded{false}; 0151 0152 for (int i=0; i<templates.rowCount(); ++i) { 0153 if (!customtemplateseparatoradded && templates[i].id.startsWith(QDir::homePath())) { 0154 m_viewTemplatesSubMenu->addSeparator(); 0155 customtemplateseparatoradded = true; 0156 } 0157 0158 QAction *newview = m_viewTemplatesSubMenu->addAction(templates[i].name); 0159 newview->setIcon(QIcon::fromTheme("document-new")); 0160 0161 Data::Generic templateData = templates[i]; 0162 0163 connect(newview, &QAction::triggered, this, [&, templateData]() { 0164 newView(templateData); 0165 }); 0166 } 0167 0168 if (templates.rowCount() > 0) { 0169 QAction *openTemplatesDirectory = m_viewTemplatesSubMenu->addAction(i18n("Templates...")); 0170 openTemplatesDirectory->setToolTip(i18n("Open templates directory")); 0171 openTemplatesDirectory->setIcon(QIcon::fromTheme("edit")); 0172 0173 connect(openTemplatesDirectory, &QAction::triggered, this, [&]() { 0174 KIO::highlightInFileManager({QString(Latte::configPath() + "/latte/templates/Dock.view.latte")}); 0175 }); 0176 } 0177 } 0178 0179 void ViewsHandler::initViewExportSubMenu() 0180 { 0181 if (!m_viewExportSubMenu) { 0182 m_viewExportSubMenu = new QMenu(m_ui->exportBtn); 0183 m_viewExportSubMenu->setMinimumWidth(m_ui->exportBtn->width() * 2); 0184 } else { 0185 m_viewExportSubMenu->clear(); 0186 } 0187 0188 QAction *exportforbackup = m_viewExportSubMenu->addAction(i18nc("export for backup","&Export For Backup...")); 0189 exportforbackup->setIcon(QIcon::fromTheme("document-export")); 0190 exportforbackup->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_E)); 0191 connect(exportforbackup, &QAction::triggered, this, &ViewsHandler::exportViewForBackup); 0192 0193 QAction *exportastemplate = m_viewExportSubMenu->addAction(i18nc("export as template","Export As &Template...")); 0194 exportastemplate->setIcon(QIcon::fromTheme("document-export")); 0195 exportastemplate->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_T)); 0196 connect(exportastemplate, &QAction::triggered, this, &ViewsHandler::exportViewAsTemplate); 0197 } 0198 0199 void ViewsHandler::reload() 0200 { 0201 o_data = m_dialog->layoutsController()->selectedLayoutCurrentData(); 0202 o_data.views = m_dialog->layoutsController()->selectedLayoutViews(); 0203 0204 Latte::Data::LayoutIcon icon = m_dialog->layoutsController()->selectedLayoutIcon(); 0205 0206 m_ui->layoutsCmb->setCurrentText(o_data.name); 0207 m_ui->layoutsCmb->setLayoutIcon(icon); 0208 0209 loadLayout(o_data); 0210 } 0211 0212 Latte::Corona *ViewsHandler::corona() const 0213 { 0214 return m_dialog->corona(); 0215 } 0216 0217 Ui::ViewsDialog *ViewsHandler::ui() const 0218 { 0219 return m_ui; 0220 } 0221 0222 Settings::Controller::Layouts *ViewsHandler::layoutsController() const 0223 { 0224 return m_dialog->layoutsController(); 0225 } 0226 0227 void ViewsHandler::loadLayout(const Latte::Data::Layout &data) 0228 { 0229 updateWindowTitle(); 0230 } 0231 0232 Latte::Data::Layout ViewsHandler::currentData() const 0233 { 0234 return o_data; 0235 } 0236 0237 Latte::Data::Layout ViewsHandler::originalData() const 0238 { 0239 return m_dialog->layoutsController()->selectedLayoutOriginalData(); 0240 } 0241 0242 bool ViewsHandler::hasChangedData() const 0243 { 0244 return m_viewsController->hasChangedData(); 0245 } 0246 0247 bool ViewsHandler::inDefaultValues() const 0248 { 0249 //nothing special 0250 return true; 0251 } 0252 0253 bool ViewsHandler::isSelectedLayoutOriginal() const 0254 { 0255 return m_dialog->layoutsController()->isSelectedLayoutOriginal(); 0256 } 0257 0258 void ViewsHandler::reset() 0259 { 0260 m_viewsController->reset(); 0261 } 0262 0263 void ViewsHandler::resetDefaults() 0264 { 0265 //do nothing 0266 } 0267 0268 void ViewsHandler::save() 0269 { 0270 int viewsforremoval = m_viewsController->viewsForRemovalCount(); 0271 0272 if (viewsforremoval <=0 || removalConfirmation(viewsforremoval) == KMessageBox::Yes) { 0273 m_viewsController->save(); 0274 } 0275 } 0276 0277 QString ViewsHandler::storedView(const QString &viewId) 0278 { 0279 Latte::Data::View viewdata = m_viewsController->currentData(viewId); 0280 0281 if (!viewdata.isValid()) { 0282 return QString(); 0283 } 0284 0285 if (viewdata.isCreated()) { 0286 CentralLayout *central = m_dialog->layoutsController()->centralLayout(currentData().id); 0287 return central->storedView(viewdata.id.toInt()); 0288 } else if (viewdata.hasViewTemplateOrigin() || viewdata.hasLayoutOrigin()) { 0289 return viewdata.originFile(); 0290 } 0291 0292 return QString(); 0293 } 0294 0295 void ViewsHandler::newView(const Data::Generic &templateData) 0296 { 0297 Data::ViewsTable views = Latte::Layouts::Storage::self()->views(templateData.id); 0298 0299 if (views.rowCount() > 0) { 0300 Data::View viewfromtemplate = views[0]; 0301 viewfromtemplate.setState(Data::View::OriginFromViewTemplate, templateData.id); 0302 viewfromtemplate.name = templateData.name; 0303 Data::View newview = m_viewsController->appendViewFromViewTemplate(viewfromtemplate); 0304 0305 showInlineMessage(i18nc("settings:dock/panel added successfully","<b>%1</b> added successfully...", newview.name), 0306 KMessageWidget::Positive); 0307 } 0308 } 0309 0310 void ViewsHandler::removeSelectedViews() 0311 { 0312 qDebug() << Q_FUNC_INFO; 0313 0314 if (!m_removeViewAction->isEnabled() || !m_viewsController->hasSelectedView()) { 0315 return; 0316 } 0317 0318 m_viewsController->removeSelectedViews(); 0319 } 0320 0321 void ViewsHandler::exportViewForBackup() 0322 { 0323 if (!m_viewsController->hasSelectedView()) { 0324 return; 0325 } 0326 0327 if (m_viewsController->selectedViewsCount() > 1) { 0328 showInlineMessage(i18n("<b>Export</b> functionality is supported only for one dock or panel each time."), 0329 KMessageWidget::Warning, 0330 false); 0331 return; 0332 } 0333 0334 Data::ViewsTable views = m_viewsController->selectedViewsCurrentData(); 0335 0336 if (views.rowCount() != 1) { 0337 return; 0338 } 0339 0340 QString temporiginfile = storedView(views[0].id); 0341 0342 QFileDialog *exportFileDialog = new QFileDialog(m_dialog, i18n("Export Dock/Panel For Backup"), QDir::homePath(), QStringLiteral("view.latte")); 0343 0344 exportFileDialog->setLabelText(QFileDialog::Accept, i18nc("export view","Export")); 0345 exportFileDialog->setFileMode(QFileDialog::AnyFile); 0346 exportFileDialog->setAcceptMode(QFileDialog::AcceptSave); 0347 exportFileDialog->setDefaultSuffix("view.latte"); 0348 0349 QStringList filters; 0350 QString filter1(i18nc("export view", "Latte Dock/Panel file v0.2") + "(*.view.latte)"); 0351 0352 filters << filter1; 0353 0354 exportFileDialog->setNameFilters(filters); 0355 0356 connect(exportFileDialog, &QFileDialog::finished, exportFileDialog, &QFileDialog::deleteLater); 0357 0358 connect(exportFileDialog, &QFileDialog::fileSelected, this, [&, temporiginfile](const QString & file) { 0359 auto showExportViewError = [this](const QString &destinationfile) { 0360 showInlineMessage(i18nc("settings:view export fail","Export in file <b>%1</b> <b>failed</b>...", QFileInfo(destinationfile).fileName()), 0361 KMessageWidget::Error, 0362 true); 0363 }; 0364 0365 if (QFile::exists(file) && !QFile::remove(file)) { 0366 showExportViewError(file); 0367 return; 0368 } 0369 0370 if (file.endsWith(".view.latte")) { 0371 if (!QFile(temporiginfile).copy(file)) { 0372 showExportViewError(file); 0373 return; 0374 } 0375 0376 QAction *openUrlAction = new QAction(i18n("Open Location..."), this); 0377 openUrlAction->setData(file); 0378 QList<QAction *> actions; 0379 actions << openUrlAction; 0380 0381 connect(openUrlAction, &QAction::triggered, this, [&, openUrlAction]() { 0382 QString file = openUrlAction->data().toString(); 0383 0384 if (!file.isEmpty()) { 0385 KIO::highlightInFileManager({file}); 0386 } 0387 }); 0388 0389 showInlineMessage(i18nc("settings:view export success","Export in file <b>%1</b> succeeded...", QFileInfo(file).fileName()), 0390 KMessageWidget::Positive, 0391 false, 0392 actions); 0393 } 0394 }); 0395 0396 exportFileDialog->open(); 0397 exportFileDialog->selectFile(views[0].name); 0398 } 0399 0400 void ViewsHandler::exportViewAsTemplate() 0401 { 0402 if (!m_viewsController->hasSelectedView()) { 0403 return; 0404 } 0405 0406 if (m_viewsController->selectedViewsCount() > 1) { 0407 showInlineMessage(i18n("<b>Export</b> functionality is supported only for one dock or panel each time."), 0408 KMessageWidget::Warning, 0409 false); 0410 return; 0411 } 0412 0413 Data::ViewsTable views = m_viewsController->selectedViewsCurrentData(); 0414 0415 if (views.rowCount() != 1) { 0416 return; 0417 } 0418 0419 Data::View exportview = views[0]; 0420 exportview.id = storedView(views[0].id); 0421 exportview.setState(Data::View::OriginFromLayout, exportview.id, currentData().name, views[0].id); 0422 0423 Dialog::ExportTemplateDialog *exportdlg = new Dialog::ExportTemplateDialog(m_dialog, exportview); 0424 exportdlg->exec(); 0425 } 0426 0427 void ViewsHandler::importView() 0428 { 0429 qDebug() << Q_FUNC_INFO; 0430 0431 QFileDialog *importFileDialog = new QFileDialog(m_dialog, i18nc("import dock/panel", "Import Dock/Panel"), QDir::homePath(), QStringLiteral("view.latte")); 0432 0433 importFileDialog->setWindowIcon(QIcon::fromTheme("document-import")); 0434 importFileDialog->setLabelText(QFileDialog::Accept, i18n("Import")); 0435 importFileDialog->setFileMode(QFileDialog::AnyFile); 0436 importFileDialog->setAcceptMode(QFileDialog::AcceptOpen); 0437 importFileDialog->setDefaultSuffix("view.latte"); 0438 0439 QStringList filters; 0440 filters << QString(i18nc("import dock panel", "Latte Dock or Panel file v0.2") + "(*.view.latte)"); 0441 importFileDialog->setNameFilters(filters); 0442 0443 connect(importFileDialog, &QFileDialog::finished, importFileDialog, &QFileDialog::deleteLater); 0444 0445 connect(importFileDialog, &QFileDialog::fileSelected, this, [&](const QString & file) { 0446 Data::Generic templatedata; 0447 templatedata.id = file; 0448 templatedata.name = QFileInfo(file).fileName(); 0449 templatedata.name = templatedata.name.remove(".view.latte"); 0450 newView(templatedata); 0451 }); 0452 0453 importFileDialog->open(); 0454 } 0455 0456 void ViewsHandler::onCurrentLayoutIndexChanged(int row) 0457 { 0458 bool switchtonewlayout{false}; 0459 0460 if (m_lastConfirmedLayoutIndex != row) { 0461 if (hasChangedData()) { //new layout was chosen but there are changes 0462 KMessageBox::ButtonCode result = saveChangesConfirmation(); 0463 0464 if (result == KMessageBox::Yes) { 0465 int removalviews = m_viewsController->viewsForRemovalCount(); 0466 KMessageBox::ButtonCode removalresponse = removalConfirmation(removalviews); 0467 0468 if (removalresponse == KMessageBox::Yes) { 0469 switchtonewlayout = true; 0470 m_lastConfirmedLayoutIndex = row; 0471 m_viewsController->save(); 0472 } else { 0473 //do nothing 0474 } 0475 } else if (result == KMessageBox::No) { 0476 switchtonewlayout = true; 0477 m_lastConfirmedLayoutIndex = row; 0478 } else if (result == KMessageBox::Cancel) { 0479 //do nothing 0480 } 0481 } else { //new layout was chosen and there are no changes 0482 switchtonewlayout = true; 0483 m_lastConfirmedLayoutIndex = row; 0484 } 0485 } 0486 0487 if (switchtonewlayout) { 0488 m_dialog->deleteInlineMessages(); 0489 QString layoutId = m_layoutsProxyModel->data(m_layoutsProxyModel->index(row, Model::Layouts::IDCOLUMN), Qt::UserRole).toString(); 0490 m_dialog->layoutsController()->selectRow(layoutId); 0491 reload(); 0492 emit currentLayoutChanged(); 0493 } else { 0494 //! reset combobox index 0495 m_ui->layoutsCmb->setCurrentText(o_data.name); 0496 } 0497 } 0498 0499 void ViewsHandler::onSelectionChanged() 0500 { 0501 bool hasselected = m_viewsController->hasSelectedView(); 0502 0503 setTwinProperty(m_duplicateViewAction, TWINENABLED, hasselected); 0504 setTwinProperty(m_removeViewAction, TWINENABLED, hasselected); 0505 setTwinProperty(m_exportViewAction, TWINENABLED, hasselected); 0506 m_viewExportSubMenu->setEnabled(hasselected); 0507 } 0508 0509 void ViewsHandler::updateWindowTitle() 0510 { 0511 m_dialog->setWindowTitle(i18nc("<layout name> Docks/Panels", 0512 "%1 Docks/Panels", 0513 m_ui->layoutsCmb->currentText())); 0514 } 0515 0516 KMessageBox::ButtonCode ViewsHandler::removalConfirmation(const int &viewsCount) 0517 { 0518 if (viewsCount<=0) { 0519 return KMessageBox::No; 0520 } 0521 0522 if (hasChangedData() && viewsCount>0) { 0523 return KMessageBox::warningYesNo(m_dialog, 0524 i18np("You are going to <b>remove 1</b> dock or panel completely from your layout.<br/>Would you like to continue?", 0525 "You are going to <b>remove %1</b> docks and panels completely from your layout.<br/>Would you like to continue?", 0526 viewsCount), 0527 i18n("Approve Removal")); 0528 } 0529 0530 return KMessageBox::No; 0531 } 0532 0533 KMessageBox::ButtonCode ViewsHandler::saveChangesConfirmation() 0534 { 0535 if (hasChangedData()) { 0536 QString layoutName = o_data.name; 0537 QString saveChangesText = i18n("The settings of <b>%1</b> layout have changed.<br/>Do you want to apply the changes <b>now</b> or discard them?", layoutName); 0538 0539 return m_dialog->saveChangesConfirmation(saveChangesText); 0540 } 0541 0542 return KMessageBox::Cancel; 0543 } 0544 0545 } 0546 } 0547 }