mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-24 02:42:52 +00:00
1529 lines
51 KiB
C++
1529 lines
51 KiB
C++
/*
|
|
Gwenview: an image viewer
|
|
Copyright 2007 Aurélien Gâteau <agateau@kde.org>
|
|
|
|
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 "moc_mainwindow.cpp"
|
|
|
|
// Qt
|
|
#include <QApplication>
|
|
#include <QDateTime>
|
|
#include <QDesktopWidget>
|
|
#include <QLabel>
|
|
#include <QPushButton>
|
|
#include <QShortcut>
|
|
#include <QSlider>
|
|
#include <QSplitter>
|
|
#include <QStackedWidget>
|
|
#include <QTimer>
|
|
#include <QUndoGroup>
|
|
#include <QVBoxLayout>
|
|
#include <QMenuBar>
|
|
|
|
// KDE
|
|
#include <KIO/NetAccess>
|
|
#include <KActionCategory>
|
|
#include <KActionCollection>
|
|
#include <KAction>
|
|
#include <KApplication>
|
|
#include <KDirLister>
|
|
#include <KEditToolBar>
|
|
#include <KFileDialog>
|
|
#include <KFileItem>
|
|
#include <KLocale>
|
|
#include <KMessageBox>
|
|
#include <KProtocolManager>
|
|
#include <KLinkItemSelectionModel>
|
|
#include <KRecentFilesAction>
|
|
#include <KStatusBar>
|
|
#include <KStandardDirs>
|
|
#include <KStandardGuiItem>
|
|
#include <KStandardShortcut>
|
|
#include <KToggleFullScreenAction>
|
|
#include <KToolBar>
|
|
#include <KUrl>
|
|
#include <KUrlNavigator>
|
|
#include <KXMLGUIFactory>
|
|
#include <KWindowSystem>
|
|
#include <Solid/PowerManagement>
|
|
|
|
// Local
|
|
#include "configdialog.h"
|
|
#include "documentinfoprovider.h"
|
|
#include "viewmainpage.h"
|
|
#include "fileopscontextmanageritem.h"
|
|
#include "fullscreencontent.h"
|
|
#include "gvcore.h"
|
|
#include "imageopscontextmanageritem.h"
|
|
#include "infocontextmanageritem.h"
|
|
#include "preloader.h"
|
|
#include "savebar.h"
|
|
#include "sidebar.h"
|
|
#include "splitter.h"
|
|
#include "startmainpage.h"
|
|
#include "thumbnailviewhelper.h"
|
|
#include "browsemainpage.h"
|
|
#include <lib/hud/hudbuttonbox.h>
|
|
#include <lib/contextmanager.h>
|
|
#include <lib/disabledactionshortcutmonitor.h>
|
|
#include <lib/document/documentfactory.h>
|
|
#include <lib/documentonlyproxymodel.h>
|
|
#include <lib/eventwatcher.h>
|
|
#include <lib/gvdebug.h>
|
|
#include <lib/gwenviewconfig.h>
|
|
#include <lib/mimetypeutils.h>
|
|
#include <lib/print/printhelper.h>
|
|
#include <lib/slideshow.h>
|
|
#include <lib/signalblocker.h>
|
|
#include <lib/sorteddirmodel.h>
|
|
#include <lib/thumbnailview/thumbnailbarview.h>
|
|
#include <lib/thumbnailview/thumbnailview.h>
|
|
#include <lib/urlutils.h>
|
|
|
|
namespace Gwenview
|
|
{
|
|
|
|
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;
|
|
Qt::WindowStates mWindowState;
|
|
};
|
|
|
|
/*
|
|
|
|
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;
|
|
BrowseMainPage* mBrowseMainPage;
|
|
StartMainPage* mStartMainPage;
|
|
SideBar* mSideBar;
|
|
QStackedWidget* mViewStackedWidget;
|
|
FullScreenContent* mFullScreenContent;
|
|
SaveBar* mSaveBar;
|
|
bool mStartSlideShowWhenDirListerCompleted;
|
|
SlideShow* mSlideShow;
|
|
Preloader* mPreloader;
|
|
bool mPreloadDirectionIsForward;
|
|
|
|
QActionGroup* mViewModeActionGroup;
|
|
KRecentFilesAction* mFileOpenRecentAction;
|
|
KAction* mBrowseAction;
|
|
KAction* mViewAction;
|
|
KAction* mGoUpAction;
|
|
KAction* mGoToPreviousAction;
|
|
KAction* mGoToNextAction;
|
|
KAction* mGoToFirstAction;
|
|
KAction* mGoToLastAction;
|
|
KToggleAction* mToggleSideBarAction;
|
|
KToggleFullScreenAction* mFullScreenAction;
|
|
KAction* mToggleSlideShowAction;
|
|
KToggleAction* mShowMenuBarAction;
|
|
|
|
SortedDirModel* mDirModel;
|
|
DocumentOnlyProxyModel* mThumbnailBarModel;
|
|
KLinkItemSelectionModel* mThumbnailBarSelectionModel;
|
|
ContextManager* mContextManager;
|
|
|
|
MainWindowState mStateBeforeFullScreen;
|
|
|
|
QString mCaption;
|
|
|
|
MainPageId mCurrentMainPageId;
|
|
|
|
QDateTime mFullScreenLeftAt;
|
|
uint mScreenCookie;
|
|
|
|
void setupContextManager()
|
|
{
|
|
mContextManager = new ContextManager(mDirModel, q);
|
|
connect(mContextManager, SIGNAL(selectionChanged()),
|
|
q, SLOT(slotSelectionChanged()));
|
|
connect(mContextManager, SIGNAL(currentDirUrlChanged(KUrl)),
|
|
q, SLOT(slotCurrentDirUrlChanged(KUrl)));
|
|
}
|
|
|
|
void setupWidgets()
|
|
{
|
|
mFullScreenContent = new FullScreenContent(q);
|
|
connect(mContextManager, SIGNAL(currentUrlChanged(KUrl)), mFullScreenContent, SLOT(setCurrentUrl(KUrl)));
|
|
|
|
mCentralSplitter = new Splitter(Qt::Horizontal, q);
|
|
q->setCentralWidget(mCentralSplitter);
|
|
|
|
// Left side of splitter
|
|
mSideBar = new SideBar(mCentralSplitter);
|
|
EventWatcher::install(mSideBar, QList<QEvent::Type>() << 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(KUrl)), mSaveBar, SLOT(setCurrentUrl(KUrl)));
|
|
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(KUrl)), mSlideShow, SLOT(setCurrentUrl(KUrl)));
|
|
|
|
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);
|
|
|
|
connect(mSaveBar, SIGNAL(requestSaveAll()),
|
|
mGvCore, SLOT(saveAll()));
|
|
connect(mSaveBar, SIGNAL(goToUrl(KUrl)),
|
|
q, SLOT(goToUrl(KUrl)));
|
|
|
|
connect(mSlideShow, SIGNAL(goToUrl(KUrl)),
|
|
q, SLOT(goToUrl(KUrl)));
|
|
}
|
|
|
|
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(KUrl)),
|
|
mThumbnailViewHelper, SLOT(setCurrentDirUrl(KUrl)));
|
|
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(KUrl)),
|
|
mGvCore, SLOT(save(KUrl)));
|
|
connect(delegate, SIGNAL(rotateDocumentLeftRequested(KUrl)),
|
|
mGvCore, SLOT(rotateLeft(KUrl)));
|
|
connect(delegate, SIGNAL(rotateDocumentRightRequested(KUrl)),
|
|
mGvCore, SLOT(rotateRight(KUrl)));
|
|
connect(delegate, SIGNAL(showDocumentInFullScreenRequested(KUrl)),
|
|
q, SLOT(showDocumentInFullScreen(KUrl)));
|
|
|
|
// Connect url navigator
|
|
connect(mUrlNavigator, SIGNAL(urlChanged(KUrl)),
|
|
q, SLOT(openDirUrl(KUrl)));
|
|
}
|
|
|
|
void setupViewMainPage(QWidget* parent)
|
|
{
|
|
mViewMainPage = new ViewMainPage(parent, mSlideShow, q->actionCollection(), mGvCore);
|
|
connect(mViewMainPage, SIGNAL(captionUpdateRequested(QString)),
|
|
q, SLOT(setCaption(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(KUrl)),
|
|
q, SLOT(slotStartMainPageUrlSelected(KUrl)));
|
|
}
|
|
|
|
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(KUrl)), q);
|
|
file->addAction("file_open_recent", mFileOpenRecentAction);
|
|
file->addAction(KStandardAction::Print, q, SLOT(print()));
|
|
file->addAction(KStandardAction::Quit, KApplication::kApplication(), SLOT(closeAllWindows()));
|
|
|
|
KAction* action = file->addAction("reload", q, SLOT(reload()));
|
|
action->setText(i18nc("@action reload the currently viewed image", "Reload"));
|
|
action->setIcon(KIcon("view-refresh"));
|
|
action->setShortcut(Qt::Key_F5);
|
|
|
|
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(KIcon("view-list-icons"));
|
|
mBrowseAction->setShortcut(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(KIcon("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 = static_cast<KToggleFullScreenAction*>(view->addAction(KStandardAction::FullScreen, q, SLOT(toggleFullScreen(bool))));
|
|
KShortcut shortcut = mFullScreenAction->shortcut();
|
|
if (shortcut.alternate().isEmpty()) {
|
|
shortcut.setAlternate(Qt::Key_F11);
|
|
}
|
|
mFullScreenAction->setShortcut(shortcut);
|
|
connect(mViewMainPage, SIGNAL(toggleFullScreenRequested()),
|
|
mFullScreenAction, SLOT(trigger()));
|
|
|
|
KAction* leaveFullScreenAction = view->addAction("leave_fullscreen", q, SLOT(leaveFullScreen()));
|
|
leaveFullScreenAction->setIcon(KIcon("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(KIcon("media-skip-backward"));
|
|
mGoToPreviousAction->setText(i18nc("@action Go to previous image", "Previous"));
|
|
mGoToPreviousAction->setToolTip(i18nc("@info:tooltip", "Go to previous image"));
|
|
mGoToPreviousAction->setShortcut(Qt::Key_Backspace);
|
|
installDisabledActionShortcutMonitor(mGoToPreviousAction, SLOT(showFirstDocumentReached()));
|
|
|
|
mGoToNextAction = view->addAction("go_next", q, SLOT(goToNext()));
|
|
mGoToNextAction->setPriority(QAction::LowPriority);
|
|
mGoToNextAction->setIcon(KIcon("media-skip-forward"));
|
|
mGoToNextAction->setText(i18nc("@action Go to next image", "Next"));
|
|
mGoToNextAction->setToolTip(i18nc("@info:tooltip", "Go to next image"));
|
|
mGoToNextAction->setShortcut(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"));
|
|
mGoToFirstAction->setShortcut(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"));
|
|
mGoToLastAction->setShortcut(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(KIcon("go-home"));
|
|
action->setText(i18nc("@action", "Start Page"));
|
|
action->setToolTip(i18nc("@info:tooltip", "Open the start page"));
|
|
|
|
mToggleSideBarAction = view->add<KToggleAction>("toggle_sidebar");
|
|
connect(mToggleSideBarAction, SIGNAL(toggled(bool)),
|
|
q, SLOT(toggleSideBar(bool)));
|
|
mToggleSideBarAction->setIcon(KIcon("view-sidetree"));
|
|
mToggleSideBarAction->setShortcut(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()));
|
|
|
|
mShowMenuBarAction = static_cast<KToggleAction*>(view->addAction(KStandardAction::ShowMenubar, q, SLOT(toggleMenuBar())));
|
|
|
|
view->addAction(KStandardAction::KeyBindings, q->guiFactory(),
|
|
SLOT(configureShortcuts()));
|
|
|
|
view->addAction(KStandardAction::Preferences, q,
|
|
SLOT(showConfigDialog()));
|
|
|
|
view->addAction(KStandardAction::ConfigureToolbars, q,
|
|
SLOT(configureToolbars()));
|
|
}
|
|
|
|
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(KIcon("edit-redo"));
|
|
action->setIconText(i18n("Redo"));
|
|
action->setShortcuts(KStandardShortcut::redo());
|
|
edit->addAction(action->objectName(), action);
|
|
|
|
action = undoGroup->createUndoAction(actionCollection);
|
|
action->setObjectName(KStandardAction::name(KStandardAction::Undo));
|
|
action->setIcon(KIcon("edit-undo"));
|
|
action->setIconText(i18n("Undo"));
|
|
action->setShortcuts(KStandardShortcut::undo());
|
|
edit->addAction(action->objectName(), action);
|
|
}
|
|
|
|
void setupContextManagerItems()
|
|
{
|
|
Q_ASSERT(mContextManager);
|
|
KActionCollection* actionCollection = q->actionCollection();
|
|
|
|
// Create context manager items
|
|
InfoContextManagerItem* infoItem = new InfoContextManagerItem(mContextManager);
|
|
|
|
|
|
ImageOpsContextManagerItem* imageOpsItem =
|
|
new ImageOpsContextManagerItem(mContextManager, q);
|
|
|
|
FileOpsContextManagerItem* fileOpsItem = new FileOpsContextManagerItem(mContextManager, mThumbnailView, actionCollection, q);
|
|
|
|
// Fill sidebar
|
|
SideBarPage* page;
|
|
page = new SideBarPage(i18n("Information"));
|
|
page->setObjectName(QLatin1String("information"));
|
|
page->addWidget(infoItem->widget());
|
|
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_IMAGE);
|
|
|
|
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 indexIsDir(const QModelIndex& index) const
|
|
{
|
|
Q_ASSERT(index.isValid());
|
|
KFileItem item = mDirModel->itemForIndex(index);
|
|
return item.isDir();
|
|
}
|
|
|
|
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() && !indexIsDir(index)) {
|
|
goTo(index);
|
|
}
|
|
}
|
|
|
|
void goToFirstDocument()
|
|
{
|
|
QModelIndex index;
|
|
for (int row = 0;; ++row) {
|
|
index = mDirModel->index(row, 0);
|
|
if (!index.isValid()) {
|
|
return;
|
|
}
|
|
|
|
if (!indexIsDir(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 {
|
|
kWarning() << "Action" << name << "not found";
|
|
}
|
|
}
|
|
|
|
void setActionsDisabledOnStartMainPageEnabled(bool enabled)
|
|
{
|
|
mBrowseAction->setEnabled(enabled);
|
|
mViewAction->setEnabled(enabled);
|
|
mToggleSideBarAction->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 KUrl url = mContextManager->currentUrl();
|
|
|
|
if (url.isValid()) {
|
|
isRasterImage = mContextManager->currentUrlIsRasterImage();
|
|
canSave = isRasterImage;
|
|
isModified = DocumentFactory::instance()->load(url)->isModified();
|
|
if (mCurrentMainPageId != ViewMainPageId) {
|
|
// Saving only makes sense if exactly one image is selected
|
|
QItemSelection selection = mThumbnailView->selectionModel()->selection();
|
|
QModelIndexList indexList = selection.indexes();
|
|
if (indexList.count() != 1) {
|
|
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 = mViewMainPage->isFullScreenMode()
|
|
? FULLSCREEN_MODE_SIDE_BAR_GROUP
|
|
: VIEW_MODE_SIDE_BAR_GROUP;
|
|
break;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
void loadSideBarConfig()
|
|
{
|
|
static QMap<const char*, bool> 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(KGlobal::config(), name);
|
|
mSideBar->setVisible(group.readEntry(SIDE_BAR_IS_VISIBLE_KEY, defaultVisibility[name]));
|
|
mSideBar->setCurrentPage(GwenviewConfig::sideBarPage());
|
|
q->updateToggleSideBarAction();
|
|
}
|
|
|
|
void saveSideBarConfig() const
|
|
{
|
|
KConfigGroup group(KGlobal::config(), sideBarConfigGroupName());
|
|
group.writeEntry(SIDE_BAR_IS_VISIBLE_KEY, mSideBar->isVisible());
|
|
GwenviewConfig::setSideBarPage(mSideBar->currentPage());
|
|
}
|
|
|
|
void setScreenSaverEnabled(bool enabled)
|
|
{
|
|
if (!enabled) {
|
|
if (mScreenCookie != 0) {
|
|
mScreenCookie = Solid::PowerManagement::beginSuppressingScreenPowerManagement();
|
|
}
|
|
} else {
|
|
if (mScreenCookie != 0) {
|
|
if (Solid::PowerManagement::stopSuppressingScreenPowerManagement(mScreenCookie)) {
|
|
mScreenCookie = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void assignThumbnailProviderToThumbnailView(ThumbnailView* thumbnailView)
|
|
{
|
|
GV_RETURN_IF_FAIL(thumbnailView);
|
|
mActiveThumbnailView = thumbnailView;
|
|
if (mActiveThumbnailView->isVisible()) {
|
|
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->mScreenCookie = 0;
|
|
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()));
|
|
|
|
setAutoSaveSettings();
|
|
}
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
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::slotModifiedDocumentListChanged()
|
|
{
|
|
d->updateActions();
|
|
|
|
// Update caption
|
|
QList<KUrl> list = DocumentFactory::instance()->modifiedDocumentList();
|
|
bool modified = list.count() > 0;
|
|
setCaption(d->mCaption, modified);
|
|
}
|
|
|
|
void MainWindow::setInitialUrl(const KUrl& _url)
|
|
{
|
|
Q_ASSERT(_url.isValid());
|
|
KUrl url = UrlUtils::fixUserEnteredUrl(_url);
|
|
if (url.protocol() == "http" || url.protocol() == "https") {
|
|
d->mGvCore->addUrlToRecentUrls(url);
|
|
}
|
|
if (UrlUtils::urlIsDirectory(url)) {
|
|
d->mBrowseAction->trigger();
|
|
openDirUrl(url);
|
|
} else {
|
|
d->mViewAction->trigger();
|
|
d->mViewMainPage->openUrl(url);
|
|
d->mContextManager->setUrlToSelect(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);
|
|
if (d->mViewMainPage->isEmpty()) {
|
|
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();
|
|
|
|
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 {
|
|
// Item is a document, switch to view mode
|
|
d->mViewAction->trigger();
|
|
}
|
|
}
|
|
|
|
static bool indexRowLessThan(const QModelIndex& i1, const QModelIndex& i2)
|
|
{
|
|
return i1.row() < i2.row();
|
|
}
|
|
|
|
void MainWindow::openSelectedDocuments()
|
|
{
|
|
if (d->mCurrentMainPageId != ViewMainPageId) {
|
|
return;
|
|
}
|
|
|
|
QModelIndex currentIndex = d->mThumbnailView->currentIndex();
|
|
if (!currentIndex.isValid()) {
|
|
return;
|
|
}
|
|
|
|
int count = 0;
|
|
|
|
KUrl::List urls;
|
|
KUrl currentUrl;
|
|
QModelIndex firstDocumentIndex;
|
|
QModelIndexList list = d->mThumbnailView->selectionModel()->selectedIndexes();
|
|
// Make 'list' follow the same order as 'mThumbnailView'
|
|
qSort(list.begin(), list.end(), indexRowLessThan);
|
|
|
|
Q_FOREACH(const QModelIndex& index, list) {
|
|
KFileItem item = d->mDirModel->itemForIndex(index);
|
|
if (!item.isNull() && !item.isDir()) {
|
|
KUrl url = item.url();
|
|
if (!firstDocumentIndex.isValid()) {
|
|
firstDocumentIndex = index;
|
|
}
|
|
urls << url;
|
|
if (index == currentIndex) {
|
|
currentUrl = url;
|
|
}
|
|
++count;
|
|
if (count == ViewMainPage::MaxViewCount) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (urls.isEmpty()) {
|
|
// No image to display
|
|
return;
|
|
}
|
|
if (currentUrl.isEmpty()) {
|
|
// Current index is not selected, or it is not a document: set
|
|
// firstDocumentIndex as current
|
|
GV_RETURN_IF_FAIL(firstDocumentIndex.isValid());
|
|
d->mThumbnailView->selectionModel()->setCurrentIndex(firstDocumentIndex, QItemSelectionModel::Current);
|
|
currentUrl = urls.first();
|
|
}
|
|
|
|
d->mViewMainPage->openUrls(urls, currentUrl);
|
|
}
|
|
|
|
void MainWindow::goUp()
|
|
{
|
|
if (d->mCurrentMainPageId == BrowseMainPageId) {
|
|
KUrl url = d->mContextManager->currentDirUrl();
|
|
url = url.upUrl();
|
|
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(KUrl());
|
|
d->mContextManager->setCurrentUrl(KUrl());
|
|
|
|
d->autoAssignThumbnailProvider();
|
|
}
|
|
|
|
void MainWindow::slotStartMainPageUrlSelected(const KUrl& 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 KUrl& url)
|
|
{
|
|
const KUrl currentUrl = d->mContextManager->currentDirUrl();
|
|
|
|
if (url.equals(currentUrl, KUrl::RemoveTrailingSlash)) {
|
|
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 KUrl::toLocalFile() instead of
|
|
// KUrl::path() here. Please don't, using KUrl::path() is the right
|
|
// thing to do here.
|
|
const QString currentPath = QDir::cleanPath(currentUrl.path(KUrl::RemoveTrailingSlash));
|
|
const QString wantedPath = QDir::cleanPath(url.path(KUrl::RemoveTrailingSlash));
|
|
const QChar separator('/');
|
|
const int slashCount = wantedPath.count(separator);
|
|
const QString pathToSelect = currentPath.section(separator, 0, slashCount + 1);
|
|
KUrl urlToSelect = url;
|
|
urlToSelect.setPath(pathToSelect);
|
|
d->mContextManager->setUrlToSelect(urlToSelect);
|
|
}
|
|
d->mContextManager->setCurrentDirUrl(url);
|
|
d->mGvCore->addUrlToRecentFolders(url);
|
|
d->mViewMainPage->reset();
|
|
}
|
|
|
|
void MainWindow::toggleSideBar(bool on)
|
|
{
|
|
d->mSideBar->setVisible(on);
|
|
}
|
|
|
|
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<QToolButton*> lst;
|
|
lst << d->mBrowseMainPage->toggleSideBarButton()
|
|
<< d->mViewMainPage->toggleSideBarButton();
|
|
Q_FOREACH(QToolButton * button, lst) {
|
|
button->setText(text);
|
|
button->setToolTip(toolTip);
|
|
}
|
|
}
|
|
|
|
void MainWindow::slotPartCompleted()
|
|
{
|
|
d->updateActions();
|
|
KUrl url = d->mViewMainPage->url();
|
|
// remember the opened file
|
|
if (!url.isEmpty()) {
|
|
d->mFileOpenRecentAction->addUrl(url);
|
|
}
|
|
if (!KProtocolManager::supportsListing(url)) {
|
|
return;
|
|
}
|
|
|
|
KUrl dirUrl = url;
|
|
dirUrl.setFileName(QString());
|
|
if (dirUrl.equals(d->mContextManager->currentDirUrl(), KUrl::RemoveTrailingSlash)) {
|
|
QModelIndex index = d->mDirModel->indexForUrl(url);
|
|
QItemSelectionModel* selectionModel = d->mThumbnailView->selectionModel();
|
|
if (index.isValid() && !selectionModel->isSelected(index)) {
|
|
// FIXME: QGV Reactivating this line prevents navigation to prev/next image
|
|
//selectionModel->select(index, QItemSelectionModel::SelectCurrent);
|
|
}
|
|
} else {
|
|
d->mContextManager->setCurrentDirUrl(dirUrl);
|
|
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() && !item.isDir()) {
|
|
KUrl url = item.url();
|
|
Document::Ptr doc = DocumentFactory::instance()->load(url);
|
|
undoGroup->addStack(doc->undoStack());
|
|
undoGroup->setActiveStack(doc->undoStack());
|
|
} else {
|
|
undoGroup->setActiveStack(0);
|
|
}
|
|
}
|
|
|
|
// 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 KUrl& url)
|
|
{
|
|
if (url.isValid()) {
|
|
d->mUrlNavigator->setLocationUrl(url);
|
|
d->mGoUpAction->setEnabled(url.path() != "/");
|
|
} else {
|
|
d->mGoUpAction->setEnabled(false);
|
|
}
|
|
}
|
|
|
|
void MainWindow::slotDirModelNewItems()
|
|
{
|
|
if (d->mThumbnailView->selectionModel()->hasSelection()) {
|
|
updatePreviousNextActions();
|
|
}
|
|
}
|
|
|
|
void MainWindow::slotDirListerCompleted()
|
|
{
|
|
if (d->mStartSlideShowWhenDirListerCompleted) {
|
|
d->mStartSlideShowWhenDirListerCompleted = false;
|
|
QTimer::singleShot(0, d->mToggleSlideShowAction, SLOT(trigger()));
|
|
}
|
|
if (d->mThumbnailView->selectionModel()->hasSelection()) {
|
|
updatePreviousNextActions();
|
|
} else if (!d->mContextManager->urlToSelect().isValid()) {
|
|
d->goToFirstDocument();
|
|
}
|
|
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 KUrl& url)
|
|
{
|
|
if (d->mCurrentMainPageId == ViewMainPageId) {
|
|
d->mViewMainPage->openUrl(url);
|
|
}
|
|
KUrl dirUrl = url;
|
|
dirUrl.setFileName("");
|
|
if (!dirUrl.equals(d->mContextManager->currentDirUrl(), KUrl::RemoveTrailingSlash)) {
|
|
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->indexIsDir(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->indexIsDir(prevIndex);
|
|
hasNext = nextIndex.isValid() && !d->indexIsDir(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.
|
|
saveMainWindowSettings(autoSaveConfigGroup());
|
|
resetAutoSaveSettings();
|
|
|
|
// Save state
|
|
d->mStateBeforeFullScreen.mToolBarVisible = toolBar()->isVisible();
|
|
d->mStateBeforeFullScreen.mWindowState = windowState();
|
|
|
|
// Go full screen
|
|
setWindowState(windowState() | Qt::WindowFullScreen);
|
|
menuBar()->hide();
|
|
toolBar()->hide();
|
|
|
|
QApplication::setPalette(d->mGvCore->palette(GvCore::FullScreenPalette));
|
|
d->mFullScreenContent->setFullScreenMode(true);
|
|
d->mBrowseMainPage->setFullScreenMode(true);
|
|
d->mViewMainPage->setFullScreenMode(true);
|
|
d->mSaveBar->setFullScreenMode(true);
|
|
d->setScreenSaverEnabled(false);
|
|
|
|
// HACK: Only load sidebar config now, because it looks at
|
|
// ViewMainPage fullScreenMode property to determine the sidebar
|
|
// config group.
|
|
d->loadSideBarConfig();
|
|
} else {
|
|
setAutoSaveSettings();
|
|
|
|
// Back to normal
|
|
QApplication::setPalette(d->mGvCore->palette(GvCore::NormalPalette));
|
|
d->mFullScreenContent->setFullScreenMode(false);
|
|
d->mBrowseMainPage->setFullScreenMode(false);
|
|
d->mViewMainPage->setFullScreenMode(false);
|
|
d->mSlideShow->stop();
|
|
d->mSaveBar->setFullScreenMode(false);
|
|
setWindowState(d->mStateBeforeFullScreen.mWindowState);
|
|
menuBar()->setVisible(d->mShowMenuBarAction->isChecked());
|
|
toolBar()->setVisible(d->mStateBeforeFullScreen.mToolBarVisible);
|
|
|
|
d->setScreenSaverEnabled(true);
|
|
|
|
// Keep this after mViewMainPage->setFullScreenMode(false).
|
|
// See call to loadSideBarConfig() above.
|
|
d->loadSideBarConfig();
|
|
|
|
// See resizeEvent
|
|
d->mFullScreenLeftAt = QDateTime::currentDateTime();
|
|
}
|
|
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()
|
|
{
|
|
KUrl dirUrl = d->mContextManager->currentDirUrl();
|
|
|
|
KFileDialog dialog(dirUrl, QString(), this);
|
|
dialog.setCaption(i18nc("@title:window", "Open Image"));
|
|
const QStringList mimeFilter = MimeTypeUtils::imageMimeTypes();
|
|
dialog.setMimeFilter(mimeFilter);
|
|
dialog.setOperationMode(KFileDialog::Opening);
|
|
if (!dialog.exec()) {
|
|
return;
|
|
}
|
|
|
|
openUrl(dialog.selectedUrl());
|
|
}
|
|
|
|
void MainWindow::openUrl(const KUrl& url)
|
|
{
|
|
d->setActionsDisabledOnStartMainPageEnabled(true);
|
|
d->mViewAction->trigger();
|
|
d->mViewMainPage->openUrl(url);
|
|
d->mContextManager->setUrlToSelect(url);
|
|
}
|
|
|
|
void MainWindow::showDocumentInFullScreen(const KUrl& url)
|
|
{
|
|
d->mViewMainPage->openUrl(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<KUrl> 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_IMAGE:
|
|
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(KIcon("media-playback-pause"));
|
|
} else {
|
|
d->mToggleSlideShowAction->setText(i18n("Start Slideshow"));
|
|
d->mToggleSlideShowAction->setIcon(KIcon("media-playback-start"));
|
|
}
|
|
}
|
|
|
|
bool MainWindow::queryClose()
|
|
{
|
|
saveConfig();
|
|
d->saveSideBarConfig();
|
|
QList<KUrl> 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()
|
|
{
|
|
ConfigDialog dialog(this);
|
|
connect(&dialog, 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->mFileOpenRecentAction->loadEntries(KConfigGroup(KGlobal::config(), "Recent Files"));
|
|
d->mStartMainPage->loadConfig();
|
|
d->mViewMainPage->loadConfig();
|
|
d->mBrowseMainPage->loadConfig();
|
|
}
|
|
|
|
void MainWindow::saveConfig()
|
|
{
|
|
d->mFileOpenRecentAction->saveEntries(KConfigGroup(KGlobal::config(), "Recent Files"));
|
|
d->mViewMainPage->saveConfig();
|
|
d->mBrowseMainPage->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) {
|
|
kDebug() << "Preloading disabled";
|
|
return;
|
|
}
|
|
QItemSelection selection = d->mThumbnailView->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 (!item.isDir()) {
|
|
KUrl 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();
|
|
}
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
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();
|
|
}
|
|
KUrl url = group.readEntry(SESSION_URL_KEY, KUrl());
|
|
if (!url.isValid()) {
|
|
kWarning() << "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
|