File indexing completed on 2024-05-05 04:19:14
0001 // vim: set tabstop=4 shiftwidth=4 expandtab: 0002 /* 0003 Gwenview: an image viewer 0004 Copyright 2007 Aurélien Gâteau <agateau@kde.org> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License 0008 as published by the Free Software Foundation; either version 2 0009 of the License, or (at your option) any later version. 0010 0011 This program is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 GNU General Public License for more details. 0015 0016 You should have received a copy of the GNU General Public License 0017 along with this program; if not, write to the Free Software 0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. 0019 0020 */ 0021 // Self 0022 #include "fileoperations.h" 0023 0024 // Qt 0025 #include <QFileDialog> 0026 #include <QMenu> 0027 #include <QPushButton> 0028 0029 // KF 0030 #include "kio_version.h" 0031 #include <KIO/CopyJob> 0032 #include <KIO/DeleteJob> 0033 #include <KIO/DeleteOrTrashJob> 0034 #include <KIO/JobUiDelegate> 0035 #include <KIO/SimpleJob> 0036 #include <KJobWidgets> 0037 #include <KLocalizedString> 0038 0039 // Local 0040 #include "gwenview_app_debug.h" 0041 #include "renamedialog.h" 0042 #include <lib/contextmanager.h> 0043 #include <lib/document/documentfactory.h> 0044 #include <lib/thumbnailprovider/thumbnailprovider.h> 0045 0046 namespace Gwenview 0047 { 0048 namespace FileOperations 0049 { 0050 static void copyMoveOrLink(Operation operation, const QList<QUrl> &urlList, QWidget *parent, ContextManager *contextManager) 0051 { 0052 Q_ASSERT(!urlList.isEmpty()); 0053 const int numberOfImages = urlList.count(); 0054 0055 auto dialog = new QFileDialog(parent->nativeParentWidget(), QString()); 0056 dialog->setAcceptMode(QFileDialog::AcceptSave); 0057 dialog->setAttribute(Qt::WA_DeleteOnClose); 0058 dialog->setModal(true); 0059 0060 // Figure out what the window title and buttons should say, 0061 // depending on the operation and how many images are selected 0062 switch (operation) { 0063 case COPY: 0064 if (numberOfImages == 1) { 0065 dialog->setWindowTitle(i18nc("@title:window %1 file name", "Copy %1", urlList.constFirst().fileName())); 0066 } else { 0067 dialog->setWindowTitle(i18ncp("@title:window %1 number of images", "Copy %1 image", "Copy %1 images", numberOfImages)); 0068 } 0069 dialog->setLabelText(QFileDialog::DialogLabel::Accept, i18nc("@action:button", "Copy")); 0070 break; 0071 case MOVE: 0072 if (numberOfImages == 1) { 0073 dialog->setWindowTitle(i18nc("@title:window %1 file name", "Move %1", urlList.constFirst().fileName())); 0074 } else { 0075 dialog->setWindowTitle(i18ncp("@title:window %1 number of images", "Move %1 image", "Move %1 images", numberOfImages)); 0076 } 0077 dialog->setLabelText(QFileDialog::DialogLabel::Accept, i18nc("@action:button", "Move")); 0078 break; 0079 case LINK: 0080 if (numberOfImages == 1) { 0081 dialog->setWindowTitle(i18nc("@title:window %1 file name", "Link %1", urlList.constFirst().fileName())); 0082 } else { 0083 dialog->setWindowTitle(i18ncp("@title:window %1 number of images", "Link %1 image", "Link %1 images", numberOfImages)); 0084 } 0085 dialog->setLabelText(QFileDialog::DialogLabel::Accept, i18nc("@action:button", "Link")); 0086 break; 0087 default: 0088 Q_ASSERT(0); 0089 } 0090 0091 if (numberOfImages == 1) { 0092 dialog->setFileMode(QFileDialog::AnyFile); 0093 dialog->selectUrl(urlList.constFirst()); 0094 } else { 0095 dialog->setFileMode(QFileDialog::Directory); 0096 dialog->setOption(QFileDialog::ShowDirsOnly, true); 0097 } 0098 0099 QUrl dirUrl = contextManager->targetDirUrl(); 0100 if (!dirUrl.isValid()) { 0101 dirUrl = urlList.constFirst().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); 0102 } 0103 dialog->setDirectoryUrl(dirUrl); 0104 0105 QObject::connect(dialog, &QDialog::accepted, parent, [=]() { 0106 QUrl destUrl = dialog->selectedUrls().at(0); 0107 0108 KIO::CopyJob *job = nullptr; 0109 switch (operation) { 0110 case COPY: 0111 job = KIO::copy(urlList, destUrl); 0112 break; 0113 case MOVE: 0114 job = KIO::move(urlList, destUrl); 0115 break; 0116 case LINK: 0117 job = KIO::link(urlList, destUrl); 0118 break; 0119 default: 0120 Q_ASSERT(0); 0121 } 0122 KJobWidgets::setWindow(job, parent); 0123 job->uiDelegate()->setAutoErrorHandlingEnabled(true); 0124 0125 if (numberOfImages == 1) { 0126 destUrl = destUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); 0127 } 0128 contextManager->setTargetDirUrl(destUrl); 0129 }); 0130 0131 dialog->show(); 0132 } 0133 0134 static void delOrTrash(KIO::JobUiDelegate::DeletionType deletionType, const QList<QUrl> &urlList, QWidget *parent) 0135 { 0136 Q_ASSERT(urlList.count() > 0); 0137 0138 KJob *job = nullptr; 0139 switch (deletionType) { 0140 case KIO::JobUiDelegate::Trash: 0141 job = new KIO::DeleteOrTrashJob(urlList, KIO::AskUserActionInterface::Trash, KIO::AskUserActionInterface::DefaultConfirmation, parent); 0142 break; 0143 case KIO::JobUiDelegate::Delete: 0144 job = new KIO::DeleteOrTrashJob(urlList, KIO::AskUserActionInterface::Delete, KIO::AskUserActionInterface::DefaultConfirmation, parent); 0145 break; 0146 default: // e.g. EmptyTrash 0147 return; 0148 } 0149 Q_ASSERT(job); 0150 KJobWidgets::setWindow(job, parent); 0151 job->start(); 0152 0153 for (const QUrl &url : urlList) { 0154 DocumentFactory::instance()->forget(url); 0155 } 0156 } 0157 0158 void copyTo(const QList<QUrl> &urlList, QWidget *parent, ContextManager *contextManager) 0159 { 0160 copyMoveOrLink(COPY, urlList, parent, contextManager); 0161 } 0162 0163 void moveTo(const QList<QUrl> &urlList, QWidget *parent, ContextManager *contextManager) 0164 { 0165 copyMoveOrLink(MOVE, urlList, parent, contextManager); 0166 } 0167 0168 void linkTo(const QList<QUrl> &urlList, QWidget *parent, ContextManager *contextManager) 0169 { 0170 copyMoveOrLink(LINK, urlList, parent, contextManager); 0171 } 0172 0173 void trash(const QList<QUrl> &urlList, QWidget *parent) 0174 { 0175 delOrTrash(KIO::JobUiDelegate::Trash, urlList, parent); 0176 } 0177 0178 void del(const QList<QUrl> &urlList, QWidget *parent) 0179 { 0180 delOrTrash(KIO::JobUiDelegate::Delete, urlList, parent); 0181 } 0182 0183 void showMenuForDroppedUrls(QWidget *parent, const QList<QUrl> &urlList, const QUrl &destUrl) 0184 { 0185 if (urlList.isEmpty()) { 0186 qCWarning(GWENVIEW_APP_LOG) << "urlList is empty!"; 0187 return; 0188 } 0189 0190 if (!destUrl.isValid()) { 0191 qCWarning(GWENVIEW_APP_LOG) << "destUrl is not valid!"; 0192 return; 0193 } 0194 0195 QMenu menu(parent); 0196 QAction *moveAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-move"), QIcon::fromTheme(QStringLiteral("go-jump"))), i18n("Move Here")); 0197 QAction *copyAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Copy Here")); 0198 QAction *linkAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-link")), i18n("Link Here")); 0199 menu.addSeparator(); 0200 menu.addAction(QIcon::fromTheme(QStringLiteral("process-stop")), i18n("Cancel")); 0201 0202 QAction *action = menu.exec(QCursor::pos()); 0203 0204 KIO::Job *job = nullptr; 0205 if (action == moveAction) { 0206 job = KIO::move(urlList, destUrl); 0207 } else if (action == copyAction) { 0208 job = KIO::copy(urlList, destUrl); 0209 } else if (action == linkAction) { 0210 job = KIO::link(urlList, destUrl); 0211 } else { 0212 return; 0213 } 0214 Q_ASSERT(job); 0215 KJobWidgets::setWindow(job, parent); 0216 } 0217 0218 void rename(const QUrl &oldUrl, QWidget *parent, ContextManager *contextManager) 0219 { 0220 auto dialog = new RenameDialog(parent); 0221 dialog->setFilename(oldUrl.fileName()); 0222 dialog->setAttribute(Qt::WA_DeleteOnClose); 0223 dialog->setModal(true); 0224 0225 QObject::connect(dialog, &QDialog::accepted, parent, [=]() { 0226 const QString name = dialog->filename(); 0227 if (name.isEmpty() || name == oldUrl.fileName()) { 0228 return; 0229 } 0230 0231 QUrl newUrl = oldUrl; 0232 newUrl = newUrl.adjusted(QUrl::RemoveFilename); 0233 newUrl.setPath(newUrl.path() + name); 0234 KIO::SimpleJob *job = KIO::rename(oldUrl, newUrl, KIO::HideProgressInfo); 0235 KJobWidgets::setWindow(job, parent); 0236 job->uiDelegate()->setAutoErrorHandlingEnabled(true); 0237 QObject::connect(job, &KJob::result, parent, [contextManager, job, oldUrl, newUrl]() { 0238 if (!job->error()) { 0239 contextManager->setCurrentUrl(newUrl); 0240 ThumbnailProvider::moveThumbnail(oldUrl, newUrl); 0241 } 0242 }); 0243 }); 0244 0245 dialog->show(); 0246 } 0247 0248 } // namespace 0249 0250 } // namespace