diff --git a/app/fileoperations.cpp b/app/fileoperations.cpp index c5542874..78697ad5 100644 --- a/app/fileoperations.cpp +++ b/app/fileoperations.cpp @@ -1,226 +1,229 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* 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, Cambridge, MA 02110-1301, USA. */ // Self #include "fileoperations.h" // Qt #include #include #include #include // KDE #include #include #include #include #include #include #include // Local #include #include +#include namespace Gwenview { namespace FileOperations { -static void copyMoveOrLink(Operation operation, const QList& urlList, QWidget* parent) +static void copyMoveOrLink(Operation operation, const QList& urlList, QWidget* parent, ContextManager* contextManager) { Q_ASSERT(!urlList.isEmpty()); - QFileDialog dialog(parent, QString(), "kfiledialog:///"); + QFileDialog dialog(parent->nativeParentWidget(), QString()); dialog.setAcceptMode(QFileDialog::AcceptSave); switch (operation) { case COPY: dialog.setWindowTitle(i18nc("@title:window", "Copy To")); dialog.setLabelText(QFileDialog::DialogLabel::Accept, i18nc("@action:button", "Copy")); break; case MOVE: dialog.setWindowTitle(i18nc("@title:window", "Move To")); dialog.setLabelText(QFileDialog::DialogLabel::Accept, i18nc("@action:button", "Move")); break; case LINK: dialog.setWindowTitle(i18nc("@title:window", "Link To")); dialog.setLabelText(QFileDialog::DialogLabel::Accept, i18nc("@action:button", "Link")); break; default: Q_ASSERT(0); } if (urlList.count() == 1) { dialog.setFileMode(QFileDialog::AnyFile); dialog.selectUrl(urlList.first()); } else { dialog.setFileMode(QFileDialog::Directory); dialog.setOption(QFileDialog::ShowDirsOnly, true); - dialog.setDirectoryUrl(urlList.first().adjusted(QUrl::RemoveFilename)); } + dialog.setDirectoryUrl(contextManager->targetUrl().adjusted(QUrl::RemoveFilename)); if (!dialog.exec()) { return; } QUrl destUrl = dialog.selectedUrls().first(); + contextManager->setTargetUrl(destUrl); + KIO::CopyJob* job = 0; switch (operation) { case COPY: job = KIO::copy(urlList, destUrl); break; case MOVE: job = KIO::move(urlList, destUrl); break; case LINK: job = KIO::link(urlList, destUrl); break; default: Q_ASSERT(0); } KJobWidgets::setWindow(job, parent); job->ui()->setAutoErrorHandlingEnabled(true); } static void delOrTrash(KIO::JobUiDelegate::DeletionType deletionType, const QList& urlList, QWidget* parent) { Q_ASSERT(urlList.count() > 0); KIO::JobUiDelegate uiDelegate; uiDelegate.setWindow(parent); if (!uiDelegate.askDeleteConfirmation(urlList, deletionType, KIO::JobUiDelegate::DefaultConfirmation)) { return; } KIO::Job* job = 0; switch (deletionType) { case KIO::JobUiDelegate::Trash: job = KIO::trash(urlList); break; case KIO::JobUiDelegate::Delete: job = KIO::del(urlList); break; default: // e.g. EmptyTrash return; } Q_ASSERT(job); KJobWidgets::setWindow(job,parent); Q_FOREACH(const QUrl &url, urlList) { DocumentFactory::instance()->forget(url); } } -void copyTo(const QList& urlList, QWidget* parent) +void copyTo(const QList& urlList, QWidget* parent, ContextManager* contextManager) { - copyMoveOrLink(COPY, urlList, parent); + copyMoveOrLink(COPY, urlList, parent, contextManager); } -void moveTo(const QList& urlList, QWidget* parent) +void moveTo(const QList& urlList, QWidget* parent, ContextManager* contextManager) { - copyMoveOrLink(MOVE, urlList, parent); + copyMoveOrLink(MOVE, urlList, parent, contextManager); } -void linkTo(const QList& urlList, QWidget* parent) +void linkTo(const QList& urlList, QWidget* parent, ContextManager* contextManager) { - copyMoveOrLink(LINK, urlList, parent); + copyMoveOrLink(LINK, urlList, parent, contextManager); } void trash(const QList& urlList, QWidget* parent) { delOrTrash(KIO::JobUiDelegate::Trash, urlList, parent); } void del(const QList& urlList, QWidget* parent) { delOrTrash(KIO::JobUiDelegate::Delete, urlList, parent); } void showMenuForDroppedUrls(QWidget* parent, const QList& urlList, const QUrl &destUrl) { if (urlList.isEmpty()) { qWarning() << "urlList is empty!"; return; } if (!destUrl.isValid()) { qWarning() << "destUrl is not valid!"; return; } QMenu menu(parent); QAction* moveAction = menu.addAction( QIcon::fromTheme("go-jump"), i18n("Move Here")); QAction* copyAction = menu.addAction( QIcon::fromTheme("edit-copy"), i18n("Copy Here")); QAction* linkAction = menu.addAction( QIcon::fromTheme("edit-link"), i18n("Link Here")); menu.addSeparator(); menu.addAction( QIcon::fromTheme("process-stop"), i18n("Cancel")); QAction* action = menu.exec(QCursor::pos()); KIO::Job* job = 0; if (action == moveAction) { job = KIO::move(urlList, destUrl); } else if (action == copyAction) { job = KIO::copy(urlList, destUrl); } else if (action == linkAction) { job = KIO::link(urlList, destUrl); } else { return; } Q_ASSERT(job); KJobWidgets::setWindow(job, parent); } void rename(const QUrl &oldUrl, QWidget* parent) { QString name = QInputDialog::getText(parent, i18nc("@title:window", "Rename") /* caption */, xi18n("Rename %1 to:", oldUrl.fileName()) /* label */, QLineEdit::Normal, oldUrl.fileName() /* value */ ); if (name.isEmpty() || name == oldUrl.fileName()) { return; } QUrl newUrl = oldUrl; newUrl = newUrl.adjusted(QUrl::RemoveFilename); newUrl.setPath(newUrl.path() + name); KIO::SimpleJob* job = KIO::rename(oldUrl, newUrl, KIO::HideProgressInfo); KJobWidgets::setWindow(job, parent); if (!job->exec()) { job->ui()->showErrorMessage(); return; } ThumbnailProvider::moveThumbnail(oldUrl, newUrl); } } // namespace } // namespace diff --git a/app/fileoperations.h b/app/fileoperations.h index 764d5ad3..ea793881 100644 --- a/app/fileoperations.h +++ b/app/fileoperations.h @@ -1,48 +1,50 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* 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, Cambridge, MA 02110-1301, USA. */ #ifndef FILEOPERATIONS_H #define FILEOPERATIONS_H #include +#include class QWidget; +class ContextManager; namespace Gwenview { namespace FileOperations { enum Operation { TRASH, COPY, MOVE, LINK, UNKNOWN, PUT }; -void copyTo(const QList& urlList, QWidget* parent); -void moveTo(const QList& urlList, QWidget* parent); -void linkTo(const QList& urlList, QWidget* parent); +void copyTo(const QList& urlList, QWidget* parent, ContextManager* contextManager); +void moveTo(const QList& urlList, QWidget* parent, ContextManager* contextManager); +void linkTo(const QList& urlList, QWidget* parent, ContextManager* contextManager); void trash(const QList& urlList, QWidget* parent); void del(const QList& urlList, QWidget* parent); void rename(const QUrl &url, QWidget* parent); void showMenuForDroppedUrls(QWidget* parent, const QList& urlList, const QUrl &destUrl); } // namespace } // namespace #endif /* FILEOPERATIONS_H */ diff --git a/app/fileopscontextmanageritem.cpp b/app/fileopscontextmanageritem.cpp index 8cda2951..64b54a96 100644 --- a/app/fileopscontextmanageritem.cpp +++ b/app/fileopscontextmanageritem.cpp @@ -1,415 +1,415 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* 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. */ // Self #include "fileopscontextmanageritem.h" // Qt #include #include #include #include #include #include #include // KDE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Local #include #include #include #include "fileoperations.h" #include "sidebar.h" namespace Gwenview { QList FileOpsContextManagerItem::urlList() const { QList urlList; KFileItemList list = contextManager()->selectedFileItemList(); if (list.count() > 0) { urlList = list.urlList(); } else { QUrl url = contextManager()->currentUrl(); Q_ASSERT(url.isValid()); urlList << url; } return urlList; } void FileOpsContextManagerItem::updateServiceList() { // This code is inspired from // kdebase/apps/lib/konq/konq_menuactions.cpp // Get list of all distinct mimetypes in selection QStringList mimeTypes; Q_FOREACH(const KFileItem & item, contextManager()->selectedFileItemList()) { const QString mimeType = item.mimetype(); if (!mimeTypes.contains(mimeType)) { mimeTypes << mimeType; } } // Query trader mServiceList = KFileItemActions::associatedApplications(mimeTypes, QString()); } QMimeData* FileOpsContextManagerItem::selectionMimeData() { QMimeData* mimeData = new QMimeData; KFileItemList list = contextManager()->selectedFileItemList(); mimeData->setUrls(list.urlList()); return mimeData; } QUrl FileOpsContextManagerItem::pasteTargetUrl() const { // If only one folder is selected, paste inside it, otherwise paste in // current KFileItemList list = contextManager()->selectedFileItemList(); if (list.count() == 1 && list.first().isDir()) { return list.first().url(); } else { return contextManager()->currentDirUrl(); } } static QAction* createSeparator(QObject* parent) { QAction* action = new QAction(parent); action->setSeparator(true); return action; } FileOpsContextManagerItem::FileOpsContextManagerItem(ContextManager* manager, QListView* thumbnailView, KActionCollection* actionCollection, KXMLGUIClient* client) : AbstractContextManagerItem(manager) { mThumbnailView = thumbnailView; mXMLGUIClient = client; mGroup = new SideBarGroup(i18n("File Operations")); setWidget(mGroup); EventWatcher::install(mGroup, QEvent::Show, this, SLOT(updateSideBarContent())); mInTrash = false; mNewFileMenu = new KNewFileMenu(Q_NULLPTR, QString(), this); connect(contextManager(), SIGNAL(selectionChanged()), SLOT(updateActions())); connect(contextManager(), SIGNAL(currentDirUrlChanged(QUrl)), SLOT(updateActions())); KActionCategory* file = new KActionCategory(i18nc("@title actions category", "File"), actionCollection); KActionCategory* edit = new KActionCategory(i18nc("@title actions category", "Edit"), actionCollection); mCutAction = edit->addAction(KStandardAction::Cut, this, SLOT(cut())); mCopyAction = edit->addAction(KStandardAction::Copy, this, SLOT(copy())); mPasteAction = edit->addAction(KStandardAction::Paste, this, SLOT(paste())); mCopyToAction = file->addAction("file_copy_to", this, SLOT(copyTo())); mCopyToAction->setText(i18nc("Verb", "Copy To...")); actionCollection->setDefaultShortcut(mCopyToAction, Qt::Key_F7); mMoveToAction = file->addAction("file_move_to", this, SLOT(moveTo())); mMoveToAction->setText(i18nc("Verb", "Move To...")); actionCollection->setDefaultShortcut(mMoveToAction, Qt::Key_F8); mLinkToAction = file->addAction("file_link_to", this, SLOT(linkTo())); mLinkToAction->setText(i18nc("Verb: create link to the file where user wants", "Link To...")); actionCollection->setDefaultShortcut(mLinkToAction, Qt::Key_F9); mRenameAction = file->addAction("file_rename", this, SLOT(rename())); mRenameAction->setText(i18nc("Verb", "Rename...")); mRenameAction->setIcon(QIcon::fromTheme("edit-rename")); actionCollection->setDefaultShortcut(mRenameAction, Qt::Key_F2); mTrashAction = file->addAction("file_trash", this, SLOT(trash())); mTrashAction->setText(i18nc("Verb", "Trash")); mTrashAction->setIcon(QIcon::fromTheme("user-trash")); actionCollection->setDefaultShortcut(mTrashAction, Qt::Key_Delete); mDelAction = file->addAction(KStandardAction::DeleteFile, this, SLOT(del())); mRestoreAction = file->addAction("file_restore", this, SLOT(restore())); mRestoreAction->setText(i18n("Restore")); mShowPropertiesAction = file->addAction("file_show_properties", this, SLOT(showProperties())); mShowPropertiesAction->setText(i18n("Properties")); mShowPropertiesAction->setIcon(QIcon::fromTheme("document-properties")); mCreateFolderAction = file->addAction("file_create_folder", this, SLOT(createFolder())); mCreateFolderAction->setText(i18n("Create Folder...")); mCreateFolderAction->setIcon(QIcon::fromTheme("folder-new")); mOpenWithAction = file->addAction("file_open_with"); mOpenWithAction->setText(i18n("Open With")); QMenu* menu = new QMenu; mOpenWithAction->setMenu(menu); connect(menu, &QMenu::aboutToShow, this, &FileOpsContextManagerItem::populateOpenMenu); connect(menu, &QMenu::triggered, this, &FileOpsContextManagerItem::openWith); mRegularFileActionList << mRenameAction << mTrashAction << mDelAction << createSeparator(this) << mCopyToAction << mMoveToAction << mLinkToAction << createSeparator(this) << mOpenWithAction << mShowPropertiesAction << createSeparator(this) << mCreateFolderAction ; mTrashFileActionList << mRestoreAction << mDelAction << createSeparator(this) << mShowPropertiesAction ; connect(QApplication::clipboard(), SIGNAL(dataChanged()), SLOT(updatePasteAction())); updatePasteAction(); // Delay action update because it must happen *after* main window has called // createGUI(), otherwise calling mXMLGUIClient->plugActionList() will // fail. QMetaObject::invokeMethod(this, "updateActions", Qt::QueuedConnection); } FileOpsContextManagerItem::~FileOpsContextManagerItem() { delete mOpenWithAction->menu(); } void FileOpsContextManagerItem::updateActions() { const int count = contextManager()->selectedFileItemList().count(); const bool selectionNotEmpty = count > 0; const bool urlIsValid = contextManager()->currentUrl().isValid(); const bool dirUrlIsValid = contextManager()->currentDirUrl().isValid(); mInTrash = contextManager()->currentDirUrl().scheme() == "trash"; mCutAction->setEnabled(selectionNotEmpty); mCopyAction->setEnabled(selectionNotEmpty); mCopyToAction->setEnabled(selectionNotEmpty); mMoveToAction->setEnabled(selectionNotEmpty); mLinkToAction->setEnabled(selectionNotEmpty); mTrashAction->setEnabled(selectionNotEmpty); mRestoreAction->setEnabled(selectionNotEmpty); mDelAction->setEnabled(selectionNotEmpty); mOpenWithAction->setEnabled(selectionNotEmpty); mRenameAction->setEnabled(count == 1); mCreateFolderAction->setEnabled(dirUrlIsValid); mShowPropertiesAction->setEnabled(dirUrlIsValid || urlIsValid); mXMLGUIClient->unplugActionList("file_action_list"); QList& list = mInTrash ? mTrashFileActionList : mRegularFileActionList; mXMLGUIClient->plugActionList("file_action_list", list); updateSideBarContent(); } void FileOpsContextManagerItem::updatePasteAction() { const QMimeData *mimeData = QApplication::clipboard()->mimeData(); bool enable; KFileItem destItem(pasteTargetUrl()); const QString text = KIO::pasteActionText(mimeData, &enable, destItem); mPasteAction->setEnabled(enable); mPasteAction->setText(text); } void FileOpsContextManagerItem::updateSideBarContent() { if (!mGroup->isVisible()) { return; } mGroup->clear(); QList& list = mInTrash ? mTrashFileActionList : mRegularFileActionList; Q_FOREACH(QAction * action, list) { if (action->isEnabled() && !action->isSeparator()) { mGroup->addAction(action); } } } void FileOpsContextManagerItem::showProperties() { KFileItemList list = contextManager()->selectedFileItemList(); if (list.count() > 0) { KPropertiesDialog::showDialog(list, mGroup); } else { QUrl url = contextManager()->currentDirUrl(); KPropertiesDialog::showDialog(url, mGroup); } } void FileOpsContextManagerItem::cut() { QMimeData* mimeData = selectionMimeData(); KIO::setClipboardDataCut(mimeData, true); QApplication::clipboard()->setMimeData(mimeData); } void FileOpsContextManagerItem::copy() { QMimeData* mimeData = selectionMimeData(); KIO::setClipboardDataCut(mimeData, false); QApplication::clipboard()->setMimeData(mimeData); } void FileOpsContextManagerItem::paste() { KIO::Job *job = KIO::paste(QApplication::clipboard()->mimeData(), pasteTargetUrl()); KJobWidgets::setWindow(job, mGroup); } void FileOpsContextManagerItem::trash() { FileOperations::trash(urlList(), mGroup); } void FileOpsContextManagerItem::del() { FileOperations::del(urlList(), mGroup); } void FileOpsContextManagerItem::restore() { KIO::RestoreJob *job = KIO::restoreFromTrash(urlList()); KJobWidgets::setWindow(job, mGroup); job->uiDelegate()->setAutoErrorHandlingEnabled(true); } void FileOpsContextManagerItem::copyTo() { - FileOperations::copyTo(urlList(), mGroup); + FileOperations::copyTo(urlList(), widget(), contextManager()); } void FileOpsContextManagerItem::moveTo() { - FileOperations::moveTo(urlList(), mGroup); + FileOperations::moveTo(urlList(), widget(), contextManager()); } void FileOpsContextManagerItem::linkTo() { - FileOperations::linkTo(urlList(), mGroup); + FileOperations::linkTo(urlList(), widget(), contextManager()); } void FileOpsContextManagerItem::rename() { if (mThumbnailView->isVisible()) { QModelIndex index = mThumbnailView->currentIndex(); mThumbnailView->edit(index); } else { FileOperations::rename(urlList().first(), mGroup); } } void FileOpsContextManagerItem::createFolder() { QUrl url = contextManager()->currentDirUrl(); mNewFileMenu->setParentWidget(mGroup); mNewFileMenu->setPopupFiles(QList() << url); mNewFileMenu->createDirectory(); } void FileOpsContextManagerItem::populateOpenMenu() { QMenu* openMenu = mOpenWithAction->menu(); qDeleteAll(openMenu->actions()); updateServiceList(); int idx = 0; Q_FOREACH(const KService::Ptr & service, mServiceList) { QString text = service->name().replace('&', "&&"); QAction* action = openMenu->addAction(text); action->setIcon(QIcon::fromTheme(service->icon())); action->setData(idx); ++idx; } openMenu->addSeparator(); QAction* action = openMenu->addAction(i18n("Other Application...")); action->setData(-1); } void FileOpsContextManagerItem::openWith(QAction* action) { Q_ASSERT(action); KService::Ptr service; QList list = urlList(); bool ok; int idx = action->data().toInt(&ok); GV_RETURN_IF_FAIL(ok); if (idx == -1) { // Other Application... KOpenWithDialog dlg(list, mGroup); if (!dlg.exec()) { return; } service = dlg.service(); if (!service) { // User entered a custom command Q_ASSERT(!dlg.text().isEmpty()); KRun::run(dlg.text(), list, mGroup); return; } } else { service = mServiceList.at(idx); } Q_ASSERT(service); KRun::runService(*service, list, mGroup); } } // namespace diff --git a/lib/contextmanager.cpp b/lib/contextmanager.cpp index 6cb05230..e758ff4f 100644 --- a/lib/contextmanager.cpp +++ b/lib/contextmanager.cpp @@ -1,318 +1,330 @@ /* 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 "contextmanager.h" #include "contextmanager.h" // Qt #include #include #include #include // KDE #include #include // Local #include #include #include namespace Gwenview { struct ContextManagerPrivate { SortedDirModel* mDirModel; QItemSelectionModel* mSelectionModel; QUrl mCurrentDirUrl; QUrl mCurrentUrl; QUrl mUrlToSelect; + QUrl mTargetUrl; bool mSelectedFileItemListNeedsUpdate; QSet mQueuedSignals; KFileItemList mSelectedFileItemList; QTimer* mQueuedSignalsTimer; void queueSignal(const QByteArray& signal) { mQueuedSignals << signal; mQueuedSignalsTimer->start(); } void updateSelectedFileItemList() { if (!mSelectedFileItemListNeedsUpdate) { return; } mSelectedFileItemList.clear(); QItemSelection selection = mSelectionModel->selection(); Q_FOREACH(const QModelIndex & index, selection.indexes()) { mSelectedFileItemList << mDirModel->itemForIndex(index); } // At least add current url if it's valid (it may not be in // the list if we are viewing a non-browsable url, for example // using http protocol) if (mSelectedFileItemList.isEmpty() && mCurrentUrl.isValid()) { KFileItem item(mCurrentUrl); mSelectedFileItemList << item; } mSelectedFileItemListNeedsUpdate = false; } }; ContextManager::ContextManager(SortedDirModel* dirModel, QObject* parent) : QObject(parent) , d(new ContextManagerPrivate) { d->mQueuedSignalsTimer = new QTimer(this); d->mQueuedSignalsTimer->setInterval(100); d->mQueuedSignalsTimer->setSingleShot(true); connect(d->mQueuedSignalsTimer, &QTimer::timeout, this, &ContextManager::emitQueuedSignals); d->mDirModel = dirModel; connect(d->mDirModel, &SortedDirModel::dataChanged, this, &ContextManager::slotDirModelDataChanged); /* HACK! In extended-selection mode, when the current index is removed, * QItemSelectionModel selects the previous index if there is one, if not it * selects the next index. This is not what we want: when the user removes * an image, he expects to go to the next one, not the previous one. * * To overcome this, we must connect to the mDirModel.rowsAboutToBeRemoved() * signal *before* QItemSelectionModel connects to it, so that our slot is * called before QItemSelectionModel slot. This allows us to pick a new * current index ourself, leaving QItemSelectionModel slot with nothing to * do. * * This is the reason ContextManager creates a QItemSelectionModel itself: * doing so ensures QItemSelectionModel cannot be connected to the * mDirModel.rowsAboutToBeRemoved() signal before us. */ connect(d->mDirModel, &SortedDirModel::rowsAboutToBeRemoved, this, &ContextManager::slotRowsAboutToBeRemoved); connect(d->mDirModel, &SortedDirModel::rowsInserted, this, &ContextManager::slotRowsInserted); connect(d->mDirModel->dirLister(), SIGNAL(redirection(QUrl)), SLOT(slotDirListerRedirection(QUrl))); d->mSelectionModel = new QItemSelectionModel(d->mDirModel); connect(d->mSelectionModel, &QItemSelectionModel::selectionChanged, this, &ContextManager::slotSelectionChanged); connect(d->mSelectionModel, &QItemSelectionModel::currentChanged, this, &ContextManager::slotCurrentChanged); d->mSelectedFileItemListNeedsUpdate = false; } ContextManager::~ContextManager() { delete d; } QItemSelectionModel* ContextManager::selectionModel() const { return d->mSelectionModel; } void ContextManager::setCurrentUrl(const QUrl ¤tUrl) { if (d->mCurrentUrl == currentUrl) { return; } d->mCurrentUrl = currentUrl; if (!d->mCurrentUrl.isEmpty()) { Document::Ptr doc = DocumentFactory::instance()->load(currentUrl); QUndoGroup* undoGroup = DocumentFactory::instance()->undoGroup(); undoGroup->addStack(doc->undoStack()); undoGroup->setActiveStack(doc->undoStack()); } currentUrlChanged(currentUrl); } KFileItemList ContextManager::selectedFileItemList() const { d->updateSelectedFileItemList(); return d->mSelectedFileItemList; } void ContextManager::setCurrentDirUrl(const QUrl &url) { if (url == d->mCurrentDirUrl) { return; } d->mCurrentDirUrl = url; if (url.isValid()) { d->mDirModel->dirLister()->openUrl(url); } currentDirUrlChanged(url); } QUrl ContextManager::currentDirUrl() const { return d->mCurrentDirUrl; } QUrl ContextManager::currentUrl() const { return d->mCurrentUrl; } SortedDirModel* ContextManager::dirModel() const { return d->mDirModel; } void ContextManager::slotDirModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { // Data change can happen in the following cases: // - items have been renamed // - item bytes have been modified // - item meta info has been retrieved or modified // // If a selected item is affected, schedule emission of a // selectionDataChanged() signal. Don't emit it directly to avoid spamming // the context items in case of a mass change. QModelIndexList selectionList = d->mSelectionModel->selectedIndexes(); if (selectionList.isEmpty()) { return; } QModelIndexList changedList; for (int row = topLeft.row(); row <= bottomRight.row(); ++row) { changedList << d->mDirModel->index(row, 0); } QModelIndexList& shortList = selectionList; QModelIndexList& longList = changedList; if (shortList.length() > longList.length()) { qSwap(shortList, longList); } Q_FOREACH(const QModelIndex & index, shortList) { if (longList.contains(index)) { d->mSelectedFileItemListNeedsUpdate = true; d->queueSignal("selectionDataChanged"); return; } } } void ContextManager::slotSelectionChanged() { d->mSelectedFileItemListNeedsUpdate = true; if (!d->mSelectionModel->hasSelection()) { setCurrentUrl(QUrl()); } d->queueSignal("selectionChanged"); } void Gwenview::ContextManager::slotCurrentChanged(const QModelIndex& index) { QUrl url = d->mDirModel->urlForIndex(index); setCurrentUrl(url); } void ContextManager::emitQueuedSignals() { Q_FOREACH(const QByteArray & signal, d->mQueuedSignals) { QMetaObject::invokeMethod(this, signal.data()); } d->mQueuedSignals.clear(); } void Gwenview::ContextManager::slotRowsAboutToBeRemoved(const QModelIndex& /*parent*/, int start, int end) { QModelIndex oldCurrent = d->mSelectionModel->currentIndex(); if (oldCurrent.row() < start || oldCurrent.row() > end) { // currentIndex has not been removed return; } QModelIndex newCurrent; if (end + 1 < d->mDirModel->rowCount()) { newCurrent = d->mDirModel->index(end + 1, 0); } else if (start > 0) { newCurrent = d->mDirModel->index(start - 1, 0); } else { // No index we can select, nothing to do return; } d->mSelectionModel->select(oldCurrent, QItemSelectionModel::Deselect); d->mSelectionModel->setCurrentIndex(newCurrent, QItemSelectionModel::Select); } bool ContextManager::currentUrlIsRasterImage() const { return MimeTypeUtils::urlKind(currentUrl()) == MimeTypeUtils::KIND_RASTER_IMAGE; } QUrl ContextManager::urlToSelect() const { return d->mUrlToSelect; } void ContextManager::setUrlToSelect(const QUrl &url) { GV_RETURN_IF_FAIL(url.isValid()); d->mUrlToSelect = url; setCurrentUrl(url); selectUrlToSelect(); } +QUrl ContextManager::targetUrl() const +{ + return d->mTargetUrl; +} + +void ContextManager::setTargetUrl(const QUrl &url) +{ + GV_RETURN_IF_FAIL(url.isValid()); + d->mTargetUrl = url; +} + void ContextManager::slotRowsInserted() { // We reach this method when rows have been inserted in the model, but views // may not have been updated yet and thus do not have the matching items. // Delay the selection of mUrlToSelect so that the view items exist. // // Without this, when Gwenview is started with an image as argument and the // thumbnail bar is visible, the image will not be selected in the thumbnail // bar. if (d->mUrlToSelect.isValid()) { QMetaObject::invokeMethod(this, "selectUrlToSelect", Qt::QueuedConnection); } } void ContextManager::selectUrlToSelect() { // Because of the queued connection above we might be called several times in a row // In this case we don't want the warning below if (d->mUrlToSelect.isEmpty()) { return; } GV_RETURN_IF_FAIL(d->mUrlToSelect.isValid()); QModelIndex index = d->mDirModel->indexForUrl(d->mUrlToSelect); if (index.isValid()) { d->mSelectionModel->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); d->mUrlToSelect = QUrl(); } } void ContextManager::slotDirListerRedirection(const QUrl &newUrl) { setCurrentDirUrl(newUrl); } } // namespace diff --git a/lib/contextmanager.h b/lib/contextmanager.h index a10438aa..22f43f36 100644 --- a/lib/contextmanager.h +++ b/lib/contextmanager.h @@ -1,96 +1,100 @@ /* 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 CONTEXTMANAGER_H #define CONTEXTMANAGER_H #include // Qt #include // KDE #include #include class QItemSelectionModel; class QModelIndex; namespace Gwenview { class SortedDirModel; struct ContextManagerPrivate; /** * Manages the state of the application. * TODO: Most of GvCore should be merged in this class */ class GWENVIEWLIB_EXPORT ContextManager : public QObject { Q_OBJECT public: ContextManager(SortedDirModel*, QObject* parent); ~ContextManager(); QUrl currentUrl() const; void setCurrentDirUrl(const QUrl&); QUrl currentDirUrl() const; void setCurrentUrl(const QUrl ¤tUrl); KFileItemList selectedFileItemList() const; SortedDirModel* dirModel() const; QItemSelectionModel* selectionModel() const; bool currentUrlIsRasterImage() const; QUrl urlToSelect() const; void setUrlToSelect(const QUrl&); + QUrl targetUrl() const; + + void setTargetUrl(const QUrl&); + Q_SIGNALS: void currentDirUrlChanged(const QUrl&); void currentUrlChanged(const QUrl&); void selectionChanged(); void selectionDataChanged(); private Q_SLOTS: void slotDirModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); void slotSelectionChanged(); void slotCurrentChanged(const QModelIndex&); void emitQueuedSignals(); void slotRowsAboutToBeRemoved(const QModelIndex& /*parent*/, int start, int end); void slotRowsInserted(); void selectUrlToSelect(); void slotDirListerRedirection(const QUrl&); private: ContextManagerPrivate* const d; }; } // namespace #endif /* CONTEXTMANAGER_H */