diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp index cc74d394..6fc0d726 100644 --- a/app/mainwindow.cpp +++ b/app/mainwindow.cpp @@ -1,1637 +1,1673 @@ /* Gwenview: an image viewer Copyright 2007 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "mainwindow.h" #include #include "dialogguard.h" // Qt #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_OSX #include #endif // KDE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Local #include "configdialog.h" #include "documentinfoprovider.h" #include "viewmainpage.h" #include "fileopscontextmanageritem.h" #include "folderviewcontextmanageritem.h" #include "fullscreencontent.h" #include "gvcore.h" #include "imageopscontextmanageritem.h" #include "infocontextmanageritem.h" #ifdef KIPI_FOUND #include "kipiexportaction.h" #include "kipiinterface.h" #endif #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE #include "semanticinfocontextmanageritem.h" #endif #include "preloader.h" #include "savebar.h" #include "sidebar.h" #include "splitter.h" #include "startmainpage.h" #include "thumbnailviewhelper.h" #include "browsemainpage.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Gwenview { #undef ENABLE_LOG #undef LOG //#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) qDebug() << x #else #define LOG(x) ; #endif static const int BROWSE_PRELOAD_DELAY = 1000; static const int VIEW_PRELOAD_DELAY = 100; static const char* BROWSE_MODE_SIDE_BAR_GROUP = "SideBar-BrowseMode"; static const char* VIEW_MODE_SIDE_BAR_GROUP = "SideBar-ViewMode"; static const char* FULLSCREEN_MODE_SIDE_BAR_GROUP = "SideBar-FullScreenMode"; static const char* SIDE_BAR_IS_VISIBLE_KEY = "IsVisible"; static const char* SESSION_CURRENT_PAGE_KEY = "Page"; static const char* SESSION_URL_KEY = "Url"; enum MainPageId { StartMainPageId, BrowseMainPageId, ViewMainPageId }; struct MainWindowState { bool mToolBarVisible; }; /* Layout of the main window looks like this: .-mCentralSplitter-----------------------------. |.-mSideBar--. .-mContentWidget---------------.| || | |.-mSaveBar-------------------.|| || | || ||| || | |'----------------------------'|| || | |.-mViewStackedWidget---------.|| || | || ||| || | || ||| || | || ||| || | || ||| || | |'----------------------------'|| |'-----------' '------------------------------'| '----------------------------------------------' */ struct MainWindow::Private { GvCore* mGvCore; MainWindow* q; QSplitter* mCentralSplitter; QWidget* mContentWidget; ViewMainPage* mViewMainPage; KUrlNavigator* mUrlNavigator; ThumbnailView* mThumbnailView; ThumbnailView* mActiveThumbnailView; DocumentInfoProvider* mDocumentInfoProvider; ThumbnailViewHelper* mThumbnailViewHelper; QPointer mThumbnailProvider; BrowseMainPage* mBrowseMainPage; StartMainPage* mStartMainPage; SideBar* mSideBar; QStackedWidget* mViewStackedWidget; FullScreenContent* mFullScreenContent; SaveBar* mSaveBar; bool mStartSlideShowWhenDirListerCompleted; SlideShow* mSlideShow; Preloader* mPreloader; bool mPreloadDirectionIsForward; #ifdef KIPI_FOUND KIPIInterface* mKIPIInterface; #endif QActionGroup* mViewModeActionGroup; KRecentFilesAction* mFileOpenRecentAction; QAction * mBrowseAction; QAction * mViewAction; QAction * mGoUpAction; QAction * mGoToPreviousAction; QAction * mGoToNextAction; QAction * mGoToFirstAction; QAction * mGoToLastAction; KToggleAction* mToggleSideBarAction; QAction* mFullScreenAction; QAction * mToggleSlideShowAction; KToggleAction* mShowMenuBarAction; KToggleAction* mShowStatusBarAction; #ifdef KIPI_FOUND KIPIExportAction* mKIPIExportAction; #endif SortedDirModel* mDirModel; DocumentOnlyProxyModel* mThumbnailBarModel; KLinkItemSelectionModel* mThumbnailBarSelectionModel; ContextManager* mContextManager; MainWindowState mStateBeforeFullScreen; QString mCaption; MainPageId mCurrentMainPageId; QDateTime mFullScreenLeftAt; KNotificationRestrictions* mNotificationRestrictions; void setupContextManager() { mContextManager = new ContextManager(mDirModel, q); connect(mContextManager, SIGNAL(selectionChanged()), q, SLOT(slotSelectionChanged())); connect(mContextManager, SIGNAL(currentDirUrlChanged(QUrl)), q, SLOT(slotCurrentDirUrlChanged(QUrl))); } void setupWidgets() { mFullScreenContent = new FullScreenContent(q, mGvCore); connect(mContextManager, SIGNAL(currentUrlChanged(QUrl)), mFullScreenContent, SLOT(setCurrentUrl(QUrl))); mCentralSplitter = new Splitter(Qt::Horizontal, q); q->setCentralWidget(mCentralSplitter); // Left side of splitter mSideBar = new SideBar(mCentralSplitter); EventWatcher::install(mSideBar, QList() << QEvent::Show << QEvent::Hide, q, SLOT(updateToggleSideBarAction())); // Right side of splitter mContentWidget = new QWidget(mCentralSplitter); mSaveBar = new SaveBar(mContentWidget, q->actionCollection()); connect(mContextManager, SIGNAL(currentUrlChanged(QUrl)), mSaveBar, SLOT(setCurrentUrl(QUrl))); mViewStackedWidget = new QStackedWidget(mContentWidget); QVBoxLayout* layout = new QVBoxLayout(mContentWidget); layout->addWidget(mSaveBar); layout->addWidget(mViewStackedWidget); layout->setMargin(0); layout->setSpacing(0); //// mStartSlideShowWhenDirListerCompleted = false; mSlideShow = new SlideShow(q); connect(mContextManager, SIGNAL(currentUrlChanged(QUrl)), mSlideShow, SLOT(setCurrentUrl(QUrl))); setupThumbnailView(mViewStackedWidget); setupViewMainPage(mViewStackedWidget); setupStartMainPage(mViewStackedWidget); mViewStackedWidget->addWidget(mBrowseMainPage); mViewStackedWidget->addWidget(mViewMainPage); mViewStackedWidget->addWidget(mStartMainPage); mViewStackedWidget->setCurrentWidget(mBrowseMainPage); mCentralSplitter->setStretchFactor(0, 0); mCentralSplitter->setStretchFactor(1, 1); mThumbnailView->setFocus(); connect(mSaveBar, SIGNAL(requestSaveAll()), mGvCore, SLOT(saveAll())); connect(mSaveBar, SIGNAL(goToUrl(QUrl)), q, SLOT(goToUrl(QUrl))); connect(mSlideShow, SIGNAL(goToUrl(QUrl)), q, SLOT(goToUrl(QUrl))); } void setupThumbnailView(QWidget* parent) { Q_ASSERT(mContextManager); mBrowseMainPage = new BrowseMainPage(parent, q->actionCollection(), mGvCore); mThumbnailView = mBrowseMainPage->thumbnailView(); mThumbnailView->setSelectionModel(mContextManager->selectionModel()); mUrlNavigator = mBrowseMainPage->urlNavigator(); mDocumentInfoProvider = new DocumentInfoProvider(mDirModel); mThumbnailView->setDocumentInfoProvider(mDocumentInfoProvider); mThumbnailViewHelper = new ThumbnailViewHelper(mDirModel, q->actionCollection()); connect(mContextManager, SIGNAL(currentDirUrlChanged(QUrl)), mThumbnailViewHelper, SLOT(setCurrentDirUrl(QUrl))); mThumbnailView->setThumbnailViewHelper(mThumbnailViewHelper); mThumbnailBarSelectionModel = new KLinkItemSelectionModel(mThumbnailBarModel, mContextManager->selectionModel(), q); // Connect thumbnail view connect(mThumbnailView, SIGNAL(indexActivated(QModelIndex)), q, SLOT(slotThumbnailViewIndexActivated(QModelIndex))); // Connect delegate QAbstractItemDelegate* delegate = mThumbnailView->itemDelegate(); connect(delegate, SIGNAL(saveDocumentRequested(QUrl)), mGvCore, SLOT(save(QUrl))); connect(delegate, SIGNAL(rotateDocumentLeftRequested(QUrl)), mGvCore, SLOT(rotateLeft(QUrl))); connect(delegate, SIGNAL(rotateDocumentRightRequested(QUrl)), mGvCore, SLOT(rotateRight(QUrl))); connect(delegate, SIGNAL(showDocumentInFullScreenRequested(QUrl)), q, SLOT(showDocumentInFullScreen(QUrl))); connect(delegate, SIGNAL(setDocumentRatingRequested(QUrl,int)), mGvCore, SLOT(setRating(QUrl,int))); // Connect url navigator connect(mUrlNavigator, SIGNAL(urlChanged(QUrl)), q, SLOT(openDirUrl(QUrl))); } void setupViewMainPage(QWidget* parent) { mViewMainPage = new ViewMainPage(parent, mSlideShow, q->actionCollection(), mGvCore); connect(mViewMainPage, SIGNAL(captionUpdateRequested(QString)), q, SLOT(slotUpdateCaption(QString))); connect(mViewMainPage, SIGNAL(completed()), q, SLOT(slotPartCompleted())); connect(mViewMainPage, SIGNAL(previousImageRequested()), q, SLOT(goToPrevious())); connect(mViewMainPage, SIGNAL(nextImageRequested()), q, SLOT(goToNext())); setupThumbnailBar(mViewMainPage->thumbnailBar()); } void setupThumbnailBar(ThumbnailView* bar) { Q_ASSERT(mThumbnailBarModel); Q_ASSERT(mThumbnailBarSelectionModel); Q_ASSERT(mDocumentInfoProvider); Q_ASSERT(mThumbnailViewHelper); bar->setModel(mThumbnailBarModel); bar->setSelectionModel(mThumbnailBarSelectionModel); bar->setDocumentInfoProvider(mDocumentInfoProvider); bar->setThumbnailViewHelper(mThumbnailViewHelper); } void setupStartMainPage(QWidget* parent) { mStartMainPage = new StartMainPage(parent, mGvCore); connect(mStartMainPage, SIGNAL(urlSelected(QUrl)), q, SLOT(slotStartMainPageUrlSelected(QUrl))); } void installDisabledActionShortcutMonitor(QAction* action, const char* slot) { DisabledActionShortcutMonitor* monitor = new DisabledActionShortcutMonitor(action, q); connect(monitor, SIGNAL(activated()), q, slot); } void setupActions() { KActionCollection* actionCollection = q->actionCollection(); KActionCategory* file = new KActionCategory(i18nc("@title actions category", "File"), actionCollection); KActionCategory* view = new KActionCategory(i18nc("@title actions category - means actions changing smth in interface", "View"), actionCollection); file->addAction(KStandardAction::Save, q, SLOT(saveCurrent())); file->addAction(KStandardAction::SaveAs, q, SLOT(saveCurrentAs())); file->addAction(KStandardAction::Open, q, SLOT(openFile())); mFileOpenRecentAction = KStandardAction::openRecent(q, SLOT(openUrl(QUrl)), q); file->addAction("file_open_recent", mFileOpenRecentAction); file->addAction(KStandardAction::Print, q, SLOT(print())); file->addAction(KStandardAction::Quit, qApp, SLOT(closeAllWindows())); QAction * action = file->addAction("reload", q, SLOT(reload())); action->setText(i18nc("@action reload the currently viewed image", "Reload")); action->setIcon(QIcon::fromTheme("view-refresh")); actionCollection->setDefaultShortcuts(action, KStandardShortcut::reload()); mBrowseAction = view->addAction("browse"); mBrowseAction->setText(i18nc("@action:intoolbar Switch to file list", "Browse")); mBrowseAction->setToolTip(i18nc("@info:tooltip", "Browse folders for images")); mBrowseAction->setCheckable(true); mBrowseAction->setIcon(QIcon::fromTheme("view-list-icons")); actionCollection->setDefaultShortcut(mBrowseAction, Qt::Key_Escape); connect(mViewMainPage, SIGNAL(goToBrowseModeRequested()), mBrowseAction, SLOT(trigger())); mViewAction = view->addAction("view"); mViewAction->setText(i18nc("@action:intoolbar Switch to image view", "View")); mViewAction->setToolTip(i18nc("@info:tooltip", "View selected images")); mViewAction->setIcon(QIcon::fromTheme("view-preview")); mViewAction->setCheckable(true); mViewModeActionGroup = new QActionGroup(q); mViewModeActionGroup->addAction(mBrowseAction); mViewModeActionGroup->addAction(mViewAction); connect(mViewModeActionGroup, SIGNAL(triggered(QAction*)), q, SLOT(setActiveViewModeAction(QAction*))); mFullScreenAction = KStandardAction::fullScreen(q, &MainWindow::toggleFullScreen, q, actionCollection); QList shortcuts = mFullScreenAction->shortcuts(); shortcuts.append(QKeySequence(Qt::Key_F11)); actionCollection->setDefaultShortcuts(mFullScreenAction, shortcuts); connect(mViewMainPage, SIGNAL(toggleFullScreenRequested()), mFullScreenAction, SLOT(trigger())); QAction * leaveFullScreenAction = view->addAction("leave_fullscreen", q, SLOT(leaveFullScreen())); leaveFullScreenAction->setIcon(QIcon::fromTheme("view-restore")); leaveFullScreenAction->setPriority(QAction::LowPriority); leaveFullScreenAction->setText(i18nc("@action", "Leave Fullscreen Mode")); mGoToPreviousAction = view->addAction("go_previous", q, SLOT(goToPrevious())); mGoToPreviousAction->setPriority(QAction::LowPriority); mGoToPreviousAction->setIcon(QIcon::fromTheme("media-skip-backward")); mGoToPreviousAction->setText(i18nc("@action Go to previous image", "Previous")); mGoToPreviousAction->setToolTip(i18nc("@info:tooltip", "Go to previous image")); actionCollection->setDefaultShortcut(mGoToPreviousAction, Qt::Key_Backspace); installDisabledActionShortcutMonitor(mGoToPreviousAction, SLOT(showFirstDocumentReached())); mGoToNextAction = view->addAction("go_next", q, SLOT(goToNext())); mGoToNextAction->setPriority(QAction::LowPriority); mGoToNextAction->setIcon(QIcon::fromTheme("media-skip-forward")); mGoToNextAction->setText(i18nc("@action Go to next image", "Next")); mGoToNextAction->setToolTip(i18nc("@info:tooltip", "Go to next image")); actionCollection->setDefaultShortcut(mGoToNextAction, Qt::Key_Space); installDisabledActionShortcutMonitor(mGoToNextAction, SLOT(showLastDocumentReached())); mGoToFirstAction = view->addAction("go_first", q, SLOT(goToFirst())); mGoToFirstAction->setPriority(QAction::LowPriority); mGoToFirstAction->setText(i18nc("@action Go to first image", "First")); mGoToFirstAction->setToolTip(i18nc("@info:tooltip", "Go to first image")); actionCollection->setDefaultShortcut(mGoToFirstAction, Qt::Key_Home); mGoToLastAction = view->addAction("go_last", q, SLOT(goToLast())); mGoToLastAction->setPriority(QAction::LowPriority); mGoToLastAction->setText(i18nc("@action Go to last image", "Last")); mGoToLastAction->setToolTip(i18nc("@info:tooltip", "Go to last image")); actionCollection->setDefaultShortcut(mGoToLastAction, Qt::Key_End); mPreloadDirectionIsForward = true; mGoUpAction = view->addAction(KStandardAction::Up, q, SLOT(goUp())); action = view->addAction("go_start_page", q, SLOT(showStartMainPage())); action->setPriority(QAction::LowPriority); action->setIcon(QIcon::fromTheme("go-home")); action->setText(i18nc("@action", "Start Page")); action->setToolTip(i18nc("@info:tooltip", "Open the start page")); mToggleSideBarAction = view->add("toggle_sidebar"); connect(mToggleSideBarAction, SIGNAL(toggled(bool)), q, SLOT(toggleSideBar(bool))); mToggleSideBarAction->setIcon(QIcon::fromTheme("view-sidetree")); actionCollection->setDefaultShortcut(mToggleSideBarAction, Qt::Key_F4); mToggleSideBarAction->setText(i18nc("@action", "Sidebar")); connect(mBrowseMainPage->toggleSideBarButton(), SIGNAL(clicked()), mToggleSideBarAction, SLOT(trigger())); connect(mViewMainPage->toggleSideBarButton(), SIGNAL(clicked()), mToggleSideBarAction, SLOT(trigger())); mToggleSlideShowAction = view->addAction("toggle_slideshow", q, SLOT(toggleSlideShow())); q->updateSlideShowAction(); connect(mSlideShow, SIGNAL(stateChanged(bool)), q, SLOT(updateSlideShowAction())); q->setStandardToolBarMenuEnabled(true); mShowMenuBarAction = static_cast(view->addAction(KStandardAction::ShowMenubar, q, SLOT(toggleMenuBar()))); - mShowStatusBarAction = static_cast(view->addAction(KStandardAction::ShowStatusbar, q, SLOT(toggleStatusBar()))); + mShowStatusBarAction = static_cast(view->addAction(KStandardAction::ShowStatusbar, q, SLOT(toggleStatusBar(bool)))); actionCollection->setDefaultShortcut(mShowStatusBarAction, Qt::Key_F3); view->addAction(KStandardAction::KeyBindings, q->guiFactory(), SLOT(configureShortcuts())); view->addAction(KStandardAction::Preferences, q, SLOT(showConfigDialog())); view->addAction(KStandardAction::ConfigureToolbars, q, SLOT(configureToolbars())); #ifdef KIPI_FOUND mKIPIExportAction = new KIPIExportAction(q); actionCollection->addAction("kipi_export", mKIPIExportAction); #endif } void setupUndoActions() { // There is no KUndoGroup similar to KUndoStack. This code basically // does the same as KUndoStack, but for the KUndoGroup actions. QUndoGroup* undoGroup = DocumentFactory::instance()->undoGroup(); QAction* action; KActionCollection* actionCollection = q->actionCollection(); KActionCategory* edit = new KActionCategory(i18nc("@title actions category - means actions changing smth in interface", "Edit"), actionCollection); action = undoGroup->createRedoAction(actionCollection); action->setObjectName(KStandardAction::name(KStandardAction::Redo)); action->setIcon(QIcon::fromTheme("edit-redo")); action->setIconText(i18n("Redo")); actionCollection->setDefaultShortcuts(action, KStandardShortcut::redo()); edit->addAction(action->objectName(), action); action = undoGroup->createUndoAction(actionCollection); action->setObjectName(KStandardAction::name(KStandardAction::Undo)); action->setIcon(QIcon::fromTheme("edit-undo")); action->setIconText(i18n("Undo")); actionCollection->setDefaultShortcuts(action, KStandardShortcut::undo()); edit->addAction(action->objectName(), action); } void setupContextManagerItems() { Q_ASSERT(mContextManager); KActionCollection* actionCollection = q->actionCollection(); // Create context manager items FolderViewContextManagerItem* folderViewItem = new FolderViewContextManagerItem(mContextManager); connect(folderViewItem, &FolderViewContextManagerItem::urlChanged, q, &MainWindow::folderViewUrlChanged); InfoContextManagerItem* infoItem = new InfoContextManagerItem(mContextManager); #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE SemanticInfoContextManagerItem* semanticInfoItem = 0; semanticInfoItem = new SemanticInfoContextManagerItem(mContextManager, actionCollection, mViewMainPage); #endif ImageOpsContextManagerItem* imageOpsItem = new ImageOpsContextManagerItem(mContextManager, q); FileOpsContextManagerItem* fileOpsItem = new FileOpsContextManagerItem(mContextManager, mThumbnailView, actionCollection, q); // Fill sidebar SideBarPage* page; page = new SideBarPage(i18n("Folders")); page->setObjectName(QLatin1String("folders")); page->addWidget(folderViewItem->widget()); page->layout()->setMargin(0); mSideBar->addPage(page); page = new SideBarPage(i18n("Information")); page->setObjectName(QLatin1String("information")); page->addWidget(infoItem->widget()); #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE if (semanticInfoItem) { page->addWidget(semanticInfoItem->widget()); } #endif mSideBar->addPage(page); page = new SideBarPage(i18n("Operations")); page->setObjectName(QLatin1String("operations")); page->addWidget(imageOpsItem->widget()); page->addWidget(fileOpsItem->widget()); page->addStretch(); mSideBar->addPage(page); } void initDirModel() { mDirModel->setKindFilter( MimeTypeUtils::KIND_DIR | MimeTypeUtils::KIND_ARCHIVE | MimeTypeUtils::KIND_RASTER_IMAGE | MimeTypeUtils::KIND_SVG_IMAGE | MimeTypeUtils::KIND_VIDEO); connect(mDirModel, SIGNAL(rowsInserted(QModelIndex,int,int)), q, SLOT(slotDirModelNewItems())); connect(mDirModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), q, SLOT(updatePreviousNextActions())); connect(mDirModel, SIGNAL(modelReset()), q, SLOT(updatePreviousNextActions())); connect(mDirModel->dirLister(), SIGNAL(completed()), q, SLOT(slotDirListerCompleted())); } void setupThumbnailBarModel() { mThumbnailBarModel = new DocumentOnlyProxyModel(q); mThumbnailBarModel->setSourceModel(mDirModel); } bool indexIsDirOrArchive(const QModelIndex& index) const { Q_ASSERT(index.isValid()); KFileItem item = mDirModel->itemForIndex(index); return ArchiveUtils::fileItemIsDirOrArchive(item); } void goTo(const QModelIndex& index) { if (!index.isValid()) { return; } mThumbnailView->setCurrentIndex(index); mThumbnailView->scrollTo(index); } void goTo(int offset) { mPreloadDirectionIsForward = offset > 0; QModelIndex index = mContextManager->selectionModel()->currentIndex(); index = mDirModel->index(index.row() + offset, 0); if (index.isValid() && !indexIsDirOrArchive(index)) { goTo(index); } } void goToFirstDocument() { QModelIndex index; for (int row = 0;; ++row) { index = mDirModel->index(row, 0); if (!index.isValid()) { return; } if (!indexIsDirOrArchive(index)) { break; } } goTo(index); } void goToLastDocument() { QModelIndex index = mDirModel->index(mDirModel->rowCount() - 1, 0); goTo(index); } void setupFullScreenContent() { mFullScreenContent->init(q->actionCollection(), mViewMainPage, mSlideShow); setupThumbnailBar(mFullScreenContent->thumbnailBar()); } inline void setActionEnabled(const char* name, bool enabled) { QAction* action = q->actionCollection()->action(name); if (action) { action->setEnabled(enabled); } else { qWarning() << "Action" << name << "not found"; } } void setActionsDisabledOnStartMainPageEnabled(bool enabled) { mBrowseAction->setEnabled(enabled); mViewAction->setEnabled(enabled); mToggleSideBarAction->setEnabled(enabled); mShowStatusBarAction->setEnabled(enabled); mFullScreenAction->setEnabled(enabled); mToggleSlideShowAction->setEnabled(enabled); setActionEnabled("reload", enabled); setActionEnabled("go_start_page", enabled); setActionEnabled("add_folder_to_places", enabled); } void updateActions() { bool isRasterImage = false; bool canSave = false; bool isModified = false; const QUrl url = mContextManager->currentUrl(); if (url.isValid()) { isRasterImage = mContextManager->currentUrlIsRasterImage(); canSave = isRasterImage; isModified = DocumentFactory::instance()->load(url)->isModified(); if (mCurrentMainPageId != ViewMainPageId && mContextManager->selectedFileItemList().count() != 1) { // Saving only makes sense if exactly one image is selected canSave = false; } } KActionCollection* actionCollection = q->actionCollection(); actionCollection->action("file_save")->setEnabled(canSave && isModified); actionCollection->action("file_save_as")->setEnabled(canSave); actionCollection->action("file_print")->setEnabled(isRasterImage); } const char* sideBarConfigGroupName() const { const char* name = 0; switch (mCurrentMainPageId) { case StartMainPageId: GV_WARN_AND_RETURN_VALUE(BROWSE_MODE_SIDE_BAR_GROUP, "mCurrentMainPageId == 'StartMainPageId'"); break; case BrowseMainPageId: name = BROWSE_MODE_SIDE_BAR_GROUP; break; case ViewMainPageId: name = q->isFullScreen() ? FULLSCREEN_MODE_SIDE_BAR_GROUP : VIEW_MODE_SIDE_BAR_GROUP; break; } return name; } void loadSideBarConfig() { static QMap defaultVisibility; if (defaultVisibility.isEmpty()) { defaultVisibility[BROWSE_MODE_SIDE_BAR_GROUP] = true; defaultVisibility[VIEW_MODE_SIDE_BAR_GROUP] = true; defaultVisibility[FULLSCREEN_MODE_SIDE_BAR_GROUP] = false; } const char* name = sideBarConfigGroupName(); KConfigGroup group(KSharedConfig::openConfig(), name); mSideBar->setVisible(group.readEntry(SIDE_BAR_IS_VISIBLE_KEY, defaultVisibility[name])); mSideBar->setCurrentPage(GwenviewConfig::sideBarPage()); q->updateToggleSideBarAction(); } void saveSideBarConfig() const { KConfigGroup group(KSharedConfig::openConfig(), sideBarConfigGroupName()); group.writeEntry(SIDE_BAR_IS_VISIBLE_KEY, mSideBar->isVisible()); GwenviewConfig::setSideBarPage(mSideBar->currentPage()); } + bool statusBarVisibility() const + { + switch (mCurrentMainPageId) { + case StartMainPageId: + GV_WARN_AND_RETURN_VALUE(false, "Statusbar not implemented on start page"); + break; + case BrowseMainPageId: + return GwenviewConfig::statusBarVisibleBrowseMode(); + break; + case ViewMainPageId: + return q->isFullScreen() + ? GwenviewConfig::statusBarVisibleViewModeFullScreen() + : GwenviewConfig::statusBarVisibleViewMode(); + break; + } + + return false; + } + + void setStatusBarVisibility(const bool visible) + { + switch (mCurrentMainPageId) { + case StartMainPageId: + GV_WARN_AND_RETURN("Statusbar not implemented on start page"); + break; + case BrowseMainPageId: + GwenviewConfig::setStatusBarVisibleBrowseMode(visible); + break; + case ViewMainPageId: + q->isFullScreen() + ? GwenviewConfig::setStatusBarVisibleViewModeFullScreen(visible) + : GwenviewConfig::setStatusBarVisibleViewMode(visible); + break; + } + } + void setScreenSaverEnabled(bool enabled) { // Always delete mNotificationRestrictions, it does not hurt delete mNotificationRestrictions; if (!enabled) { mNotificationRestrictions = new KNotificationRestrictions(KNotificationRestrictions::ScreenSaver, q); } else { mNotificationRestrictions = 0; } } void assignThumbnailProviderToThumbnailView(ThumbnailView* thumbnailView) { GV_RETURN_IF_FAIL(thumbnailView); if (mActiveThumbnailView) { mActiveThumbnailView->setThumbnailProvider(0); } thumbnailView->setThumbnailProvider(mThumbnailProvider); mActiveThumbnailView = thumbnailView; if (mActiveThumbnailView->isVisible()) { mThumbnailProvider->stop(); mActiveThumbnailView->generateThumbnailsForItems(); } } void autoAssignThumbnailProvider() { if (mCurrentMainPageId == ViewMainPageId) { if (q->windowState() & Qt::WindowFullScreen) { assignThumbnailProviderToThumbnailView(mFullScreenContent->thumbnailBar()); } else { assignThumbnailProviderToThumbnailView(mViewMainPage->thumbnailBar()); } } else if (mCurrentMainPageId == BrowseMainPageId) { assignThumbnailProviderToThumbnailView(mThumbnailView); } else if (mCurrentMainPageId == StartMainPageId) { assignThumbnailProviderToThumbnailView(mStartMainPage->recentFoldersView()); } } }; MainWindow::MainWindow() : KXmlGuiWindow(), d(new MainWindow::Private) { d->q = this; d->mCurrentMainPageId = StartMainPageId; d->mDirModel = new SortedDirModel(this); d->setupContextManager(); d->setupThumbnailBarModel(); d->mGvCore = new GvCore(this, d->mDirModel); d->mPreloader = new Preloader(this); d->mNotificationRestrictions = 0; d->mThumbnailProvider = new ThumbnailProvider(); d->mActiveThumbnailView = 0; d->initDirModel(); d->setupWidgets(); d->setupActions(); d->setupUndoActions(); d->setupContextManagerItems(); d->setupFullScreenContent(); d->updateActions(); updatePreviousNextActions(); d->mSaveBar->initActionDependentWidgets(); createGUI(); loadConfig(); connect(DocumentFactory::instance(), SIGNAL(modifiedDocumentListChanged()), SLOT(slotModifiedDocumentListChanged())); #ifdef KIPI_FOUND d->mKIPIInterface = new KIPIInterface(this); d->mKIPIExportAction->setKIPIInterface(d->mKIPIInterface); #endif setAutoSaveSettings(); #ifdef Q_OS_OSX qApp->installEventFilter(this); #endif } MainWindow::~MainWindow() { if (GwenviewConfig::deleteThumbnailCacheOnExit()) { QDir dir(ThumbnailProvider::thumbnailBaseDir()); if (dir.exists()) { dir.removeRecursively(); } } delete d->mThumbnailProvider; delete d; } ContextManager* MainWindow::contextManager() const { return d->mContextManager; } ViewMainPage* MainWindow::viewMainPage() const { return d->mViewMainPage; } void MainWindow::setCaption(const QString& caption) { // Keep a trace of caption to use it in slotModifiedDocumentListChanged() d->mCaption = caption; KXmlGuiWindow::setCaption(caption); } void MainWindow::setCaption(const QString& caption, bool modified) { d->mCaption = caption; KXmlGuiWindow::setCaption(caption, modified); } void MainWindow::slotUpdateCaption(const QString& caption) { const QUrl url = d->mContextManager->currentUrl(); const QList list = DocumentFactory::instance()->modifiedDocumentList(); setCaption(caption, list.contains(url)); } void MainWindow::slotModifiedDocumentListChanged() { d->updateActions(); slotUpdateCaption(d->mCaption); } void MainWindow::setInitialUrl(const QUrl &_url) { Q_ASSERT(_url.isValid()); QUrl url = UrlUtils::fixUserEnteredUrl(_url); if (UrlUtils::urlIsDirectory(url)) { d->mBrowseAction->trigger(); openDirUrl(url); } else { openUrl(url); } } void MainWindow::startSlideShow() { d->mViewAction->trigger(); // We need to wait until we have listed all images in the dirlister to // start the slideshow because the SlideShow objects needs an image list to // work. d->mStartSlideShowWhenDirListerCompleted = true; } void MainWindow::setActiveViewModeAction(QAction* action) { if (d->mCurrentMainPageId != StartMainPageId) { d->saveSideBarConfig(); } if (action == d->mViewAction) { d->mCurrentMainPageId = ViewMainPageId; // Switching to view mode d->mViewStackedWidget->setCurrentWidget(d->mViewMainPage); openSelectedDocuments(); d->mPreloadDirectionIsForward = true; QTimer::singleShot(VIEW_PRELOAD_DELAY, this, SLOT(preloadNextUrl())); } else { d->mCurrentMainPageId = BrowseMainPageId; // Switching to browse mode d->mViewStackedWidget->setCurrentWidget(d->mBrowseMainPage); if (!d->mViewMainPage->isEmpty() && KProtocolManager::supportsListing(d->mViewMainPage->url())) { // Reset the view to spare resources, but don't do it if we can't // browse the url, otherwise if the user starts Gwenview this way: // gwenview http://example.com/example.png // and switch to browse mode, switching back to view mode won't bring // his image back. d->mViewMainPage->reset(); } setCaption(QString()); } d->loadSideBarConfig(); d->autoAssignThumbnailProvider(); + toggleStatusBar(d->statusBarVisibility()); emit viewModeChanged(); } void MainWindow::slotThumbnailViewIndexActivated(const QModelIndex& index) { if (!index.isValid()) { return; } KFileItem item = d->mDirModel->itemForIndex(index); if (item.isDir()) { // Item is a dir, open it openDirUrl(item.url()); } else { QString protocol = ArchiveUtils::protocolForMimeType(item.mimetype()); if (!protocol.isEmpty()) { // Item is an archive, tweak url then open it QUrl url = item.url(); url.setScheme(protocol); openDirUrl(url); } else { // Item is a document, switch to view mode d->mViewAction->trigger(); } } } void MainWindow::openSelectedDocuments() { if (d->mCurrentMainPageId != ViewMainPageId) { return; } int count = 0; QList urls; const auto list = d->mContextManager->selectedFileItemList(); for (const auto &item : list) { if (!item.isNull() && !ArchiveUtils::fileItemIsDirOrArchive(item)) { urls << item.url(); ++count; if (count == ViewMainPage::MaxViewCount) { break; } } } if (urls.isEmpty()) { // Selection contains no fitting items // Switch back to browsing mode d->mBrowseAction->trigger(); d->mViewMainPage->reset(); return; } QUrl currentUrl = d->mContextManager->currentUrl(); if (currentUrl.isEmpty() || !urls.contains(currentUrl)) { // There is no current URL or it doesn't belong to selection // This can happen when user manually selects a group of items currentUrl = urls.first(); } d->mViewMainPage->openUrls(urls, currentUrl); } void MainWindow::goUp() { if (d->mCurrentMainPageId == BrowseMainPageId) { QUrl url = d->mContextManager->currentDirUrl(); url = KIO::upUrl(url); openDirUrl(url); } else { d->mBrowseAction->trigger(); } } void MainWindow::showStartMainPage() { if (d->mCurrentMainPageId != StartMainPageId) { d->saveSideBarConfig(); d->mCurrentMainPageId = StartMainPageId; } d->setActionsDisabledOnStartMainPageEnabled(false); d->mSideBar->hide(); d->mViewStackedWidget->setCurrentWidget(d->mStartMainPage); d->updateActions(); updatePreviousNextActions(); d->mContextManager->setCurrentDirUrl(QUrl()); d->mContextManager->setCurrentUrl(QUrl()); d->autoAssignThumbnailProvider(); } void MainWindow::slotStartMainPageUrlSelected(const QUrl &url) { d->setActionsDisabledOnStartMainPageEnabled(true); if (d->mBrowseAction->isChecked()) { // Silently uncheck the action so that setInitialUrl() does the right thing SignalBlocker blocker(d->mBrowseAction); d->mBrowseAction->setChecked(false); } setInitialUrl(url); } void MainWindow::openDirUrl(const QUrl &url) { const QUrl currentUrl = d->mContextManager->currentDirUrl(); if (url == currentUrl) { return; } if (url.isParentOf(currentUrl)) { // Keep first child between url and currentUrl selected // If currentPath is "/home/user/photos/2008/event" // and wantedPath is "/home/user/photos" // pathToSelect should be "/home/user/photos/2008" // To anyone considering using QUrl::toLocalFile() instead of // QUrl::path() here. Please don't, using QUrl::path() is the right // thing to do here. const QString currentPath = QDir::cleanPath(currentUrl.adjusted(QUrl::StripTrailingSlash).path()); const QString wantedPath = QDir::cleanPath(url.adjusted(QUrl::StripTrailingSlash).path()); const QChar separator('/'); const int slashCount = wantedPath.count(separator); const QString pathToSelect = currentPath.section(separator, 0, slashCount + 1); QUrl urlToSelect = url; urlToSelect.setPath(pathToSelect); d->mContextManager->setUrlToSelect(urlToSelect); } d->mThumbnailProvider->stop(); d->mContextManager->setCurrentDirUrl(url); d->mGvCore->addUrlToRecentFolders(url); d->mViewMainPage->reset(); } void MainWindow::folderViewUrlChanged(const QUrl &url) { const QUrl currentUrl = d->mContextManager->currentDirUrl(); if (url == currentUrl) { switch (d->mCurrentMainPageId) { case ViewMainPageId: d->mBrowseAction->trigger(); break; case BrowseMainPageId: d->mViewAction->trigger(); break; case StartMainPageId: break; } } else { openDirUrl(url); } } void MainWindow::toggleSideBar(bool on) { d->mSideBar->setVisible(on); } -void MainWindow::toggleStatusBar() +void MainWindow::toggleStatusBar(bool visible) { - d->mViewMainPage->setStatusBarVisible(d->mShowStatusBarAction->isChecked()); - d->mBrowseMainPage->setStatusBarVisible(d->mShowStatusBarAction->isChecked()); + d->mShowStatusBarAction->setChecked(visible); + d->setStatusBarVisibility(visible); + + d->mViewMainPage->setStatusBarVisible(visible); + d->mBrowseMainPage->setStatusBarVisible(visible); } void MainWindow::updateToggleSideBarAction() { SignalBlocker blocker(d->mToggleSideBarAction); bool visible = d->mSideBar->isVisible(); d->mToggleSideBarAction->setChecked(visible); QString text; if (QApplication::isRightToLeft()) { text = QString::fromUtf8(visible ? "▮→" : "▮←"); } else { text = QString::fromUtf8(visible ? "▮←" : "▮→"); } QString toolTip = visible ? i18nc("@info:tooltip", "Hide sidebar") : i18nc("@info:tooltip", "Show sidebar"); QList lst; lst << d->mBrowseMainPage->toggleSideBarButton() << d->mViewMainPage->toggleSideBarButton(); Q_FOREACH(QToolButton * button, lst) { button->setText(text); button->setToolTip(toolTip); } } void MainWindow::slotPartCompleted() { d->updateActions(); const QUrl url = d->mContextManager->currentUrl(); if (!url.isEmpty()) { d->mFileOpenRecentAction->addUrl(url); d->mGvCore->addUrlToRecentFiles(url); } if (KProtocolManager::supportsListing(url)) { const QUrl dirUrl = d->mContextManager->currentDirUrl(); d->mGvCore->addUrlToRecentFolders(dirUrl); } } void MainWindow::slotSelectionChanged() { if (d->mCurrentMainPageId == ViewMainPageId) { // The user selected a new file in the thumbnail view, since the // document view is visible, let's show it openSelectedDocuments(); } else { // No document view, we need to load the document to set the undo group // of document factory to the correct QUndoStack QModelIndex index = d->mThumbnailView->currentIndex(); KFileItem item; if (index.isValid()) { item = d->mDirModel->itemForIndex(index); } QUndoGroup* undoGroup = DocumentFactory::instance()->undoGroup(); if (!item.isNull() && !ArchiveUtils::fileItemIsDirOrArchive(item)) { QUrl url = item.url(); Document::Ptr doc = DocumentFactory::instance()->load(url); undoGroup->addStack(doc->undoStack()); undoGroup->setActiveStack(doc->undoStack()); } else { undoGroup->setActiveStack(nullptr); } } // Update UI d->updateActions(); updatePreviousNextActions(); // Start preloading int preloadDelay = d->mCurrentMainPageId == ViewMainPageId ? VIEW_PRELOAD_DELAY : BROWSE_PRELOAD_DELAY; QTimer::singleShot(preloadDelay, this, SLOT(preloadNextUrl())); } void MainWindow::slotCurrentDirUrlChanged(const QUrl &url) { if (url.isValid()) { d->mUrlNavigator->setLocationUrl(url); d->mGoUpAction->setEnabled(url.path() != "/"); } else { d->mGoUpAction->setEnabled(false); } } void MainWindow::slotDirModelNewItems() { if (d->mContextManager->selectionModel()->hasSelection()) { updatePreviousNextActions(); } } void MainWindow::slotDirListerCompleted() { if (d->mStartSlideShowWhenDirListerCompleted) { d->mStartSlideShowWhenDirListerCompleted = false; QTimer::singleShot(0, d->mToggleSlideShowAction, SLOT(trigger())); } if (d->mContextManager->selectionModel()->hasSelection()) { updatePreviousNextActions(); } else { d->goToFirstDocument(); if (!d->mContextManager->selectionModel()->hasSelection()) { const QModelIndex index = d->mThumbnailView->model()->index(0, 0); if (index.isValid()) { d->mThumbnailView->setCurrentIndex(index); } } } d->mThumbnailView->scrollToSelectedIndex(); d->mViewMainPage->thumbnailBar()->scrollToSelectedIndex(); } void MainWindow::goToPrevious() { d->goTo(-1); } void MainWindow::goToNext() { d->goTo(1); } void MainWindow::goToFirst() { d->goToFirstDocument(); } void MainWindow::goToLast() { d->goToLastDocument(); } void MainWindow::goToUrl(const QUrl &url) { if (d->mCurrentMainPageId == ViewMainPageId) { d->mViewMainPage->openUrl(url); } QUrl dirUrl = url; dirUrl = dirUrl.adjusted(QUrl::RemoveFilename); dirUrl.setPath(dirUrl.path() + ""); if (dirUrl != d->mContextManager->currentDirUrl()) { d->mContextManager->setCurrentDirUrl(dirUrl); d->mGvCore->addUrlToRecentFolders(dirUrl); } d->mContextManager->setUrlToSelect(url); } void MainWindow::updatePreviousNextActions() { bool hasPrevious; bool hasNext; QModelIndex currentIndex = d->mContextManager->selectionModel()->currentIndex(); if (currentIndex.isValid() && !d->indexIsDirOrArchive(currentIndex)) { int row = currentIndex.row(); QModelIndex prevIndex = d->mDirModel->index(row - 1, 0); QModelIndex nextIndex = d->mDirModel->index(row + 1, 0); hasPrevious = prevIndex.isValid() && !d->indexIsDirOrArchive(prevIndex); hasNext = nextIndex.isValid() && !d->indexIsDirOrArchive(nextIndex); } else { hasPrevious = false; hasNext = false; } d->mGoToPreviousAction->setEnabled(hasPrevious); d->mGoToNextAction->setEnabled(hasNext); d->mGoToFirstAction->setEnabled(hasPrevious); d->mGoToLastAction->setEnabled(hasNext); } void MainWindow::leaveFullScreen() { if (d->mFullScreenAction->isChecked()) { d->mFullScreenAction->trigger(); } } void MainWindow::toggleFullScreen(bool checked) { setUpdatesEnabled(false); d->saveSideBarConfig(); if (checked) { // Save MainWindow config now, this way if we quit while in // fullscreen, we are sure latest MainWindow changes are remembered. KConfigGroup saveConfigGroup = autoSaveConfigGroup(); if (!isFullScreen()) { // Save state if window manager did not already switch to fullscreen. saveMainWindowSettings(saveConfigGroup); d->mStateBeforeFullScreen.mToolBarVisible = toolBar()->isVisible(); } setAutoSaveSettings(saveConfigGroup, false); resetAutoSaveSettings(); // Go full screen KToggleFullScreenAction::setFullScreen(this, true); menuBar()->hide(); toolBar()->hide(); qApp->setProperty("KDE_COLOR_SCHEME_PATH", d->mGvCore->fullScreenPaletteName()); QApplication::setPalette(d->mGvCore->palette(GvCore::FullScreenPalette)); d->setScreenSaverEnabled(false); } else { setAutoSaveSettings(); // Back to normal qApp->setProperty("KDE_COLOR_SCHEME_PATH", QVariant()); QApplication::setPalette(d->mGvCore->palette(GvCore::NormalPalette)); d->mSlideShow->stop(); KToggleFullScreenAction::setFullScreen(this, false); menuBar()->setVisible(d->mShowMenuBarAction->isChecked()); - toggleStatusBar(); toolBar()->setVisible(d->mStateBeforeFullScreen.mToolBarVisible); d->setScreenSaverEnabled(true); // See resizeEvent d->mFullScreenLeftAt = QDateTime::currentDateTime(); } d->mFullScreenContent->setFullScreenMode(checked); d->mBrowseMainPage->setFullScreenMode(checked); d->mViewMainPage->setFullScreenMode(checked); d->mSaveBar->setFullScreenMode(checked); d->loadSideBarConfig(); + toggleStatusBar(d->statusBarVisibility()); setUpdatesEnabled(true); d->autoAssignThumbnailProvider(); } void MainWindow::saveCurrent() { d->mGvCore->save(d->mContextManager->currentUrl()); } void MainWindow::saveCurrentAs() { d->mGvCore->saveAs(d->mContextManager->currentUrl()); } void MainWindow::reload() { if (d->mCurrentMainPageId == ViewMainPageId) { d->mViewMainPage->reload(); } else { d->mBrowseMainPage->reload(); } } void MainWindow::openFile() { QUrl dirUrl = d->mContextManager->currentDirUrl(); DialogGuard dialog(this); dialog->selectUrl(dirUrl); dialog->setWindowTitle(i18nc("@title:window", "Open Image")); const QStringList mimeFilter = MimeTypeUtils::imageMimeTypes(); dialog->setMimeTypeFilters(mimeFilter); dialog->setAcceptMode(QFileDialog::AcceptOpen); if (!dialog->exec()) { return; } if (!dialog->selectedUrls().isEmpty()) { openUrl(dialog->selectedUrls().first()); } } void MainWindow::openUrl(const QUrl& url) { d->setActionsDisabledOnStartMainPageEnabled(true); d->mContextManager->setUrlToSelect(url); d->mViewAction->trigger(); } void MainWindow::showDocumentInFullScreen(const QUrl &url) { d->mContextManager->setUrlToSelect(url); d->mFullScreenAction->trigger(); } void MainWindow::toggleSlideShow() { if (d->mSlideShow->isRunning()) { d->mSlideShow->stop(); } else { if (!d->mViewAction->isChecked()) { d->mViewAction->trigger(); } if (!d->mFullScreenAction->isChecked()) { d->mFullScreenAction->trigger(); } QList list; for (int pos = 0; pos < d->mDirModel->rowCount(); ++pos) { QModelIndex index = d->mDirModel->index(pos, 0); KFileItem item = d->mDirModel->itemForIndex(index); MimeTypeUtils::Kind kind = MimeTypeUtils::fileItemKind(item); switch (kind) { case MimeTypeUtils::KIND_SVG_IMAGE: case MimeTypeUtils::KIND_RASTER_IMAGE: case MimeTypeUtils::KIND_VIDEO: list << item.url(); break; default: break; } } d->mSlideShow->start(list); } updateSlideShowAction(); } void MainWindow::updateSlideShowAction() { if (d->mSlideShow->isRunning()) { d->mToggleSlideShowAction->setText(i18n("Stop Slideshow")); d->mToggleSlideShowAction->setIcon(QIcon::fromTheme("media-playback-pause")); } else { d->mToggleSlideShowAction->setText(i18n("Start Slideshow")); d->mToggleSlideShowAction->setIcon(QIcon::fromTheme("media-playback-start")); } } bool MainWindow::queryClose() { saveConfig(); d->saveSideBarConfig(); QList list = DocumentFactory::instance()->modifiedDocumentList(); if (list.size() == 0) { return true; } KGuiItem yes(i18n("Save All Changes"), "document-save-all"); KGuiItem no(i18n("Discard Changes")); QString msg = i18np("One image has been modified.", "%1 images have been modified.", list.size()) + '\n' + i18n("If you quit now, your changes will be lost."); int answer = KMessageBox::warningYesNoCancel( this, msg, QString() /* caption */, yes, no); switch (answer) { case KMessageBox::Yes: d->mGvCore->saveAll(); // We need to wait a bit because the DocumentFactory is notified about // saved documents through a queued connection. qApp->processEvents(); return DocumentFactory::instance()->modifiedDocumentList().isEmpty(); case KMessageBox::No: return true; default: // cancel return false; } } void MainWindow::showConfigDialog() { // Save first so changes like thumbnail zoom level are not lost when reloading config saveConfig(); DialogGuard dialog(this); connect(dialog.data(), SIGNAL(settingsChanged(QString)), SLOT(loadConfig())); dialog->exec(); } void MainWindow::toggleMenuBar() { if (!d->mFullScreenAction->isChecked()) { menuBar()->setVisible(d->mShowMenuBarAction->isChecked()); } } void MainWindow::loadConfig() { d->mDirModel->setBlackListedExtensions(GwenviewConfig::blackListedExtensions()); d->mDirModel->adjustKindFilter(MimeTypeUtils::KIND_VIDEO, GwenviewConfig::listVideos()); d->mFileOpenRecentAction->loadEntries(KConfigGroup(KSharedConfig::openConfig(), "Recent Files")); foreach(const QUrl &url, d->mFileOpenRecentAction->urls()) { d->mGvCore->addUrlToRecentFiles(url); } d->mStartMainPage->loadConfig(); d->mViewMainPage->loadConfig(); d->mBrowseMainPage->loadConfig(); d->mContextManager->loadConfig(); - - d->mShowStatusBarAction->setChecked(GwenviewConfig::statusBarIsVisible()); - toggleStatusBar(); } void MainWindow::saveConfig() { d->mFileOpenRecentAction->saveEntries(KConfigGroup(KSharedConfig::openConfig(), "Recent Files")); - GwenviewConfig::setStatusBarIsVisible(d->mShowStatusBarAction->isChecked()); d->mViewMainPage->saveConfig(); d->mBrowseMainPage->saveConfig(); d->mContextManager->saveConfig(); } void MainWindow::print() { if (!d->mContextManager->currentUrlIsRasterImage()) { return; } Document::Ptr doc = DocumentFactory::instance()->load(d->mContextManager->currentUrl()); PrintHelper printHelper(this); printHelper.print(doc); } void MainWindow::preloadNextUrl() { static bool disablePreload = qgetenv("GV_MAX_UNREFERENCED_IMAGES") == "0"; if (disablePreload) { qDebug() << "Preloading disabled"; return; } QItemSelection selection = d->mContextManager->selectionModel()->selection(); if (selection.size() != 1) { return; } QModelIndexList indexList = selection.indexes(); if (indexList.isEmpty()) { return; } QModelIndex index = indexList.at(0); if (!index.isValid()) { return; } if (d->mCurrentMainPageId == ViewMainPageId) { // If we are in view mode, preload the next url, otherwise preload the // selected one int offset = d->mPreloadDirectionIsForward ? 1 : -1; index = d->mDirModel->sibling(index.row() + offset, index.column(), index); if (!index.isValid()) { return; } } KFileItem item = d->mDirModel->itemForIndex(index); if (!ArchiveUtils::fileItemIsDirOrArchive(item)) { QUrl url = item.url(); if (url.isLocalFile()) { QSize size = d->mViewStackedWidget->size(); d->mPreloader->preload(url, size); } } } QSize MainWindow::sizeHint() const { return KXmlGuiWindow::sizeHint().expandedTo(QSize(750, 500)); } void MainWindow::showEvent(QShowEvent *event) { // We need to delay initializing the action state until the menu bar has // been initialized, that's why it's done only in the showEvent() d->mShowMenuBarAction->setChecked(menuBar()->isVisible()); KXmlGuiWindow::showEvent(event); } void MainWindow::resizeEvent(QResizeEvent* event) { KXmlGuiWindow::resizeEvent(event); // This is a small hack to execute code after leaving fullscreen, and after // the window has been resized back to its former size. if (d->mFullScreenLeftAt.isValid() && d->mFullScreenLeftAt.secsTo(QDateTime::currentDateTime()) < 2) { if (d->mCurrentMainPageId == BrowseMainPageId) { d->mThumbnailView->scrollToSelectedIndex(); } d->mFullScreenLeftAt = QDateTime(); } } bool MainWindow::eventFilter(QObject *obj, QEvent *event) { Q_UNUSED(obj); Q_UNUSED(event); #ifdef Q_OS_OSX /** * handle Mac OS X file open events (only exist on OS X) */ if (event->type() == QEvent::FileOpen) { QFileOpenEvent *fileOpenEvent = static_cast(event); openUrl(fileOpenEvent->url()); return true; } #endif return false; } void MainWindow::setDistractionFreeMode(bool value) { d->mFullScreenContent->setDistractionFreeMode(value); } void MainWindow::saveProperties(KConfigGroup& group) { group.writeEntry(SESSION_CURRENT_PAGE_KEY, int(d->mCurrentMainPageId)); group.writeEntry(SESSION_URL_KEY, d->mContextManager->currentUrl().toString()); } void MainWindow::readProperties(const KConfigGroup& group) { MainPageId pageId = MainPageId(group.readEntry(SESSION_CURRENT_PAGE_KEY, int(StartMainPageId))); if (pageId == StartMainPageId) { d->mCurrentMainPageId = StartMainPageId; showStartMainPage(); } else if (pageId == BrowseMainPageId) { d->mBrowseAction->trigger(); } else { d->mViewAction->trigger(); } QUrl url = group.readEntry(SESSION_URL_KEY, QUrl()); if (!url.isValid()) { qWarning() << "Invalid url!"; return; } goToUrl(url); } void MainWindow::showFirstDocumentReached() { if (d->mCurrentMainPageId != ViewMainPageId) { return; } HudButtonBox* dlg = new HudButtonBox; dlg->setText(i18n("You reached the first document, what do you want to do?")); dlg->addButton(i18n("Stay There")); dlg->addAction(d->mGoToLastAction, i18n("Go to the Last Document")); dlg->addAction(d->mBrowseAction, i18n("Go Back to the Document List")); dlg->addCountDown(15000); d->mViewMainPage->showMessageWidget(dlg, Qt::AlignCenter); } void MainWindow::showLastDocumentReached() { if (d->mCurrentMainPageId != ViewMainPageId) { return; } HudButtonBox* dlg = new HudButtonBox; dlg->setText(i18n("You reached the last document, what do you want to do?")); dlg->addButton(i18n("Stay There")); dlg->addAction(d->mGoToFirstAction, i18n("Go to the First Document")); dlg->addAction(d->mBrowseAction, i18n("Go Back to the Document List")); dlg->addCountDown(15000); d->mViewMainPage->showMessageWidget(dlg, Qt::AlignCenter); } } // namespace diff --git a/app/mainwindow.h b/app/mainwindow.h index 9183f380..0bad71f8 100644 --- a/app/mainwindow.h +++ b/app/mainwindow.h @@ -1,145 +1,145 @@ /* Gwenview: an image viewer Copyright 2007 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MAINWINDOW_H #define MAINWINDOW_H // Qt #include // KDE #include class QModelIndex; class QUrl; namespace Gwenview { class ViewMainPage; class ContextManager; class MainWindow : public KXmlGuiWindow { Q_OBJECT public: MainWindow(); ~MainWindow(); /** * Defines the url to display when the window is shown for the first time. */ void setInitialUrl(const QUrl&); void startSlideShow(); ViewMainPage* viewMainPage() const; ContextManager* contextManager() const; void setDistractionFreeMode(bool); public Q_SLOTS: void showStartMainPage(); /** * Go to url, without changing current mode */ void goToUrl(const QUrl&); Q_SIGNALS: void viewModeChanged(); public Q_SLOTS: virtual void setCaption(const QString&) Q_DECL_OVERRIDE; virtual void setCaption(const QString&, bool modified) Q_DECL_OVERRIDE; protected: virtual bool queryClose() Q_DECL_OVERRIDE; virtual QSize sizeHint() const Q_DECL_OVERRIDE; virtual void showEvent(QShowEvent*) Q_DECL_OVERRIDE; virtual void resizeEvent(QResizeEvent*) Q_DECL_OVERRIDE; virtual void saveProperties(KConfigGroup&) Q_DECL_OVERRIDE; virtual void readProperties(const KConfigGroup&) Q_DECL_OVERRIDE; bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE; private Q_SLOTS: void setActiveViewModeAction(QAction* action); void openDirUrl(const QUrl&); void slotThumbnailViewIndexActivated(const QModelIndex&); void slotStartMainPageUrlSelected(const QUrl&); void goUp(); void toggleSideBar(bool visible); void updateToggleSideBarAction(); void slotModifiedDocumentListChanged(); void slotUpdateCaption(const QString& caption); void slotPartCompleted(); void slotDirModelNewItems(); void slotDirListerCompleted(); void slotSelectionChanged(); void slotCurrentDirUrlChanged(const QUrl &url); void goToPrevious(); void goToNext(); void goToFirst(); void goToLast(); void updatePreviousNextActions(); void leaveFullScreen(); void toggleFullScreen(bool); void toggleSlideShow(); void updateSlideShowAction(); void saveCurrent(); void saveCurrentAs(); void openFile(); void openUrl(const QUrl& url); void reload(); void showDocumentInFullScreen(const QUrl&); void showConfigDialog(); void loadConfig(); void print(); void preloadNextUrl(); void toggleMenuBar(); - void toggleStatusBar(); + void toggleStatusBar(bool visible); void showFirstDocumentReached(); void showLastDocumentReached(); private: struct Private; MainWindow::Private* const d; void openSelectedDocuments(); void saveConfig(); void folderViewUrlChanged(const QUrl &url); }; } // namespace #endif /* MAINWINDOW_H */ diff --git a/app/viewmainpage.cpp b/app/viewmainpage.cpp index ed616865..1117ac09 100644 --- a/app/viewmainpage.cpp +++ b/app/viewmainpage.cpp @@ -1,821 +1,819 @@ /* Gwenview: an image viewer Copyright 2007 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "viewmainpage.h" // Qt #include #include #include #include #include #include #include // KDE #include #include #include #include #include #include #include // Local #include "fileoperations.h" #include #include "splitter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Gwenview { #undef ENABLE_LOG #undef LOG //#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) qDebug() << x #else #define LOG(x) ; #endif const int ViewMainPage::MaxViewCount = 6; /* * Layout of mThumbnailSplitter is: * * +-mThumbnailSplitter------------------------------------------------+ * |+-mAdapterContainer-----------------------------------------------+| * ||+-mDocumentViewContainer----------------------------------------+|| * |||+-DocumentView----------------++-DocumentView-----------------+||| * |||| || |||| * |||| || |||| * |||| || |||| * |||| || |||| * |||| || |||| * |||| || |||| * |||+-----------------------------++------------------------------+||| * ||+---------------------------------------------------------------+|| * ||+-mToolContainer------------------------------------------------+|| * ||| ||| * ||+---------------------------------------------------------------+|| * ||+-mStatusBarContainer-------------------------------------------+|| * |||[mToggleSideBarButton][mToggleThumbnailBarButton] [mZoomWidget]||| * ||+---------------------------------------------------------------+|| * |+-----------------------------------------------------------------+| * |===================================================================| * |+-mThumbnailBar---------------------------------------------------+| * || || * || || * |+-----------------------------------------------------------------+| * +-------------------------------------------------------------------+ */ struct ViewMainPagePrivate { ViewMainPage* q; SlideShow* mSlideShow; KActionCollection* mActionCollection; GvCore* mGvCore; KModelIndexProxyMapper* mDirModelToBarModelProxyMapper; QSplitter *mThumbnailSplitter; QWidget* mAdapterContainer; DocumentViewController* mDocumentViewController; QList mDocumentViews; DocumentViewSynchronizer* mSynchronizer; QToolButton* mToggleSideBarButton; QToolButton* mToggleThumbnailBarButton; ZoomWidget* mZoomWidget; DocumentViewContainer* mDocumentViewContainer; SlideContainer* mToolContainer; QWidget* mStatusBarContainer; ThumbnailBarView* mThumbnailBar; KToggleAction* mToggleThumbnailBarAction; KToggleAction* mSynchronizeAction; QCheckBox* mSynchronizeCheckBox; // Activity Resource events reporting needs to be above KPart, // in the shell itself, to avoid problems with other MDI applications // that use this KPart QHash mActivityResources; bool mCompareMode; bool mThumbnailBarVisibleBeforeFullScreen; ZoomMode::Enum mZoomMode; void setupThumbnailBar() { mThumbnailBar = new ThumbnailBarView; ThumbnailBarItemDelegate* delegate = new ThumbnailBarItemDelegate(mThumbnailBar); mThumbnailBar->setItemDelegate(delegate); mThumbnailBar->setVisible(GwenviewConfig::thumbnailBarIsVisible()); mThumbnailBar->setSelectionMode(QAbstractItemView::ExtendedSelection); } void setupThumbnailBarStyleSheet() { QPalette pal = mGvCore->palette(GvCore::NormalViewPalette); mThumbnailBar->setPalette(pal); Qt::Orientation orientation = mThumbnailBar->orientation(); QColor bgColor = pal.color(QPalette::Normal, QPalette::Base); QColor bgSelColor = pal.color(QPalette::Normal, QPalette::Highlight); QColor bgHovColor = pal.color(QPalette::Normal, QPalette::Highlight); // Avoid dark and bright colors bgColor.setHsv(bgColor.hue(), bgColor.saturation(), (127 + 3 * bgColor.value()) / 4); // Hover uses lighter/faded version of select color. Combine with bgColor to adapt to different backgrounds bgHovColor.setHsv(bgHovColor.hue(), (bgHovColor.saturation() / 2), ((bgHovColor.value() + bgColor.value()) / 2)); QColor leftBorderColor = PaintUtils::adjustedHsv(bgColor, 0, 0, qMin(20, 255 - bgColor.value())); QColor rightBorderColor = PaintUtils::adjustedHsv(bgColor, 0, 0, -qMin(40, bgColor.value())); QColor borderSelColor = PaintUtils::adjustedHsv(bgSelColor, 0, 0, -qMin(60, bgSelColor.value())); QString itemCss = "QListView::item {" " background-color: %1;" " border-left: 1px solid %2;" " border-right: 1px solid %3;" "}"; itemCss = itemCss.arg( StyleSheetUtils::gradient(orientation, bgColor, 46), StyleSheetUtils::gradient(orientation, leftBorderColor, 36), StyleSheetUtils::gradient(orientation, rightBorderColor, 26)); QString itemSelCss = "QListView::item:selected {" " background-color: %1;" " border-left: 1px solid %2;" " border-right: 1px solid %2;" "}"; itemSelCss = itemSelCss.arg( StyleSheetUtils::gradient(orientation, bgSelColor, 56), StyleSheetUtils::rgba(borderSelColor)); QString itemHovCss = "QListView::item:hover:!selected {" " background-color: %1;" " border-left: 1px solid %2;" " border-right: 1px solid %3;" "}"; itemHovCss = itemHovCss.arg( StyleSheetUtils::gradient(orientation, bgHovColor, 56), StyleSheetUtils::rgba(leftBorderColor), StyleSheetUtils::rgba(rightBorderColor)); QString css = itemCss + itemSelCss + itemHovCss; if (orientation == Qt::Vertical) { css.replace("left", "top").replace("right", "bottom"); } mThumbnailBar->setStyleSheet(css); } void setupAdapterContainer() { mAdapterContainer = new QWidget; QVBoxLayout* layout = new QVBoxLayout(mAdapterContainer); layout->setMargin(0); layout->setSpacing(0); mDocumentViewContainer = new DocumentViewContainer; mDocumentViewContainer->setAutoFillBackground(true); mDocumentViewContainer->setBackgroundRole(QPalette::Base); layout->addWidget(mDocumentViewContainer); layout->addWidget(mToolContainer); layout->addWidget(mStatusBarContainer); } void setupDocumentViewController() { mDocumentViewController = new DocumentViewController(mActionCollection, q); mDocumentViewController->setZoomWidget(mZoomWidget); mDocumentViewController->setToolContainer(mToolContainer); mSynchronizer = new DocumentViewSynchronizer(&mDocumentViews, q); } DocumentView* createDocumentView() { DocumentView* view = mDocumentViewContainer->createView(); // Connect context menu // If you need to connect another view signal, make sure it is disconnected in deleteDocumentView QObject::connect(view, &DocumentView::contextMenuRequested, q, &ViewMainPage::showContextMenu); QObject::connect(view, &DocumentView::completed, q, &ViewMainPage::completed); QObject::connect(view, &DocumentView::previousImageRequested, q, &ViewMainPage::previousImageRequested); QObject::connect(view, &DocumentView::nextImageRequested, q, &ViewMainPage::nextImageRequested); QObject::connect(view, &DocumentView::captionUpdateRequested, q, &ViewMainPage::captionUpdateRequested); QObject::connect(view, &DocumentView::toggleFullScreenRequested, q, &ViewMainPage::toggleFullScreenRequested); QObject::connect(view, &DocumentView::focused, q, &ViewMainPage::slotViewFocused); QObject::connect(view, &DocumentView::hudTrashClicked, q, &ViewMainPage::trashView); QObject::connect(view, &DocumentView::hudDeselectClicked, q, &ViewMainPage::deselectView); QObject::connect(view, &DocumentView::videoFinished, mSlideShow, &SlideShow::resumeAndGoToNextUrl); mDocumentViews << view; mActivityResources.insert(view, new KActivities::ResourceInstance(q->window()->winId(), view)); return view; } void deleteDocumentView(DocumentView* view) { if (mDocumentViewController->view() == view) { mDocumentViewController->setView(0); } // Make sure we do not get notified about this view while it is going away. // mDocumentViewController->deleteView() animates the view deletion so // the view still exists for a short while when we come back to the // event loop) QObject::disconnect(view, 0, q, 0); QObject::disconnect(view, 0, mSlideShow, 0); mDocumentViews.removeOne(view); mActivityResources.remove(view); mDocumentViewContainer->deleteView(view); } void setupToolContainer() { mToolContainer = new SlideContainer; } void setupStatusBar() { mStatusBarContainer = new QWidget; mToggleSideBarButton = new StatusBarToolButton; mToggleThumbnailBarButton = new StatusBarToolButton; mZoomWidget = new ZoomWidget; mSynchronizeCheckBox = new QCheckBox(i18n("Synchronize")); mSynchronizeCheckBox->hide(); QHBoxLayout* layout = new QHBoxLayout(mStatusBarContainer); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(mToggleSideBarButton); layout->addWidget(mToggleThumbnailBarButton); layout->addStretch(); layout->addWidget(mSynchronizeCheckBox); layout->addStretch(); layout->addWidget(mZoomWidget); } void setupSplitter() { Qt::Orientation orientation = GwenviewConfig::thumbnailBarOrientation(); mThumbnailSplitter = new Splitter(orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal, q); mThumbnailBar->setOrientation(orientation); mThumbnailBar->setThumbnailAspectRatio(GwenviewConfig::thumbnailAspectRatio()); mThumbnailBar->setRowCount(GwenviewConfig::thumbnailBarRowCount()); mThumbnailSplitter->addWidget(mAdapterContainer); mThumbnailSplitter->addWidget(mThumbnailBar); mThumbnailSplitter->setSizes(GwenviewConfig::thumbnailSplitterSizes()); QVBoxLayout* layout = new QVBoxLayout(q); layout->setMargin(0); layout->addWidget(mThumbnailSplitter); } void saveSplitterConfig() { if (mThumbnailBar->isVisible()) { GwenviewConfig::setThumbnailSplitterSizes(mThumbnailSplitter->sizes()); } } DocumentView* currentView() const { return mDocumentViewController->view(); } void setCurrentView(DocumentView* view) { DocumentView* oldView = currentView(); if (view == oldView) { return; } if (oldView) { oldView->setCurrent(false); Q_ASSERT(mActivityResources.contains(oldView)); mActivityResources.value(oldView)->notifyFocusedOut(); } view->setCurrent(true); mDocumentViewController->setView(view); mSynchronizer->setCurrentView(view); QModelIndex index = indexForView(view); if (index.isValid()) { // Index may be invalid when Gwenview is started as // `gwenview /foo/image.png` because in this situation it loads image.png // *before* listing /foo (because it matters less to the user) mThumbnailBar->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Current); } Q_ASSERT(mActivityResources.contains(view)); mActivityResources.value(view)->notifyFocusedIn(); } QModelIndex indexForView(DocumentView* view) const { QUrl url = view->url(); if (!url.isValid()) { qWarning() << "View does not display any document!"; return QModelIndex(); } SortedDirModel* dirModel = mGvCore->sortedDirModel(); QModelIndex srcIndex = dirModel->indexForUrl(url); if (!mDirModelToBarModelProxyMapper) { // Delay the initialization of the mapper to its first use because // mThumbnailBar->model() is not set after ViewMainPage ctor is // done. const_cast(this)->mDirModelToBarModelProxyMapper = new KModelIndexProxyMapper(dirModel, mThumbnailBar->model(), q); } QModelIndex index = mDirModelToBarModelProxyMapper->mapLeftToRight(srcIndex); return index; } void applyPalette(bool fullScreenMode) { mDocumentViewContainer->setPalette(mGvCore->palette(fullScreenMode ? GvCore::FullScreenViewPalette : GvCore::NormalViewPalette)); setupThumbnailBarStyleSheet(); } }; ViewMainPage::ViewMainPage(QWidget* parent, SlideShow* slideShow, KActionCollection* actionCollection, GvCore* gvCore) : QWidget(parent) , d(new ViewMainPagePrivate) { d->q = this; d->mDirModelToBarModelProxyMapper = 0; // Initialized later d->mSlideShow = slideShow; d->mActionCollection = actionCollection; d->mGvCore = gvCore; d->mCompareMode = false; d->mThumbnailBarVisibleBeforeFullScreen = false; QShortcut* enterKeyShortcut = new QShortcut(Qt::Key_Return, this); connect(enterKeyShortcut, &QShortcut::activated, this, &ViewMainPage::slotEnterPressed); d->setupToolContainer(); d->setupStatusBar(); d->setupAdapterContainer(); d->setupThumbnailBar(); d->setupSplitter(); d->setupDocumentViewController(); KActionCategory* view = new KActionCategory(i18nc("@title actions category - means actions changing smth in interface", "View"), actionCollection); d->mToggleThumbnailBarAction = view->add(QString("toggle_thumbnailbar")); d->mToggleThumbnailBarAction->setText(i18n("Thumbnail Bar")); d->mToggleThumbnailBarAction->setIcon(QIcon::fromTheme("folder-image")); actionCollection->setDefaultShortcut(d->mToggleThumbnailBarAction, Qt::CTRL + Qt::Key_B); d->mToggleThumbnailBarAction->setChecked(GwenviewConfig::thumbnailBarIsVisible()); connect(d->mToggleThumbnailBarAction, &KToggleAction::triggered, this, &ViewMainPage::setThumbnailBarVisibility); d->mToggleThumbnailBarButton->setDefaultAction(d->mToggleThumbnailBarAction); d->mSynchronizeAction = view->add("synchronize_views"); d->mSynchronizeAction->setText(i18n("Synchronize")); actionCollection->setDefaultShortcut(d->mSynchronizeAction, Qt::CTRL + Qt::Key_Y); connect(d->mSynchronizeAction, SIGNAL(toggled(bool)), d->mSynchronizer, SLOT(setActive(bool))); // Ensure mSynchronizeAction and mSynchronizeCheckBox are in sync connect(d->mSynchronizeAction, SIGNAL(toggled(bool)), d->mSynchronizeCheckBox, SLOT(setChecked(bool))); connect(d->mSynchronizeCheckBox, SIGNAL(toggled(bool)), d->mSynchronizeAction, SLOT(setChecked(bool))); installEventFilter(this); } ViewMainPage::~ViewMainPage() { delete d; } void ViewMainPage::loadConfig() { d->applyPalette(window()->isFullScreen()); // FIXME: Not symetric with saveConfig(). Check if it matters. Q_FOREACH(DocumentView * view, d->mDocumentViews) { view->loadAdapterConfig(); } Qt::Orientation orientation = GwenviewConfig::thumbnailBarOrientation(); d->mThumbnailSplitter->setOrientation(orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal); d->mThumbnailBar->setOrientation(orientation); d->setupThumbnailBarStyleSheet(); int oldRowCount = d->mThumbnailBar->rowCount(); int newRowCount = GwenviewConfig::thumbnailBarRowCount(); if (oldRowCount != newRowCount) { d->mThumbnailBar->setUpdatesEnabled(false); int gridSize = d->mThumbnailBar->gridSize().width(); d->mThumbnailBar->setRowCount(newRowCount); // Adjust splitter to ensure thumbnail size remains the same int delta = (newRowCount - oldRowCount) * gridSize; QList sizes = d->mThumbnailSplitter->sizes(); Q_ASSERT(sizes.count() == 2); sizes[0] -= delta; sizes[1] += delta; d->mThumbnailSplitter->setSizes(sizes); d->mThumbnailBar->setUpdatesEnabled(true); } d->mZoomMode = GwenviewConfig::zoomMode(); } void ViewMainPage::saveConfig() { d->saveSplitterConfig(); GwenviewConfig::setThumbnailBarIsVisible(d->mToggleThumbnailBarAction->isChecked()); } void ViewMainPage::setThumbnailBarVisibility(bool visible) { d->saveSplitterConfig(); d->mThumbnailBar->setVisible(visible); } int ViewMainPage::statusBarHeight() const { return d->mStatusBarContainer->height(); } void ViewMainPage::setStatusBarVisible(bool visible) { d->mStatusBarContainer->setVisible(visible); } void ViewMainPage::setFullScreenMode(bool fullScreenMode) { - d->mStatusBarContainer->setVisible(!fullScreenMode); - if (fullScreenMode) { d->mThumbnailBarVisibleBeforeFullScreen = d->mToggleThumbnailBarAction->isChecked(); if (d->mThumbnailBarVisibleBeforeFullScreen) { d->mToggleThumbnailBarAction->trigger(); } } else { if (d->mThumbnailBarVisibleBeforeFullScreen) { d->mToggleThumbnailBarAction->trigger(); } } d->applyPalette(fullScreenMode); d->mToggleThumbnailBarAction->setEnabled(!fullScreenMode); } ThumbnailBarView* ViewMainPage::thumbnailBar() const { return d->mThumbnailBar; } inline void addActionToMenu(QMenu* menu, KActionCollection* actionCollection, const char* name) { QAction* action = actionCollection->action(name); if (action) { menu->addAction(action); } } void ViewMainPage::showContextMenu() { QMenu menu(this); addActionToMenu(&menu, d->mActionCollection, "fullscreen"); menu.addSeparator(); addActionToMenu(&menu, d->mActionCollection, "go_previous"); addActionToMenu(&menu, d->mActionCollection, "go_next"); if (d->currentView()->canZoom()) { menu.addSeparator(); addActionToMenu(&menu, d->mActionCollection, "view_actual_size"); addActionToMenu(&menu, d->mActionCollection, "view_zoom_to_fit"); addActionToMenu(&menu, d->mActionCollection, "view_zoom_in"); addActionToMenu(&menu, d->mActionCollection, "view_zoom_out"); } if (d->mCompareMode) { menu.addSeparator(); addActionToMenu(&menu, d->mActionCollection, "synchronize_views"); } menu.addSeparator(); addActionToMenu(&menu, d->mActionCollection, "file_copy_to"); addActionToMenu(&menu, d->mActionCollection, "file_move_to"); addActionToMenu(&menu, d->mActionCollection, "file_link_to"); menu.addSeparator(); addActionToMenu(&menu, d->mActionCollection, "file_open_with"); addActionToMenu(&menu, d->mActionCollection, "file_open_containing_folder"); menu.exec(QCursor::pos()); } QSize ViewMainPage::sizeHint() const { return QSize(400, 300); } QSize ViewMainPage::minimumSizeHint() const { if (!layout()) { return QSize(); } QSize minimumSize = layout()->minimumSize(); if (window()->isFullScreen()) { // Check minimum width of the overlay fullscreen bar // since there is no layout link which could do this const FullScreenBar* fullScreenBar = findChild(); if (fullScreenBar && fullScreenBar->layout()) { const int fullScreenBarWidth = fullScreenBar->layout()->minimumSize().width(); if (fullScreenBarWidth > minimumSize.width()) { minimumSize.setWidth(fullScreenBarWidth); } } } return minimumSize; } QUrl ViewMainPage::url() const { GV_RETURN_VALUE_IF_FAIL(d->currentView(), QUrl()); return d->currentView()->url(); } Document::Ptr ViewMainPage::currentDocument() const { if (!d->currentView()) { LOG("!d->documentView()"); return Document::Ptr(); } return d->currentView()->document(); } bool ViewMainPage::isEmpty() const { return !currentDocument(); } RasterImageView* ViewMainPage::imageView() const { if (!d->currentView()) { return 0; } return d->currentView()->imageView(); } DocumentView* ViewMainPage::documentView() const { return d->currentView(); } void ViewMainPage::openUrl(const QUrl &url) { openUrls(QList() << url, url); } void ViewMainPage::openUrls(const QList& allUrls, const QUrl ¤tUrl) { DocumentView::Setup setup; QSet urls = allUrls.toSet(); d->mCompareMode = urls.count() > 1; typedef QMap ViewForUrlMap; ViewForUrlMap viewForUrlMap; if (!d->mDocumentViews.isEmpty()) { d->mDocumentViewContainer->updateSetup(d->mDocumentViews.last()); } if (d->mDocumentViews.isEmpty() || d->mZoomMode == ZoomMode::Autofit) { setup.valid = true; setup.zoomToFit = true; } else { setup = d->mDocumentViews.last()->setup(); } // Destroy views which show urls we don't care about, remove from "urls" the // urls which already have a view. Q_FOREACH(DocumentView * view, d->mDocumentViews) { QUrl url = view->url(); if (urls.contains(url)) { // view displays an url we must display, keep it urls.remove(url); viewForUrlMap.insert(url, view); } else { // view url is not interesting, drop it d->deleteDocumentView(view); } } // Create view for remaining urls Q_FOREACH(const QUrl &url, urls) { if (d->mDocumentViews.count() >= MaxViewCount) { qWarning() << "Too many documents to show"; break; } DocumentView* view = d->createDocumentView(); viewForUrlMap.insert(url, view); } // Set sortKey to match url order int sortKey = 0; Q_FOREACH(const QUrl &url, allUrls) { viewForUrlMap[url]->setSortKey(sortKey); ++sortKey; } d->mDocumentViewContainer->updateLayout(); // Load urls for new views. Do it only now because the view must have the // correct size before it starts loading its url. Do not do it later because // view->url() needs to be set for the next loop. ViewForUrlMap::ConstIterator it = viewForUrlMap.constBegin(), end = viewForUrlMap.constEnd(); for (; it != end; ++it) { QUrl url = it.key(); DocumentView* view = it.value(); DocumentView::Setup savedSetup = d->mDocumentViewContainer->savedSetup(url); view->openUrl(url, d->mZoomMode == ZoomMode::Individual && savedSetup.valid ? savedSetup : setup); d->mActivityResources.value(view)->setUri(url); } // Init views Q_FOREACH(DocumentView * view, d->mDocumentViews) { view->setCompareMode(d->mCompareMode); if (view->url() == currentUrl) { d->setCurrentView(view); } else { view->setCurrent(false); } } d->mSynchronizeCheckBox->setVisible(d->mCompareMode); if (d->mCompareMode) { d->mSynchronizer->setActive(d->mSynchronizeCheckBox->isChecked()); } else { d->mSynchronizer->setActive(false); } } void ViewMainPage::reload() { DocumentView *view = d->currentView(); if (!view) { return; } Document::Ptr doc = view->document(); if (!doc) { qWarning() << "!doc"; return; } if (doc->isModified()) { KGuiItem cont = KStandardGuiItem::cont(); cont.setText(i18nc("@action:button", "Discard Changes and Reload")); int answer = KMessageBox::warningContinueCancel(this, i18nc("@info", "This image has been modified. Reloading it will discard all your changes."), QString() /* caption */, cont); if (answer != KMessageBox::Continue) { return; } } doc->reload(); // Call openUrl again because DocumentView may need to switch to a new // adapter (for example because document was broken and it is not anymore) d->currentView()->openUrl(doc->url(), d->currentView()->setup()); } void ViewMainPage::reset() { d->mDocumentViewController->setView(0); d->mDocumentViewContainer->reset(); d->mDocumentViews.clear(); } void ViewMainPage::slotViewFocused(DocumentView* view) { d->setCurrentView(view); } void ViewMainPage::slotEnterPressed() { DocumentView *view = d->currentView(); if (view) { AbstractRasterImageViewTool *tool = view->currentTool(); if (tool) { QKeyEvent event(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier); tool->keyPressEvent(&event); if (event.isAccepted()) { return; } } } emit goToBrowseModeRequested(); } bool ViewMainPage::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::ShortcutOverride) { const QKeyEvent* keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Escape) { const DocumentView* view = d->currentView(); if (view) { AbstractRasterImageViewTool* tool = view->currentTool(); if (tool) { QKeyEvent toolKeyEvent(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier); tool->keyPressEvent(&toolKeyEvent); if (toolKeyEvent.isAccepted()) { event->accept(); } } } } } return QWidget::eventFilter(watched, event); } void ViewMainPage::trashView(DocumentView* view) { QUrl url = view->url(); deselectView(view); FileOperations::trash(QList() << url, this); } void ViewMainPage::deselectView(DocumentView* view) { DocumentView* newCurrentView = 0; if (view == d->currentView()) { // We need to find a new view to set as current int idx = d->mDocumentViews.indexOf(view); if (idx + 1 < d->mDocumentViews.count()) { newCurrentView = d->mDocumentViews.at(idx + 1); } else if (idx > 0) { newCurrentView = d->mDocumentViews.at(idx - 1); } else { GV_WARN_AND_RETURN("No view found to set as current"); } } QModelIndex index = d->indexForView(view); QItemSelectionModel* selectionModel = d->mThumbnailBar->selectionModel(); selectionModel->select(index, QItemSelectionModel::Deselect); if (newCurrentView) { d->setCurrentView(newCurrentView); } } QToolButton* ViewMainPage::toggleSideBarButton() const { return d->mToggleSideBarButton; } void ViewMainPage::showMessageWidget(QGraphicsWidget* widget, Qt::Alignment align) { d->mDocumentViewContainer->showMessageWidget(widget, align); } } // namespace diff --git a/lib/gwenviewconfig.kcfg b/lib/gwenviewconfig.kcfg index 0f46f548..9d8c9cad 100644 --- a/lib/gwenviewconfig.kcfg +++ b/lib/gwenviewconfig.kcfg @@ -1,298 +1,306 @@ lib/sorting.h lib/zoommode.h lib/thumbnailactions.h lib/mousewheelbehavior.h lib/documentview/documentview.h lib/documentview/rasterimageview.h lib/print/printoptionspage.h lib/renderingintent.h General.Name,General.ImageSize,Exif.Photo.ExposureTime,Exif.Photo.Flash 100 true 0.5 The percentage of memory used by Gwenview before it warns the user and suggest saving changes. new A list of filename extensions Gwenview should not try to load. We exclude *.new as well because this is the extension used for temporary files by KSaveFile. false - - true - QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)).toString() Horizontal 1 false false information ThumbnailActions::AllButtons General.Name,Exif.Image.DateTime 75 true RasterImageView::AlphaBackgroundCheckBoard #ffffff MouseWheelBehavior::Scroll false true 350, 100 DocumentView::SoftwareAnimation ZoomMode::Autofit Defines what happens when going to image B after having zoomed in on an area of image A. If set to Autofit, image B is zoomed out to fit the screen. If set to KeepSame, all images share the same zoom and position: image B is set to the same zoom parameters as image A (and if these are changed, image A will then be displayed with the updated zoom and position). If set to Individual, all images remember their own zoom and position: image B is initially set to the same zoom parameters as image A, but will then remember its own zoom and position (if these are changed, image A will NOT be displayed with the updated zoom and position). RenderingIntent::Perceptual Defines how colors are rendered when your display uses an ICC color profile and an image has colors that do not fit within the profile's color gamut. "Perceptual" will scale the colors of the entire image so that they all fit within the display's capabilities. "Relative" will squash only the colors that cannot be displayed, and leave the other colors alone. 128 3./2. false Sorting::Name 1 true Qt::AlignHCenter | Qt::AlignVCenter PrintOptionsPage::ScaleToPage false 15.0 10.0 PrintOptionsPage::Centimeters true false true false false 5.0 24 false + + + true + + + true + + + false + +