File indexing completed on 2024-05-12 05:50:08

0001 /*
0002     SPDX-FileCopyrightText: 2022 Jiří Wolker <woljiri@gmail.com>
0003     SPDX-FileCopyrightText: 2022 Eugene Popov <popov895@ukr.net>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "welcomeview.h"
0009 
0010 #include "mainwindow.h"
0011 #include "recentfilesmodel.h"
0012 
0013 #include <KAboutData>
0014 #include <KConfigGroup>
0015 #include <KIO/OpenFileManagerWindowJob>
0016 #include <KIconLoader>
0017 #include <KRecentFilesMenu>
0018 #include <KSharedConfig>
0019 
0020 #include <QClipboard>
0021 #include <QDesktopServices>
0022 #include <QGraphicsOpacityEffect>
0023 #include <QLabel>
0024 #include <QMenu>
0025 #include <QTimer>
0026 
0027 class Placeholder : public QLabel
0028 {
0029 public:
0030     explicit Placeholder(QWidget *parent = nullptr)
0031         : QLabel(parent)
0032     {
0033         setAlignment(Qt::AlignCenter);
0034         setMargin(20);
0035         setWordWrap(true);
0036         // Match opacity of QML placeholder label component
0037         QGraphicsOpacityEffect *opacityEffect = new QGraphicsOpacityEffect;
0038         opacityEffect->setOpacity(0.5);
0039         setGraphicsEffect(opacityEffect);
0040     }
0041 };
0042 
0043 WelcomeView::WelcomeView(MainWindow *mainWindow, QWidget *parent)
0044     : QScrollArea(parent)
0045     , m_mainWindow(mainWindow)
0046 {
0047     setupUi(this);
0048 
0049     const KAboutData aboutData = KAboutData::applicationData();
0050     labelTitle->setText(i18n("Welcome to %1", aboutData.displayName()));
0051     labelDescription->setText(aboutData.shortDescription());
0052     labelIcon->setPixmap(aboutData.programLogo().value<QIcon>().pixmap(KIconLoader::SizeEnormous));
0053 
0054     m_placeholderRecentFiles = new Placeholder;
0055     m_placeholderRecentFiles->setText(i18n("No recent files"));
0056 
0057     QVBoxLayout *layoutPlaceholderRecentFiles = new QVBoxLayout;
0058     layoutPlaceholderRecentFiles->addWidget(m_placeholderRecentFiles);
0059     listViewRecentFiles->setLayout(layoutPlaceholderRecentFiles);
0060 
0061     m_recentFilesModel = new RecentFilesModel(this);
0062     connect(m_recentFilesModel, &RecentFilesModel::modelReset, this, [this]() {
0063         const bool noRecentFiles = m_recentFilesModel->rowCount() == 0;
0064         buttonClearRecentFiles->setDisabled(noRecentFiles);
0065         m_placeholderRecentFiles->setVisible(noRecentFiles);
0066     });
0067 
0068     KRecentFilesMenu *recentFilesMenu = m_mainWindow->recentFilesMenu();
0069     m_recentFilesModel->refresh(recentFilesMenu->recentFiles());
0070     connect(recentFilesMenu, &KRecentFilesMenu::recentFilesChanged, this, [this, recentFilesMenu]() {
0071         m_recentFilesModel->refresh(recentFilesMenu->recentFiles());
0072     });
0073 
0074     listViewRecentFiles->setModel(m_recentFilesModel);
0075     connect(listViewRecentFiles, &QListView::customContextMenuRequested, this, &WelcomeView::onRecentFilesContextMenuRequested);
0076     connect(listViewRecentFiles, &QListView::activated, this, [this](const QModelIndex &index) {
0077         if (index.isValid()) {
0078             const QUrl url = m_recentFilesModel->url(index);
0079             Q_ASSERT(url.isValid());
0080             m_mainWindow->openUrl(url);
0081         }
0082     });
0083 
0084     connect(buttonNewArchive, SIGNAL(clicked()), m_mainWindow, SLOT(newArchive()));
0085     connect(buttonOpenArchive, SIGNAL(clicked()), m_mainWindow, SLOT(openArchive()));
0086     connect(buttonClearRecentFiles, &QPushButton::clicked, recentFilesMenu, &KRecentFilesMenu::clearRecentFiles);
0087 
0088     connect(labelHomepage, qOverload<>(&KUrlLabel::leftClickedUrl), this, [aboutData]() {
0089         QDesktopServices::openUrl(QUrl(aboutData.homepage()));
0090     });
0091     connect(labelHandbook, qOverload<>(&KUrlLabel::leftClickedUrl), m_mainWindow, &MainWindow::appHelpActivated);
0092     connect(buttonClose, &QPushButton::clicked, m_mainWindow, &MainWindow::hideWelcomeScreen);
0093 
0094     static const char showOnStartupKey[] = "ShowWelcomeScreenOnStartup";
0095     KConfigGroup configGroup = KSharedConfig::openConfig()->group(QStringLiteral("General"));
0096     checkBoxShowOnStartup->setChecked(configGroup.readEntry(showOnStartupKey, true));
0097     connect(checkBoxShowOnStartup, &QCheckBox::toggled, this, [configGroup](bool checked) mutable {
0098         configGroup.writeEntry(showOnStartupKey, checked);
0099     });
0100 
0101     updateFonts();
0102     updateButtons();
0103 }
0104 
0105 bool WelcomeView::event(QEvent *event)
0106 {
0107     switch (event->type()) {
0108     case QEvent::FontChange:
0109         updateFonts();
0110         updateButtons();
0111         break;
0112     case QEvent::Resize:
0113         if (updateLayout()) {
0114             return true;
0115         }
0116         break;
0117     default:
0118         break;
0119     }
0120 
0121     return QScrollArea::event(event);
0122 }
0123 
0124 void WelcomeView::resizeEvent(QResizeEvent *event)
0125 {
0126     QScrollArea::resizeEvent(event);
0127 
0128     updateLayout();
0129 }
0130 
0131 void WelcomeView::onRecentFilesContextMenuRequested(const QPoint &pos)
0132 {
0133     const QModelIndex index = listViewRecentFiles->indexAt(pos);
0134     if (!index.isValid()) {
0135         return;
0136     }
0137 
0138     const QUrl url = m_recentFilesModel->url(index);
0139     Q_ASSERT(url.isValid());
0140 
0141     QMenu contextMenu;
0142 
0143     QAction *action = contextMenu.addAction(i18n("Copy &Location"));
0144     action->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy-path")));
0145     connect(action, &QAction::triggered, this, [url]() {
0146         qApp->clipboard()->setText(url.toString(QUrl::PreferLocalFile));
0147     });
0148 
0149     action = contextMenu.addAction(i18n("&Open Containing Folder"));
0150     action->setEnabled(url.isLocalFile());
0151     action->setIcon(QIcon::fromTheme(QStringLiteral("document-open-folder")));
0152     connect(action, &QAction::triggered, this, [url]() {
0153         KIO::highlightInFileManager({url});
0154     });
0155 
0156     action = contextMenu.addAction(i18n("&Remove"));
0157     action->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
0158     connect(action, &QAction::triggered, this, [this, url]() {
0159         KRecentFilesMenu *recentFilesMenu = m_mainWindow->recentFilesMenu();
0160         recentFilesMenu->removeUrl(url);
0161         m_recentFilesModel->refresh(recentFilesMenu->recentFiles());
0162     });
0163 
0164     contextMenu.exec(listViewRecentFiles->mapToGlobal(pos));
0165 }
0166 
0167 void WelcomeView::updateButtons()
0168 {
0169     QVector<QPushButton *> buttons{buttonNewArchive, buttonOpenArchive};
0170     const int maxWidth = std::accumulate(buttons.cbegin(), buttons.cend(), 0, [](int maxWidth, const QPushButton *button) {
0171         return std::max(maxWidth, button->sizeHint().width());
0172     });
0173     for (QPushButton *button : std::as_const(buttons)) {
0174         button->setFixedWidth(maxWidth);
0175     }
0176 }
0177 
0178 void WelcomeView::updateFonts()
0179 {
0180     QFont titleFont = font();
0181     titleFont.setPointSize(titleFont.pointSize() + 6);
0182     titleFont.setWeight(QFont::Bold);
0183     labelTitle->setFont(titleFont);
0184 
0185     QFont panelTitleFont = font();
0186     panelTitleFont.setPointSize(panelTitleFont.pointSize() + 2);
0187     labelRecentFiles->setFont(panelTitleFont);
0188     labelHelp->setFont(panelTitleFont);
0189 
0190     QFont placeholderFont = font();
0191     placeholderFont.setPointSize(qRound(placeholderFont.pointSize() * 1.3));
0192     m_placeholderRecentFiles->setFont(placeholderFont);
0193 }
0194 
0195 bool WelcomeView::updateLayout()
0196 {
0197     // Align labelHelp with labelRecentFiles
0198     labelHelp->setMinimumHeight(labelRecentFiles->height());
0199 
0200     bool result = false;
0201 
0202     // show/hide widgetHeader depending on the view height
0203     if (widgetHeader->isVisible()) {
0204         if (height() <= frameContent->height() + widgetClose->height()) {
0205             widgetHeader->hide();
0206             result = true;
0207         }
0208     } else {
0209         const int implicitHeight = frameContent->height() + widgetHeader->height() + layoutContent->spacing() + widgetClose->height();
0210         if (height() > implicitHeight) {
0211             widgetHeader->show();
0212             result = true;
0213         }
0214     }
0215 
0216     // show/hide widgetHelp depending on the view height
0217     if (widgetHelp->isVisible()) {
0218         if (width() <= frameContent->width() + widgetClose->width()) {
0219             widgetHelp->hide();
0220             result = true;
0221         }
0222     } else {
0223         const int implicitWidth = frameContent->width() + widgetHelp->width() + layoutPanels->spacing() + widgetClose->width();
0224         if (width() > implicitWidth) {
0225             widgetHelp->show();
0226             return true;
0227         }
0228     }
0229 
0230     return result;
0231 }
0232 
0233 #include "moc_welcomeview.cpp"