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