1 /*****************************************************************************
2  * Copyright (C) 2000 Shie Erlich <krusader@users.sourceforge.net>           *
3  * Copyright (C) 2000 Rafi Yanai <krusader@users.sourceforge.net>            *
4  * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org]              *
5  *                                                                           *
6  * This file is part of Krusader [https://krusader.org].                     *
7  *                                                                           *
8  * Krusader is free software: you can redistribute it and/or modify          *
9  * it under the terms of the GNU General Public License as published by      *
10  * the Free Software Foundation, either version 2 of the License, or         *
11  * (at your option) any later version.                                       *
12  *                                                                           *
13  * Krusader is distributed in the hope that it will be useful,               *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
16  * GNU General Public License for more details.                              *
17  *                                                                           *
18  * You should have received a copy of the GNU General Public License         *
19  * along with Krusader.  If not, see [http://www.gnu.org/licenses/].         *
20  *****************************************************************************/
21 
22 #include "panelfunc.h"
23 
24 // QtCore
25 #include <QEventLoop>
26 #include <QList>
27 #include <QMimeData>
28 #include <QDir>
29 #include <QTemporaryFile>
30 #include <QUrl>
31 // QtGui
32 #include <QClipboard>
33 #include <QDrag>
34 // QtWidgets
35 #include <QApplication>
36 #include <QInputDialog>
37 
38 #include <KArchive/KTar>
39 #include <KConfigCore/KDesktopFile>
40 #include <KCoreAddons/KJobTrackerInterface>
41 #include <KCoreAddons/KProcess>
42 #include <KCoreAddons/KShell>
43 #include <KCoreAddons/KUrlMimeData>
44 #include <KI18n/KLocalizedString>
45 #include <KIO/DesktopExecParser>
46 #include <KIO/JobUiDelegate>
47 #include <KIOCore/KProtocolInfo>
48 #include <KIOWidgets/KOpenWithDialog>
49 #include <KIOWidgets/KPropertiesDialog>
50 #include <KIOWidgets/KRun>
51 #include <KService/KMimeTypeTrader>
52 #include <KWidgetsAddons/KCursor>
53 #include <KWidgetsAddons/KMessageBox>
54 #include <KWidgetsAddons/KToggleAction>
55 
56 #include "dirhistoryqueue.h"
57 #include "krcalcspacedialog.h"
58 #include "krerrordisplay.h"
59 #include "krsearchbar.h"
60 #include "listpanel.h"
61 #include "listpanelactions.h"
62 #include "PanelView/krview.h"
63 #include "PanelView/krviewitem.h"
64 #include "../krglobal.h"
65 #include "../krslots.h"
66 #include "../kractions.h"
67 #include "../defaults.h"
68 #include "../abstractpanelmanager.h"
69 #include "../krservices.h"
70 #include "../Archive/krarchandler.h"
71 #include "../Archive/packjob.h"
72 #include "../FileSystem/fileitem.h"
73 #include "../FileSystem/virtualfilesystem.h"
74 #include "../FileSystem/krpermhandler.h"
75 #include "../FileSystem/filesystemprovider.h"
76 #include "../FileSystem/sizecalculator.h"
77 #include "../Dialogs/packgui.h"
78 #include "../Dialogs/krdialogs.h"
79 #include "../Dialogs/krpleasewait.h"
80 #include "../Dialogs/krspwidgets.h"
81 #include "../Dialogs/checksumdlg.h"
82 #include "../KViewer/krviewer.h"
83 #include "../MountMan/kmountman.h"
84 
85 
86 QPointer<ListPanelFunc> ListPanelFunc::copyToClipboardOrigin;
87 
ListPanelFunc(ListPanel * parent)88 ListPanelFunc::ListPanelFunc(ListPanel *parent) : QObject(parent),
89         panel(parent), fileSystemP(0), urlManuallyEntered(false),
90         _isPaused(true), _refreshAfterPaused(true), _quickSizeCalculator(0)
91 {
92     history = new DirHistoryQueue(panel);
93     delayTimer.setSingleShot(true);
94     connect(&delayTimer, SIGNAL(timeout()), this, SLOT(doRefresh()));
95 }
96 
~ListPanelFunc()97 ListPanelFunc::~ListPanelFunc()
98 {
99     if (fileSystemP) {
100         fileSystemP->deleteLater();
101     }
102     delete history;
103     if (_quickSizeCalculator)
104         _quickSizeCalculator->deleteLater();
105 }
106 
isSyncing(const QUrl & url)107 bool ListPanelFunc::isSyncing(const QUrl &url)
108 {
109     if(otherFunc()->otherFunc() == this &&
110        panel->otherPanel()->gui->syncBrowseButton->isChecked() &&
111        !otherFunc()->syncURL.isEmpty() &&
112        otherFunc()->syncURL == url)
113         return true;
114 
115     return false;
116 }
117 
openFileNameInternal(const QString & name,bool externallyExecutable)118 void ListPanelFunc::openFileNameInternal(const QString &name, bool externallyExecutable)
119 {
120     if (name == "..") {
121         dirUp();
122         return;
123     }
124 
125     FileItem *fileitem = files()->getFileItem(name);
126     if (fileitem == 0)
127         return;
128 
129     QUrl url = files()->getUrl(name);
130 
131     if (fileitem->isDir()) {
132         panel->view->setNameToMakeCurrent(QString());
133         openUrl(url);
134         return;
135     }
136 
137     QString mime = fileitem->getMime();
138 
139     QUrl arcPath = browsableArchivePath(name);
140     if (!arcPath.isEmpty()) {
141         bool browseAsDirectory = !externallyExecutable
142                 || (KConfigGroup(krConfig, "Archives").readEntry("ArchivesAsDirectories", _ArchivesAsDirectories)
143                 && (KRarcHandler::arcSupported(mime) || KrServices::isoSupported(mime)));
144         if (browseAsDirectory) {
145             openUrl(arcPath);
146             return;
147         }
148     }
149 
150     if (externallyExecutable) {
151         if (KRun::isExecutableFile(url, mime)) {
152             runCommand(KShell::quoteArg(url.path()));
153             return;
154         }
155 
156         KService::Ptr service = KMimeTypeTrader::self()->preferredService(mime);
157         if(service) {
158             runService(*service, QList<QUrl>() << url);
159             return;
160         }
161 
162         displayOpenWithDialog(QList<QUrl>() << url);
163     }
164 }
165 
cleanPath(const QUrl & urlIn)166 QUrl ListPanelFunc::cleanPath(const QUrl &urlIn)
167 {
168     QUrl url = urlIn;
169     url.setPath(QDir::cleanPath(url.path()));
170     if (!url.isValid() || url.isRelative()) {
171         if (url.url() == "~")
172             url = QUrl::fromLocalFile(QDir::homePath());
173         else if (!url.url().startsWith('/')) {
174             // possible relative URL - translate to full URL
175             url = files()->currentDirectory();
176             url.setPath(url.path() + '/' + urlIn.path());
177         }
178     }
179     url.setPath(QDir::cleanPath(url.path()));
180     return url;
181 }
182 
openUrl(const QUrl & url,const QString & nameToMakeCurrent,bool manuallyEntered)183 void ListPanelFunc::openUrl(const QUrl &url, const QString& nameToMakeCurrent,
184                             bool manuallyEntered)
185 {
186     qDebug() << "URL=" << url.toDisplayString() << "; name to current=" << nameToMakeCurrent;
187     if (panel->syncBrowseButton->isChecked()) {
188         //do sync-browse stuff....
189         if(syncURL.isEmpty())
190             syncURL = panel->otherPanel()->virtualPath();
191 
192         QString relative = QDir(panel->virtualPath().path() + '/').relativeFilePath(url.path());
193         syncURL.setPath(QDir::cleanPath(syncURL.path() + '/' + relative));
194         panel->otherPanel()->gui->setTabState(ListPanel::TabState::DEFAULT);
195         otherFunc()->openUrlInternal(syncURL, nameToMakeCurrent, false, false);
196     }
197     openUrlInternal(url, nameToMakeCurrent, false, manuallyEntered);
198 }
199 
immediateOpenUrl(const QUrl & url)200 void ListPanelFunc::immediateOpenUrl(const QUrl &url)
201 {
202     openUrlInternal(url, QString(), true, false);
203 }
204 
openUrlInternal(const QUrl & url,const QString & nameToMakeCurrent,bool immediately,bool manuallyEntered)205 void ListPanelFunc::openUrlInternal(const QUrl &url, const QString& nameToMakeCurrent,
206                                     bool immediately, bool manuallyEntered)
207 {
208     const QUrl cleanUrl = cleanPath(url);
209 
210     if (panel->isLocked() &&
211             !files()->currentDirectory().matches(cleanUrl, QUrl::StripTrailingSlash)) {
212         panel->_manager->newTab(url);
213         urlManuallyEntered = false;
214         return;
215     }
216 
217     urlManuallyEntered = manuallyEntered;
218 
219     const QString currentItem = history->currentUrl().path() == cleanUrl.path()
220                                 ? history->currentItem()
221                                 : nameToMakeCurrent;
222 
223     history->add(cleanUrl, currentItem);
224 
225     if(immediately)
226         doRefresh();
227     else
228         refresh();
229 }
230 
refresh()231 void ListPanelFunc::refresh()
232 {
233     panel->cancelProgress();
234     delayTimer.start(0); // to avoid qApp->processEvents() deadlock situaltion
235 }
236 
doRefresh()237 void ListPanelFunc::doRefresh()
238 {
239     delayTimer.stop();
240 
241     if (_isPaused) {
242         _refreshAfterPaused = true;
243         // simulate refresh
244         panel->slotStartUpdate(true);
245         return;
246     } else {
247         _refreshAfterPaused = false;
248     }
249 
250     const QUrl url = history->currentUrl();
251 
252     if(!url.isValid()) {
253         panel->slotStartUpdate(true);  // refresh the panel
254         urlManuallyEntered = false;
255         return;
256     }
257 
258     panel->cancelProgress();
259 
260     // if we are not refreshing to current URL
261     const bool isEqualUrl = files()->currentDirectory().matches(url, QUrl::StripTrailingSlash);
262 
263     if (!isEqualUrl) {
264         panel->setCursor(Qt::WaitCursor);
265         panel->view->clearSavedSelection();
266     }
267 
268     if (panel->fileSystemError) {
269         panel->fileSystemError->hide();
270     }
271 
272     panel->setNavigatorUrl(url);
273 
274     // may get a new filesystem for this url
275     FileSystem *fileSystem = FileSystemProvider::instance().getFilesystem(url, files());
276     fileSystem->setParentWindow(krMainWindow);
277     connect(fileSystem, &FileSystem::aboutToOpenDir, &krMtMan, &KMountMan::autoMount, Qt::DirectConnection);
278     if (fileSystem != fileSystemP) {
279         panel->view->setFiles(0);
280 
281         // disconnect older signals
282         disconnect(fileSystemP, 0, panel, 0);
283 
284         fileSystemP->deleteLater();
285         fileSystemP = fileSystem; // v != 0 so this is safe
286     } else {
287         if (fileSystemP->isRefreshing()) {
288             // TODO remove busy waiting here
289             delayTimer.start(100); /* if filesystem is busy try refreshing later */
290             return;
291         }
292     }
293     // (re)connect filesystem signals
294     disconnect(files(), 0, panel, 0);
295     connect(files(), &DirListerInterface::scanDone, panel, &ListPanel::slotStartUpdate);
296     connect(files(), &FileSystem::fileSystemInfoChanged, panel, &ListPanel::updateFilesystemStats);
297     connect(files(), &FileSystem::refreshJobStarted, panel, &ListPanel::slotRefreshJobStarted);
298     connect(files(), SIGNAL(error(QString)),
299             panel, SLOT(slotFilesystemError(QString)));
300 
301     panel->view->setFiles(files());
302 
303     if (!isEqualUrl || !panel->view->getCurrentKrViewItem()) {
304         // set current item after refresh from history, if there is none yet
305         panel->view->setNameToMakeCurrent(history->currentItem());
306     }
307 
308     // workaround for detecting panel deletion while filesystem is refreshing
309     QPointer<ListPanel> panelSave = panel;
310     // NOTE: this is blocking. Returns false on error or interruption (cancel requested or panel
311     // was deleted)
312     const bool scanned = fileSystemP->refresh(url);
313     if (scanned) {
314         // update the history and address bar, as the actual url might differ from the one requested
315         history->setCurrentUrl(fileSystemP->currentDirectory());
316         panel->setNavigatorUrl(fileSystemP->currentDirectory());
317     } else if (!panelSave) {
318         return;
319     }
320 
321     panel->view->setNameToMakeCurrent(QString());
322 
323     panel->setCursor(Qt::ArrowCursor);
324 
325     // on local file system change the working directory
326     if (files()->isLocal())
327         QDir::setCurrent(KrServices::urlToLocalPath(files()->currentDirectory()));
328 
329     // see if the open url operation failed, and if so,
330     // put the attempted url in the navigator bar and let the user change it
331     if (!scanned) {
332         if(isSyncing(url))
333             panel->otherPanel()->gui->syncBrowseButton->setChecked(false);
334         else if(urlManuallyEntered) {
335             panel->setNavigatorUrl(url);
336             if(panel == ACTIVE_PANEL)
337                 panel->editLocation();
338         }
339     }
340 
341     if(otherFunc()->otherFunc() == this)  // not true if our tab is not active
342         otherFunc()->syncURL = QUrl();
343 
344     urlManuallyEntered = false;
345 
346     refreshActions();
347 }
348 
setPaused(bool paused)349 void ListPanelFunc::setPaused(bool paused) {
350     if (paused == _isPaused)
351         return;
352     _isPaused = paused;
353 
354     // TODO: disable refresh() in local file system when paused
355 
356     if (!_isPaused && _refreshAfterPaused)
357         refresh();
358 }
359 
redirectLink()360 void ListPanelFunc::redirectLink()
361 {
362     if (!files()->isLocal()) {
363         KMessageBox::sorry(krMainWindow, i18n("You can edit links only on local file systems"));
364         return;
365     }
366 
367     FileItem *fileitem = files()->getFileItem(panel->getCurrentName());
368     if (!fileitem)
369         return;
370 
371     QString file = fileitem->getUrl().path();
372     QString currentLink = fileitem->getSymDest();
373     if (currentLink.isEmpty()) {
374         KMessageBox::sorry(krMainWindow, i18n("The current file is not a link, so it cannot be redirected."));
375         return;
376     }
377 
378     // ask the user for a new destination
379     bool ok = false;
380     QString newLink =
381         QInputDialog::getText(krMainWindow, i18n("Link Redirection"), i18n("Please enter the new link destination:"), QLineEdit::Normal, currentLink, &ok);
382 
383     // if the user canceled - quit
384     if (!ok || newLink == currentLink)
385         return;
386     // delete the current link
387     if (unlink(file.toLocal8Bit()) == -1) {
388         KMessageBox::sorry(krMainWindow, i18n("Cannot remove old link: %1", file));
389         return;
390     }
391     // try to create a new symlink
392     if (symlink(newLink.toLocal8Bit(), file.toLocal8Bit()) == -1) {
393         KMessageBox:: /* --=={ Patch by Heiner <h.eichmann@gmx.de> }==-- */sorry(krMainWindow, i18n("Failed to create a new link: %1", file));
394         return;
395     }
396 }
397 
krlink(bool sym)398 void ListPanelFunc::krlink(bool sym)
399 {
400     if (!files()->isLocal()) {
401         KMessageBox::sorry(krMainWindow, i18n("You can create links only on local file systems"));
402         return;
403     }
404 
405     QString name = panel->getCurrentName();
406 
407     // ask the new link name..
408     bool ok = false;
409     QString linkName =
410         QInputDialog::getText(krMainWindow, i18n("New Link"),
411                               i18n("Create a new link to: %1", name), QLineEdit::Normal, name, &ok);
412 
413     // if the user canceled - quit
414     if (!ok || linkName == name)
415         return;
416 
417     // if the name is already taken - quit
418     if (files()->getFileItem(linkName) != 0) {
419         KMessageBox::sorry(krMainWindow, i18n("A folder or a file with this name already exists."));
420         return;
421     }
422 
423     // make link name and target absolute path
424     if (linkName.left(1) != "/")
425         linkName = files()->currentDirectory().path() + '/' + linkName;
426     name = files()->getUrl(name).path();
427 
428     if (sym) {
429         if (symlink(name.toLocal8Bit(), linkName.toLocal8Bit()) == -1)
430             KMessageBox::sorry(krMainWindow,
431                                i18n("Failed to create a new symlink '%1' to: '%2'", linkName, name));
432     } else {
433         if (link(name.toLocal8Bit(), linkName.toLocal8Bit()) == -1)
434             KMessageBox::sorry(krMainWindow,
435                                i18n("Failed to create a new link '%1' to '%2'", linkName, name));
436     }
437 }
438 
view()439 void ListPanelFunc::view()
440 {
441     panel->searchBar->hideBarIfSearching();
442 
443     QString fileName = panel->getCurrentName();
444     if (fileName.isNull())
445         return;
446 
447     // if we're trying to view a directory, just exit
448     FileItem *fileitem = files()->getFileItem(fileName);
449     if (!fileitem || fileitem->isDir())
450         return;
451     if (!fileitem->isReadable()) {
452         KMessageBox::sorry(0, i18n("No permissions to view this file."));
453         return;
454     }
455     // call KViewer.
456     KrViewer::view(files()->getUrl(fileName));
457     // nothing more to it!
458 }
459 
viewDlg()460 void ListPanelFunc::viewDlg()
461 {
462     // ask the user for a url to view
463     QUrl dest = KChooseDir::getFile(i18n("Enter a URL to view:"), QUrl(panel->getCurrentName()), panel->virtualPath());
464     if (dest.isEmpty())
465         return ;   // the user canceled
466 
467     KrViewer::view(dest);   // view the file
468 }
469 
terminal()470 void ListPanelFunc::terminal()
471 {
472     SLOTS->runTerminal(panel->lastLocalPath());
473 }
474 
editFile(const QUrl & newFilePath)475 void ListPanelFunc::editFile(const QUrl &newFilePath)
476 {
477     panel->searchBar->hideBarIfSearching();
478 
479     QUrl editPath;
480     if (!newFilePath.isEmpty()) {
481         editPath = newFilePath;
482     } else {
483         const QString name = panel->getCurrentName();
484         if (name.isNull())
485             return;
486         editPath = files()->getUrl(name);
487     }
488 
489     const KFileItem fileToEdit = KFileItem(newFilePath);
490 
491     if (fileToEdit.isDir()) {
492         KMessageBox::sorry(krMainWindow, i18n("You cannot edit a folder"));
493         return;
494     }
495 
496     if (!fileToEdit.isReadable()) {
497         KMessageBox::sorry(0, i18n("No permissions to edit this file."));
498         return;
499     }
500 
501     KrViewer::edit(editPath);
502 }
503 
editNewFile()504 void ListPanelFunc::editNewFile()
505 {
506     // ask the user for the filename to edit
507     const QUrl filePath = KChooseDir::getFile(i18n("Enter the filename to edit:"),
508                                        QUrl(panel->getCurrentName()), panel->virtualPath());
509     if(filePath.isEmpty())
510         return ;   // the user canceled
511 
512     if (filePath.isLocalFile()) {
513         // if the file exists, edit it instead of creating a new one
514         QFile file(filePath.toLocalFile());
515         if (file.exists()) {
516             editFile();
517             return;
518         } else {
519             // simply create a local file
520             // also because KIO::CopyJob::setDefaultPermissions does not work
521 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
522             file.open(QIODevice::NewOnly);
523 #else
524             file.open(QIODevice::WriteOnly);
525 #endif
526             file.close();
527             slotFileCreated(nullptr, filePath);
528             return;
529         }
530     }
531 
532     auto *tempFile = new QTemporaryFile;
533     tempFile->setAutoRemove(false); // done below
534     tempFile->open(); // create file
535 
536     KIO::CopyJob *job = KIO::copy(QUrl::fromLocalFile(tempFile->fileName()), filePath);
537     job->setUiDelegate(nullptr);
538     job->setDefaultPermissions(true);
539     connect(job, &KIO::CopyJob::result, this, [=](KJob *job) { slotFileCreated(job, filePath); });
540     connect(job, &KIO::CopyJob::result, tempFile, &QTemporaryFile::deleteLater);
541 }
542 
slotFileCreated(KJob * job,const QUrl filePath)543 void ListPanelFunc::slotFileCreated(KJob *job, const QUrl filePath)
544 {
545     if (!job || (!job->error() || job->error() == KIO::ERR_FILE_ALREADY_EXIST)) {
546         KrViewer::edit(filePath);
547 
548         if (KIO::upUrl(filePath).matches(panel->virtualPath(), QUrl::StripTrailingSlash)) {
549             refresh();
550         }
551         if (KIO::upUrl(filePath).matches(panel->otherPanel()->virtualPath(), QUrl::StripTrailingSlash)) {
552             otherFunc()->refresh();
553         }
554     } else {
555         KMessageBox::sorry(krMainWindow, job->errorString());
556     }
557 }
558 
copyFiles(bool enqueue,bool move)559 void ListPanelFunc::copyFiles(bool enqueue, bool move)
560 {
561     panel->searchBar->hideBarIfSearching();
562 
563     const QStringList fileNames = panel->getSelectedNames();
564     if (fileNames.isEmpty())
565         return ;  // safety
566 
567     QUrl destination = panel->otherPanel()->virtualPath();
568 
569     bool fullDestPath = false;
570     if (fileNames.count() == 1 && otherFunc()->files()->type() != FileSystem::FS_VIRTUAL) {
571         FileItem *item = files()->getFileItem(fileNames[0]);
572         if (item && !item->isDir()) {
573             fullDestPath = true;
574             // add original filename to destination
575             destination.setPath(QDir(destination.path()).filePath(item->getUrl().fileName()));
576         }
577     }
578     if (!fullDestPath) {
579         destination = FileSystem::ensureTrailingSlash(destination);
580     }
581 
582     const KConfigGroup group(krConfig, "Advanced");
583     const bool showDialog = move ? group.readEntry("Confirm Move", _ConfirmMove) :
584                                    group.readEntry("Confirm Copy", _ConfirmCopy);
585 
586     if (showDialog) {
587         QString operationText;
588         if (move) {
589             operationText = fileNames.count() == 1
590                                 ? i18n("Move %1 to:", fileNames.first())
591                                 : i18np("Move %1 file to:", "Move %1 files to:", fileNames.count());
592         } else {
593             operationText = fileNames.count() == 1
594                                 ? i18n("Copy %1 to:", fileNames.first())
595                                 : i18np("Copy %1 file to:", "Copy %1 files to:", fileNames.count());
596         }
597 
598         // ask the user for the copy/move dest
599         const KChooseDir::ChooseResult result =
600             KChooseDir::getCopyDir(operationText, destination, panel->virtualPath());
601         destination = result.url;
602         if (destination.isEmpty())
603             return ; // the user canceled
604 
605         enqueue = result.enqueue;
606     }
607 
608     const JobMan::StartMode startMode =
609         enqueue && krJobMan->isQueueModeEnabled() ? JobMan::Delay :
610         !enqueue && !krJobMan->isQueueModeEnabled() ? JobMan::Start : JobMan::Enqueue;
611 
612     const QList<QUrl> fileUrls = files()->getUrls(fileNames);
613 
614     if (move) {
615         // after the delete return the cursor to the first unmarked file above the current item
616         panel->prepareToDelete();
617     }
618 
619     // make sure the user does not overwrite multiple files by mistake
620     if (fileNames.count() > 1) {
621         destination = FileSystem::ensureTrailingSlash(destination);
622     }
623 
624     const KIO::CopyJob::CopyMode mode = move ? KIO::CopyJob::Move : KIO::CopyJob::Copy;
625     FileSystemProvider::instance().startCopyFiles(fileUrls, destination, mode, true, startMode);
626 
627     if(KConfigGroup(krConfig, "Look&Feel").readEntry("UnselectBeforeOperation", _UnselectBeforeOperation)) {
628         panel->view->saveSelection();
629         panel->view->unselectAll();
630     }
631 }
632 
633 // called from SLOTS to begin the renaming process
rename()634 void ListPanelFunc::rename()
635 {
636     panel->searchBar->hideBarIfSearching();
637     panel->view->renameCurrentItem();
638 }
639 
640 // called by signal itemRenamed() from the view to complete the renaming process
rename(const QString & oldname,const QString & newname)641 void ListPanelFunc::rename(const QString &oldname, const QString &newname)
642 {
643     if (oldname == newname)
644         return ; // do nothing
645 
646     // set current after rename
647     panel->view->setNameToMakeCurrent(newname);
648 
649     // as always - the filesystem do the job
650     files()->rename(oldname, newname);
651 }
652 
mkdir()653 void ListPanelFunc::mkdir()
654 {
655     // ask the new dir name..
656     // suggested name is the complete name for the directories
657     // while filenames are suggested without their extension
658     QString suggestedName = panel->getCurrentName();
659     if (!suggestedName.isEmpty() && !files()->getFileItem(suggestedName)->isDir())
660         suggestedName = QFileInfo(suggestedName).completeBaseName();
661 
662     const QString dirName = QInputDialog::getText(krMainWindow, i18n("New folder"), i18n("Folder's name:"), QLineEdit::Normal, suggestedName);
663 
664     const QString firstName = dirName.section('/', 0, 0, QString::SectionIncludeLeadingSep);
665 
666     // if the user canceled or the name was composed of slashes - quit
667     if (!dirName.startsWith('/') && firstName.isEmpty()) {
668         return;
669     }
670 
671     // notify user about existing folder if only single directory was given
672     if (!dirName.contains('/') && files()->getFileItem(firstName)) {
673         // focus the existing directory
674         panel->view->setCurrentItem(firstName);
675         // show error message
676         KMessageBox::sorry(krMainWindow, i18n("A folder or a file with this name already exists."));
677         return;
678     }
679 
680     // focus new directory when next refresh happens
681     panel->view->setNameToMakeCurrent(firstName);
682 
683     // create new directory (along with underlying directories if necessary)
684     files()->mkDir(dirName);
685 }
686 
defaultOrAlternativeDeleteFiles(bool invert)687 void ListPanelFunc::defaultOrAlternativeDeleteFiles(bool invert)
688 {
689     const bool trash = KConfigGroup(krConfig, "General").readEntry("Move To Trash", _MoveToTrash);
690     deleteFiles(trash != invert);
691 }
692 
deleteFiles(bool moveToTrash)693 void ListPanelFunc::deleteFiles(bool moveToTrash)
694 {
695     panel->searchBar->hideBarIfSearching();
696 
697     const bool isVFS = files()->type() == FileSystem::FS_VIRTUAL;
698     if (isVFS && files()->isRoot()) {
699         // only virtual deletion possible
700         removeVirtualFiles();
701         return;
702     }
703 
704     // first get the selected file names list
705     const QStringList fileNames = panel->getSelectedNames();
706     if (fileNames.isEmpty())
707         return;
708 
709     // move to trash: only if possible
710     moveToTrash = moveToTrash && files()->canMoveToTrash(fileNames);
711 
712     // now ask the user if he/she is sure:
713 
714     const QList<QUrl> confirmedUrls = confirmDeletion(
715         files()->getUrls(fileNames), moveToTrash, isVFS, false);
716 
717     if (confirmedUrls.isEmpty())
718         return; // nothing to delete
719 
720     // after the delete return the cursor to the first unmarked
721     // file above the current item;
722     panel->prepareToDelete();
723 
724     // let the filesystem do the job...
725     files()->deleteFiles(confirmedUrls, moveToTrash);
726 }
727 
confirmDeletion(const QList<QUrl> & urls,bool moveToTrash,bool virtualFS,bool showPath)728 QList<QUrl> ListPanelFunc::confirmDeletion(const QList<QUrl> &urls, bool moveToTrash,
729                                            bool virtualFS, bool showPath)
730 {
731     QStringList files;
732     for (QUrl fileUrl : urls) {
733         files.append(showPath ? fileUrl.toDisplayString(QUrl::PreferLocalFile) : fileUrl.fileName());
734     }
735 
736     const KConfigGroup advancedGroup(krConfig, "Advanced");
737     if (advancedGroup.readEntry("Confirm Delete", _ConfirmDelete)) {
738         QString s; // text
739         KGuiItem b; // continue button
740 
741         if (moveToTrash) {
742             s = i18np("Do you really want to move this item to the trash?",
743                       "Do you really want to move these %1 items to the trash?", files.count());
744             b = KGuiItem(i18n("&Trash"));
745         } else if (virtualFS) {
746             s = i18np("<qt>Do you really want to delete this item <b>physically</b> (not just "
747                       "removing it from the virtual items)?</qt>",
748                       "<qt>Do you really want to delete these %1 items <b>physically</b> (not just "
749                       "removing them from the virtual items)?</qt>",
750                       files.count());
751             b = KStandardGuiItem::del();
752         } else {
753             s = i18np("Do you really want to delete this item?",
754                       "Do you really want to delete these %1 items?", files.count());
755             b = KStandardGuiItem::del();
756         }
757 
758         // show message
759         // note: i'm using continue and not yes/no because the yes/no has cancel as default button
760         if (KMessageBox::warningContinueCancelList(krMainWindow, s, files, i18n("Warning"),
761                                                    b) != KMessageBox::Continue) {
762             return QList<QUrl>();
763         }
764     }
765 
766     // we want to warn the user about non empty dir
767     const bool emptyDirVerify = advancedGroup.readEntry("Confirm Unempty Dir", _ConfirmUnemptyDir);
768 
769     QList<QUrl> toDelete;
770     if (emptyDirVerify) {
771         QSet<QUrl> confirmedFiles = urls.toSet();
772         for (QUrl fileUrl : urls) {
773             if (!fileUrl.isLocalFile()) {
774                 continue; // TODO only local fs supported
775             }
776 
777             const QString filePath = fileUrl.toLocalFile();
778             QFileInfo fileInfo(filePath);
779             if (fileInfo.isDir() && !fileInfo.isSymLink()) {
780                 // read local dir...
781                 const QDir dir(filePath);
782                 if (!dir.entryList(QDir::AllEntries | QDir::System | QDir::Hidden |
783                                    QDir::NoDotAndDotDot).isEmpty()) {
784 
785                     // ...is not empty, ask user
786                     const QString fileString = showPath ? filePath : fileUrl.fileName();
787                     const KMessageBox::ButtonCode result = KMessageBox::warningYesNoCancel(
788                         krMainWindow,
789                         i18n("<qt><p>Folder <b>%1</b> is not empty.</p>", fileString) +
790                             (moveToTrash ? i18n("<p>Skip this one or trash all?</p></qt>") :
791                                            i18n("<p>Skip this one or delete all?</p></qt>")),
792                         QString(), KGuiItem(i18n("&Skip")),
793                         KGuiItem(moveToTrash ? i18n("&Trash All") : i18n("&Delete All")));
794                     if (result == KMessageBox::Yes) {
795                         confirmedFiles.remove(fileUrl); // skip this dir
796                     } else if (result == KMessageBox::No) {
797                         break; // accept all remaining
798                     } else {
799                         return QList<QUrl>(); // cancel
800                     }
801                 }
802             }
803         }
804         toDelete = confirmedFiles.toList();
805     } else {
806         toDelete = urls;
807     }
808 
809     return toDelete;
810 }
811 
removeVirtualFiles()812 void ListPanelFunc::removeVirtualFiles()
813 {
814     if (files()->type() != FileSystem::FS_VIRTUAL) {
815         qWarning() << "filesystem not virtual";
816         return;
817     }
818 
819     const QStringList fileNames = panel->getSelectedNames();
820     if (fileNames.isEmpty())
821         return;
822 
823     const QString text =
824         i18np("Do you really want to delete this virtual item (physical files stay untouched)?",
825               "Do you really want to delete these %1 virtual items (physical files stay "
826               "untouched)?",
827               fileNames.count());
828     if (KMessageBox::warningContinueCancelList(krMainWindow, text, fileNames, i18n("Warning"),
829                                                KStandardGuiItem::remove()) != KMessageBox::Continue)
830         return;
831 
832     VirtualFileSystem *fileSystem = static_cast<VirtualFileSystem*>(files());
833     fileSystem->remove(fileNames);
834 }
835 
goInside(const QString & name)836 void ListPanelFunc::goInside(const QString& name)
837 {
838     openFileNameInternal(name, false);
839 }
840 
runCommand(QString cmd)841 void ListPanelFunc::runCommand(QString cmd)
842 {
843     qDebug() << "command=" << cmd;
844     const QString workdir = panel->virtualPath().isLocalFile() ?
845             panel->virtualPath().path() : QDir::homePath();
846     if(!KRun::runCommand(cmd, krMainWindow, workdir))
847         KMessageBox::error(0, i18n("Could not start %1", cmd));
848 }
849 
runService(const KService & service,QList<QUrl> urls)850 void ListPanelFunc::runService(const KService &service, QList<QUrl> urls)
851 {
852     qDebug() << "service name=" << service.name();
853     KIO::DesktopExecParser parser(service, urls);
854     QStringList args = parser.resultingArguments();
855     if (!args.isEmpty())
856         runCommand(KShell::joinArgs(args));
857     else
858         KMessageBox::error(0, i18n("%1 cannot open %2", service.name(), KrServices::toStringList(urls).join(", ")));
859 }
860 
displayOpenWithDialog(QList<QUrl> urls)861 void ListPanelFunc::displayOpenWithDialog(QList<QUrl> urls)
862 {
863     // NOTE: we are not using KRun::displayOpenWithDialog() because we want the commands working
864     // directory to be the panel directory
865     KOpenWithDialog dialog(urls, panel);
866     dialog.hideRunInTerminal();
867     if (dialog.exec()) {
868         KService::Ptr service = dialog.service();
869         if(!service)
870             service = KService::Ptr(new KService(dialog.text(), dialog.text(), QString()));
871         runService(*service, urls);
872     }
873 }
874 
browsableArchivePath(const QString & filename)875 QUrl ListPanelFunc::browsableArchivePath(const QString &filename)
876 {
877     FileItem *fileitem = files()->getFileItem(filename);
878     QUrl url = files()->getUrl(filename);
879     QString mime = fileitem->getMime();
880 
881     if(url.isLocalFile()) {
882         QString protocol = KrServices::registeredProtocol(mime);
883         if(!protocol.isEmpty()) {
884             url.setScheme(protocol);
885             return url;
886         }
887     }
888     return QUrl();
889 }
890 
891 // this is done when you double click on a file
execute(const QString & name)892 void ListPanelFunc::execute(const QString& name)
893 {
894     openFileNameInternal(name, true);
895 }
896 
pack()897 void ListPanelFunc::pack()
898 {
899     const QStringList fileNames = panel->getSelectedNames();
900     if (fileNames.isEmpty())
901         return ;  // safety
902 
903     if (fileNames.count() == 0)
904         return ; // nothing to pack
905 
906     // choose the default name
907     QString defaultName = panel->virtualPath().fileName();
908     if (defaultName.isEmpty())
909         defaultName = "pack";
910     if (fileNames.count() == 1)
911         defaultName = fileNames.first();
912     // ask the user for archive name and packer
913     new PackGUI(defaultName, panel->otherPanel()->virtualPath().toDisplayString(QUrl::PreferLocalFile | QUrl::StripTrailingSlash),
914                 fileNames.count(), fileNames.first());
915     if (PackGUI::type.isEmpty()) {
916         return ; // the user canceled
917     }
918 
919     // check for partial URLs
920     if (!PackGUI::destination.contains(":/") && !PackGUI::destination.startsWith('/')) {
921         PackGUI::destination = panel->virtualPath().toDisplayString() + '/' + PackGUI::destination;
922     }
923 
924     QString destDir = PackGUI::destination;
925     if (!destDir.endsWith('/'))
926         destDir += '/';
927 
928     bool packToOtherPanel = (destDir == FileSystem::ensureTrailingSlash(panel->otherPanel()->virtualPath()).toDisplayString(QUrl::PreferLocalFile));
929 
930     QUrl destURL = QUrl::fromUserInput(destDir + PackGUI::filename + '.' + PackGUI::type, QString(), QUrl::AssumeLocalFile);
931     if (destURL.isLocalFile() && QFile::exists(destURL.path())) {
932         QString msg = i18n("<qt><p>The archive <b>%1.%2</b> already exists. Do you want to overwrite it?</p><p>All data in the previous archive will be lost.</p></qt>", PackGUI::filename, PackGUI::type);
933         if (PackGUI::type == "zip") {
934             msg = i18n("<qt><p>The archive <b>%1.%2</b> already exists. Do you want to overwrite it?</p><p>Zip will replace identically named entries in the zip archive or add entries for new names.</p></qt>", PackGUI::filename, PackGUI::type);
935         }
936         if (KMessageBox::warningContinueCancel(krMainWindow, msg, QString(), KStandardGuiItem::overwrite())
937                 == KMessageBox::Cancel)
938             return ; // stop operation
939     } else if (destURL.scheme() == QStringLiteral("virt")) {
940         KMessageBox::error(krMainWindow, i18n("Cannot pack files onto a virtual destination."));
941         return;
942     }
943 
944     PackJob * job = PackJob::createPacker(files()->currentDirectory(), destURL, fileNames, PackGUI::type, PackGUI::extraProps);
945     job->setUiDelegate(new KIO::JobUiDelegate());
946     KIO::getJobTracker()->registerJob(job);
947     job->uiDelegate()->setAutoErrorHandlingEnabled(true);
948 
949     if (packToOtherPanel)
950         connect(job, SIGNAL(result(KJob*)), panel->otherPanel()->func, SLOT(refresh()));
951 
952 }
953 
testArchive()954 void ListPanelFunc::testArchive()
955 {
956     const QStringList fileNames = panel->getSelectedNames();
957     if (fileNames.isEmpty())
958         return ;  // safety
959 
960     TestArchiveJob * job = TestArchiveJob::testArchives(files()->currentDirectory(), fileNames);
961     job->setUiDelegate(new KIO::JobUiDelegate());
962     KIO::getJobTracker()->registerJob(job);
963     job->uiDelegate()->setAutoErrorHandlingEnabled(true);
964 }
965 
unpack()966 void ListPanelFunc::unpack()
967 {
968     const QStringList fileNames = panel->getSelectedNames();
969     if (fileNames.isEmpty())
970         return ;  // safety
971 
972     QString s;
973     if (fileNames.count() == 1)
974         s = i18n("Unpack %1 to:", fileNames[0]);
975     else
976         s = i18np("Unpack %1 file to:", "Unpack %1 files to:", fileNames.count());
977 
978     // ask the user for the copy dest
979     QUrl dest = KChooseDir::getDir(s, panel->otherPanel()->virtualPath(), panel->virtualPath());
980     if (dest.isEmpty()) return ;   // the user canceled
981 
982     bool packToOtherPanel = (dest.matches(panel->otherPanel()->virtualPath(), QUrl::StripTrailingSlash));
983 
984     UnpackJob * job = UnpackJob::createUnpacker(files()->currentDirectory(), dest, fileNames);
985     job->setUiDelegate(new KIO::JobUiDelegate());
986     KIO::getJobTracker()->registerJob(job);
987     job->uiDelegate()->setAutoErrorHandlingEnabled(true);
988 
989     if (packToOtherPanel)
990         connect(job, SIGNAL(result(KJob*)), panel->otherPanel()->func, SLOT(refresh()));
991 
992 }
993 
createChecksum()994 void ListPanelFunc::createChecksum()
995 {
996     if (!panel->func->files()->isLocal())
997         return; // only local, non-virtual files are supported
998 
999     const KrViewItemList items = panel->view->getSelectedKrViewItems();
1000 
1001     QStringList fileNames;
1002     for (KrViewItem *item : items) {
1003         FileItem *file = panel->func->getFileItem(item);
1004         fileNames.append(file->getUrl().fileName());
1005     }
1006 
1007     if (fileNames.isEmpty())
1008         return; // nothing selected and no valid current file
1009 
1010     Checksum::startCreationWizard(panel->virtualPath().toLocalFile(), fileNames);
1011 }
1012 
matchChecksum()1013 void ListPanelFunc::matchChecksum()
1014 {
1015     if (!panel->func->files()->isLocal())
1016         return; // only local, non-virtual files are supported
1017 
1018     FileItem *currentItem = files()->getFileItem(panel->getCurrentName());
1019     const QString checksumFilePath = currentItem ? currentItem->getUrl().toLocalFile() : QString();
1020 
1021     Checksum::startVerifyWizard(panel->virtualPath().toLocalFile(), checksumFilePath);
1022 }
1023 
calcSpace()1024 void ListPanelFunc::calcSpace()
1025 {
1026     QStringList fileNames;
1027     panel->view->getSelectedItems(&fileNames);
1028     if (fileNames.isEmpty()) {
1029         // current file is ".." dummy file
1030         panel->view->selectAllIncludingDirs();
1031         panel->view->getSelectedItems(&fileNames);
1032         panel->view->unselectAll();
1033     }
1034 
1035     SizeCalculator *sizeCalculator = createAndConnectSizeCalculator(files()->getUrls(fileNames));
1036     KrCalcSpaceDialog::showDialog(panel, sizeCalculator);
1037 }
1038 
quickCalcSpace()1039 void ListPanelFunc::quickCalcSpace()
1040 {
1041     const QString currentName = panel->getCurrentName();
1042     if (currentName.isEmpty()) {
1043         // current file is ".." dummy, do a verbose calcSpace
1044         calcSpace();
1045         return;
1046     }
1047 
1048     if (!_quickSizeCalculator) {
1049         _quickSizeCalculator = createAndConnectSizeCalculator(QList<QUrl>());
1050         panel->connectQuickSizeCalculator(_quickSizeCalculator);
1051     }
1052 
1053     _quickSizeCalculator->add(files()->getUrl(currentName));
1054 }
1055 
1056 
createAndConnectSizeCalculator(const QList<QUrl> & urls)1057 SizeCalculator *ListPanelFunc::createAndConnectSizeCalculator(const QList<QUrl> &urls)
1058 {
1059     SizeCalculator *sizeCalculator = new SizeCalculator(urls);
1060     connect(sizeCalculator, &SizeCalculator::calculated, this, &ListPanelFunc::slotSizeCalculated);
1061     connect(sizeCalculator, &SizeCalculator::finished, panel, &ListPanel::slotUpdateTotals);
1062     connect(this, &ListPanelFunc::destroyed, sizeCalculator, &SizeCalculator::deleteLater);
1063     return sizeCalculator;
1064 }
1065 
slotSizeCalculated(const QUrl & url,KIO::filesize_t size)1066 void ListPanelFunc::slotSizeCalculated(const QUrl &url, KIO::filesize_t size)
1067 {
1068     KrViewItem *item = panel->view->findItemByUrl(url);
1069     if (!item)
1070         return;
1071 
1072     item->setSize(size);
1073     item->redraw();
1074 }
1075 
FTPDisconnect()1076 void ListPanelFunc::FTPDisconnect()
1077 {
1078     // you can disconnect only if connected!
1079     if (files()->isRemote()) {
1080         panel->_actions->actFTPDisconnect->setEnabled(false);
1081         panel->view->setNameToMakeCurrent(QString());
1082         openUrl(QUrl::fromLocalFile(panel->lastLocalPath()));
1083     }
1084 }
1085 
newFTPconnection()1086 void ListPanelFunc::newFTPconnection()
1087 {
1088     QUrl url = KRSpWidgets::newFTP();
1089     // if the user canceled - quit
1090     if (url.isEmpty())
1091         return;
1092 
1093     panel->_actions->actFTPDisconnect->setEnabled(true);
1094 
1095     qDebug() << "URL=" << url.toDisplayString();
1096 
1097     openUrl(url);
1098 }
1099 
properties()1100 void ListPanelFunc::properties()
1101 {
1102     const QStringList names = panel->getSelectedNames();
1103     if (names.isEmpty()) {
1104         return ;  // no names...
1105     }
1106 
1107     KFileItemList fileItems;
1108 
1109     for (const QString name : names) {
1110         FileItem *fileitem = files()->getFileItem(name);
1111         if (!fileitem) {
1112             continue;
1113         }
1114 
1115         fileItems.push_back(KFileItem(fileitem->getEntry(), files()->getUrl(name)));
1116     }
1117 
1118     if (fileItems.isEmpty())
1119         return;
1120 
1121     // Show the properties dialog
1122     KPropertiesDialog *dialog = new KPropertiesDialog(fileItems, krMainWindow);
1123     connect(dialog, &KPropertiesDialog::applied, this, &ListPanelFunc::refresh);
1124     dialog->show();
1125 }
1126 
refreshActions()1127 void ListPanelFunc::refreshActions()
1128 {
1129     panel->updateButtons();
1130 
1131     if(ACTIVE_PANEL != panel)
1132         return;
1133 
1134     QString protocol = files()->currentDirectory().scheme();
1135     krRemoteEncoding->setEnabled(protocol == "ftp" || protocol == "sftp" || protocol == "fish" || protocol == "krarc");
1136     //krMultiRename->setEnabled( fileSystemType == FileSystem::FS_NORMAL );  // batch rename
1137     //krProperties ->setEnabled( fileSystemType == FileSystem::FS_NORMAL || fileSystemType == FileSystem::FS_FTP ); // file properties
1138 
1139     /*
1140       krUnpack->setEnabled(true);                            // unpack archive
1141       krTest->setEnabled(true);                              // test archive
1142       krSelect->setEnabled(true);                            // select a group by filter
1143       krSelectAll->setEnabled(true);                         // select all files
1144       krUnselect->setEnabled(true);                          // unselect by filter
1145       krUnselectAll->setEnabled( true);                      // remove all selections
1146       krInvert->setEnabled(true);                            // invert the selection
1147       krFTPConnect->setEnabled(true);                        // connect to an ftp
1148       krFTPNew->setEnabled(true);                            // create a new connection
1149       krAllFiles->setEnabled(true);                          // show all files in list
1150       krCustomFiles->setEnabled(true);                       // show a custom set of files
1151       krRoot->setEnabled(true);                              // go all the way up
1152           krExecFiles->setEnabled(true);                         // show only executables
1153     */
1154 
1155     panel->_actions->setViewActions[panel->panelType]->setChecked(true);
1156     panel->_actions->actFTPDisconnect->setEnabled(files()->isRemote()); // allow disconnecting a network session
1157     panel->_actions->actCreateChecksum->setEnabled(files()->isLocal());
1158     panel->_actions->actDirUp->setEnabled(!files()->isRoot());
1159     panel->_actions->actRoot->setEnabled(!panel->virtualPath().matches(QUrl::fromLocalFile(ROOT_DIR),
1160                                                                        QUrl::StripTrailingSlash));
1161     panel->_actions->actHome->setEnabled(!atHome());
1162     panel->_actions->actHistoryBackward->setEnabled(history->canGoBack());
1163     panel->_actions->actHistoryForward->setEnabled(history->canGoForward());
1164     panel->view->op()->emitRefreshActions();
1165 }
1166 
files()1167 FileSystem* ListPanelFunc::files()
1168 {
1169     if (!fileSystemP)
1170         fileSystemP = FileSystemProvider::instance().getFilesystem(QUrl::fromLocalFile(ROOT_DIR));
1171     return fileSystemP;
1172 }
1173 
virtualDirectory()1174 QUrl ListPanelFunc::virtualDirectory()
1175 {
1176     return _isPaused ? history->currentUrl() : files()->currentDirectory();
1177 }
1178 
getFileItem(const QString & name)1179 FileItem *ListPanelFunc::getFileItem(const QString &name)
1180 {
1181     return files()->getFileItem(name);
1182 }
1183 
getFileItem(KrViewItem * item)1184 FileItem *ListPanelFunc::getFileItem(KrViewItem *item)
1185 {
1186     return files()->getFileItem(item->name());
1187 }
1188 
clipboardChanged(QClipboard::Mode mode)1189 void ListPanelFunc::clipboardChanged(QClipboard::Mode mode)
1190 {
1191     if (mode == QClipboard::Clipboard && this == copyToClipboardOrigin) {
1192         disconnect(QApplication::clipboard(), 0, this, 0);
1193         copyToClipboardOrigin = 0;
1194     }
1195 }
1196 
copyToClipboard(bool move)1197 void ListPanelFunc::copyToClipboard(bool move)
1198 {
1199     const QStringList fileNames = panel->getSelectedNames();
1200     if (fileNames.isEmpty())
1201         return ;  // safety
1202 
1203     QList<QUrl> fileUrls = files()->getUrls(fileNames);
1204     QMimeData *mimeData = new QMimeData;
1205     mimeData->setData("application/x-kde-cutselection", move ? "1" : "0");
1206     mimeData->setUrls(fileUrls);
1207 
1208     if (copyToClipboardOrigin)
1209         disconnect(QApplication::clipboard(), 0, copyToClipboardOrigin, 0);
1210     copyToClipboardOrigin = this;
1211 
1212     QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard);
1213 
1214     connect(QApplication::clipboard(), SIGNAL(changed(QClipboard::Mode)), this, SLOT(clipboardChanged(QClipboard::Mode)));
1215 }
1216 
pasteFromClipboard()1217 void ListPanelFunc::pasteFromClipboard()
1218 {
1219     QClipboard * cb = QApplication::clipboard();
1220 
1221     ListPanelFunc *origin = 0;
1222 
1223     if (copyToClipboardOrigin) {
1224         disconnect(QApplication::clipboard(), 0, copyToClipboardOrigin, 0);
1225         origin = copyToClipboardOrigin;
1226         copyToClipboardOrigin = 0;
1227     }
1228 
1229     bool move = false;
1230     const QMimeData *data = cb->mimeData();
1231     if (data->hasFormat("application/x-kde-cutselection")) {
1232         QByteArray a = data->data("application/x-kde-cutselection");
1233         if (!a.isEmpty())
1234             move = (a.at(0) == '1'); // true if 1
1235     }
1236 
1237     QList<QUrl> urls = data->urls();
1238     if (urls.isEmpty())
1239         return;
1240 
1241     if(origin && KConfigGroup(krConfig, "Look&Feel").readEntry("UnselectBeforeOperation", _UnselectBeforeOperation)) {
1242         origin->panel->view->saveSelection();
1243         for(KrViewItem *item = origin->panel->view->getFirst(); item != 0; item = origin->panel->view->getNext(item)) {
1244             if (urls.contains(item->getFileItem()->getUrl()))
1245                 item->setSelected(false);
1246         }
1247     }
1248 
1249     files()->addFiles(urls, move ? KIO::CopyJob::Move : KIO::CopyJob::Copy);
1250 }
1251 
otherFunc()1252 ListPanelFunc* ListPanelFunc::otherFunc()
1253 {
1254     return panel->otherPanel()->func;
1255 }
1256 
historyGotoPos(int pos)1257 void ListPanelFunc::historyGotoPos(int pos)
1258 {
1259     if(history->gotoPos(pos))
1260         refresh();
1261 }
1262 
historyBackward()1263 void ListPanelFunc::historyBackward()
1264 {
1265     if(history->goBack())
1266         refresh();
1267 }
1268 
historyForward()1269 void ListPanelFunc::historyForward()
1270 {
1271     if(history->goForward())
1272         refresh();
1273 }
1274 
dirUp()1275 void ListPanelFunc::dirUp()
1276 {
1277     openUrl(KIO::upUrl(files()->currentDirectory()), files()->currentDirectory().fileName());
1278 }
1279 
home()1280 void ListPanelFunc::home()
1281 {
1282     openUrl(QUrl::fromLocalFile(QDir::homePath()));
1283 }
1284 
root()1285 void ListPanelFunc::root()
1286 {
1287     openUrl(QUrl::fromLocalFile(ROOT_DIR));
1288 }
1289 
cdToOtherPanel()1290 void ListPanelFunc::cdToOtherPanel()
1291 {
1292     openUrl(panel->otherPanel()->virtualPath());
1293 }
1294 
syncOtherPanel()1295 void ListPanelFunc::syncOtherPanel()
1296 {
1297     otherFunc()->openUrl(panel->virtualPath());
1298 }
1299 
atHome()1300 bool ListPanelFunc::atHome()
1301 {
1302     return QUrl::fromLocalFile(QDir::homePath()).matches(panel->virtualPath(), QUrl::StripTrailingSlash);
1303 }
1304