File indexing completed on 2024-05-12 05:13:32

0001 /*
0002    SPDX-FileCopyrightText: 2013-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 #include "themeeditorpage.h"
0007 #include "desktopfilepage.h"
0008 #include "editorpage.h"
0009 #include "previewwidget.h"
0010 #include "themeeditortabwidget.h"
0011 #include "themeeditorwidget.h"
0012 #include "themesession.h"
0013 
0014 #include <KLocalizedString>
0015 #include <KMessageBox>
0016 #include <KZip>
0017 #include <QInputDialog>
0018 #include <QTemporaryDir>
0019 #include <QUrl>
0020 
0021 #include <QDir>
0022 #include <QFileDialog>
0023 #include <QHBoxLayout>
0024 #include <QPointer>
0025 
0026 ThemeEditorPage::ThemeEditorPage(const QString &projectDir, const QString &themeName, QWidget *parent)
0027     : QWidget(parent)
0028     , mThemeSession(new GrantleeThemeEditor::ThemeSession(projectDir, QStringLiteral("headerthemeeditor")))
0029 {
0030     auto lay = new QHBoxLayout(this);
0031     mTabWidget = new GrantleeThemeEditor::ThemeEditorTabWidget;
0032     connect(mTabWidget, &GrantleeThemeEditor::ThemeEditorTabWidget::currentChanged, this, &ThemeEditorPage::slotCurrentWidgetChanged);
0033     lay->addWidget(mTabWidget);
0034     mEditorPage = new EditorPage(EditorPage::MainPage, projectDir);
0035     connect(mEditorPage, &EditorPage::needUpdateViewer, this, &ThemeEditorPage::slotUpdateViewer);
0036     connect(mEditorPage, &EditorPage::changed, this, &ThemeEditorPage::slotChanged);
0037     mTabWidget->addTab(mEditorPage, i18n("Editor (%1)", QStringLiteral("header.html")));
0038 
0039     GrantleeThemeEditor::DesktopFilePage::DesktopFileOptions opt;
0040     opt |= GrantleeThemeEditor::DesktopFilePage::ExtraDisplayVariables;
0041     opt |= GrantleeThemeEditor::DesktopFilePage::SpecifyFileName;
0042 
0043     mDesktopPage = new GrantleeThemeEditor::DesktopFilePage(QStringLiteral("header.html"), opt, this);
0044     mDesktopPage->setDefaultDesktopName(QStringLiteral("header.desktop"));
0045     mDesktopPage->setThemeName(themeName);
0046     mTabWidget->addTab(mDesktopPage, i18n("Desktop File"));
0047 
0048     connect(mDesktopPage,
0049             &GrantleeThemeEditor::DesktopFilePage::mainFileNameChanged,
0050             mEditorPage->preview(),
0051             &GrantleeThemeEditor::PreviewWidget::slotMainFileNameChanged);
0052     connect(mDesktopPage,
0053             &GrantleeThemeEditor::DesktopFilePage::mainFileNameChanged,
0054             mTabWidget,
0055             &GrantleeThemeEditor::ThemeEditorTabWidget::slotMainFileNameChanged);
0056     connect(mDesktopPage, &GrantleeThemeEditor::DesktopFilePage::extraDisplayHeaderChanged, this, &ThemeEditorPage::slotExtraHeaderDisplayChanged);
0057     connect(mDesktopPage, &GrantleeThemeEditor::DesktopFilePage::changed, this, &ThemeEditorPage::slotChanged);
0058     connect(mTabWidget, &GrantleeThemeEditor::ThemeEditorTabWidget::tabCloseRequested, this, &ThemeEditorPage::slotCloseTab);
0059 }
0060 
0061 ThemeEditorPage::~ThemeEditorPage()
0062 {
0063     qDeleteAll(mExtraPage);
0064     mExtraPage.clear();
0065     delete mThemeSession;
0066 }
0067 
0068 void ThemeEditorPage::slotCurrentWidgetChanged(int index)
0069 {
0070     if (index < 0) {
0071         return;
0072     }
0073     auto page = qobject_cast<GrantleeThemeEditor::EditorPage *>(mTabWidget->widget(index));
0074     Q_EMIT canInsertFile(page);
0075 }
0076 
0077 void ThemeEditorPage::updatePreview()
0078 {
0079     mEditorPage->preview()->updateViewer();
0080 }
0081 
0082 void ThemeEditorPage::setPrinting(bool print)
0083 {
0084     mEditorPage->preview()->setPrinting(print);
0085 }
0086 
0087 void ThemeEditorPage::slotExtraHeaderDisplayChanged(const QStringList &extraHeaders)
0088 {
0089     mEditorPage->preview()->slotExtraHeaderDisplayChanged(extraHeaders);
0090 
0091     QStringList result;
0092     result.reserve(extraHeaders.count());
0093     for (QString var : extraHeaders) {
0094         var = QLatin1StringView("header.") + var.remove(QLatin1Char('-'));
0095         result << var;
0096     }
0097 
0098     mEditorPage->editor()->createCompleterList(result);
0099     for (EditorPage *page : std::as_const(mExtraPage)) {
0100         page->editor()->createCompleterList(result);
0101     }
0102 }
0103 
0104 void ThemeEditorPage::slotChanged()
0105 {
0106     setChanged(true);
0107 }
0108 
0109 void ThemeEditorPage::setChanged(bool b)
0110 {
0111     if (mChanged != b) {
0112         mChanged = b;
0113         Q_EMIT changed(b);
0114     }
0115 }
0116 
0117 void ThemeEditorPage::slotUpdateViewer()
0118 {
0119     if (themeWasChanged()) {
0120         saveTheme(false);
0121     }
0122     mEditorPage->preview()->updateViewer();
0123 }
0124 
0125 void ThemeEditorPage::slotCloseTab(int index)
0126 {
0127     mTabWidget->removeTab(index);
0128     setChanged(true);
0129 }
0130 
0131 void ThemeEditorPage::insertFile()
0132 {
0133     QWidget *w = mTabWidget->currentWidget();
0134     if (!w) {
0135         return;
0136     }
0137     auto page = qobject_cast<GrantleeThemeEditor::EditorPage *>(w);
0138     if (page) {
0139         const QString fileName = QFileDialog::getOpenFileName(this);
0140         if (!fileName.isEmpty()) {
0141             page->insertFile(fileName);
0142         }
0143     }
0144 }
0145 
0146 bool ThemeEditorPage::themeWasChanged() const
0147 {
0148     return mChanged;
0149 }
0150 
0151 void ThemeEditorPage::installTheme(const QString &themePath)
0152 {
0153     QDir dir(themePath);
0154     QDir themeDir(themePath + QLatin1Char('/') + mDesktopPage->themeName());
0155     if (themeDir.exists()) {
0156         const int answer = KMessageBox::questionTwoActions(this,
0157                                                            i18n("Theme already exists. Do you want to overwrite it?"),
0158                                                            i18n("Theme already exists"),
0159                                                            KStandardGuiItem::overwrite(),
0160                                                            KStandardGuiItem::cancel());
0161         if (answer == KMessageBox::ButtonCode::SecondaryAction) {
0162             return;
0163         }
0164     } else {
0165         if (!dir.mkdir(mDesktopPage->themeName())) {
0166             KMessageBox::error(this, i18n("Cannot create theme folder."));
0167             return;
0168         }
0169     }
0170     const QString newPath = themePath + QLatin1Char('/') + mDesktopPage->themeName();
0171     mEditorPage->setPageFileName(mDesktopPage->filename());
0172     mEditorPage->installTheme(newPath);
0173     for (EditorPage *page : std::as_const(mExtraPage)) {
0174         page->installTheme(newPath);
0175     }
0176     mDesktopPage->installTheme(newPath);
0177     KMessageBox::information(this, i18n("Theme installed in \"%1\"", themeDir.absolutePath()));
0178 }
0179 
0180 void ThemeEditorPage::uploadTheme()
0181 {
0182 #if 0
0183     // force update for screenshot
0184     mEditorPage->preview()->updateViewer();
0185     QTemporaryDir tmp;
0186     const QString themename = mDesktopPage->themeName();
0187     const QString zipFileName = tmp.path() + QLatin1Char('/') + themename + QLatin1StringView(".zip");
0188     KZip *zip = new KZip(zipFileName);
0189     if (zip->open(QIODevice::WriteOnly)) {
0190         const QString previewFileName = tmp.path() + QLatin1Char('/') + themename + QLatin1StringView("_preview.png");
0191         // qCDebug(HEADERTHEMEEDITOR_LOG)<<" previewFileName"<<previewFileName;
0192         QStringList lst;
0193         lst << previewFileName;
0194         mEditorPage->preview()->createScreenShot(lst);
0195 
0196         const bool fileAdded = zip->addLocalFile(previewFileName, themename + QLatin1Char('/') + QLatin1StringView("theme_preview.png"));
0197         if (!fileAdded) {
0198             KMessageBox::error(this, i18n("We cannot add preview file in zip file"), i18n("Failed to add file."));
0199             delete zip;
0200             return;
0201         }
0202 
0203         createZip(themename, zip);
0204         zip->close();
0205         // qCDebug(HEADERTHEMEEDITOR_LOG)<< "zipFilename"<<zipFileName;
0206 
0207         QPointer<KNS3::UploadDialog> dialog = new KNS3::UploadDialog(QStringLiteral("messageviewer_header_themes.knsrc"), this);
0208         dialog->setUploadFile(QUrl::fromLocalFile(zipFileName));
0209         dialog->setUploadName(themename);
0210         dialog->setPreviewImageFile(0, QUrl::fromLocalFile(previewFileName));
0211         const QString description = mDesktopPage->description();
0212         dialog->setDescription(description.isEmpty() ? i18n("My favorite KMail header") : description);
0213         dialog->exec();
0214         delete dialog;
0215     } else {
0216         qCDebug(HEADERTHEMEEDITOR_LOG) << " We can't open in zip write mode";
0217     }
0218     delete zip;
0219 #endif
0220 }
0221 
0222 void ThemeEditorPage::createZip(const QString &themeName, KZip *zip)
0223 {
0224     mEditorPage->createZip(themeName, zip);
0225 
0226     for (EditorPage *page : std::as_const(mExtraPage)) {
0227         page->createZip(themeName, zip);
0228     }
0229     mDesktopPage->createZip(themeName, zip);
0230 }
0231 
0232 void ThemeEditorPage::addExtraPage()
0233 {
0234     bool ok = false;
0235     QString filename = QInputDialog::getText(this, i18n("Filename of extra page"), i18n("Filename:"), {}, {}, &ok);
0236     if (ok) {
0237         if (!filename.trimmed().isEmpty()) {
0238             if (!filename.endsWith(QLatin1StringView(".html")) && !filename.endsWith(QLatin1StringView(".css"))
0239                 && !filename.endsWith(QLatin1StringView(".js"))) {
0240                 filename += QLatin1StringView(".html");
0241             }
0242             createExtraPage(filename);
0243             mThemeSession->addExtraPage(filename);
0244             setChanged(true);
0245         }
0246     }
0247 }
0248 
0249 EditorPage *ThemeEditorPage::createExtraPage(const QString &filename)
0250 {
0251     auto extraPage = new EditorPage(EditorPage::ExtraPage, QString());
0252     connect(extraPage, &EditorPage::changed, this, &ThemeEditorPage::slotChanged);
0253     extraPage->setPageFileName(filename);
0254     mTabWidget->addTab(extraPage, filename);
0255     mTabWidget->setCurrentWidget(extraPage);
0256     mExtraPage.append(extraPage);
0257     return extraPage;
0258 }
0259 
0260 void ThemeEditorPage::storeTheme(const QString &directory)
0261 {
0262     const QString themeDirectory = directory.isEmpty() ? projectDirectory() : directory;
0263     // set default page filename before saving
0264     mEditorPage->setPageFileName(mDesktopPage->filename());
0265     mEditorPage->saveTheme(themeDirectory);
0266 
0267     for (EditorPage *page : std::as_const(mExtraPage)) {
0268         page->saveTheme(themeDirectory);
0269     }
0270     mDesktopPage->saveTheme(themeDirectory);
0271     mThemeSession->setMainPageFileName(mDesktopPage->filename());
0272     mThemeSession->writeSession(directory);
0273     if (directory.isEmpty()) {
0274         setChanged(false);
0275     }
0276 }
0277 
0278 bool ThemeEditorPage::saveTheme(bool withConfirmation)
0279 {
0280     if (themeWasChanged()) {
0281         if (withConfirmation) {
0282             const int result = KMessageBox::questionTwoActionsCancel(this,
0283                                                                      i18n("Do you want to save current project?"),
0284                                                                      i18n("Save current project"),
0285                                                                      KStandardGuiItem::save(),
0286                                                                      KStandardGuiItem::discard());
0287             if (result == KMessageBox::ButtonCode::PrimaryAction) {
0288                 storeTheme();
0289             } else if (result == KMessageBox::Cancel) {
0290                 return false;
0291             }
0292         } else {
0293             storeTheme();
0294         }
0295     }
0296     setChanged(false);
0297     return true;
0298 }
0299 
0300 void ThemeEditorPage::loadTheme(const QString &filename)
0301 {
0302     if (mThemeSession->loadSession(filename)) {
0303         mDesktopPage->loadTheme(mThemeSession->projectDirectory());
0304         mEditorPage->loadTheme(mThemeSession->projectDirectory() + QLatin1Char('/') + mThemeSession->mainPageFileName());
0305         mEditorPage->preview()->setThemePath(mThemeSession->projectDirectory(), mThemeSession->mainPageFileName());
0306 
0307         const QStringList lstExtraPages = mThemeSession->extraPages();
0308         for (const QString &page : lstExtraPages) {
0309             EditorPage *extraPage = createExtraPage(page);
0310             extraPage->loadTheme(mThemeSession->projectDirectory() + QLatin1Char('/') + page);
0311         }
0312         mTabWidget->setCurrentIndex(0);
0313         setChanged(false);
0314     }
0315 }
0316 
0317 void ThemeEditorPage::reloadConfig()
0318 {
0319     mEditorPage->preview()->loadConfig();
0320 }
0321 
0322 QString ThemeEditorPage::projectDirectory() const
0323 {
0324     return mThemeSession->projectDirectory();
0325 }
0326 
0327 void ThemeEditorPage::saveThemeAs(const QString &directory)
0328 {
0329     storeTheme(directory);
0330 }
0331 
0332 #include "moc_themeeditorpage.cpp"