diff --git a/doc/index.docbook b/doc/index.docbook index 85d6f16b..6a346934 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -1,389 +1,391 @@ + + NFS"> USB"> Samba"> - + Left"> Right"> Up"> Down"> Ins"> Space"> Home"> End"> Meta"> PgUp"> PgDn"> Debian"> PDF"> ]> The &krusader; Handbook Davide Gianforte Developer
davide@gengisdave.org
Toni Asensi Esteve Developer
toni.asensi@kdemail.net
Alexander Bikadorov Developer
alex.bikadorov@kdemail.net
Martin Kostolný Developer
clearmartin@gmail.com
Nikita Melnichenko Developer
nikita+kde@melnichenko.name
Yuri Chornoivan Documentation
yurchor@ukr.net
Shie Erlich Author and initial Handbook author (retired)
erlich@users.sourceforge.net
Rafi Yanai Author and initial Handbook author (retired)
yanai@users.sourceforge.net
Csaba Karai Developer (retired)
ckarai@users.sourceforge.net
Heiner Eichmann Developer (retired)
h.eichmann@gmx.de
Jonas Bähr Developer (retired)
jonas.baehr@web.de
Václav Jůza Developer (retired)
vaclavjuza@seznam.cz
Simon Persson Developer (retired)
simon.persson@mykolab.com
Jan Lepper Developer (retired)
jan_lepper@gmx.de
Andrey Matveyakin Developer (retired)
a.matveyakin@gmail.com
Dirk Eschler Webmaster (retired)
deschler@users.sourceforge.net
Frank Schoolmeesters Documentation and Marketing coordinator (retired)
frank_schoolmeesters@yahoo.com
Richard Holt Documentation and Proofing (retired)
richard.holt@gmail.com
Matej Urbančič Marketing and Product Research (retired)
matej.urban@gmail.com
2000 2001 2002 2003 Rafi Yanai Shie Erlich 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 Krusader Krew Permission is granted to copy, distribute and/or modify this document under the terms of the &GNU; Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the &krusader; sources. 2019-08-25 2.7.2 &krusader; is an advanced, twin-panel (commander-style) file-manager for Plasma and other desktops in the *nix world KDE krusader commander filemanager twinpanel linux file manager dual pane linux-commander kde-commander
&help; &editors-note; &introduction; &features; &user-interface; &basic-functions; &advanced-functions; &menu-commands; &keyboard-commands; &mouse-commands; &krusader-tools; &konfigurator; &faq; &credits-and-license; &configuration-files; &useraction-xml; &release-overview; &glossary; &documentation.index;
diff --git a/krusader/FileSystem/fileitem.h b/krusader/FileSystem/fileitem.h index d07c8943..b25a607b 100644 --- a/krusader/FileSystem/fileitem.h +++ b/krusader/FileSystem/fileitem.h @@ -1,171 +1,171 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * Krusader is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef FILEITEM_H #define FILEITEM_H #include // QtCore #include #include #include #include /** * A file item gives access all meta information of a (virtual, dummy or real) file or directory in * the filesystem. * * NOTE: The name of a file item is supposed to be unique within a directory. */ class FileItem { public: /** * Create a new file item. * * Don't use this constructor outside of FileSystem! If you really need to, create a new static * factory method below. * * NOTE: According to Unix standard uid and gid CAN have signed or unsigned type. We use (e.g.) * "(uid_t) -1" as a special invalid user ID for non-local files. * NOTE: ACLs are currently only used by Synchronizer. * * @param name the display name of this file. Don't have to be the real filename. * @param url (real) absolute URL of this file * @param isDir true if this file is a directory. Else false. * @param size size of file * @param mode mode of file (file type and permissions) * @param mtime file modification time * @param ctime file change time. Use -1 if unknown. * @param atime file access time * @param btime file creation time. Use -1 if unknown. * @param uid Unix user id of file owner. Use -1 here and provide an owner name for non-local files. * @param gid Unix group id of file group. Use -1 here and provide a group name for non-local files. * @param owner user name of file owner. Can be empty for local files * @param group group name of file group. Can be empty for local files. * @param isLink true if file is a symbolic link. Else false. * @param linkDest link destination path if file is a link. Relative or absolute. Empty by default. * @param isBrokenLink true if file is a symbolic link and destination file does not exists. Else false. * @param acl ACL string of file. Can be empty and is loaded on demand. * @param defaultAcl default ACL string of file (only for directories). Can be empty and is loaded on demand. */ FileItem(const QString &name, const QUrl &url, bool isDir, KIO::filesize_t size, mode_t mode, time_t mtime, time_t ctime, time_t atime, time_t btime, uid_t uid = -1, gid_t gid = -1, const QString &owner = QString(), const QString &group = QString(), bool isLink = false, const QString &linkDest = QString(), bool isBrokenLink = false, const QString &acl = QString(), const QString &defaultAcl = QString()); /** Create a new ".." dummy file item. */ static FileItem *createDummy(); /** Create a file item for a broken file which metadata could not be read. */ static FileItem *createBroken(const QString &name, const QUrl &url); /** Create a new virtual directory. */ static FileItem *createVirtualDir(const QString &name, const QUrl &url); /** Create a new file item copy with a different name. */ static FileItem *createCopy(const FileItem &file, const QString &newName); // following functions give-out file details inline const QString &getName() const { return m_name; } /** Return the file size. Returns 0 for directories with unknown size. */ inline KIO::filesize_t getSize() const { return m_size == (KIO::filesize_t)-1 ? 0 : m_size; } /** Return the file size. Returns (KIO::filesize_t)-1 for directories with unknown size. */ inline KIO::filesize_t getUISize() const { return m_size; } inline const QString &getPerm() const { return m_permissions; } /** Return true if the file is a directory or a symlink to a directory, otherwise false. */ inline bool isDir() const { return m_isDir; } inline bool isSymLink() const { return m_isLink; } inline bool isBrokenLink() const { return m_isBrokenLink; } inline const QString &getSymDest() const { return m_linkDest; } inline mode_t getMode() const { return m_mode; } /** Return the file modification time (mtime) or -1 if unknown. */ inline time_t getModificationTime() const { return m_mtime; } /** Return the file change time (ctime) or -1 if unknown. */ inline time_t getChangeTime() const { return m_ctime; } /** Return the file access time (atime) or -1 if unknown. */ inline time_t getAccessTime() const { return m_atime; } /** Return the file creation time (btime) or -1 if unknown. */ inline time_t getCreationTime() const { return m_btime; } inline const QUrl &getUrl() const { return m_url; } inline const QString &getOwner() const { return m_owner; } inline const QString &getGroup() const { return m_group; } const QString &getMime(); const QString &getIcon(); const QString &getACL(); const QString &getDefaultACL(); const KIO::UDSEntry getEntry(); //< return the UDSEntry from the file item char isReadable() const; char isWriteable() const; char isExecutable() const; /** * Set the file size. * used ONLY when calculating a directory's space, needs to change the * displayed size of the viewitem and thus the file item. For INTERNAL USE ! */ void setSize(KIO::filesize_t size); inline static void loadUserDefinedFolderIcons(bool load) { userDefinedFolderIcons = load; } private: void setIconName(const QString &icon) { m_iconName = icon; m_mimeType = "?"; } void loadACL(); QString m_name; //< file name QUrl m_url; //< file URL bool m_isDir; //< flag, true if it's a directory KIO::filesize_t m_size; //< file size mode_t m_mode; //< file mode (file type and permissions) time_t m_mtime; //< file modification time time_t m_ctime; //< file change time time_t m_atime; //< file access time time_t m_btime; //< file creation time uid_t m_uid; //< file owner id gid_t m_gid; //< file group id QString m_owner; //< file owner name QString m_group; //< file group name bool m_isLink; //< true if the file is a symlink - QString m_linkDest; //< if it's a sym link - its destination + QString m_linkDest; //< if it's a symlink - its destination bool m_isBrokenLink; //< true if the link destination does not exist QString m_permissions; //< file permissions string QString m_acl; //< ACL permission string, may lazy initialized QString m_defaulfAcl; //< ACL default string, may lazy initialized bool m_AclLoaded; //< flag, indicates that ACL permissions already loaded QString m_mimeType; //< file mimetype, lazy initialized QString m_iconName; //< the name of the icon file, lazy initialized static bool userDefinedFolderIcons; }; #endif diff --git a/krusader/Panel/panelfunc.cpp b/krusader/Panel/panelfunc.cpp index adc83d18..ec402077 100644 --- a/krusader/Panel/panelfunc.cpp +++ b/krusader/Panel/panelfunc.cpp @@ -1,1331 +1,1332 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * Krusader is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "panelfunc.h" // QtCore #include #include #include #include #include #include // QtGui #include #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dirhistoryqueue.h" #include "krcalcspacedialog.h" #include "krerrordisplay.h" #include "krsearchbar.h" #include "listpanel.h" #include "listpanelactions.h" #include "PanelView/krview.h" #include "PanelView/krviewitem.h" #include "../krglobal.h" #include "../krslots.h" #include "../kractions.h" #include "../defaults.h" #include "../abstractpanelmanager.h" #include "../krservices.h" #include "../Archive/krarchandler.h" #include "../Archive/packjob.h" #include "../FileSystem/fileitem.h" #include "../FileSystem/virtualfilesystem.h" #include "../FileSystem/krpermhandler.h" #include "../FileSystem/filesystemprovider.h" #include "../FileSystem/sizecalculator.h" #include "../Dialogs/packgui.h" #include "../Dialogs/krdialogs.h" #include "../Dialogs/krpleasewait.h" #include "../Dialogs/krspwidgets.h" #include "../Dialogs/checksumdlg.h" #include "../KViewer/krviewer.h" #include "../MountMan/kmountman.h" QPointer ListPanelFunc::copyToClipboardOrigin; ListPanelFunc::ListPanelFunc(ListPanel *parent) : QObject(parent), panel(parent), fileSystemP(nullptr), urlManuallyEntered(false), _isPaused(true), _refreshAfterPaused(true), _quickSizeCalculator(nullptr) { history = new DirHistoryQueue(panel); delayTimer.setSingleShot(true); connect(&delayTimer, &QTimer::timeout, this, &ListPanelFunc::doRefresh); } ListPanelFunc::~ListPanelFunc() { if (fileSystemP) { fileSystemP->deleteLater(); } delete history; if (_quickSizeCalculator) _quickSizeCalculator->deleteLater(); } bool ListPanelFunc::isSyncing(const QUrl &url) { if(otherFunc()->otherFunc() == this && panel->otherPanel()->gui->syncBrowseButton->isChecked() && !otherFunc()->syncURL.isEmpty() && otherFunc()->syncURL == url) return true; return false; } void ListPanelFunc::openFileNameInternal(const QString &name, bool externallyExecutable) { if (name == "..") { dirUp(); return; } FileItem *fileitem = files()->getFileItem(name); if (fileitem == nullptr) return; QUrl url = files()->getUrl(name); if (fileitem->isDir()) { panel->view->setNameToMakeCurrent(QString()); openUrl(url); return; } QString mime = fileitem->getMime(); QUrl arcPath = browsableArchivePath(name); if (!arcPath.isEmpty()) { bool browseAsDirectory = !externallyExecutable || (KConfigGroup(krConfig, "Archives").readEntry("ArchivesAsDirectories", _ArchivesAsDirectories) && (KRarcHandler::arcSupported(mime) || KrServices::isoSupported(mime))); if (browseAsDirectory) { openUrl(arcPath); return; } } if (externallyExecutable) { if (KRun::isExecutableFile(url, mime)) { runCommand(KShell::quoteArg(url.path())); return; } KService::Ptr service = KMimeTypeTrader::self()->preferredService(mime); if(service) { runService(*service, QList() << url); return; } displayOpenWithDialog(QList() << url); } } QUrl ListPanelFunc::cleanPath(const QUrl &urlIn) { QUrl url = urlIn; url.setPath(QDir::cleanPath(url.path())); if (!url.isValid() || url.isRelative()) { if (url.url() == "~") url = QUrl::fromLocalFile(QDir::homePath()); else if (!url.url().startsWith('/')) { // possible relative URL - translate to full URL url = files()->currentDirectory(); url.setPath(url.path() + '/' + urlIn.path()); } } url.setPath(QDir::cleanPath(url.path())); return url; } void ListPanelFunc::openUrl(const QUrl &url, const QString& nameToMakeCurrent, bool manuallyEntered) { qDebug() << "URL=" << url.toDisplayString() << "; name to current=" << nameToMakeCurrent; if (panel->syncBrowseButton->isChecked()) { //do sync-browse stuff.... if(syncURL.isEmpty()) syncURL = panel->otherPanel()->virtualPath(); QString relative = QDir(panel->virtualPath().path() + '/').relativeFilePath(url.path()); syncURL.setPath(QDir::cleanPath(syncURL.path() + '/' + relative)); panel->otherPanel()->gui->setTabState(ListPanel::TabState::DEFAULT); otherFunc()->openUrlInternal(syncURL, nameToMakeCurrent, false, false); } openUrlInternal(url, nameToMakeCurrent, false, manuallyEntered); } void ListPanelFunc::immediateOpenUrl(const QUrl &url) { openUrlInternal(url, QString(), true, false); } void ListPanelFunc::openUrlInternal(const QUrl &url, const QString& nameToMakeCurrent, bool immediately, bool manuallyEntered) { const QUrl cleanUrl = cleanPath(url); if (panel->isLocked() && !files()->currentDirectory().matches(cleanUrl, QUrl::StripTrailingSlash)) { panel->_manager->newTab(url); urlManuallyEntered = false; return; } urlManuallyEntered = manuallyEntered; const QString currentItem = history->currentUrl().path() == cleanUrl.path() ? history->currentItem() : nameToMakeCurrent; panel->view->setNameToMakeCurrent(nameToMakeCurrent); history->add(cleanUrl, currentItem); if(immediately) doRefresh(); else refresh(); } void ListPanelFunc::refresh() { panel->cancelProgress(); delayTimer.start(0); // to avoid qApp->processEvents() deadlock situation } void ListPanelFunc::doRefresh() { delayTimer.stop(); if (_isPaused) { _refreshAfterPaused = true; // simulate refresh panel->slotStartUpdate(true); return; } else { _refreshAfterPaused = false; } const QUrl url = history->currentUrl(); if(!url.isValid()) { panel->slotStartUpdate(true); // refresh the panel urlManuallyEntered = false; return; } panel->cancelProgress(); // if we are not refreshing to current URL const bool isEqualUrl = files()->currentDirectory().matches(url, QUrl::StripTrailingSlash); if (!isEqualUrl) { panel->setCursor(Qt::WaitCursor); panel->view->clearSavedSelection(); } if (panel->fileSystemError) { panel->fileSystemError->hide(); } panel->setNavigatorUrl(url); // may get a new filesystem for this url FileSystem *fileSystem = FileSystemProvider::instance().getFilesystem(url, files()); fileSystem->setParentWindow(krMainWindow); connect(fileSystem, &FileSystem::aboutToOpenDir, &krMtMan, &KMountMan::autoMount, Qt::DirectConnection); if (fileSystem != fileSystemP) { panel->view->setFiles(nullptr); // disconnect older signals disconnect(fileSystemP, nullptr, panel, nullptr); fileSystemP->deleteLater(); fileSystemP = fileSystem; // v != 0 so this is safe } else { if (fileSystemP->isRefreshing()) { // TODO remove busy waiting here delayTimer.start(100); /* if filesystem is busy try refreshing later */ return; } } // (re)connect filesystem signals disconnect(files(), nullptr, panel, nullptr); connect(files(), &DirListerInterface::scanDone, panel, &ListPanel::slotStartUpdate); connect(files(), &FileSystem::fileSystemInfoChanged, panel, &ListPanel::updateFilesystemStats); connect(files(), &FileSystem::refreshJobStarted, panel, &ListPanel::slotRefreshJobStarted); connect(files(), &FileSystem::error, panel, &ListPanel::slotFilesystemError); panel->view->setFiles(files()); if (!isEqualUrl || !panel->view->getCurrentKrViewItem()) { // set current item after refresh from history, if there is none yet panel->view->setNameToMakeCurrent(history->currentItem()); } // workaround for detecting panel deletion while filesystem is refreshing QPointer panelSave = panel; // NOTE: this is blocking. Returns false on error or interruption (cancel requested or panel // was deleted) const bool scanned = fileSystemP->refresh(url); if (scanned) { // update the history and address bar, as the actual url might differ from the one requested history->setCurrentUrl(fileSystemP->currentDirectory()); panel->setNavigatorUrl(fileSystemP->currentDirectory()); } else if (!panelSave) { return; } panel->view->setNameToMakeCurrent(QString()); panel->setCursor(Qt::ArrowCursor); // on local file system change the working directory if (files()->isLocal()) QDir::setCurrent(KrServices::urlToLocalPath(files()->currentDirectory())); // see if the open url operation failed, and if so, // put the attempted url in the navigator bar and let the user change it if (!scanned) { if(isSyncing(url)) panel->otherPanel()->gui->syncBrowseButton->setChecked(false); else if(urlManuallyEntered) { panel->setNavigatorUrl(url); if(panel == ACTIVE_PANEL) panel->editLocation(); } } if(otherFunc()->otherFunc() == this) // not true if our tab is not active otherFunc()->syncURL = QUrl(); urlManuallyEntered = false; refreshActions(); } void ListPanelFunc::setPaused(bool paused) { if (paused == _isPaused) return; _isPaused = paused; // TODO: disable refresh() in local file system when paused if (!_isPaused && _refreshAfterPaused) refresh(); } void ListPanelFunc::redirectLink() { if (!files()->isLocal()) { KMessageBox::sorry(krMainWindow, i18n("You can edit links only on local file systems")); return; } FileItem *fileitem = files()->getFileItem(panel->getCurrentName()); if (!fileitem) return; QString file = fileitem->getUrl().path(); QString currentLink = fileitem->getSymDest(); if (currentLink.isEmpty()) { KMessageBox::sorry(krMainWindow, i18n("The current file is not a link, so it cannot be redirected.")); return; } // ask the user for a new destination bool ok = false; QString newLink = QInputDialog::getText(krMainWindow, i18n("Link Redirection"), i18n("Please enter the new link destination:"), QLineEdit::Normal, currentLink, &ok); // if the user canceled - quit if (!ok || newLink == currentLink) return; // delete the current link if (unlink(file.toLocal8Bit()) == -1) { KMessageBox::sorry(krMainWindow, i18n("Cannot remove old link: %1", file)); return; } // try to create a new symlink if (symlink(newLink.toLocal8Bit(), file.toLocal8Bit()) == -1) { KMessageBox:: /* --=={ Patch by Heiner }==-- */sorry(krMainWindow, i18n("Failed to create a new link: %1", file)); return; } } void ListPanelFunc::krlink(bool sym) { if (!files()->isLocal()) { KMessageBox::sorry(krMainWindow, i18n("You can create links only on local file systems")); return; } QString name = panel->getCurrentName(); // ask the new link name.. bool ok = false; QString linkName = QInputDialog::getText(krMainWindow, i18n("New Link"), i18n("Create a new link to: %1", name), QLineEdit::Normal, name, &ok); // if the user canceled - quit if (!ok || linkName == name) return; // if the name is already taken - quit if (files()->getFileItem(linkName) != nullptr) { KMessageBox::sorry(krMainWindow, i18n("A folder or a file with this name already exists.")); return; } // make link name and target absolute path if (linkName.left(1) != "/") linkName = files()->currentDirectory().path() + '/' + linkName; name = files()->getUrl(name).path(); if (sym) { if (symlink(name.toLocal8Bit(), linkName.toLocal8Bit()) == -1) KMessageBox::sorry(krMainWindow, i18n("Failed to create a new symlink '%1' to: '%2'", linkName, name)); } else { if (link(name.toLocal8Bit(), linkName.toLocal8Bit()) == -1) KMessageBox::sorry(krMainWindow, i18n("Failed to create a new link '%1' to '%2'", linkName, name)); } } void ListPanelFunc::view() { panel->searchBar->hideBarIfSearching(); QString fileName = panel->getCurrentName(); if (fileName.isNull()) return; // if we're trying to view a directory, just exit FileItem *fileitem = files()->getFileItem(fileName); if (!fileitem || fileitem->isDir()) return; if (!fileitem->isReadable()) { KMessageBox::sorry(nullptr, i18n("No permissions to view this file.")); return; } // call KViewer. KrViewer::view(files()->getUrl(fileName)); // nothing more to it! } void ListPanelFunc::viewDlg() { // ask the user for a url to view QUrl dest = KChooseDir::getFile(i18n("Enter a URL to view:"), QUrl(panel->getCurrentName()), panel->virtualPath()); if (dest.isEmpty()) return ; // the user canceled KrViewer::view(dest); // view the file } void ListPanelFunc::terminal() { SLOTS->runTerminal(panel->lastLocalPath()); } void ListPanelFunc::editFile(const QUrl &filePath) { panel->searchBar->hideBarIfSearching(); QUrl editPath; if (!filePath.isEmpty()) { editPath = filePath; } else { const QString name = panel->getCurrentName(); if (name.isNull()) return; editPath = files()->getUrl(name); } const KFileItem fileToEdit = KFileItem(filePath); if (fileToEdit.isDir()) { KMessageBox::sorry(krMainWindow, i18n("You cannot edit a folder")); return; } if (!fileToEdit.isReadable()) { KMessageBox::sorry(nullptr, i18n("No permissions to edit this file.")); return; } KrViewer::edit(editPath); } void ListPanelFunc::askEditFile() { // ask the user for the filename to edit const QUrl filePath = KChooseDir::getFile(i18n("Enter the filename to edit:"), QUrl(panel->getCurrentName()), panel->virtualPath()); - if(filePath.isEmpty()) - return ; // the user canceled + if (filePath.isEmpty()) { + return; // the user canceled + } if (filePath.isLocalFile()) { // if the file exists, edit it instead of creating a new one QFile file(filePath.toLocalFile()); if (file.exists()) { editFile(filePath); return; } else { // simply create a local file // also because KIO::CopyJob::setDefaultPermissions does not work #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) file.open(QIODevice::NewOnly); #else file.open(QIODevice::WriteOnly); #endif file.close(); slotFileCreated(nullptr, filePath); return; } } else { KIO::StatJob* statJob = KIO::stat(filePath, KIO::HideProgressInfo); connect(statJob, &KIO::StatJob::result, this, &ListPanelFunc::slotStatEdit); } } void ListPanelFunc::slotStatEdit(KJob* job) { if (!job) return; const KIO::StatJob *statJob = dynamic_cast(job); const QUrl &url = statJob->url(); if (job->error()) { if (job->error() == KIO::ERR_DOES_NOT_EXIST) { // create a new file auto *tempFile = new QTemporaryFile; tempFile->setAutoRemove(false); // done below tempFile->open(); // create file KIO::CopyJob *job = KIO::copy(QUrl::fromLocalFile(tempFile->fileName()), url); job->setUiDelegate(nullptr); job->setDefaultPermissions(true); connect(job, &KIO::CopyJob::result, this, [=](KJob *job) { slotFileCreated(job, url); }); connect(job, &KIO::CopyJob::result, tempFile, &QTemporaryFile::deleteLater); return; } else { KMessageBox::error(nullptr, job->errorString()); return; } } if (statJob->statResult().isDir()) { - KMessageBox::error(nullptr, i18n("You cannot edit a folder")); + KMessageBox::sorry(nullptr, i18n("You cannot edit a folder")); return; } KrViewer::edit(url); } void ListPanelFunc::slotFileCreated(KJob *job, const QUrl filePath) { if (!job || (!job->error() || job->error() == KIO::ERR_FILE_ALREADY_EXIST)) { KrViewer::edit(filePath); if (KIO::upUrl(filePath).matches(panel->virtualPath(), QUrl::StripTrailingSlash)) { refresh(); } if (KIO::upUrl(filePath).matches(panel->otherPanel()->virtualPath(), QUrl::StripTrailingSlash)) { otherFunc()->refresh(); } } else { KMessageBox::sorry(krMainWindow, job->errorString()); } } void ListPanelFunc::copyFiles(bool enqueue, bool move) { panel->searchBar->hideBarIfSearching(); const QStringList fileNames = panel->getSelectedNames(); if (fileNames.isEmpty()) return ; // safety QUrl destination = panel->otherPanel()->virtualPath(); bool fullDestPath = false; if (fileNames.count() == 1 && otherFunc()->files()->type() != FileSystem::FS_VIRTUAL) { FileItem *item = files()->getFileItem(fileNames[0]); if (item && !item->isDir()) { fullDestPath = true; // add original filename to destination destination.setPath(QDir(destination.path()).filePath(item->getUrl().fileName())); } } if (!fullDestPath) { destination = FileSystem::ensureTrailingSlash(destination); } const KConfigGroup group(krConfig, "Advanced"); const bool showDialog = move ? group.readEntry("Confirm Move", _ConfirmMove) : group.readEntry("Confirm Copy", _ConfirmCopy); if (showDialog) { QString operationText; if (move) { operationText = fileNames.count() == 1 ? i18n("Move %1 to:", fileNames.first()) : i18np("Move %1 file to:", "Move %1 files to:", fileNames.count()); } else { operationText = fileNames.count() == 1 ? i18n("Copy %1 to:", fileNames.first()) : i18np("Copy %1 file to:", "Copy %1 files to:", fileNames.count()); } // ask the user for the copy/move dest const KChooseDir::ChooseResult result = KChooseDir::getCopyDir(operationText, destination, panel->virtualPath()); destination = result.url; if (destination.isEmpty()) return ; // the user canceled enqueue = result.enqueue; } const JobMan::StartMode startMode = enqueue && krJobMan->isQueueModeEnabled() ? JobMan::Delay : !enqueue && !krJobMan->isQueueModeEnabled() ? JobMan::Start : JobMan::Enqueue; const QList fileUrls = files()->getUrls(fileNames); if (move) { // after the delete return the cursor to the first unmarked file above the current item panel->prepareToDelete(); } // make sure the user does not overwrite multiple files by mistake if (fileNames.count() > 1) { destination = FileSystem::ensureTrailingSlash(destination); } const KIO::CopyJob::CopyMode mode = move ? KIO::CopyJob::Move : KIO::CopyJob::Copy; FileSystemProvider::instance().startCopyFiles(fileUrls, destination, mode, true, startMode); if(KConfigGroup(krConfig, "Look&Feel").readEntry("UnselectBeforeOperation", _UnselectBeforeOperation)) { panel->view->saveSelection(); panel->view->unselectAll(); } } // called from SLOTS to begin the renaming process void ListPanelFunc::rename() { panel->searchBar->hideBarIfSearching(); panel->view->renameCurrentItem(); } // called by signal itemRenamed() from the view to complete the renaming process void ListPanelFunc::rename(const QString &oldname, const QString &newname) { if (oldname == newname) return ; // do nothing // set current after rename panel->view->setNameToMakeCurrent(newname); // as always - the filesystem do the job files()->rename(oldname, newname); } void ListPanelFunc::mkdir() { // ask the new dir name.. // suggested name is the complete name for the directories // while filenames are suggested without their extension QString suggestedName = panel->getCurrentName(); if (!suggestedName.isEmpty() && !files()->getFileItem(suggestedName)->isDir()) suggestedName = QFileInfo(suggestedName).completeBaseName(); const QString dirName = QInputDialog::getText(krMainWindow, i18n("New folder"), i18n("Folder's name:"), QLineEdit::Normal, suggestedName); const QString firstName = dirName.section('/', 0, 0, QString::SectionIncludeLeadingSep); // if the user canceled or the name was composed of slashes - quit if (!dirName.startsWith('/') && firstName.isEmpty()) { return; } // notify user about existing folder if only single directory was given if (!dirName.contains('/') && files()->getFileItem(firstName)) { // focus the existing directory panel->view->setCurrentItem(firstName); // show error message KMessageBox::sorry(krMainWindow, i18n("A folder or a file with this name already exists.")); return; } // focus new directory when next refresh happens panel->view->setNameToMakeCurrent(firstName); // create new directory (along with underlying directories if necessary) files()->mkDir(dirName); } void ListPanelFunc::defaultOrAlternativeDeleteFiles(bool invert) { const bool trash = KConfigGroup(krConfig, "General").readEntry("Move To Trash", _MoveToTrash); deleteFiles(trash != invert); } void ListPanelFunc::deleteFiles(bool moveToTrash) { panel->searchBar->hideBarIfSearching(); const bool isVFS = files()->type() == FileSystem::FS_VIRTUAL; if (isVFS && files()->isRoot()) { // only virtual deletion possible removeVirtualFiles(); return; } // first get the selected file names list const QStringList fileNames = panel->getSelectedNames(); if (fileNames.isEmpty()) return; // move to trash: only if possible moveToTrash = moveToTrash && files()->canMoveToTrash(fileNames); // now ask the user if he/she is sure: const QList confirmedUrls = confirmDeletion( files()->getUrls(fileNames), moveToTrash, isVFS, false); if (confirmedUrls.isEmpty()) return; // nothing to delete // after the delete return the cursor to the first unmarked // file above the current item; panel->prepareToDelete(); // let the filesystem do the job... files()->deleteFiles(confirmedUrls, moveToTrash); } QList ListPanelFunc::confirmDeletion(const QList &urls, bool moveToTrash, bool virtualFS, bool showPath) { QStringList files; for (const QUrl& fileUrl : urls) { files.append(showPath ? fileUrl.toDisplayString(QUrl::PreferLocalFile) : fileUrl.fileName()); } const KConfigGroup advancedGroup(krConfig, "Advanced"); if (advancedGroup.readEntry("Confirm Delete", _ConfirmDelete)) { QString s; // text KGuiItem b; // continue button if (moveToTrash) { s = i18np("Do you really want to move this item to the trash?", "Do you really want to move these %1 items to the trash?", files.count()); b = KGuiItem(i18n("&Trash")); } else if (virtualFS) { s = i18np("Do you really want to delete this item physically (not just " "removing it from the virtual items)?", "Do you really want to delete these %1 items physically (not just " "removing them from the virtual items)?", files.count()); b = KStandardGuiItem::del(); } else { s = i18np("Do you really want to delete this item?", "Do you really want to delete these %1 items?", files.count()); b = KStandardGuiItem::del(); } // show message // note: i'm using continue and not yes/no because the yes/no has cancel as default button if (KMessageBox::warningContinueCancelList(krMainWindow, s, files, i18n("Warning"), b) != KMessageBox::Continue) { return QList(); } } // we want to warn the user about non empty dir const bool emptyDirVerify = advancedGroup.readEntry("Confirm Unempty Dir", _ConfirmUnemptyDir); QList toDelete; if (emptyDirVerify) { QSet confirmedFiles = urls.toSet(); for (const QUrl& fileUrl : urls) { if (!fileUrl.isLocalFile()) { continue; // TODO only local fs supported } const QString filePath = fileUrl.toLocalFile(); QFileInfo fileInfo(filePath); if (fileInfo.isDir() && !fileInfo.isSymLink()) { // read local dir... const QDir dir(filePath); if (!dir.entryList(QDir::AllEntries | QDir::System | QDir::Hidden | QDir::NoDotAndDotDot).isEmpty()) { // ...is not empty, ask user const QString fileString = showPath ? filePath : fileUrl.fileName(); const KMessageBox::ButtonCode result = KMessageBox::warningYesNoCancel( krMainWindow, i18n("

Folder %1 is not empty.

", fileString) + (moveToTrash ? i18n("

Skip this one or trash all?

") : i18n("

Skip this one or delete all?

")), QString(), KGuiItem(i18n("&Skip")), KGuiItem(moveToTrash ? i18n("&Trash All") : i18n("&Delete All"))); if (result == KMessageBox::Yes) { confirmedFiles.remove(fileUrl); // skip this dir } else if (result == KMessageBox::No) { break; // accept all remaining } else { return QList(); // cancel } } } } toDelete = confirmedFiles.toList(); } else { toDelete = urls; } return toDelete; } void ListPanelFunc::removeVirtualFiles() { if (files()->type() != FileSystem::FS_VIRTUAL) { qWarning() << "filesystem not virtual"; return; } const QStringList fileNames = panel->getSelectedNames(); if (fileNames.isEmpty()) return; const QString text = i18np("Do you really want to delete this virtual item (physical files stay untouched)?", "Do you really want to delete these %1 virtual items (physical files stay " "untouched)?", fileNames.count()); if (KMessageBox::warningContinueCancelList(krMainWindow, text, fileNames, i18n("Warning"), KStandardGuiItem::remove()) != KMessageBox::Continue) return; auto *fileSystem = dynamic_cast(files()); fileSystem->remove(fileNames); } void ListPanelFunc::goInside(const QString& name) { openFileNameInternal(name, false); } void ListPanelFunc::runCommand(const QString& cmd) { qDebug() << "command=" << cmd; const QString workdir = panel->virtualPath().isLocalFile() ? panel->virtualPath().path() : QDir::homePath(); if(!KRun::runCommand(cmd, krMainWindow, workdir)) KMessageBox::error(nullptr, i18n("Could not start %1", cmd)); } void ListPanelFunc::runService(const KService &service, const QList& urls) { qDebug() << "service name=" << service.name(); KIO::DesktopExecParser parser(service, urls); QStringList args = parser.resultingArguments(); if (!args.isEmpty()) runCommand(KShell::joinArgs(args)); else KMessageBox::error(nullptr, i18n("%1 cannot open %2", service.name(), KrServices::toStringList(urls).join(", "))); } void ListPanelFunc::displayOpenWithDialog(const QList& urls) { // NOTE: we are not using KRun::displayOpenWithDialog() because we want the commands working // directory to be the panel directory KOpenWithDialog dialog(urls, panel); dialog.hideRunInTerminal(); if (dialog.exec()) { KService::Ptr service = dialog.service(); if(!service) service = KService::Ptr(new KService(dialog.text(), dialog.text(), QString())); runService(*service, urls); } } QUrl ListPanelFunc::browsableArchivePath(const QString &filename) { FileItem *fileitem = files()->getFileItem(filename); QUrl url = files()->getUrl(filename); QString mime = fileitem->getMime(); if(url.isLocalFile()) { QString protocol = KrServices::registeredProtocol(mime); if(!protocol.isEmpty()) { url.setScheme(protocol); return url; } } return QUrl(); } // this is done when you double click on a file void ListPanelFunc::execute(const QString& name) { openFileNameInternal(name, true); } void ListPanelFunc::pack() { const QStringList fileNames = panel->getSelectedNames(); if (fileNames.isEmpty()) return ; // safety if (fileNames.count() == 0) return ; // nothing to pack // choose the default name QString defaultName = panel->virtualPath().fileName(); if (defaultName.isEmpty()) defaultName = "pack"; if (fileNames.count() == 1) defaultName = fileNames.first(); // ask the user for archive name and packer new PackGUI(defaultName, panel->otherPanel()->virtualPath().toDisplayString(QUrl::PreferLocalFile | QUrl::StripTrailingSlash), fileNames.count(), fileNames.first()); if (PackGUI::type.isEmpty()) { return ; // the user canceled } // check for partial URLs if (!PackGUI::destination.contains(":/") && !PackGUI::destination.startsWith('/')) { PackGUI::destination = panel->virtualPath().toDisplayString() + '/' + PackGUI::destination; } QString destDir = PackGUI::destination; if (!destDir.endsWith('/')) destDir += '/'; bool packToOtherPanel = (destDir == FileSystem::ensureTrailingSlash(panel->otherPanel()->virtualPath()).toDisplayString(QUrl::PreferLocalFile)); QUrl destURL = QUrl::fromUserInput(destDir + PackGUI::filename + '.' + PackGUI::type, QString(), QUrl::AssumeLocalFile); if (destURL.isLocalFile() && QFile::exists(destURL.path())) { QString msg = i18n("

The archive %1.%2 already exists. Do you want to overwrite it?

All data in the previous archive will be lost.

", PackGUI::filename, PackGUI::type); if (PackGUI::type == "zip") { msg = i18n("

The archive %1.%2 already exists. Do you want to overwrite it?

Zip will replace identically named entries in the zip archive or add entries for new names.

", PackGUI::filename, PackGUI::type); } if (KMessageBox::warningContinueCancel(krMainWindow, msg, QString(), KStandardGuiItem::overwrite()) == KMessageBox::Cancel) return ; // stop operation } else if (destURL.scheme() == QStringLiteral("virt")) { KMessageBox::error(krMainWindow, i18n("Cannot pack files onto a virtual destination.")); return; } PackJob * job = PackJob::createPacker(files()->currentDirectory(), destURL, fileNames, PackGUI::type, PackGUI::extraProps); job->setUiDelegate(new KIO::JobUiDelegate()); KIO::getJobTracker()->registerJob(job); job->uiDelegate()->setAutoErrorHandlingEnabled(true); if (packToOtherPanel) connect(job, &PackJob::result, panel->otherPanel()->func, &ListPanelFunc::refresh); } void ListPanelFunc::testArchive() { const QStringList fileNames = panel->getSelectedNames(); if (fileNames.isEmpty()) return ; // safety TestArchiveJob * job = TestArchiveJob::testArchives(files()->currentDirectory(), fileNames); job->setUiDelegate(new KIO::JobUiDelegate()); KIO::getJobTracker()->registerJob(job); job->uiDelegate()->setAutoErrorHandlingEnabled(true); } void ListPanelFunc::unpack() { const QStringList fileNames = panel->getSelectedNames(); if (fileNames.isEmpty()) return ; // safety QString s; if (fileNames.count() == 1) s = i18n("Unpack %1 to:", fileNames[0]); else s = i18np("Unpack %1 file to:", "Unpack %1 files to:", fileNames.count()); // ask the user for the copy dest QUrl dest = KChooseDir::getDir(s, panel->otherPanel()->virtualPath(), panel->virtualPath()); if (dest.isEmpty()) return ; // the user canceled bool packToOtherPanel = (dest.matches(panel->otherPanel()->virtualPath(), QUrl::StripTrailingSlash)); UnpackJob * job = UnpackJob::createUnpacker(files()->currentDirectory(), dest, fileNames); job->setUiDelegate(new KIO::JobUiDelegate()); KIO::getJobTracker()->registerJob(job); job->uiDelegate()->setAutoErrorHandlingEnabled(true); if (packToOtherPanel) connect(job, &UnpackJob::result, panel->otherPanel()->func, &ListPanelFunc::refresh); } void ListPanelFunc::createChecksum() { if (!panel->func->files()->isLocal()) return; // only local, non-virtual files are supported const KrViewItemList items = panel->view->getSelectedKrViewItems(); QStringList fileNames; for (KrViewItem *item : items) { FileItem *file = panel->func->getFileItem(item); fileNames.append(file->getUrl().fileName()); } if (fileNames.isEmpty()) return; // nothing selected and no valid current file Checksum::startCreationWizard(panel->virtualPath().toLocalFile(), fileNames); } void ListPanelFunc::matchChecksum() { if (!panel->func->files()->isLocal()) return; // only local, non-virtual files are supported FileItem *currentItem = files()->getFileItem(panel->getCurrentName()); const QString checksumFilePath = currentItem ? currentItem->getUrl().toLocalFile() : QString(); Checksum::startVerifyWizard(panel->virtualPath().toLocalFile(), checksumFilePath); } void ListPanelFunc::calcSpace() { QStringList fileNames; panel->view->getSelectedItems(&fileNames); if (fileNames.isEmpty()) { // current file is ".." dummy file panel->view->selectAllIncludingDirs(); panel->view->getSelectedItems(&fileNames); panel->view->unselectAll(); } SizeCalculator *sizeCalculator = createAndConnectSizeCalculator(files()->getUrls(fileNames)); KrCalcSpaceDialog::showDialog(panel, sizeCalculator); } void ListPanelFunc::quickCalcSpace() { const QString currentName = panel->getCurrentName(); if (currentName.isEmpty()) { // current file is ".." dummy, do a verbose calcSpace calcSpace(); return; } if (!_quickSizeCalculator) { _quickSizeCalculator = createAndConnectSizeCalculator(QList()); panel->connectQuickSizeCalculator(_quickSizeCalculator); } _quickSizeCalculator->add(files()->getUrl(currentName)); } SizeCalculator *ListPanelFunc::createAndConnectSizeCalculator(const QList &urls) { auto *sizeCalculator = new SizeCalculator(urls); connect(sizeCalculator, &SizeCalculator::calculated, this, &ListPanelFunc::slotSizeCalculated); connect(sizeCalculator, &SizeCalculator::finished, panel, &ListPanel::slotUpdateTotals); connect(this, &ListPanelFunc::destroyed, sizeCalculator, &SizeCalculator::deleteLater); return sizeCalculator; } void ListPanelFunc::slotSizeCalculated(const QUrl &url, KIO::filesize_t size) { KrViewItem *item = panel->view->findItemByUrl(url); if (!item) return; item->setSize(size); item->redraw(); } void ListPanelFunc::FTPDisconnect() { // you can disconnect only if connected! if (files()->isRemote()) { panel->_actions->actFTPDisconnect->setEnabled(false); panel->view->setNameToMakeCurrent(QString()); openUrl(QUrl::fromLocalFile(panel->lastLocalPath())); } } void ListPanelFunc::newFTPconnection() { QUrl url = KRSpWidgets::newFTP(); // if the user canceled - quit if (url.isEmpty()) return; panel->_actions->actFTPDisconnect->setEnabled(true); qDebug() << "URL=" << url.toDisplayString(); openUrl(url); } void ListPanelFunc::properties() { const QStringList names = panel->getSelectedNames(); if (names.isEmpty()) { return ; // no names... } KFileItemList fileItems; for (const QString& name : names) { FileItem *fileitem = files()->getFileItem(name); if (!fileitem) { continue; } fileItems.push_back(KFileItem(fileitem->getEntry(), files()->getUrl(name))); } if (fileItems.isEmpty()) return; // Show the properties dialog auto *dialog = new KPropertiesDialog(fileItems, krMainWindow); connect(dialog, &KPropertiesDialog::applied, this, &ListPanelFunc::refresh); dialog->show(); } void ListPanelFunc::refreshActions() { panel->updateButtons(); if(ACTIVE_PANEL != panel) return; QString protocol = files()->currentDirectory().scheme(); krRemoteEncoding->setEnabled(protocol == "ftp" || protocol == "sftp" || protocol == "fish" || protocol == "krarc"); //krMultiRename->setEnabled( fileSystemType == FileSystem::FS_NORMAL ); // batch rename //krProperties ->setEnabled( fileSystemType == FileSystem::FS_NORMAL || fileSystemType == FileSystem::FS_FTP ); // file properties /* krUnpack->setEnabled(true); // unpack archive krTest->setEnabled(true); // test archive krSelect->setEnabled(true); // select a group by filter krSelectAll->setEnabled(true); // select all files krUnselect->setEnabled(true); // unselect by filter krUnselectAll->setEnabled( true); // remove all selections krInvert->setEnabled(true); // invert the selection krFTPConnect->setEnabled(true); // connect to an ftp krFTPNew->setEnabled(true); // create a new connection krAllFiles->setEnabled(true); // show all files in list krCustomFiles->setEnabled(true); // show a custom set of files krRoot->setEnabled(true); // go all the way up krExecFiles->setEnabled(true); // show only executables */ panel->_actions->setViewActions[panel->panelType]->setChecked(true); panel->_actions->actFTPDisconnect->setEnabled(files()->isRemote()); // allow disconnecting a network session panel->_actions->actCreateChecksum->setEnabled(files()->isLocal()); panel->_actions->actDirUp->setEnabled(!files()->isRoot()); panel->_actions->actRoot->setEnabled(!panel->virtualPath().matches(QUrl::fromLocalFile(ROOT_DIR), QUrl::StripTrailingSlash)); panel->_actions->actHome->setEnabled(!atHome()); panel->_actions->actHistoryBackward->setEnabled(history->canGoBack()); panel->_actions->actHistoryForward->setEnabled(history->canGoForward()); panel->view->op()->emitRefreshActions(); } FileSystem* ListPanelFunc::files() { if (!fileSystemP) fileSystemP = FileSystemProvider::instance().getFilesystem(QUrl::fromLocalFile(ROOT_DIR)); return fileSystemP; } QUrl ListPanelFunc::virtualDirectory() { return _isPaused ? history->currentUrl() : files()->currentDirectory(); } FileItem *ListPanelFunc::getFileItem(const QString &name) { return files()->getFileItem(name); } FileItem *ListPanelFunc::getFileItem(KrViewItem *item) { return files()->getFileItem(item->name()); } void ListPanelFunc::clipboardChanged(QClipboard::Mode mode) { if (mode == QClipboard::Clipboard && this == copyToClipboardOrigin) { disconnect(QApplication::clipboard(), nullptr, this, nullptr); copyToClipboardOrigin = nullptr; } } void ListPanelFunc::copyToClipboard(bool move) { const QStringList fileNames = panel->getSelectedNames(); if (fileNames.isEmpty()) return ; // safety QList fileUrls = files()->getUrls(fileNames); auto *mimeData = new QMimeData; mimeData->setData("application/x-kde-cutselection", move ? "1" : "0"); mimeData->setUrls(fileUrls); if (copyToClipboardOrigin) disconnect(QApplication::clipboard(), nullptr, copyToClipboardOrigin, nullptr); copyToClipboardOrigin = this; QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); connect(QApplication::clipboard(), &QClipboard::changed, this, &ListPanelFunc::clipboardChanged); } void ListPanelFunc::pasteFromClipboard() { QClipboard * cb = QApplication::clipboard(); ListPanelFunc *origin = nullptr; if (copyToClipboardOrigin) { disconnect(QApplication::clipboard(), nullptr, copyToClipboardOrigin, nullptr); origin = copyToClipboardOrigin; copyToClipboardOrigin = nullptr; } bool move = false; const QMimeData *data = cb->mimeData(); if (data->hasFormat("application/x-kde-cutselection")) { QByteArray a = data->data("application/x-kde-cutselection"); if (!a.isEmpty()) move = (a.at(0) == '1'); // true if 1 } QList urls = data->urls(); if (urls.isEmpty()) return; if(origin && KConfigGroup(krConfig, "Look&Feel").readEntry("UnselectBeforeOperation", _UnselectBeforeOperation)) { origin->panel->view->saveSelection(); for(KrViewItem *item = origin->panel->view->getFirst(); item != nullptr; item = origin->panel->view->getNext(item)) { if (urls.contains(item->getFileItem()->getUrl())) item->setSelected(false); } } files()->addFiles(urls, move ? KIO::CopyJob::Move : KIO::CopyJob::Copy); } ListPanelFunc* ListPanelFunc::otherFunc() { return panel->otherPanel()->func; } void ListPanelFunc::historyGotoPos(int pos) { if(history->gotoPos(pos)) refresh(); } void ListPanelFunc::historyBackward() { if(history->goBack()) refresh(); } void ListPanelFunc::historyForward() { if(history->goForward()) refresh(); } void ListPanelFunc::dirUp() { openUrl(KIO::upUrl(files()->currentDirectory()), files()->currentDirectory().fileName()); } void ListPanelFunc::home() { openUrl(QUrl::fromLocalFile(QDir::homePath())); } void ListPanelFunc::root() { openUrl(QUrl::fromLocalFile(ROOT_DIR)); } void ListPanelFunc::cdToOtherPanel() { openUrl(panel->otherPanel()->virtualPath()); } void ListPanelFunc::syncOtherPanel() { otherFunc()->openUrl(panel->virtualPath()); } bool ListPanelFunc::atHome() { return QUrl::fromLocalFile(QDir::homePath()).matches(panel->virtualPath(), QUrl::StripTrailingSlash); } diff --git a/krusader/krglobal.cpp b/krusader/krglobal.cpp index e7a40b4e..f71d737e 100644 --- a/krusader/krglobal.cpp +++ b/krusader/krglobal.cpp @@ -1,62 +1,62 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * Krusader is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krglobal.h" #include "krusader.h" #include "krusaderview.h" // QtCore #include #include KConfig *KrGlobal::config = nullptr; KMountMan *KrGlobal::mountMan = nullptr; KrBookmarkHandler *KrGlobal::bookman = nullptr; -KRslots* KrGlobal::slot = nullptr; +KRslots *KrGlobal::slot = nullptr; KrusaderView *KrGlobal::mainView = nullptr; QWidget *KrGlobal::mainWindow = nullptr; UserAction *KrGlobal::userAction = nullptr; JobMan *KrGlobal::jobMan = nullptr; // ListPanel *KrGlobal::activePanel = 0; QKeySequence KrGlobal::copyShortcut; const int KrGlobal::sConfigVersion; int KrGlobal::sCurrentConfigVersion; KrPanel *KrGlobal::activePanel() { return mainView->activePanel(); } // void KrGlobal::enableAction(const char *name, bool enable) // { // getAction(name)->setEnabled(enable); // } // // QAction* KrGlobal::getAction(const char *name) // { // QAction *act = krApp->actionCollection()->action(name); // if(!act) // qFatal("no such action: %s", name); // return act; // } diff --git a/krusader/krslots.cpp b/krusader/krslots.cpp index 2f4b97d5..b29d652b 100644 --- a/krusader/krslots.cpp +++ b/krusader/krslots.cpp @@ -1,748 +1,750 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * Krusader is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krslots.h" // QtCore #include #include #include #include #include #include // QtGui #include #include // QtWidgets #include #include #include #include #include #include #include #ifdef __KJSEMBED__ #include #include "KrJS/krjs.h" #endif #include "defaults.h" #include "icon.h" #include "kractions.h" #include "krservices.h" #include "krtrashhandler.h" #include "krusader.h" #include "krusaderview.h" #include "panelmanager.h" #include "ActionMan/actionman.h" #include "BookMan/krbookmarkbutton.h" #include "BookMan/krbookmarkhandler.h" #include "Dialogs/krdialogs.h" #include "Dialogs/krspecialwidgets.h" #include "Dialogs/krspwidgets.h" #include "DiskUsage/diskusagegui.h" #include "FileSystem/fileitem.h" #include "FileSystem/filesystem.h" #include "FileSystem/krquery.h" #include "GUI/dirhistorybutton.h" #include "GUI/kcmdline.h" #include "GUI/kfnkeys.h" #include "GUI/krusaderstatus.h" #include "GUI/mediabutton.h" #include "GUI/terminaldock.h" #include "KViewer/krviewer.h" #include "Konfigurator/konfigurator.h" #include "Locate/locate.h" #include "MountMan/kmountman.h" #include "Panel/PanelView/krselectionmode.h" #include "Panel/PanelView/krview.h" #include "Panel/PanelView/krviewfactory.h" #include "Panel/PanelView/krviewitem.h" #include "Panel/listpanel.h" #include "Panel/panelfunc.h" #include "Panel/sidebar.h" #include "Search/krsearchdialog.h" #include "Search/krsearchmod.h" #include "Splitter/combiner.h" #include "Splitter/splitter.h" #include "Splitter/splittergui.h" #ifdef SYNCHRONIZER_ENABLED #include "Synchronizer/synchronizergui.h" #endif #define ACTIVE_VIEW _mainWindow->activeView() KRslots::KRslots(QObject *parent) : QObject(parent), _mainWindow(krApp) { } void KRslots::sendFileByEmail(const QList &urls) { if (urls.count() == 0) { KMessageBox::error(nullptr, i18n("No selected files to send.")); return; } QString mailProg; QStringList lst = KrServices::supportedTools(); if (lst.contains("MAIL")) mailProg = lst[lst.indexOf("MAIL") + 1]; else { KMessageBox::error(nullptr, i18n("Krusader cannot find a supported mail client. Please install one to your path. Hint: Krusader supports KMail.")); return; } QString subject, separator; foreach(const QUrl &url, urls) { subject += separator + url.fileName(); separator = ','; } subject = i18np("Sending file: %2", "Sending files: %2", urls.count(), subject); KProcess proc; QString executable = QUrl::fromLocalFile(mailProg).fileName(); if (executable == QStringLiteral("kmail")) { proc << mailProg << "--subject" << subject; foreach(const QUrl &url2, urls) proc << "--attach" << url2.toDisplayString(); } else if (executable == QStringLiteral("thunderbird")) { QString param = "attachment=\'"; separator = ""; foreach(const QUrl &url2, urls) { param += separator + url2.toDisplayString(); separator = ','; } param += "\',subject=\'" + subject + "\'"; proc << mailProg << "--compose" << param; } else if (executable == QStringLiteral("evolution")) { QString param = "mailto:?cc=&subject=" + subject + "&attach="; separator = ""; foreach(const QUrl &url2, urls) { param += separator + url2.toDisplayString(); separator = "&attach="; } proc << mailProg << param + ""; } if (!proc.startDetached()) KMessageBox::error(nullptr, i18n("Error executing %1.", mailProg)); } void KRslots::compareContent() { const QStringList lstLeft = LEFT_PANEL->getSelectedNames(); const QStringList lstRight = RIGHT_PANEL->getSelectedNames(); const QStringList lstActive = ACTIVE_PANEL->gui->isLeft() ? lstLeft : lstRight; QUrl name1, name2; if (lstLeft.count() == 1 && lstRight.count() == 1) { // first, see if we've got exactly 1 selected file in each panel: name1 = LEFT_PANEL->func->files()->getUrl(lstLeft[0]); name2 = RIGHT_PANEL->func->files()->getUrl(lstRight[0]); } else if (lstActive.count() == 2) { // next try: are in the current panel exactly 2 files selected? name1 = ACTIVE_PANEL->func->files()->getUrl(lstActive[0]); name2 = ACTIVE_PANEL->func->files()->getUrl(lstActive[1]); } else if (ACTIVE_PANEL->otherPanel()->func->files()->getFileItem(ACTIVE_VIEW->getCurrentItem())) { // next try: is in the other panel a file with the same name? name1 = ACTIVE_PANEL->func->files()->getUrl(ACTIVE_VIEW->getCurrentItem()); name2 = ACTIVE_PANEL->otherPanel()->func->files()->getUrl(ACTIVE_VIEW->getCurrentItem()); } else { // if we got here, then we can't be sure what file to diff KMessageBox::sorry(nullptr, "" + i18n("Do not know which files to compare.") + "

" + i18n("To compare two files by content, you can either:
  • Select one file in the left panel, and one in the right panel.
  • Select exactly two files in the active panel.
  • Make sure there is a file in the other panel, with the same name as the current file in the active panel.
") + "
"); return; } // else implied: all ok, let's call an external program to compare files // but if any of the files isn't local, download it first compareContent(name1, name2); } bool downloadToTemp(const QUrl &url, QString &dest) { QTemporaryFile tmpFile; tmpFile.setAutoRemove(false); if (tmpFile.open()) { dest = tmpFile.fileName(); KIO::Job* job = KIO::file_copy(url, QUrl::fromLocalFile(dest), -1, KIO::Overwrite | KIO::HideProgressInfo); if(!job->exec()) { KMessageBox::error(krApp, i18n("Krusader is unable to download %1", url.fileName())); return false; } return true; } return false; } void KRslots::compareContent(const QUrl& url1, const QUrl& url2) { QString diffProg; QStringList lst = KrServices::supportedTools(); if (lst.contains("DIFF")) diffProg = lst[lst.indexOf("DIFF") + 1]; else { KMessageBox::error(nullptr, i18n("Krusader cannot find any of the supported diff-frontends. Please install one to your path. Hint: Krusader supports Kompare, KDiff3 and Xxdiff.")); return; } QString tmp1; QString tmp2; // kdiff3 sucks with spaces if (QUrl::fromLocalFile(diffProg).fileName() == "kdiff3" && !url1.toDisplayString().contains(" ") && !url2.toDisplayString().contains(" ")) { tmp1 = url1.toDisplayString(); tmp2 = url2.toDisplayString(); } else { if (!url1.isLocalFile()) { if (!downloadToTemp(url1, tmp1)) { return; } } else tmp1 = url1.path(); if (!url2.isLocalFile()) { if (!downloadToTemp(url2, tmp2)) { - if (tmp1 != url1.path()) + if (tmp1 != url1.path()) { QFile::remove(tmp1); + } return; } } else tmp2 = url2.path(); } KrProcess *p = new KrProcess(tmp1 != url1.path() ? tmp1 : QString(), tmp2 != url2.path() ? tmp2 : QString()); *p << diffProg << tmp1 << tmp2; p->start(); - if (!p->waitForStarted()) + if (!p->waitForStarted()) { KMessageBox::error(nullptr, i18n("Error executing %1.", diffProg)); + } } // GUI toggle slots void KRslots::toggleFnkeys() { if (MAIN_VIEW->fnKeys()->isVisible()) MAIN_VIEW->fnKeys()->hide(); else MAIN_VIEW->fnKeys()->show(); } void KRslots::toggleCmdline() { if (MAIN_VIEW->cmdLine()->isVisible()) MAIN_VIEW->cmdLine()->hide(); else MAIN_VIEW->cmdLine()->show(); } void KRslots::updateStatusbarVisibility() { krApp->statusBar()->setVisible(KrActions::actShowStatusBar->isChecked()); } void KRslots::toggleTerminal() { MAIN_VIEW->setTerminalEmulator(KrActions::actToggleTerminal->isChecked()); } void KRslots::insertFileName(bool fullPath) { QString filename = ACTIVE_VIEW->getCurrentItem(); if (filename.isEmpty()) { return; } if (fullPath) { const QString path = FileSystem::ensureTrailingSlash(ACTIVE_PANEL->virtualPath()) .toDisplayString(QUrl::PreferLocalFile); filename = path + filename; } filename = KrServices::quote(filename); if (MAIN_VIEW->cmdLine()->isVisible() || !MAIN_VIEW->terminalDock()->isTerminalVisible()) { QString current = MAIN_VIEW->cmdLine()->text(); if (current.length() != 0 && !current.endsWith(' ')) current += ' '; MAIN_VIEW->cmdLine()->setText(current + filename); MAIN_VIEW->cmdLine()->setFocus(); } else if (MAIN_VIEW->terminalDock()->isTerminalVisible()) { filename = ' ' + filename + ' '; MAIN_VIEW->terminalDock()->sendInput(filename, false); MAIN_VIEW->terminalDock()->setFocus(); } } void KRslots::refresh(const QUrl &u) { ACTIVE_FUNC->openUrl(u); } void KRslots::runKonfigurator(bool firstTime) { auto *konfigurator = new Konfigurator(firstTime); connect(konfigurator, &Konfigurator::configChanged, this, &KRslots::configChanged); //FIXME - no need to exec konfigurator->exec(); delete konfigurator; } void KRslots::configChanged(bool isGUIRestartNeeded) { krConfig->sync(); if (isGUIRestartNeeded) { krApp->setUpdatesEnabled(false); KConfigGroup group(krConfig, "Look&Feel"); FileItem::loadUserDefinedFolderIcons(group.readEntry("Load User Defined Folder Icons", _UserDefinedFolderIcons)); bool leftActive = ACTIVE_PANEL->gui->isLeft(); MAIN_VIEW->leftManager()->slotRecreatePanels(); MAIN_VIEW->rightManager()->slotRecreatePanels(); if(leftActive) LEFT_PANEL->slotFocusOnMe(); else RIGHT_PANEL->slotFocusOnMe(); MAIN_VIEW->fnKeys()->updateShortcuts(); KrSelectionMode::resetSelectionHandler(); krApp->setUpdatesEnabled(true); } krApp->setTray(); // really ugly, but reload the Fn keys just in case - csaba: any better idea? MAIN_VIEW->fnKeys()->updateShortcuts(); const bool showHidden = KConfigGroup(krConfig, "Look&Feel") .readEntry("Show Hidden", KrActions::actToggleHidden->isChecked()); if (showHidden != KrActions::actToggleHidden->isChecked()) { KrActions::actToggleHidden->setChecked(showHidden); MAIN_VIEW->leftManager()->reloadConfig(); MAIN_VIEW->rightManager()->reloadConfig(); } } void KRslots::showHiddenFiles(bool show) { KConfigGroup group(krConfig, "Look&Feel"); group.writeEntry("Show Hidden", show); MAIN_VIEW->leftManager()->reloadConfig(); MAIN_VIEW->rightManager()->reloadConfig(); } void KRslots::swapPanels() { QUrl leftURL = LEFT_PANEL->virtualPath(); QUrl rightURL = RIGHT_PANEL->virtualPath(); LEFT_PANEL->func->openUrl(rightURL); RIGHT_PANEL->func->openUrl(leftURL); } void KRslots::toggleSwapSides() { MAIN_VIEW->swapSides(); } void KRslots::search() { if (KrSearchDialog::SearchDialog != nullptr) { KConfigGroup group(krConfig, "Search"); if (group.readEntry("Window Maximized", false)) KrSearchDialog::SearchDialog->showMaximized(); else KrSearchDialog::SearchDialog->showNormal(); KrSearchDialog::SearchDialog->raise(); KrSearchDialog::SearchDialog->activateWindow(); } else KrSearchDialog::SearchDialog = new KrSearchDialog(); } void KRslots::locate() { if (!KrServices::cmdExist("locate")) { KMessageBox::error(krApp, i18n("Cannot find the 'locate' command. Please install the " "findutils-locate package of GNU, or set its dependencies in " "Konfigurator")); return; } if (LocateDlg::LocateDialog != nullptr) { LocateDlg::LocateDialog->showNormal(); LocateDlg::LocateDialog->raise(); LocateDlg::LocateDialog->activateWindow(); LocateDlg::LocateDialog->reset(); } else LocateDlg::LocateDialog = new LocateDlg(krApp); } void KRslots::runTerminal(const QString & dir) { KProcess proc; proc.setWorkingDirectory(dir); KConfigGroup group(krConfig, "General"); QString term = group.readEntry("Terminal", _Terminal); QStringList sepdArgs = KShell::splitArgs(term, KShell::TildeExpand); if (sepdArgs.isEmpty()) { KMessageBox::error(krMainWindow, i18nc("Arg is a string containing the bad quoting.", "Bad quoting in terminal command:\n%1", term)); return; } for (int i = 0; i < sepdArgs.size(); i++) { if (sepdArgs[i] == "%d") { sepdArgs[i] = dir; } } proc << sepdArgs; if (!proc.startDetached()) KMessageBox::sorry(krApp, i18n("Error executing %1.", term)); } void KRslots::homeTerminal() { runTerminal(QDir::homePath()); } void KRslots::multiRename() { QStringList lst = KrServices::supportedTools(); int i = lst.indexOf("RENAME"); if (i == -1) { KMessageBox::sorry(krApp, i18n("Cannot find a batch rename tool.\nYou can get KRename at %1", QLatin1String("https://www.kde.org/applications/utilities/krename/"))); return; } QString pathToRename = lst[i+1]; const QStringList names = ACTIVE_PANEL->gui->getSelectedNames(); if (names.isEmpty()) { return; } KProcess proc; proc << pathToRename; for (const QString& name: names) { FileItem *file = ACTIVE_FUNC->files()->getFileItem(name); if (!file) continue; const QUrl url = file->getUrl(); // KRename only supports the recursive option combined with a local directory path if (file->isDir() && url.scheme() == "file") { proc << "-r" << url.path(); } else { proc << url.toString(); } } if (!proc.startDetached()) KMessageBox::error(nullptr, i18n("Error executing '%1'.", proc.program().join(" "))); } void KRslots::rootKrusader() { if (KMessageBox::warningContinueCancel( krApp, i18n("Improper operations in root mode can damage your operating system. " "

Furthermore, running UI applications as root is insecure and can " "allow attackers to gain root access."), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "Confirm Root Mode", KMessageBox::Notify | KMessageBox::Dangerous) != KMessageBox::Continue) return; if (!KrServices::isExecutable(KDESU_PATH)) { KMessageBox::sorry(krApp, i18n("Cannot start root mode Krusader, %1 not found or not executable. " "Please verify that kde-cli-tools are installed.", QString(KDESU_PATH))); return; } KProcess proc; proc << KDESU_PATH << "-c" << QApplication::instance()->applicationFilePath() + " --left=" + KrServices::quote(LEFT_PANEL->virtualPath().toDisplayString(QUrl::PreferLocalFile)) + " --right=" + KrServices::quote(RIGHT_PANEL->virtualPath().toDisplayString(QUrl::PreferLocalFile)); if (!proc.startDetached()) KMessageBox::error(nullptr, i18n("Error executing %1.", proc.program()[0])); } void KRslots::slotSplit() { const QStringList list = ACTIVE_PANEL->gui->getSelectedNames(); QString name; // first, see if we've got exactly 1 selected file, if not, try the current one if (list.count() == 1) name = list[0]; if (name.isEmpty()) { // if we got here, then one of the panel can't be sure what file to diff KMessageBox::error(nullptr, i18n("Do not know which file to split.")); return; } QUrl fileURL = ACTIVE_FUNC->files()->getUrl(name); if (fileURL.isEmpty()) return; if (ACTIVE_FUNC->files()->getFileItem(name)->isDir()) { KMessageBox::sorry(krApp, i18n("You cannot split a folder.")); return; } const QUrl destDir = ACTIVE_PANEL->otherPanel()->virtualPath(); SplitterGUI splitterGUI(MAIN_VIEW, fileURL, destDir); if (splitterGUI.exec() == QDialog::Accepted) { bool splitToOtherPanel = splitterGUI.getDestinationDir().matches(ACTIVE_PANEL->otherPanel()->virtualPath(), QUrl::StripTrailingSlash); Splitter split(MAIN_VIEW, fileURL, splitterGUI.getDestinationDir(), splitterGUI.overWriteFiles()); split.split(splitterGUI.getSplitSize()); if (splitToOtherPanel) ACTIVE_PANEL->otherPanel()->func->refresh(); } } void KRslots::slotCombine() { const QStringList list = ACTIVE_PANEL->gui->getSelectedNames(); if (list.isEmpty()) { KMessageBox::error(nullptr, i18n("Do not know which files to combine.")); return; } QUrl baseURL; bool unixStyle = false; bool windowsStyle = false; QString commonName; int commonLength = 0; /* checking splitter names */ for (const auto & it : list) { QUrl url = ACTIVE_FUNC->files()->getUrl(it); if (url.isEmpty()) return; if (ACTIVE_FUNC->files()->getFileItem(it)->isDir()) { KMessageBox::sorry(krApp, i18n("You cannot combine a folder.")); return; } if (!unixStyle) { QString name = url.fileName(); int extPos = name.lastIndexOf('.'); QString ext = name.mid(extPos + 1); name.truncate(extPos); url = url.adjusted(QUrl::RemoveFilename); url.setPath(url.path() + name); bool isExtInt; ext.toInt(&isExtInt, 10); if (extPos < 1 || ext.isEmpty() || (ext != "crc" && !isExtInt)) { if (windowsStyle) { KMessageBox::error(nullptr, i18n("Not a split file: %1.", url.toDisplayString(QUrl::PreferLocalFile))); return; } unixStyle = true; } else { if (ext != "crc") windowsStyle = true; if (baseURL.isEmpty()) baseURL = url; else if (baseURL != url) { KMessageBox::error(nullptr, i18n("Select only one split file.")); return; } } } if (unixStyle) { bool error = true; do { const QString& shortName = it; QChar lastChar = shortName.at(shortName.length() - 1); if (lastChar.isLetter()) { char fillLetter = (lastChar.toUpper() == lastChar) ? 'A' : 'a'; if (commonName.isNull()) { commonLength = shortName.length(); commonName = shortName; while (commonName.length()) { QString shorter = commonName.left(commonName.length() - 1); QString testFile = shorter.leftJustified(commonLength, fillLetter); if (ACTIVE_FUNC->files()->getFileItem(testFile) == nullptr) break; else { commonName = shorter; baseURL = ACTIVE_PANEL->virtualPath().adjusted(QUrl::StripTrailingSlash); baseURL.setPath(baseURL.path() + '/' + (testFile)); } } error = (commonName == shortName); } else if (commonLength == shortName.length() && shortName.startsWith(commonName)) error = false; } } while (false); if (error) { KMessageBox::error(nullptr, i18n("Not a split file: %1.", url.toDisplayString(QUrl::PreferLocalFile))); return; } } } // ask the user for the copy dest QUrl dest = KChooseDir::getDir(i18n("Combining %1.* to folder:", baseURL.toDisplayString(QUrl::PreferLocalFile)), ACTIVE_PANEL->otherPanel()->virtualPath(), ACTIVE_PANEL->virtualPath()); if (dest.isEmpty()) return ; // the user canceled bool combineToOtherPanel = (dest.matches(ACTIVE_PANEL->otherPanel()->virtualPath(), QUrl::StripTrailingSlash)); Combiner combine(MAIN_VIEW, baseURL, dest, unixStyle); combine.combine(); if (combineToOtherPanel) ACTIVE_PANEL->otherPanel()->func->refresh(); } void KRslots::manageUseractions() { ActionMan actionMan(MAIN_VIEW); } #ifdef SYNCHRONIZER_ENABLED void KRslots::slotSynchronizeDirs(QStringList selected) { SynchronizerGUI *synchronizerDialog = new SynchronizerGUI(MAIN_VIEW, LEFT_PANEL->virtualPath(), RIGHT_PANEL->virtualPath(), std::move(selected)); synchronizerDialog->show(); // destroyed on close } #endif void KRslots::compareSetup() { for (int i = 0; KrActions::compareArray[i] != nullptr; i++) if ((*KrActions::compareArray[i])->isChecked()) { KConfigGroup group(krConfig, "Private"); group.writeEntry("Compare Mode", i); break; } } /** called by actions actExec* to choose the built-in command line mode */ void KRslots::execTypeSetup() { for (int i = 0; KrActions::execTypeArray[i] != nullptr; i++) if ((*KrActions::execTypeArray[i])->isChecked()) { if (*KrActions::execTypeArray[i] == KrActions::actExecTerminalEmbedded) { // if commands are to be executed in the TE, it must be loaded MAIN_VIEW->terminalDock()->initialise(); } KConfigGroup grp(krConfig, "Private"); grp.writeEntry("Command Execution Mode", i); break; } } void KRslots::slotDiskUsage() { DiskUsageGUI *diskUsageDialog = new DiskUsageGUI(ACTIVE_PANEL->virtualPath()); diskUsageDialog->askDirAndShow(); } void KRslots::applicationStateChanged() { if (MAIN_VIEW == nullptr) { /* CRASH FIX: it's possible that the method is called after destroying the main view */ return; } if(qApp->applicationState() == Qt::ApplicationActive || qApp->applicationState() == Qt::ApplicationInactive) { LEFT_PANEL->panelVisible(); RIGHT_PANEL->panelVisible(); } else { LEFT_PANEL->panelHidden(); RIGHT_PANEL->panelHidden(); } } void KRslots::emptyTrash() { KrTrashHandler::emptyTrash(); } #define OPEN_ID 100001 #define EMPTY_TRASH_ID 100002 void KRslots::trashPopupMenu() { QMenu trashMenu(krApp); QAction * act = trashMenu.addAction(Icon("document-open"), i18n("Open trash bin")); act->setData(QVariant(OPEN_ID)); act = trashMenu.addAction(Icon("trash-empty"), i18n("Empty trash bin")); act->setData(QVariant(EMPTY_TRASH_ID)); int result = -1; QAction *res = trashMenu.exec(QCursor::pos()); if (res && res->data().canConvert ()) result = res->data().toInt(); if (result == OPEN_ID) { ACTIVE_FUNC->openUrl(QUrl(QStringLiteral("trash:/"))); } else if (result == EMPTY_TRASH_ID) { KrTrashHandler::emptyTrash(); } } //shows the JavaScript-Console void KRslots::jsConsole() { #ifdef __KJSEMBED__ if (! krJS) krJS = new KrJS(); krJS->view()->show(); #endif } void KRslots::addBookmark() { krBookMan->bookmarkCurrent(ACTIVE_PANEL->virtualPath()); } void KRslots::cmdlinePopup() { MAIN_VIEW->cmdLine()->popup(); }