File indexing completed on 2024-05-05 04:19:20

0001 // vim: set tabstop=4 shiftwidth=4 expandtab:
0002 /*
0003 Gwenview: an image viewer
0004 Copyright 2008 Aurélien Gâteau <agateau@kde.org>
0005 
0006 This program is free software; you can redistribute it and/or
0007 modify it under the terms of the GNU General Public License
0008 as published by the Free Software Foundation; either version 2
0009 of the License, or (at your option) any later version.
0010 
0011 This program is distributed in the hope that it will be useful,
0012 but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 GNU General Public License for more details.
0015 
0016 You should have received a copy of the GNU General Public License
0017 along with this program; if not, write to the Free Software
0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.
0019 
0020 */
0021 // Self
0022 #include "startmainpage.h"
0023 #include <config-gwenview.h>
0024 
0025 // Qt
0026 #include <QListView>
0027 #include <QMenu>
0028 
0029 #ifdef GTK_WORKAROUND_BROKE_IN_KF5_PORT
0030 #include <QPlastiqueStyle>
0031 #endif
0032 
0033 #include <QIcon>
0034 #include <QStyledItemDelegate>
0035 
0036 // KF
0037 #include <KFilePlacesModel>
0038 #include <KLocalizedString>
0039 
0040 // Local
0041 #include "gwenview_app_debug.h"
0042 #include <gvcore.h>
0043 #include <lib/dialogguard.h>
0044 #include <lib/flowlayout.h>
0045 #include <lib/gvdebug.h>
0046 #include <lib/gwenviewconfig.h>
0047 #include <lib/scrollerutils.h>
0048 #include <lib/thumbnailprovider/thumbnailprovider.h>
0049 #include <lib/thumbnailview/abstractthumbnailviewhelper.h>
0050 #include <lib/thumbnailview/previewitemdelegate.h>
0051 #include <ui_startmainpage.h>
0052 
0053 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
0054 #include <lib/semanticinfo/tagmodel.h>
0055 #endif
0056 
0057 namespace Gwenview
0058 {
0059 class HistoryThumbnailViewHelper : public AbstractThumbnailViewHelper
0060 {
0061 public:
0062     HistoryThumbnailViewHelper(QObject *parent)
0063         : AbstractThumbnailViewHelper(parent)
0064     {
0065     }
0066 
0067     void showContextMenu(QWidget *) override
0068     {
0069     }
0070 
0071     void showMenuForUrlDroppedOnViewport(QWidget *, const QList<QUrl> &) override
0072     {
0073     }
0074 
0075     void showMenuForUrlDroppedOnDir(QWidget *, const QList<QUrl> &, const QUrl &) override
0076     {
0077     }
0078 };
0079 
0080 struct StartMainPagePrivate : public Ui_StartMainPage {
0081     StartMainPage *q = nullptr;
0082     GvCore *mGvCore = nullptr;
0083     KFilePlacesModel *mBookmarksModel = nullptr;
0084     ThumbnailProvider *mRecentFilesThumbnailProvider = nullptr;
0085     bool mSearchUiInitialized;
0086 
0087     void setupSearchUi()
0088     {
0089 #ifdef GWENVIEW_SEMANTICINFO_BACKEND_BALOO
0090         mTagView->setModel(TagModel::createAllTagsModel(mTagView, mGvCore->semanticInfoBackEnd()));
0091         mTagView->show();
0092         mTagLabel->hide();
0093 #else
0094         mTagView->hide();
0095         mTagLabel->hide();
0096 #endif
0097     }
0098 
0099     void updateHistoryTab()
0100     {
0101         mHistoryWidget->setVisible(GwenviewConfig::historyEnabled());
0102         mHistoryDisabledLabel->setVisible(!GwenviewConfig::historyEnabled());
0103     }
0104 
0105     void setupHistoryView(ThumbnailView *view)
0106     {
0107         view->setThumbnailViewHelper(new HistoryThumbnailViewHelper(view));
0108         auto delegate = new PreviewItemDelegate(view);
0109         delegate->setContextBarActions(PreviewItemDelegate::NoAction);
0110         delegate->setTextElideMode(Qt::ElideLeft);
0111         view->setItemDelegate(delegate);
0112         view->setThumbnailWidth(128);
0113         view->setCreateThumbnailsForRemoteUrls(false);
0114         QModelIndex index = view->model()->index(0, 0);
0115         if (index.isValid()) {
0116             view->setCurrentIndex(index);
0117         }
0118     }
0119 };
0120 
0121 static void initViewPalette(QAbstractItemView *view, const QColor &fgColor)
0122 {
0123     QWidget *viewport = view->viewport();
0124     QPalette palette = viewport->palette();
0125     palette.setColor(viewport->backgroundRole(), Qt::transparent);
0126     palette.setColor(QPalette::WindowText, fgColor);
0127     palette.setColor(QPalette::Text, fgColor);
0128 
0129     // QListView uses QStyledItemDelegate, which uses the view palette for
0130     // foreground color, while KFilePlacesView uses the viewport palette.
0131     viewport->setPalette(palette);
0132     view->setPalette(palette);
0133 }
0134 
0135 static bool styleIsGtkBased()
0136 {
0137     const char *name = QApplication::style()->metaObject()->className();
0138     return qstrcmp(name, "QGtkStyle") == 0;
0139 }
0140 
0141 StartMainPage::StartMainPage(QWidget *parent, GvCore *gvCore)
0142     : QFrame(parent)
0143     , d(new StartMainPagePrivate)
0144 {
0145     d->mRecentFilesThumbnailProvider = nullptr;
0146     d->q = this;
0147     d->mGvCore = gvCore;
0148     d->mSearchUiInitialized = false;
0149 
0150     d->setupUi(this);
0151     if (styleIsGtkBased()) {
0152 #ifdef GTK_WORKAROUND_BROKE_IN_KF5_PORT
0153 
0154         // Gtk-based styles do not apply the correct background color on tabs.
0155         // As a workaround, use the Plastique style instead.
0156         QStyle *fix = new QPlastiqueStyle();
0157         fix->setParent(this);
0158         d->mHistoryWidget->tabBar()->setStyle(fix);
0159         d->mPlacesTagsWidget->tabBar()->setStyle(fix);
0160 #endif
0161     }
0162     setFrameStyle(QFrame::NoFrame);
0163 
0164     // Bookmark view
0165     d->mBookmarksModel = new KFilePlacesModel(this);
0166 
0167     d->mBookmarksView->setModel(d->mBookmarksModel);
0168     d->mBookmarksView->setAutoResizeItemsEnabled(false);
0169 
0170     connect(d->mBookmarksView, &KFilePlacesView::urlChanged, this, &StartMainPage::urlSelected);
0171 
0172     // Tag view
0173     connect(d->mTagView, &QListView::clicked, this, &StartMainPage::slotTagViewClicked);
0174 
0175     // Recent folders view
0176     connect(d->mRecentFoldersView, &Gwenview::ThumbnailView::indexActivated, this, &StartMainPage::slotListViewActivated);
0177     connect(d->mRecentFoldersView, &Gwenview::ThumbnailView::customContextMenuRequested, this, &StartMainPage::showContextMenu);
0178 
0179     // Recent files view
0180     connect(d->mRecentFilesView, &Gwenview::ThumbnailView::indexActivated, this, &StartMainPage::slotListViewActivated);
0181     connect(d->mRecentFilesView, &Gwenview::ThumbnailView::customContextMenuRequested, this, &StartMainPage::showContextMenu);
0182 
0183     d->updateHistoryTab();
0184     connect(GwenviewConfig::self(), &GwenviewConfig::configChanged, this, &StartMainPage::loadConfig);
0185 
0186     connect(qApp, &QApplication::paletteChanged, this, [this]() {
0187         applyPalette(d->mGvCore->palette(GvCore::NormalViewPalette));
0188     });
0189 
0190     d->mRecentFoldersView->setFocus();
0191 
0192     ScrollerUtils::setQScroller(d->mBookmarksView->viewport());
0193     d->mBookmarksView->viewport()->installEventFilter(this);
0194 }
0195 
0196 StartMainPage::~StartMainPage()
0197 {
0198     delete d->mRecentFilesThumbnailProvider;
0199     delete d;
0200 }
0201 
0202 bool StartMainPage::eventFilter(QObject *, QEvent *event)
0203 {
0204     if (event->type() == QEvent::MouseMove) {
0205         auto mouseEvent = static_cast<QMouseEvent *>(event);
0206         if (mouseEvent->source() == Qt::MouseEventSynthesizedByQt) {
0207             return true;
0208         }
0209     }
0210     return false;
0211 }
0212 
0213 void StartMainPage::slotTagViewClicked(const QModelIndex &index)
0214 {
0215 #ifdef GWENVIEW_SEMANTICINFO_BACKEND_BALOO
0216     if (!index.isValid()) {
0217         return;
0218     }
0219     // FIXME: Check label encoding
0220     const QString tag = index.data().toString();
0221     Q_EMIT urlSelected(QUrl("tags:/" + tag));
0222 #endif
0223 }
0224 
0225 void StartMainPage::applyPalette(const QPalette &newPalette)
0226 {
0227     QColor fgColor = newPalette.text().color();
0228 
0229     QPalette pal = palette();
0230     pal.setBrush(backgroundRole(), newPalette.base());
0231     pal.setBrush(QPalette::Button, newPalette.base());
0232     pal.setBrush(QPalette::WindowText, fgColor);
0233     pal.setBrush(QPalette::ButtonText, fgColor);
0234     pal.setBrush(QPalette::Text, fgColor);
0235     setPalette(pal);
0236 
0237     initViewPalette(d->mBookmarksView, fgColor);
0238     initViewPalette(d->mTagView, fgColor);
0239     initViewPalette(d->mRecentFoldersView, fgColor);
0240     initViewPalette(d->mRecentFilesView, fgColor);
0241 }
0242 
0243 void StartMainPage::slotListViewActivated(const QModelIndex &index)
0244 {
0245     if (!index.isValid()) {
0246         return;
0247     }
0248     QVariant data = index.data(KFilePlacesModel::UrlRole);
0249     QUrl url = data.toUrl();
0250 
0251     // Prevent dir lister error
0252     if (!url.isValid()) {
0253         qCCritical(GWENVIEW_APP_LOG) << "Tried to open an invalid url";
0254         return;
0255     }
0256     Q_EMIT urlSelected(url);
0257 }
0258 
0259 void StartMainPage::showEvent(QShowEvent *event)
0260 {
0261     if (GwenviewConfig::historyEnabled()) {
0262         if (!d->mRecentFoldersView->model()) {
0263             d->mRecentFoldersView->setModel(d->mGvCore->recentFoldersModel());
0264             d->setupHistoryView(d->mRecentFoldersView);
0265         }
0266         if (!d->mRecentFilesView->model()) {
0267             d->mRecentFilesView->setModel(d->mGvCore->recentFilesModel());
0268             d->mRecentFilesThumbnailProvider = new ThumbnailProvider();
0269             d->mRecentFilesView->setThumbnailProvider(d->mRecentFilesThumbnailProvider);
0270             d->setupHistoryView(d->mRecentFilesView);
0271         }
0272     }
0273     if (!d->mSearchUiInitialized) {
0274         d->mSearchUiInitialized = true;
0275         d->setupSearchUi();
0276     }
0277     QFrame::showEvent(event);
0278 }
0279 
0280 void StartMainPage::showContextMenu(const QPoint &pos)
0281 {
0282     // Create menu
0283     DialogGuard<QMenu> menu(this);
0284 
0285     QAction *addAction = menu->addAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), QString());
0286     QAction *forgetAction = menu->addAction(QIcon::fromTheme(QStringLiteral("edit-delete")), QString());
0287     menu->addSeparator();
0288     QAction *forgetAllAction = menu->addAction(QIcon::fromTheme(QStringLiteral("edit-delete-all")), QString());
0289 
0290     if (d->mHistoryWidget->currentWidget() == d->mRecentFoldersTab) {
0291         addAction->setText(i18nc("@action Recent Folders view", "Add Folder to Places"));
0292         forgetAction->setText(i18nc("@action Recent Folders view", "Forget This Folder"));
0293         forgetAllAction->setText(i18nc("@action Recent Folders view", "Forget All Folders"));
0294     } else if (d->mHistoryWidget->currentWidget() == d->mRecentFilesTab) {
0295         addAction->setText(i18nc("@action Recent Files view", "Add Containing Folder to Places"));
0296         forgetAction->setText(i18nc("@action Recent Files view", "Forget This File"));
0297         forgetAllAction->setText(i18nc("@action Recent Files view", "Forget All Files"));
0298     } else {
0299         GV_WARN_AND_RETURN("Context menu not implemented on this tab page");
0300     }
0301 
0302     const QAbstractItemView *view = qobject_cast<QAbstractItemView *>(sender());
0303     const QModelIndex index = view->indexAt(pos);
0304     addAction->setEnabled(index.isValid());
0305     forgetAction->setEnabled(index.isValid());
0306 
0307     // Handle menu
0308     const QAction *action = menu->exec(view->mapToGlobal(pos));
0309     if (!action) {
0310         return;
0311     }
0312 
0313     const QVariant data = index.data(KFilePlacesModel::UrlRole);
0314     QUrl url = data.toUrl();
0315     if (action == addAction) {
0316         if (d->mHistoryWidget->currentWidget() == d->mRecentFilesTab) {
0317             url = url.adjusted(QUrl::RemoveFilename);
0318         }
0319         QString text = url.adjusted(QUrl::StripTrailingSlash).fileName();
0320         if (text.isEmpty()) {
0321             text = url.toDisplayString();
0322         }
0323         d->mBookmarksModel->addPlace(text, url);
0324     } else if (action == forgetAction) {
0325         view->model()->removeRow(index.row());
0326         if (d->mHistoryWidget->currentWidget() == d->mRecentFilesTab) {
0327             Q_EMIT recentFileRemoved(url);
0328         }
0329     } else if (action == forgetAllAction) {
0330         view->model()->removeRows(0, view->model()->rowCount());
0331         if (d->mHistoryWidget->currentWidget() == d->mRecentFilesTab) {
0332             Q_EMIT recentFilesCleared();
0333         }
0334     }
0335 }
0336 
0337 void StartMainPage::loadConfig()
0338 {
0339     d->updateHistoryTab();
0340     applyPalette(d->mGvCore->palette(GvCore::NormalViewPalette));
0341 }
0342 
0343 ThumbnailView *StartMainPage::recentFoldersView() const
0344 {
0345     return d->mRecentFoldersView;
0346 }
0347 
0348 } // namespace
0349 
0350 #include "moc_startmainpage.cpp"